The OpenD Programming Language

1 /**
2  * The demangle module converts mangled D symbols to a representation similar
3  * to what would have existed in code.
4  *
5  * Copyright: Copyright Sean Kelly 2010 - 2014.
6  * License: Distributed under the
7  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
8  *    (See accompanying file LICENSE)
9  * Authors:   Sean Kelly
10  * Source:    $(DRUNTIMESRC core/_demangle.d)
11  */
12 
13 module core.demangle;
14 
15 version (OSX)
16     version = Darwin;
17 else version (iOS)
18     version = Darwin;
19 else version (TVOS)
20     version = Darwin;
21 else version (WatchOS)
22     version = Darwin;
23 
24 debug(trace) import core.stdc.stdio : printf;
25 debug(info) import core.stdc.stdio : printf;
26 
27 extern (C) alias CXX_DEMANGLER = char* function (const char* mangled_name,
28                                                 char* output_buffer,
29                                                 size_t* length,
30                                                 int* status) nothrow pure @trusted;
31 
32 private struct NoHooks
33 {
34     // supported hooks
35     // static bool parseLName(ref Demangle);
36     // static char[] parseType(ref Demangle, char[])
37 }
38 
39 private struct Demangle(Hooks = NoHooks)
40 {
41     // NOTE: This implementation currently only works with mangled function
42     //       names as they exist in an object file.  Type names mangled via
43     //       the .mangleof property are effectively incomplete as far as the
44     //       ABI is concerned and so are not considered to be mangled symbol
45     //       names.
46 
47     // NOTE: This implementation builds the demangled buffer in place by
48     //       writing data as it is decoded and then rearranging it later as
49     //       needed.  In practice this results in very little data movement,
50     //       and the performance cost is more than offset by the gain from
51     //       not allocating dynamic memory to assemble the name piecemeal.
52     //
53     //       If the destination buffer is too small, parsing will restart
54     //       with a larger buffer.  Since this generally means only one
55     //       allocation during the course of a parsing run, this is still
56     //       faster than assembling the result piecemeal.
57 
58 pure @safe:
59 
60     bool forBacktrace = false;
61 
62     enum AddType { no, yes }
63 
64 
65     this( return scope const(char)[] buf_, return scope char[] dst_ = null )
66     {
67         this( buf_, AddType.yes, dst_ );
68     }
69 
70 
71     this( return scope const(char)[] buf_, AddType addType_, return scope char[] dst_ = null )
72     {
73         buf     = buf_;
74         addType = addType_;
75         dst.dst = dst_;
76     }
77 
78     const(char)[]   buf     = null;
79     Buffer          dst;
80     size_t          pos     = 0;
81     size_t          brp     = 0; // current back reference pos
82     AddType         addType = AddType.yes;
83     bool            mute    = false;
84     Hooks           hooks;
85 
86     //////////////////////////////////////////////////////////////////////////
87     // Type Testing and Conversion
88     //////////////////////////////////////////////////////////////////////////
89 
90 
91     static bool isAlpha( char val )
92     {
93         return ('a' <= val && 'z' >= val) ||
94                ('A' <= val && 'Z' >= val) ||
95                (0x80 & val); // treat all unicode as alphabetic
96     }
97 
98 
99     static bool isDigit( char val )
100     {
101         return '0' <= val && '9' >= val;
102     }
103 
104 
105     static bool isHexDigit( char val )
106     {
107         return ('0' <= val && '9' >= val) ||
108                ('a' <= val && 'f' >= val) ||
109                ('A' <= val && 'F' >= val);
110     }
111 
112 
113     static ubyte ascii2hex( char val )
114     {
115         if (val >= 'a' && val <= 'f')
116             return cast(ubyte)(val - 'a' + 10);
117         if (val >= 'A' && val <= 'F')
118             return cast(ubyte)(val - 'A' + 10);
119         if (val >= '0' && val <= '9')
120             return cast(ubyte)(val - '0');
121         error();
122     }
123 
124     char[] shift(scope const(char)[] val) return scope
125     {
126         if (mute)
127             return null;
128         return dst.shift(val);
129     }
130 
131     void putComma(size_t n)
132     {
133         version (DigitalMars) pragma(inline, false);
134         if (n)
135             put(", ");
136     }
137 
138     void put(char c) return scope
139     {
140         char[1] val = c;
141         put(val[]);
142     }
143 
144     void put(scope const(char)[] val) return scope
145     {
146         if (mute)
147             return;
148         dst.append(val);
149     }
150 
151 
152     void putAsHex( size_t val, int width = 0 )
153     {
154         import core.internal.string;
155 
156         UnsignedStringBuf buf = void;
157 
158         auto s = unsignedToTempString!16(val, buf);
159         int slen = cast(int)s.length;
160         if (slen < width)
161         {
162             foreach (i; slen .. width)
163                 put('0');
164         }
165         put(s);
166     }
167 
168 
169     void pad( const(char)[] val )
170     {
171         if ( val.length )
172         {
173             put(" ");
174             put( val );
175         }
176     }
177 
178 
179     void silent( void delegate() pure @safe dg )
180     {
181         debug(trace) printf( "silent+\n" );
182         debug(trace) scope(success) printf( "silent-\n" );
183         auto n = dst.length; dg(); dst.len = n;
184     }
185 
186 
187     //////////////////////////////////////////////////////////////////////////
188     // Parsing Utility
189     //////////////////////////////////////////////////////////////////////////
190 
191     @property bool empty()
192     {
193         return pos >= buf.length;
194     }
195 
196     @property char front()
197     {
198         if ( pos < buf.length )
199             return buf[pos];
200         return char.init;
201     }
202 
203     char peek( size_t n )
204     {
205         if ( pos + n < buf.length )
206             return buf[pos + n];
207         return char.init;
208     }
209 
210 
211     void test( char val )
212     {
213         if ( val != front )
214             error();
215     }
216 
217 
218     void popFront()
219     {
220         if ( pos++ >= buf.length )
221             error();
222     }
223 
224 
225     void popFront(int i)
226     {
227         while (i--)
228             popFront();
229     }
230 
231 
232     void match( char val )
233     {
234         test( val );
235         popFront();
236     }
237 
238 
239     void match( const(char)[] val )
240     {
241         foreach (char e; val )
242         {
243             test( e );
244             popFront();
245         }
246     }
247 
248 
249     void eat( char val )
250     {
251         if ( val == front )
252             popFront();
253     }
254 
255     bool isSymbolNameFront()
256     {
257         char val = front;
258         if ( isDigit( val ) || val == '_' )
259             return true;
260         if ( val != 'Q' )
261             return false;
262 
263         // check the back reference encoding after 'Q'
264         val = peekBackref();
265         return isDigit( val ); // identifier ref
266     }
267 
268     // return the first character at the back reference
269     char peekBackref()
270     {
271         assert( front == 'Q' );
272         auto n = decodeBackref!1();
273         if (!n || n > pos)
274             error("invalid back reference");
275 
276         return buf[pos - n];
277     }
278 
279     size_t decodeBackref(size_t peekAt = 0)()
280     {
281         enum base = 26;
282         size_t n = 0;
283         for (size_t p; ; p++)
284         {
285             char t;
286             static if (peekAt > 0)
287             {
288                 t = peek(peekAt + p);
289             }
290             else
291             {
292                 t = front;
293                 popFront();
294             }
295             if (t < 'A' || t > 'Z')
296             {
297                 if (t < 'a' || t > 'z')
298                     error("invalid back reference");
299                 n = base * n + t - 'a';
300                 return n;
301             }
302             n = base * n + t - 'A';
303         }
304     }
305 
306     //////////////////////////////////////////////////////////////////////////
307     // Parsing Implementation
308     //////////////////////////////////////////////////////////////////////////
309 
310 
311     /*
312     Number:
313         Digit
314         Digit Number
315     */
316     const(char)[] sliceNumber() return scope
317     {
318         debug(trace) printf( "sliceNumber+\n" );
319         debug(trace) scope(success) printf( "sliceNumber-\n" );
320 
321         auto beg = pos;
322 
323         while ( true )
324         {
325             auto t = front;
326             if (t >= '0' && t <= '9')
327                 popFront();
328             else
329                 return buf[beg .. pos];
330         }
331     }
332 
333 
334     size_t decodeNumber() scope
335     {
336         debug(trace) printf( "decodeNumber+\n" );
337         debug(trace) scope(success) printf( "decodeNumber-\n" );
338 
339         return decodeNumber( sliceNumber() );
340     }
341 
342 
343     size_t decodeNumber( scope const(char)[] num ) scope
344     {
345         debug(trace) printf( "decodeNumber+\n" );
346         debug(trace) scope(success) printf( "decodeNumber-\n" );
347 
348         size_t val = 0;
349 
350         foreach ( c; num )
351         {
352             import core.checkedint : mulu, addu;
353 
354             bool overflow = false;
355             val = mulu(val, 10, overflow);
356             val = addu(val, c - '0',  overflow);
357             if (overflow)
358                 error();
359         }
360         return val;
361     }
362 
363 
364     void parseReal() scope
365     {
366         debug(trace) printf( "parseReal+\n" );
367         debug(trace) scope(success) printf( "parseReal-\n" );
368 
369         char[64] tbuf = void;
370         size_t   tlen = 0;
371         real     val  = void;
372 
373         if ( 'I' == front )
374         {
375             match( "INF" );
376             put( "real.infinity" );
377             return;
378         }
379         if ( 'N' == front )
380         {
381             popFront();
382             if ( 'I' == front )
383             {
384                 match( "INF" );
385                 put( "-real.infinity" );
386                 return;
387             }
388             if ( 'A' == front )
389             {
390                 match( "AN" );
391                 put( "real.nan" );
392                 return;
393             }
394             tbuf[tlen++] = '-';
395         }
396 
397         tbuf[tlen++] = '0';
398         tbuf[tlen++] = 'X';
399         if ( !isHexDigit( front ) )
400             error( "Expected hex digit" );
401         tbuf[tlen++] = front;
402         tbuf[tlen++] = '.';
403         popFront();
404 
405         while ( isHexDigit( front ) )
406         {
407             tbuf[tlen++] = front;
408             popFront();
409         }
410         match( 'P' );
411         tbuf[tlen++] = 'p';
412         if ( 'N' == front )
413         {
414             tbuf[tlen++] = '-';
415             popFront();
416         }
417         else
418         {
419             tbuf[tlen++] = '+';
420         }
421         while ( isDigit( front ) )
422         {
423             tbuf[tlen++] = front;
424             popFront();
425         }
426 
427         tbuf[tlen] = 0;
428         debug(info) printf( "got (%s)\n", tbuf.ptr );
429         pureReprintReal( tbuf[] );
430         debug(info) printf( "converted (%.*s)\n", cast(int) tlen, tbuf.ptr );
431         put( tbuf[0 .. tlen] );
432     }
433 
434 
435     /*
436     LName:
437         Number Name
438 
439     Name:
440         Namestart
441         Namestart Namechars
442 
443     Namestart:
444         _
445         Alpha
446 
447     Namechar:
448         Namestart
449         Digit
450 
451     Namechars:
452         Namechar
453         Namechar Namechars
454     */
455     void parseLName() scope
456     {
457         debug(trace) printf( "parseLName+\n" );
458         debug(trace) scope(success) printf( "parseLName-\n" );
459 
460         static if (__traits(hasMember, Hooks, "parseLName"))
461             if (hooks.parseLName(this))
462                 return;
463 
464         if ( front == 'Q' )
465         {
466             // back reference to LName
467             auto refPos = pos;
468             popFront();
469             size_t n = decodeBackref();
470             if ( !n || n > refPos )
471                 error( "Invalid LName back reference" );
472             if ( !mute )
473             {
474                 auto savePos = pos;
475                 scope(exit) pos = savePos;
476                 pos = refPos - n;
477                 parseLName();
478             }
479             return;
480         }
481         auto n = decodeNumber();
482         if ( n == 0 )
483         {
484             put( "__anonymous" );
485             return;
486         }
487         if ( n > buf.length || n > buf.length - pos )
488             error( "LName must be at least 1 character" );
489         if ( '_' != front && !isAlpha( front ) )
490             error( "Invalid character in LName" );
491         foreach (char e; buf[pos + 1 .. pos + n] )
492         {
493             if ( '_' != e && !isAlpha( e ) && !isDigit( e ) )
494                 error( "Invalid character in LName" );
495         }
496 
497         put( buf[pos .. pos + n] );
498         pos += n;
499     }
500 
501 
502     /*
503     Type:
504         Shared
505         Const
506         Immutable
507         Wild
508         TypeArray
509         TypeVector
510         TypeStaticArray
511         TypeAssocArray
512         TypePointer
513         TypeFunction
514         TypeIdent
515         TypeClass
516         TypeStruct
517         TypeEnum
518         TypeTypedef
519         TypeDelegate
520         TypeNone
521         TypeVoid
522         TypeNoreturn
523         TypeByte
524         TypeUbyte
525         TypeShort
526         TypeUshort
527         TypeInt
528         TypeUint
529         TypeLong
530         TypeUlong
531         TypeCent
532         TypeUcent
533         TypeFloat
534         TypeDouble
535         TypeReal
536         TypeIfloat
537         TypeIdouble
538         TypeIreal
539         TypeCfloat
540         TypeCdouble
541         TypeCreal
542         TypeBool
543         TypeChar
544         TypeWchar
545         TypeDchar
546         TypeTuple
547 
548     Shared:
549         O Type
550 
551     Const:
552         x Type
553 
554     Immutable:
555         y Type
556 
557     Wild:
558         Ng Type
559 
560     TypeArray:
561         A Type
562 
563     TypeVector:
564         Nh Type
565 
566     TypeStaticArray:
567         G Number Type
568 
569     TypeAssocArray:
570         H Type Type
571 
572     TypePointer:
573         P Type
574 
575     TypeFunction:
576         CallConvention FuncAttrs Arguments ArgClose Type
577 
578     TypeIdent:
579         I LName
580 
581     TypeClass:
582         C LName
583 
584     TypeStruct:
585         S LName
586 
587     TypeEnum:
588         E LName
589 
590     TypeTypedef:
591         T LName
592 
593     TypeDelegate:
594         D TypeFunction
595 
596     TypeNone:
597         n
598 
599     TypeVoid:
600         v
601 
602     TypeNoreturn
603         Nn
604 
605     TypeByte:
606         g
607 
608     TypeUbyte:
609         h
610 
611     TypeShort:
612         s
613 
614     TypeUshort:
615         t
616 
617     TypeInt:
618         i
619 
620     TypeUint:
621         k
622 
623     TypeLong:
624         l
625 
626     TypeUlong:
627         m
628 
629     TypeCent
630         zi
631 
632     TypeUcent
633         zk
634 
635     TypeFloat:
636         f
637 
638     TypeDouble:
639         d
640 
641     TypeReal:
642         e
643 
644     TypeIfloat:
645         o
646 
647     TypeIdouble:
648         p
649 
650     TypeIreal:
651         j
652 
653     TypeCfloat:
654         q
655 
656     TypeCdouble:
657         r
658 
659     TypeCreal:
660         c
661 
662     TypeBool:
663         b
664 
665     TypeChar:
666         a
667 
668     TypeWchar:
669         u
670 
671     TypeDchar:
672         w
673 
674     TypeTuple:
675         B Number Arguments
676     */
677     char[] parseType() return scope
678     {
679         static immutable string[23] primitives = [
680             "char", // a
681             "bool", // b
682             "creal", // c
683             "double", // d
684             "real", // e
685             "float", // f
686             "byte", // g
687             "ubyte", // h
688             "int", // i
689             "ireal", // j
690             "uint", // k
691             "long", // l
692             "ulong", // m
693             null, // n
694             "ifloat", // o
695             "idouble", // p
696             "cfloat", // q
697             "cdouble", // r
698             "short", // s
699             "ushort", // t
700             "wchar", // u
701             "void", // v
702             "dchar", // w
703         ];
704 
705         static if (__traits(hasMember, Hooks, "parseType"))
706             if (auto n = hooks.parseType(this, null))
707                 return n;
708 
709         debug(trace) printf( "parseType+\n" );
710         debug(trace) scope(success) printf( "parseType-\n" );
711         auto beg = dst.length;
712         auto t = front;
713 
714         char[] parseBackrefType(scope char[] delegate() pure @safe parseDg) pure @safe
715         {
716             if (pos == brp)
717                 error("recursive back reference");
718             auto refPos = pos;
719             popFront();
720             auto n = decodeBackref();
721             if (n == 0 || n > pos)
722                 error("invalid back reference");
723             if ( mute )
724                 return null;
725             auto savePos = pos;
726             auto saveBrp = brp;
727             scope(success) { pos = savePos; brp = saveBrp; }
728             pos = refPos - n;
729             brp = refPos;
730             auto ret = parseDg();
731             return ret;
732         }
733 
734         switch ( t )
735         {
736         case 'Q': // Type back reference
737             return parseBackrefType(() => parseType());
738         case 'O': // Shared (O Type)
739             popFront();
740             put( "shared(" );
741             parseType();
742             put( ')' );
743             return dst[beg .. $];
744         case 'x': // Const (x Type)
745             popFront();
746             put( "const(" );
747             parseType();
748             put( ')' );
749             return dst[beg .. $];
750         case 'y': // Immutable (y Type)
751             popFront();
752             put( "immutable(" );
753             parseType();
754             put( ')' );
755             return dst[beg .. $];
756         case 'N':
757             popFront();
758             switch ( front )
759             {
760             case 'n': // Noreturn
761                 popFront();
762                 put("noreturn");
763                 return dst[beg .. $];
764             case 'g': // Wild (Ng Type)
765                 popFront();
766                 // TODO: Anything needed here?
767                 put( "inout(" );
768                 parseType();
769                 put( ')' );
770                 return dst[beg .. $];
771             case 'h': // TypeVector (Nh Type)
772                 popFront();
773                 put( "__vector(" );
774                 parseType();
775                 put( ')' );
776                 return dst[beg .. $];
777             default:
778                 error();
779             }
780         case 'A': // TypeArray (A Type)
781             popFront();
782 
783             if(forBacktrace && front == 'y') {
784                 auto at = pos;
785                 popFront();
786                 if(front == 'a') {
787                     popFront();
788                     put( "string" );
789                     return dst[beg .. $];
790                 } else {
791                     pos = at; // abandon the string shortcut
792                 }
793             }
794 
795             parseType();
796             put( "[]" );
797             return dst[beg .. $];
798         case 'G': // TypeStaticArray (G Number Type)
799             popFront();
800             auto num = sliceNumber();
801             parseType();
802             put( '[' );
803             put( num );
804             put( ']' );
805             return dst[beg .. $];
806         case 'H': // TypeAssocArray (H Type Type)
807             popFront();
808             // skip t1
809             auto tx = parseType();
810             parseType();
811             put( '[' );
812             shift(tx);
813             put( ']' );
814             return dst[beg .. $];
815         case 'P': // TypePointer (P Type)
816             popFront();
817             parseType();
818             put( '*' );
819             return dst[beg .. $];
820         case 'F': case 'U': case 'W': case 'V': case 'R': // TypeFunction
821             return parseTypeFunction();
822         case 'C': // TypeClass (C LName)
823         case 'S': // TypeStruct (S LName)
824         case 'E': // TypeEnum (E LName)
825         case 'T': // TypeTypedef (T LName)
826             popFront();
827             parseQualifiedName();
828             return dst[beg .. $];
829         case 'D': // TypeDelegate (D TypeFunction)
830             popFront();
831             auto modifiers = parseModifier();
832             if ( front == 'Q' )
833                 parseBackrefType(() => parseTypeFunction(IsDelegate.yes));
834             else
835                 parseTypeFunction(IsDelegate.yes);
836             if (modifiers)
837             {
838                 // write modifiers behind the function arguments
839                 while (auto str = typeCtors.toStringConsume(modifiers))
840                 {
841                     put(' ');
842                     put(str);
843                 }
844             }
845             return dst[beg .. $];
846         case 'n': // TypeNone (n)
847             popFront();
848             // TODO: Anything needed here?
849             return dst[beg .. $];
850         case 'B': // TypeTuple (B Number Arguments)
851             popFront();
852             // TODO: Handle this.
853             return dst[beg .. $];
854         case 'Z': // Internal symbol
855             // This 'type' is used for untyped internal symbols, i.e.:
856             // __array
857             // __init
858             // __vtbl
859             // __Class
860             // __Interface
861             // __ModuleInfo
862             popFront();
863             return dst[beg .. $];
864         default:
865             if (t >= 'a' && t <= 'w')
866             {
867                 popFront();
868                 put( primitives[cast(size_t)(t - 'a')] );
869                 return dst[beg .. $];
870             }
871             else if (t == 'z')
872             {
873                 popFront();
874                 switch ( front )
875                 {
876                 case 'i':
877                     popFront();
878                     put( "cent" );
879                     return dst[beg .. $];
880                 case 'k':
881                     popFront();
882                     put( "ucent" );
883                     return dst[beg .. $];
884                 default:
885                     error();
886                 }
887             }
888             error();
889         }
890     }
891 
892 
893     /*
894     TypeFunction:
895         CallConvention FuncAttrs Arguments ArgClose Type
896 
897     CallConvention:
898         F       // D
899         U       // C
900         W       // Windows
901         R       // C++
902 
903     FuncAttrs:
904         FuncAttr
905         FuncAttr FuncAttrs
906 
907     FuncAttr:
908         empty
909         FuncAttrPure
910         FuncAttrNothrow
911         FuncAttrProperty
912         FuncAttrRef
913         FuncAttrReturn
914         FuncAttrScope
915         FuncAttrTrusted
916         FuncAttrSafe
917 
918     FuncAttrPure:
919         Na
920 
921     FuncAttrNothrow:
922         Nb
923 
924     FuncAttrRef:
925         Nc
926 
927     FuncAttrProperty:
928         Nd
929 
930     FuncAttrTrusted:
931         Ne
932 
933     FuncAttrSafe:
934         Nf
935 
936     FuncAttrNogc:
937         Ni
938 
939     FuncAttrReturn:
940         Nj
941 
942     FuncAttrScope:
943         Nl
944 
945     Arguments:
946         Argument
947         Argument Arguments
948 
949     Argument:
950         Argument2
951         M Argument2     // scope
952 
953     Argument2:
954         Type
955         J Type     // out
956         K Type     // ref
957         L Type     // lazy
958 
959     ArgClose
960         X     // variadic T t,...) style
961         Y     // variadic T t...) style
962         Z     // not variadic
963     */
964     void parseCallConvention()
965     {
966         // CallConvention
967         switch ( front )
968         {
969         case 'F': // D
970             popFront();
971             break;
972         case 'U': // C
973             popFront();
974             put( "extern (C) " );
975             break;
976         case 'W': // Windows
977             popFront();
978             put( "extern (Windows) " );
979             break;
980         case 'R': // C++
981             popFront();
982             put( "extern (C++) " );
983             break;
984         default:
985             error();
986         }
987     }
988 
989     /// Returns: Flags of `TypeCtor`
990     ushort parseModifier()
991     {
992         TypeCtor res = TypeCtor.None;
993         switch ( front )
994         {
995         case 'y':
996             popFront();
997             return TypeCtor.Immutable;
998         case 'O':
999             popFront();
1000             res |= TypeCtor.Shared;
1001             if (front == 'x')
1002                 goto case 'x';
1003             if (front == 'N')
1004                 goto case 'N';
1005             return TypeCtor.Shared;
1006         case 'N':
1007             if (peek( 1 ) != 'g')
1008                 return res;
1009             popFront();
1010             popFront();
1011             res |= TypeCtor.InOut;
1012             if ( front == 'x' )
1013                 goto case 'x';
1014             return res;
1015         case 'x':
1016             popFront();
1017             res |= TypeCtor.Const;
1018             return res;
1019         default: return TypeCtor.None;
1020         }
1021     }
1022 
1023     ushort parseFuncAttr()
1024     {
1025         // FuncAttrs
1026         ushort result;
1027         while ('N' == front)
1028         {
1029             popFront();
1030             switch ( front )
1031             {
1032             case 'a': // FuncAttrPure
1033                 popFront();
1034                 result |= FuncAttributes.Pure;
1035                 continue;
1036             case 'b': // FuncAttrNoThrow
1037                 popFront();
1038                 result |= FuncAttributes.Nothrow;
1039                 continue;
1040             case 'c': // FuncAttrRef
1041                 popFront();
1042                 result |= FuncAttributes.Ref;
1043                 continue;
1044             case 'd': // FuncAttrProperty
1045                 popFront();
1046                 result |= FuncAttributes.Property;
1047                 continue;
1048             case 'e': // FuncAttrTrusted
1049                 popFront();
1050                 result |= FuncAttributes.Trusted;
1051                 continue;
1052             case 'f': // FuncAttrSafe
1053                 popFront();
1054                 result |= FuncAttributes.Safe;
1055                 continue;
1056             case 'g':
1057             case 'h':
1058             case 'k':
1059             case 'n':
1060                 // NOTE: The inout parameter type is represented as "Ng".
1061                 //       The vector parameter type is represented as "Nh".
1062                 //       The return parameter type is represented as "Nk".
1063                 //       The noreturn parameter type is represented as "Nn".
1064                 //       These make it look like a FuncAttr, but infact
1065                 //       if we see these, then we know we're really in
1066                 //       the parameter list.  Rewind and break.
1067                 pos--;
1068                 return result;
1069             case 'i': // FuncAttrNogc
1070                 popFront();
1071                 result |= FuncAttributes.NoGC;
1072                 continue;
1073             case 'j': // FuncAttrReturn
1074                 popFront();
1075                 if (this.peek(0) == 'N' && this.peek(1) == 'l')
1076                 {
1077                     result |= FuncAttributes.ReturnScope;
1078                     popFront();
1079                     popFront();
1080                 } else {
1081                     result |= FuncAttributes.Return;
1082                 }
1083                 continue;
1084             case 'l': // FuncAttrScope
1085                 popFront();
1086                 if (this.peek(0) == 'N' && this.peek(1) == 'j')
1087                 {
1088                     result |= FuncAttributes.ScopeReturn;
1089                     popFront();
1090                     popFront();
1091                 } else {
1092                     result |= FuncAttributes.Scope;
1093                 }
1094                 continue;
1095             case 'm': // FuncAttrLive
1096                 popFront();
1097                 result |= FuncAttributes.Live;
1098                 continue;
1099             default:
1100                 error();
1101             }
1102         }
1103         return result;
1104     }
1105 
1106     void parseFuncArguments() scope
1107     {
1108         // Arguments
1109         for ( size_t n = 0; true; n++ )
1110         {
1111             debug(info) printf( "tok (%c)\n", front );
1112             switch ( front )
1113             {
1114             case 'X': // ArgClose (variadic T t...) style)
1115                 popFront();
1116                 put( "..." );
1117                 return;
1118             case 'Y': // ArgClose (variadic T t,...) style)
1119                 popFront();
1120                 put( ", ..." );
1121                 return;
1122             case 'Z': // ArgClose (not variadic)
1123                 popFront();
1124                 return;
1125             default:
1126                 break;
1127             }
1128             putComma(n);
1129 
1130             /* Do special return, scope, ref, out combinations
1131              */
1132             int npops;
1133             if ( 'M' == front && peek(1) == 'N' && peek(2) == 'k')
1134             {
1135                 const c3 = peek(3);
1136                 if (c3 == 'J')
1137                 {
1138                     put("scope return out ");   // MNkJ
1139                     npops = 4;
1140                 }
1141                 else if (c3 == 'K')
1142                 {
1143                     put("scope return ref ");   // MNkK
1144                     npops = 4;
1145                 }
1146             }
1147             else if ('N' == front && peek(1) == 'k')
1148             {
1149                 const c2 = peek(2);
1150                 if (c2 == 'J')
1151                 {
1152                     put("return out ");         // NkJ
1153                     npops = 3;
1154                 }
1155                 else if (c2 == 'K')
1156                 {
1157                     put("return ref ");         // NkK
1158                     npops = 3;
1159                 }
1160                 else if (c2 == 'M')
1161                 {
1162                     const c3 = peek(3);
1163                     if (c3 == 'J')
1164                     {
1165                         put("return scope out ");       // NkMJ
1166                         npops = 4;
1167                     }
1168                     else if (c3 == 'K')
1169                     {
1170                         put("return scope ref ");       // NkMK
1171                         npops = 4;
1172                     }
1173                     else
1174                     {
1175                         put("return scope ");           // NkM
1176                         npops = 3;
1177                     }
1178                 }
1179             }
1180             popFront(npops);
1181 
1182             if ( 'M' == front )
1183             {
1184                 popFront();
1185                 put( "scope " );
1186             }
1187             if ( 'N' == front )
1188             {
1189                 popFront();
1190                 if ( 'k' == front ) // Return (Nk Parameter2)
1191                 {
1192                     popFront();
1193                     put( "return " );
1194                 }
1195                 else
1196                     pos--;
1197             }
1198             switch ( front )
1199             {
1200             case 'I': // in  (I Type)
1201                 popFront();
1202                 put("in ");
1203                 if (front == 'K')
1204                     goto case;
1205                 parseType();
1206                 continue;
1207             case 'K': // ref (K Type)
1208                 popFront();
1209                 put( "ref " );
1210                 parseType();
1211                 continue;
1212             case 'J': // out (J Type)
1213                 popFront();
1214                 put( "out " );
1215                 parseType();
1216                 continue;
1217             case 'L': // lazy (L Type)
1218                 popFront();
1219                 put( "lazy " );
1220                 parseType();
1221                 continue;
1222             default:
1223                 parseType();
1224             }
1225         }
1226     }
1227 
1228     enum IsDelegate { no, yes }
1229 
1230     /*
1231         TypeFunction:
1232             CallConvention FuncAttrs Arguments ArgClose Type
1233     */
1234     char[] parseTypeFunction(IsDelegate isdg = IsDelegate.no) return scope
1235     {
1236         debug(trace) printf( "parseTypeFunction+\n" );
1237         debug(trace) scope(success) printf( "parseTypeFunction-\n" );
1238         auto beg = dst.length;
1239 
1240         parseCallConvention();
1241         auto attributes = parseFuncAttr();
1242 
1243         auto argbeg = dst.length;
1244         put(IsDelegate.yes == isdg ? "delegate" : "function");
1245         put( '(' );
1246         parseFuncArguments();
1247         put( ')' );
1248         if (attributes)
1249         {
1250             // write function attributes behind arguments
1251             while (auto str = funcAttrs.toStringConsume(attributes))
1252             {
1253                 put(' ');
1254                 put(str);
1255             }
1256         }
1257 
1258         // A function / delegate return type is located at the end of its mangling
1259         // Write it in order, then shift it back to 'code order'
1260         // e.g. `delegate(int) @safedouble ' => 'double delegate(int) @safe'
1261         {
1262             auto retbeg = dst.length;
1263             parseType();
1264             put(' ');
1265             shift(dst[argbeg .. retbeg]);
1266         }
1267 
1268         return dst[beg .. $];
1269     }
1270 
1271     static bool isCallConvention( char ch )
1272     {
1273         switch ( ch )
1274         {
1275             case 'F', 'U', 'V', 'W', 'R':
1276                 return true;
1277             default:
1278                 return false;
1279         }
1280     }
1281 
1282     /*
1283     Value:
1284         n
1285         Number
1286         i Number
1287         N Number
1288         e HexFloat
1289         c HexFloat c HexFloat
1290         A Number Value...
1291 
1292     HexFloat:
1293         NAN
1294         INF
1295         NINF
1296         N HexDigits P Exponent
1297         HexDigits P Exponent
1298 
1299     Exponent:
1300         N Number
1301         Number
1302 
1303     HexDigits:
1304         HexDigit
1305         HexDigit HexDigits
1306 
1307     HexDigit:
1308         Digit
1309         A
1310         B
1311         C
1312         D
1313         E
1314         F
1315     */
1316     void parseValue(scope  char[] name = null, char type = '\0' ) scope
1317     {
1318         debug(trace) printf( "parseValue+\n" );
1319         debug(trace) scope(success) printf( "parseValue-\n" );
1320 
1321 //        printf( "*** %c\n", front );
1322         switch ( front )
1323         {
1324         case 'n':
1325             popFront();
1326             put( "null" );
1327             return;
1328         case 'i':
1329             popFront();
1330             if ( '0' > front || '9' < front )
1331                 error( "Number expected" );
1332             goto case;
1333         case '0': .. case '9':
1334             parseIntegerValue( name, type );
1335             return;
1336         case 'N':
1337             popFront();
1338             put( '-' );
1339             parseIntegerValue( name, type );
1340             return;
1341         case 'e':
1342             popFront();
1343             parseReal();
1344             return;
1345         case 'c':
1346             popFront();
1347             parseReal();
1348             put( '+' );
1349             match( 'c' );
1350             parseReal();
1351             put( 'i' );
1352             return;
1353         case 'a': case 'w': case 'd':
1354             char t = front;
1355             popFront();
1356             auto n = decodeNumber();
1357             match( '_' );
1358             put( '"' );
1359             foreach (i; 0..n)
1360             {
1361                 auto a = ascii2hex( front ); popFront();
1362                 auto b = ascii2hex( front ); popFront();
1363                 auto v = cast(char)((a << 4) | b);
1364                 if (' ' <= v && v <= '~')   // ASCII printable
1365                 {
1366                     put(v);
1367                 }
1368                 else
1369                 {
1370                     put("\\x");
1371                     putAsHex(v, 2);
1372                 }
1373             }
1374             put( '"' );
1375             if ( 'a' != t )
1376                 put(t);
1377             return;
1378         case 'A':
1379             // NOTE: This is kind of a hack.  An associative array literal
1380             //       [1:2, 3:4] is represented as HiiA2i1i2i3i4, so the type
1381             //       is "Hii" and the value is "A2i1i2i3i4".  Thus the only
1382             //       way to determine that this is an AA value rather than an
1383             //       array value is for the caller to supply the type char.
1384             //       Hopefully, this will change so that the value is
1385             //       "H2i1i2i3i4", rendering this unnecesary.
1386             if ( 'H' == type )
1387                 goto LassocArray;
1388             // A Number Value...
1389             // An array literal. Value is repeated Number times.
1390             popFront();
1391             put( '[' );
1392             auto n = decodeNumber();
1393             foreach ( i; 0 .. n )
1394             {
1395                 putComma(i);
1396                 parseValue();
1397             }
1398             put( ']' );
1399             return;
1400         case 'H':
1401         LassocArray:
1402             // H Number Value...
1403             // An associative array literal. Value is repeated 2*Number times.
1404             popFront();
1405             put( '[' );
1406             auto n = decodeNumber();
1407             foreach ( i; 0 .. n )
1408             {
1409                 putComma(i);
1410                 parseValue();
1411                 put(':');
1412                 parseValue();
1413             }
1414             put( ']' );
1415             return;
1416         case 'S':
1417             // S Number Value...
1418             // A struct literal. Value is repeated Number times.
1419             popFront();
1420             if ( name.length )
1421                 put( name );
1422             put( '(' );
1423             auto n = decodeNumber();
1424             foreach ( i; 0 .. n )
1425             {
1426                 putComma(i);
1427                 parseValue();
1428             }
1429             put( ')' );
1430             return;
1431         case 'f':
1432             // f MangledName
1433             // A function literal symbol
1434             popFront();
1435             parseMangledName(false, 1);
1436             return;
1437         default:
1438             error();
1439         }
1440     }
1441 
1442 
1443     void parseIntegerValue( scope char[] name = null, char type = '\0' ) scope
1444     {
1445         debug(trace) printf( "parseIntegerValue+\n" );
1446         debug(trace) scope(success) printf( "parseIntegerValue-\n" );
1447 
1448         switch ( type )
1449         {
1450         case 'a': // char
1451         case 'u': // wchar
1452         case 'w': // dchar
1453         {
1454             auto val = sliceNumber();
1455             auto num = decodeNumber( val );
1456 
1457             switch ( num )
1458             {
1459             case '\'':
1460                 put( "'\\''" );
1461                 return;
1462             // \", \?
1463             case '\\':
1464                 put( "'\\\\'" );
1465                 return;
1466             case '\a':
1467                 put( "'\\a'" );
1468                 return;
1469             case '\b':
1470                 put( "'\\b'" );
1471                 return;
1472             case '\f':
1473                 put( "'\\f'" );
1474                 return;
1475             case '\n':
1476                 put( "'\\n'" );
1477                 return;
1478             case '\r':
1479                 put( "'\\r'" );
1480                 return;
1481             case '\t':
1482                 put( "'\\t'" );
1483                 return;
1484             case '\v':
1485                 put( "'\\v'" );
1486                 return;
1487             default:
1488                 switch ( type )
1489                 {
1490                 case 'a':
1491                     if ( num >= 0x20 && num < 0x7F )
1492                     {
1493                         put( '\'' );
1494                         put( cast(char)num );
1495                         put( '\'' );
1496                         return;
1497                     }
1498                     put( "\\x" );
1499                     putAsHex( num, 2 );
1500                     return;
1501                 case 'u':
1502                     put( "'\\u" );
1503                     putAsHex( num, 4 );
1504                     put( '\'' );
1505                     return;
1506                 case 'w':
1507                     put( "'\\U" );
1508                     putAsHex( num, 8 );
1509                     put( '\'' );
1510                     return;
1511                 default:
1512                     assert( 0 );
1513                 }
1514             }
1515         }
1516         case 'b': // bool
1517             put( decodeNumber() ? "true" : "false" );
1518             return;
1519         case 'h', 't', 'k': // ubyte, ushort, uint
1520             put( sliceNumber() );
1521             put( 'u' );
1522             return;
1523         case 'l': // long
1524             put( sliceNumber() );
1525             put( 'L' );
1526             return;
1527         case 'm': // ulong
1528             put( sliceNumber() );
1529             put( "uL" );
1530             return;
1531         default:
1532             put( sliceNumber() );
1533             return;
1534         }
1535     }
1536 
1537 
1538     /*
1539     TemplateArgs:
1540         TemplateArg
1541         TemplateArg TemplateArgs
1542 
1543     TemplateArg:
1544         TemplateArgX
1545         H TemplateArgX
1546 
1547     TemplateArgX:
1548         T Type
1549         V Type Value
1550         S Number_opt QualifiedName
1551         X ExternallyMangledName
1552     */
1553     void parseTemplateArgs() scope
1554     {
1555         debug(trace) printf( "parseTemplateArgs+\n" );
1556         debug(trace) scope(success) printf( "parseTemplateArgs-\n" );
1557 
1558     L_nextArg:
1559         for ( size_t n = 0; true; n++ )
1560         {
1561             if ( front == 'H' )
1562                 popFront();
1563 
1564             switch ( front )
1565             {
1566             case 'T':
1567                 popFront();
1568                 putComma(n);
1569                 parseType();
1570                 continue;
1571             case 'V':
1572                 popFront();
1573                 putComma(n);
1574                 // NOTE: In the few instances where the type is actually
1575                 //       desired in the output it should precede the value
1576                 //       generated by parseValue, so it is safe to simply
1577                 //       decrement len and let put/append do its thing.
1578                 char t = front; // peek at type for parseValue
1579                 if ( t == 'Q' )
1580                     t = peekBackref();
1581                 char[] name; silent( delegate void() { name = parseType(); } );
1582                 parseValue( name, t );
1583                 continue;
1584             case 'S':
1585                 popFront();
1586                 putComma(n);
1587 
1588                 if ( mayBeMangledNameArg() )
1589                 {
1590                     auto l = dst.length;
1591                     auto p = pos;
1592                     auto b = brp;
1593                     try
1594                     {
1595                         debug(trace) printf( "may be mangled name arg\n" );
1596                         parseMangledNameArg();
1597                         continue;
1598                     }
1599                     catch ( ParseException e )
1600                     {
1601                         dst.len = l;
1602                         pos = p;
1603                         brp = b;
1604                         debug(trace) printf( "not a mangled name arg\n" );
1605                     }
1606                 }
1607                 if ( isDigit( front ) && isDigit( peek( 1 ) ) )
1608                 {
1609                     // ambiguity: length followed by qualified name (starting with number)
1610                     // try all possible pairs of numbers
1611                     auto qlen = decodeNumber() / 10; // last digit needed for QualifiedName
1612                     pos--;
1613                     auto l = dst.length;
1614                     auto p = pos;
1615                     auto b = brp;
1616                     while ( qlen > 0 )
1617                     {
1618                         try
1619                         {
1620                             parseQualifiedName();
1621                             if ( pos == p + qlen )
1622                                 continue L_nextArg;
1623                         }
1624                         catch ( ParseException e )
1625                         {
1626                         }
1627                         qlen /= 10; // retry with one digit less
1628                         pos = --p;
1629                         dst.len = l;
1630                         brp = b;
1631                     }
1632                 }
1633                 parseQualifiedName();
1634                 continue;
1635             case 'X':
1636                 popFront();
1637                 putComma(n);
1638                 parseLName();
1639                 continue;
1640             default:
1641                 return;
1642             }
1643         }
1644     }
1645 
1646 
1647     bool mayBeMangledNameArg()
1648     {
1649         debug(trace) printf( "mayBeMangledNameArg+\n" );
1650         debug(trace) scope(success) printf( "mayBeMangledNameArg-\n" );
1651 
1652         auto p = pos;
1653         scope(exit) pos = p;
1654         if ( isDigit( buf[pos] ) )
1655         {
1656             auto n = decodeNumber();
1657             return n >= 4 &&
1658                 pos < buf.length && '_' == buf[pos++] &&
1659                 pos < buf.length && 'D' == buf[pos++] &&
1660                 isDigit( buf[pos] );
1661         }
1662         else
1663         {
1664             return pos < buf.length && '_' == buf[pos++] &&
1665                    pos < buf.length && 'D' == buf[pos++] &&
1666                    isSymbolNameFront();
1667         }
1668     }
1669 
1670 
1671     void parseMangledNameArg()
1672     {
1673         debug(trace) printf( "parseMangledNameArg+\n" );
1674         debug(trace) scope(success) printf( "parseMangledNameArg-\n" );
1675 
1676         size_t n = 0;
1677         if ( isDigit( front ) )
1678             n = decodeNumber();
1679         parseMangledName( false, n );
1680     }
1681 
1682 
1683     /*
1684     TemplateInstanceName:
1685         Number __T LName TemplateArgs Z
1686     */
1687     void parseTemplateInstanceName(bool hasNumber) scope
1688     {
1689         debug(trace) printf( "parseTemplateInstanceName+\n" );
1690         debug(trace) scope(success) printf( "parseTemplateInstanceName-\n" );
1691 
1692         auto sav = pos;
1693         auto saveBrp = brp;
1694         scope(failure)
1695         {
1696             pos = sav;
1697             brp = saveBrp;
1698         }
1699         auto n = hasNumber ? decodeNumber() : 0;
1700         auto beg = pos;
1701         match( "__T" );
1702         parseLName();
1703         put( "!(" );
1704         parseTemplateArgs();
1705         match( 'Z' );
1706         if ( hasNumber && pos - beg != n )
1707             error( "Template name length mismatch" );
1708         put( ')' );
1709     }
1710 
1711 
1712     bool mayBeTemplateInstanceName() scope
1713     {
1714         debug(trace) printf( "mayBeTemplateInstanceName+\n" );
1715         debug(trace) scope(success) printf( "mayBeTemplateInstanceName-\n" );
1716 
1717         auto p = pos;
1718         scope(exit) pos = p;
1719         auto n = decodeNumber();
1720         return n >= 5 &&
1721                pos < buf.length && '_' == buf[pos++] &&
1722                pos < buf.length && '_' == buf[pos++] &&
1723                pos < buf.length && 'T' == buf[pos++];
1724     }
1725 
1726 
1727     /*
1728     SymbolName:
1729         LName
1730         TemplateInstanceName
1731     */
1732     void parseSymbolName() scope
1733     {
1734         debug(trace) printf( "parseSymbolName+\n" );
1735         debug(trace) scope(success) printf( "parseSymbolName-\n" );
1736 
1737         // LName -> Number
1738         // TemplateInstanceName -> Number "__T"
1739         switch ( front )
1740         {
1741         case '_':
1742             // no length encoding for templates for new mangling
1743             parseTemplateInstanceName(false);
1744             return;
1745 
1746         case '0': .. case '9':
1747             if ( mayBeTemplateInstanceName() )
1748             {
1749                 auto t = dst.length;
1750 
1751                 try
1752                 {
1753                     debug(trace) printf( "may be template instance name\n" );
1754                     parseTemplateInstanceName(true);
1755                     return;
1756                 }
1757                 catch ( ParseException e )
1758                 {
1759                     debug(trace) printf( "not a template instance name\n" );
1760                     dst.len = t;
1761                 }
1762             }
1763             goto case;
1764         case 'Q':
1765             parseLName();
1766             return;
1767         default:
1768             error();
1769         }
1770     }
1771 
1772     // parse optional function arguments as part of a symbol name, i.e without return type
1773     // if keepAttr, the calling convention and function attributes are not discarded, but returned
1774     char[] parseFunctionTypeNoReturn( bool keepAttr = false ) return scope
1775     {
1776 
1777         if (forBacktrace)
1778           keepAttr = false;
1779 
1780         // try to demangle a function, in case we are pointing to some function local
1781         auto prevpos = pos;
1782         auto prevlen = dst.length;
1783         auto prevbrp = brp;
1784 
1785         try
1786         {
1787             if ( 'M' == front )
1788             {
1789                 // do not emit "needs this"
1790                 popFront();
1791                 auto modifiers = parseModifier();
1792                 while (auto str = typeCtors.toStringConsume(modifiers))
1793                 {
1794                     put(str);
1795                     put(' ');
1796                 }
1797             }
1798             if ( isCallConvention( front ) )
1799             {
1800                 char[] attr;
1801                 // we don't want calling convention and attributes in the qualified name
1802                 parseCallConvention();
1803                 auto attributes = parseFuncAttr();
1804                 if (keepAttr) {
1805                     while (auto str = funcAttrs.toStringConsume(attributes))
1806                     {
1807                         put(str);
1808                         put(' ');
1809                     }
1810                     attr = dst[prevlen .. $];
1811                 }
1812 
1813                 put( '(' );
1814                 parseFuncArguments();
1815                 put( ')' );
1816                 return attr;
1817             }
1818         }
1819         catch ( ParseException )
1820         {
1821             // not part of a qualified name, so back up
1822             pos = prevpos;
1823             dst.len = prevlen;
1824             brp = prevbrp;
1825         }
1826         return null;
1827     }
1828 
1829     /*
1830     QualifiedName:
1831         SymbolName
1832         SymbolName QualifiedName
1833     */
1834     char[] parseQualifiedName() return scope
1835     {
1836         debug(trace) printf( "parseQualifiedName+\n" );
1837         debug(trace) scope(success) printf( "parseQualifiedName-\n" );
1838         size_t  beg = dst.length;
1839         size_t  n   = 0;
1840 
1841         do
1842         {
1843             if ( n++ )
1844                 put( '.' );
1845             parseSymbolName();
1846             parseFunctionTypeNoReturn();
1847 
1848         } while ( isSymbolNameFront() );
1849         return dst[beg .. $];
1850     }
1851 
1852 
1853     /*
1854     MangledName:
1855         _D QualifiedName Type
1856         _D QualifiedName M Type
1857     */
1858     void parseMangledName( bool displayType, size_t n = 0 ) scope
1859     {
1860         if(forBacktrace)
1861             displayType = false;
1862 
1863         debug(trace) printf( "parseMangledName+\n" );
1864         debug(trace) scope(success) printf( "parseMangledName-\n" );
1865         char[] name = null;
1866 
1867         auto end = pos + n;
1868 
1869         eat( '_' );
1870         match( 'D' );
1871         do
1872         {
1873             size_t  beg = dst.length;
1874             size_t  nameEnd = dst.length;
1875             char[] attr;
1876             do
1877             {
1878                 if ( attr )
1879                     dst.remove(attr); // dump attributes of parent symbols
1880                 if (beg != dst.length)
1881                     put( '.' );
1882                 parseSymbolName();
1883                 nameEnd = dst.length;
1884                 attr = parseFunctionTypeNoReturn( displayType );
1885 
1886             } while ( isSymbolNameFront() );
1887 
1888             if ( displayType )
1889             {
1890                 attr = shift( attr );
1891                 nameEnd = dst.length - attr.length;  // name includes function arguments
1892             }
1893             name = dst[beg .. nameEnd];
1894 
1895             debug(info) printf( "name (%.*s)\n", cast(int) name.length, name.ptr );
1896             if ( 'M' == front )
1897                 popFront(); // has 'this' pointer
1898 
1899             auto lastlen = dst.length;
1900             auto type = parseType();
1901             if ( displayType )
1902             {
1903                 if ( type.length )
1904                     put( ' ' );
1905                 // sort (name,attr,type) -> (attr,type,name)
1906                 shift( name );
1907             }
1908             else
1909             {
1910                 // remove type
1911                 assert( attr.length == 0 );
1912                 dst.len = lastlen;
1913             }
1914             if ( pos >= buf.length || (n != 0 && pos >= end) )
1915                 return;
1916 
1917             switch ( front )
1918             {
1919             case 'T': // terminators when used as template alias parameter
1920             case 'V':
1921             case 'S':
1922             case 'Z':
1923                 return;
1924             default:
1925             }
1926             put( '.' );
1927 
1928         } while ( true );
1929     }
1930 
1931     void parseMangledName()
1932     {
1933         parseMangledName( AddType.yes == addType );
1934     }
1935 
1936     char[] doDemangle(alias FUNC)() return scope
1937     {
1938         while ( true )
1939         {
1940             try
1941             {
1942                 debug(info) printf( "demangle(%.*s)\n", cast(int) buf.length, buf.ptr );
1943                 FUNC();
1944                 return dst[0 .. $];
1945             }
1946             catch ( OverflowException e )
1947             {
1948                 debug(trace) printf( "overflow... restarting\n" );
1949                 auto a = Buffer.minSize;
1950                 auto b = 2 * dst.dst.length;
1951                 auto newsz = a < b ? b : a;
1952                 debug(info) printf( "growing dst to %lu bytes\n", newsz );
1953                 dst.dst.length = newsz;
1954                 pos = dst.len = brp = 0;
1955                 continue;
1956             }
1957             catch ( ParseException e )
1958             {
1959                 debug(info)
1960                 {
1961                     auto msg = e.toString();
1962                     printf( "error: %.*s\n", cast(int) msg.length, msg.ptr );
1963                 }
1964                 return dst.copyInput(buf);
1965             }
1966             catch ( Exception e )
1967             {
1968                 assert( false ); // no other exceptions thrown
1969             }
1970         }
1971     }
1972 
1973     char[] demangleName() nothrow
1974     {
1975         return doDemangle!parseMangledName();
1976     }
1977 
1978     char[] demangleType() nothrow
1979     {
1980         return doDemangle!parseType();
1981     }
1982 }
1983 
1984 
1985 /**
1986  * Demangles D/C++ mangled names.  If it is not a D/C++ mangled name, it
1987  * returns its argument name.
1988  *
1989  * Params:
1990  *  buf = The string to demangle.
1991  *  dst = An optional destination buffer.
1992  *  __cxa_demangle = optional C++ demangler
1993  *
1994  * Returns:
1995  *  The demangled name or the original string if the name is not a mangled
1996  *  D/C++ name.
1997  */
1998 char[] demangle(return scope const(char)[] buf, return scope char[] dst = null, CXX_DEMANGLER __cxa_demangle = null, bool forBacktrace = false) nothrow pure @safe
1999 {
2000     if (__cxa_demangle && buf.length > 2 && buf[0..2] == "_Z")
2001         return demangleCXX(buf, __cxa_demangle, dst);
2002     auto d = Demangle!()(buf, dst);
2003     d.forBacktrace = forBacktrace;
2004     // fast path (avoiding throwing & catching exception) for obvious
2005     // non-D mangled names
2006     if (buf.length < 2 || !(buf[0] == 'D' || buf[0..2] == "_D"))
2007         return d.dst.copyInput(buf);
2008     return d.demangleName();
2009 }
2010 
2011 /**
2012  * Demangles a D mangled type.
2013  *
2014  * Params:
2015  *  buf = The string to demangle.
2016  *  dst = An optional destination buffer.
2017  *
2018  * Returns:
2019  *  The demangled type name or the original string if the name is not a
2020  *  mangled D type.
2021 */
2022 char[] demangleType( const(char)[] buf, char[] dst = null ) nothrow pure @safe
2023 {
2024     auto d = Demangle!()(buf, dst);
2025     return d.demangleType();
2026 }
2027 
2028 /**
2029 * reencode a mangled symbol name that might include duplicate occurrences
2030 * of the same identifier by replacing all but the first occurence with
2031 * a back reference.
2032 *
2033 * Params:
2034 *  mangled = The mangled string representing the type
2035 *
2036 * Returns:
2037 *  The mangled name with deduplicated identifiers
2038 */
2039 char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe
2040 {
2041     static struct PrependHooks
2042     {
2043         size_t lastpos;
2044         char[] result;
2045         size_t[const(char)[]] idpos; // identifier positions
2046 
2047         static struct Replacement
2048         {
2049             size_t pos;    // postion in original mangled string
2050             size_t respos; // postion in result string
2051         }
2052         Replacement [] replacements;
2053 
2054     pure @safe:
2055         size_t positionInResult(size_t pos) scope
2056         {
2057             foreach_reverse (r; replacements)
2058                 if (pos >= r.pos)
2059                     return r.respos + pos - r.pos;
2060             return pos;
2061         }
2062 
2063         alias Remangle = Demangle!(PrependHooks);
2064 
2065         void flushPosition(ref Remangle d) scope
2066         {
2067             if (lastpos < d.pos)
2068             {
2069                 result ~= d.buf[lastpos .. d.pos];
2070             }
2071             else if (lastpos > d.pos)
2072             {
2073                 // roll back to earlier position
2074                 while (replacements.length > 0 && replacements[$-1].pos > d.pos)
2075                     replacements = replacements[0 .. $-1];
2076 
2077                 if (replacements.length > 0)
2078                     result.length = replacements[$-1].respos + d.pos - replacements[$-1].pos;
2079                 else
2080                     result.length = d.pos;
2081             }
2082         }
2083 
2084         bool parseLName(scope ref Remangle d) scope @trusted
2085         {
2086             flushPosition(d);
2087 
2088             auto reslen = result.length;
2089             auto refpos = d.pos;
2090             if (d.front == 'Q')
2091             {
2092                 size_t npos;
2093                 {
2094                     scope(exit) result.length = reslen; // remove all intermediate additions
2095                     // only support identifier back references
2096                     d.popFront();
2097                     size_t n = d.decodeBackref();
2098                     if (!n || n > refpos)
2099                         error("invalid back reference");
2100 
2101                     auto savepos = d.pos;
2102                     scope(exit) d.pos = savepos;
2103                     size_t srcpos = refpos - n;
2104 
2105                     auto idlen = d.decodeNumber();
2106                     if (d.pos + idlen > d.buf.length)
2107                         error("invalid back reference");
2108                     auto id = d.buf[d.pos .. d.pos + idlen];
2109                     auto pid = id in idpos;
2110                     if (!pid)
2111                         error("invalid back reference");
2112                     npos = positionInResult(*pid);
2113                 }
2114                 encodeBackref(reslen - npos);
2115                 const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675
2116                 replacements ~= Replacement(pos, result.length);
2117             }
2118             else
2119             {
2120                 auto n = d.decodeNumber();
2121                 if (!n || n > d.buf.length || n > d.buf.length - d.pos)
2122                     error("LName too shot or too long");
2123                 auto id = d.buf[d.pos .. d.pos + n];
2124                 d.pos += n;
2125                 if (auto pid = id in idpos)
2126                 {
2127                     size_t npos = positionInResult(*pid);
2128                     result.length = reslen;
2129                     encodeBackref(reslen - npos);
2130                     const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675
2131                     replacements ~= Replacement(pos, result.length);
2132                 }
2133                 else
2134                 {
2135                     idpos[id] = refpos; //! scope variable id used as AA key, makes this function @trusted
2136                     result ~= d.buf[refpos .. d.pos];
2137                 }
2138             }
2139             lastpos = d.pos;
2140             return true;
2141         }
2142 
2143         char[] parseType( ref Remangle d, char[] name = null ) return scope
2144         {
2145             if (d.front != 'Q')
2146                 return null;
2147 
2148             flushPosition(d);
2149 
2150             auto refPos = d.pos;
2151             d.popFront();
2152             auto n = d.decodeBackref();
2153             if (n == 0 || n > refPos)
2154                 error("invalid back reference");
2155 
2156             size_t npos = positionInResult(refPos - n);
2157             size_t reslen = result.length;
2158             encodeBackref(reslen - npos);
2159 
2160             lastpos = d.pos;
2161             return result[reslen .. $]; // anything but null
2162         }
2163 
2164         void encodeBackref(size_t relpos) scope
2165         {
2166             result ~= 'Q';
2167             enum base = 26;
2168             size_t div = 1;
2169             while (relpos >= div * base)
2170                 div *= base;
2171             while (div >= base)
2172             {
2173                 auto dig = (relpos / div);
2174                 result ~= cast(char)('A' + dig);
2175                 relpos -= dig * div;
2176                 div /= base;
2177             }
2178             result ~= cast(char)('a' + relpos);
2179         }
2180     }
2181 
2182     auto d = Demangle!(PrependHooks)(mangled, null);
2183     d.hooks = PrependHooks();
2184     d.mute = true; // no demangled output
2185     try
2186     {
2187         d.parseMangledName();
2188         if (d.hooks.lastpos < d.pos)
2189             d.hooks.result ~= d.buf[d.hooks.lastpos .. d.pos];
2190         return d.hooks.result;
2191     }
2192     catch (Exception)
2193     {
2194         // overflow exception cannot occur
2195         return mangled.dup;
2196     }
2197 }
2198 
2199 /**
2200  * Mangles a D symbol.
2201  *
2202  * Params:
2203  *  T = The type of the symbol.
2204  *  fqn = The fully qualified name of the symbol.
2205  *  dst = An optional destination buffer.
2206  *
2207  * Returns:
2208  *  The mangled name for a symbols of type T and the given fully
2209  *  qualified name.
2210  */
2211 char[] mangle(T)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow
2212 {
2213     import core.internal.string : numDigits, unsignedToTempString;
2214 
2215     static struct DotSplitter
2216     {
2217     @safe pure nothrow:
2218         const(char)[] s;
2219 
2220         @property bool empty() const { return !s.length; }
2221 
2222         @property const(char)[] front() const return scope
2223         {
2224             immutable i = indexOfDot();
2225             return i == -1 ? s[0 .. $] : s[0 .. i];
2226         }
2227 
2228         void popFront() scope
2229         {
2230             immutable i = indexOfDot();
2231             s = i == -1 ? s[$ .. $] : s[i+1 .. $];
2232         }
2233 
2234         private ptrdiff_t indexOfDot() const scope
2235         {
2236             foreach (i, c; s) if (c == '.') return i;
2237             return -1;
2238         }
2239     }
2240 
2241     size_t len = "_D".length;
2242     foreach (comp; DotSplitter(fqn))
2243         len += numDigits(comp.length) + comp.length;
2244     len += T.mangleof.length;
2245     if (dst.length < len) dst.length = len;
2246 
2247     size_t i = "_D".length;
2248     dst[0 .. i] = "_D";
2249     foreach (comp; DotSplitter(fqn))
2250     {
2251         const ndigits = numDigits(comp.length);
2252         unsignedToTempString(comp.length, dst[i .. i + ndigits]);
2253         i += ndigits;
2254         dst[i .. i + comp.length] = comp[];
2255         i += comp.length;
2256     }
2257     dst[i .. i + T.mangleof.length] = T.mangleof[];
2258     i += T.mangleof.length;
2259 
2260     static if (hasTypeBackRef)
2261         return reencodeMangled(dst[0 .. i]);
2262     else
2263         return dst[0 .. i];
2264 }
2265 
2266 
2267 ///
2268 @safe pure nothrow unittest
2269 {
2270     assert(mangle!int("a.b") == "_D1a1bi");
2271     assert(mangle!(char[])("test.foo") == "_D4test3fooAa");
2272     assert(mangle!(int function(int))("a.b") == "_D1a1bPFiZi");
2273 }
2274 
2275 @safe pure nothrow unittest
2276 {
2277     static assert(mangle!int("a.b") == "_D1a1bi");
2278 
2279     auto buf = new char[](10);
2280     buf = mangle!int("a.b", buf);
2281     assert(buf == "_D1a1bi");
2282     buf = mangle!(char[])("test.foo", buf);
2283     assert(buf == "_D4test3fooAa");
2284     buf = mangle!(real delegate(int))("modµ.dg");
2285     assert(buf == "_D5modµ2dgDFiZe", buf);
2286 }
2287 
2288 
2289 /**
2290  * Mangles a D function.
2291  *
2292  * Params:
2293  *  T = function pointer type.
2294  *  fqn = The fully qualified name of the symbol.
2295  *  dst = An optional destination buffer.
2296  *
2297  * Returns:
2298  *  The mangled name for a function with function pointer type T and
2299  *  the given fully qualified name.
2300  */
2301 char[] mangleFunc(T:FT*, FT)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow if (is(FT == function))
2302 {
2303     static if (isExternD!FT)
2304     {
2305         return mangle!FT(fqn, dst);
2306     }
2307     else static if (hasPlainMangling!FT)
2308     {
2309         dst.length = fqn.length;
2310         dst[] = fqn[];
2311         return dst;
2312     }
2313     else static if (isExternCPP!FT)
2314     {
2315         static assert(0, "Can't mangle extern(C++) functions.");
2316     }
2317     else
2318     {
2319         static assert(0, "Can't mangle function with unknown linkage ("~FT.stringof~").");
2320     }
2321 }
2322 
2323 private enum hasTypeBackRef = (int function(void**,void**)).mangleof[$-4 .. $] == "QdZi";
2324 
2325 @safe pure nothrow unittest
2326 {
2327     assert(mangleFunc!(int function(int))("a.b") == "_D1a1bFiZi");
2328     static if (hasTypeBackRef)
2329     {
2330         assert(mangleFunc!(int function(Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsZi");
2331         assert(mangleFunc!(int function(Object, Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsQdZi");
2332     }
2333     else
2334     {
2335         auto mngl = mangleFunc!(int function(Object))("object.Object.opEquals");
2336         assert(mngl == "_D6object6Object8opEqualsFC6ObjectZi");
2337         auto remngl = reencodeMangled(mngl);
2338         assert(remngl == "_D6object6Object8opEqualsFCQsZi");
2339     }
2340     // trigger back tracking with ambiguity on '__T', template or identifier
2341     assert(reencodeMangled("_D3std4conv4conv7__T3std4convi") == "_D3std4convQf7__T3stdQpi");
2342 }
2343 
2344 @safe pure nothrow unittest
2345 {
2346     int function(lazy int[], ...) fp;
2347     assert(mangle!(typeof(fp))("demangle.test") == "_D8demangle4testPFLAiYi");
2348     assert(mangle!(typeof(*fp))("demangle.test") == "_D8demangle4testFLAiYi");
2349 }
2350 
2351 private template isExternD(FT) if (is(FT == function))
2352 {
2353     enum isExternD = __traits(getLinkage, FT) == "D";
2354 }
2355 
2356 private template isExternCPP(FT) if (is(FT == function))
2357 {
2358     enum isExternCPP = __traits(getLinkage, FT) == "C++";
2359 }
2360 
2361 private template hasPlainMangling(FT) if (is(FT == function))
2362 {
2363     enum lnk = __traits(getLinkage, FT);
2364     // C || Windows
2365     enum hasPlainMangling = lnk == "C" || lnk == "Windows" || lnk == "System";
2366 }
2367 
2368 @safe pure nothrow unittest
2369 {
2370     static extern(D) void fooD();
2371     static extern(C) void fooC();
2372     static extern(Windows) void fooW();
2373     static extern(C++) void fooCPP();
2374 
2375     bool check(FT)(bool isD, bool isCPP, bool isPlain)
2376     {
2377         return isExternD!FT == isD && isExternCPP!FT == isCPP &&
2378             hasPlainMangling!FT == isPlain;
2379     }
2380     static assert(check!(typeof(fooD))(true, false, false));
2381     static assert(check!(typeof(fooC))(false, false, true));
2382     static assert(check!(typeof(fooW))(false, false, true));
2383     static assert(check!(typeof(fooCPP))(false, true, false));
2384 
2385     static assert(__traits(compiles, mangleFunc!(typeof(&fooD))("")));
2386     static assert(__traits(compiles, mangleFunc!(typeof(&fooC))("")));
2387     static assert(__traits(compiles, mangleFunc!(typeof(&fooW))("")));
2388     static assert(!__traits(compiles, mangleFunc!(typeof(&fooCPP))("")));
2389 }
2390 
2391 /***
2392  * C name mangling is done by adding a prefix on some platforms.
2393  */
2394 version (Win32)
2395     enum string cPrefix = "_";
2396 else version (Darwin)
2397     enum string cPrefix = "_";
2398 else
2399     enum string cPrefix = "";
2400 
2401 @safe pure nothrow unittest
2402 {
2403     immutable string[2][] table =
2404     [
2405         ["printf", "printf"],
2406         ["_foo", "_foo"],
2407         ["_D88", "_D88"],
2408         ["_D3fooQeFIAyaZv", "void foo.foo(in immutable(char)[])" ],
2409         ["_D3barQeFIKAyaZv", "void bar.bar(in ref immutable(char)[])" ],
2410         ["_D4test3fooAa", "char[] test.foo"],
2411         ["_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])"],
2412         ["_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(Object)"],
2413         ["_D4test2dgDFiYd", "double delegate(int, ...) test.dg"],
2414         ["_D4test2dgDxFNfiYd", "double delegate(int, ...) @safe const test.dg"],
2415         //["_D4test58__T9factorialVde67666666666666860140VG5aa5_68656c6c6fVPvnZ9factorialf", ""],
2416         //["_D4test101__T9factorialVde67666666666666860140Vrc9a999999999999d9014000000000000000c00040VG5aa5_68656c6c6fVPvnZ9factorialf", ""],
2417         ["_D4test34__T3barVG3uw3_616263VG3wd3_646566Z1xi", "int test.bar!(\"abc\"w, \"def\"d).x"],
2418         ["_D8demangle4testFLC6ObjectLDFLiZiZi", "int demangle.test(lazy Object, lazy int delegate(lazy int))"],
2419         ["_D8demangle4testFAiXi", "int demangle.test(int[]...)"],
2420         ["_D8demangle4testFAiYi", "int demangle.test(int[], ...)"],
2421         ["_D8demangle4testFLAiXi", "int demangle.test(lazy int[]...)"],
2422         ["_D8demangle4testFLAiYi", "int demangle.test(lazy int[], ...)"],
2423         ["_D6plugin8generateFiiZAya", "immutable(char)[] plugin.generate(int, int)"],
2424         ["_D6plugin8generateFiiZAxa", "const(char)[] plugin.generate(int, int)"],
2425         ["_D6plugin8generateFiiZAOa", "shared(char)[] plugin.generate(int, int)"],
2426         ["_D8demangle3fnAFZ3fnBMFZv", "void demangle.fnA().fnB()"],
2427         ["_D8demangle4mainFZ1S3fnCMFZv", "void demangle.main().S.fnC()"],
2428         ["_D8demangle4mainFZ1S3fnDMFZv", "void demangle.main().S.fnD()"],
2429         ["_D8demangle20__T2fnVAiA4i1i2i3i4Z2fnFZv", "void demangle.fn!([1, 2, 3, 4]).fn()"],
2430         ["_D8demangle10__T2fnVi1Z2fnFZv", "void demangle.fn!(1).fn()"],
2431         ["_D8demangle26__T2fnVS8demangle1SS2i1i2Z2fnFZv", "void demangle.fn!(demangle.S(1, 2)).fn()"],
2432         ["_D8demangle13__T2fnVeeNANZ2fnFZv", "void demangle.fn!(real.nan).fn()"],
2433         ["_D8demangle14__T2fnVeeNINFZ2fnFZv", "void demangle.fn!(-real.infinity).fn()"],
2434         ["_D8demangle13__T2fnVeeINFZ2fnFZv", "void demangle.fn!(real.infinity).fn()"],
2435         ["_D8demangle21__T2fnVHiiA2i1i2i3i4Z2fnFZv", "void demangle.fn!([1:2, 3:4]).fn()"],
2436         ["_D8demangle2fnFNgiZNgi", "inout(int) demangle.fn(inout(int))"],
2437         ["_D8demangle29__T2fnVa97Va9Va0Vu257Vw65537Z2fnFZv", "void demangle.fn!('a', '\\t', \\x00, '\\u0101', '\\U00010001').fn()"],
2438         ["_D2gc11gctemplates56__T8mkBitmapTS3std5range13__T4iotaTiTiZ4iotaFiiZ6ResultZ8mkBitmapFNbNiNfPmmZv",
2439          "nothrow @nogc @safe void gc.gctemplates.mkBitmap!(std.range.iota!(int, int).iota(int, int).Result).mkBitmap(ulong*, ulong)"],
2440         ["_D8serenity9persister6Sqlite69__T15SqlitePersisterTS8serenity9persister6Sqlite11__unittest6FZ4TestZ15SqlitePersister12__T7opIndexZ7opIndexMFmZS8serenity9persister6Sqlite11__unittest6FZ4Test",
2441          "serenity.persister.Sqlite.__unittest6().Test serenity.persister.Sqlite.SqlitePersister!(serenity.persister.Sqlite.__unittest6().Test).SqlitePersister.opIndex!().opIndex(ulong)"],
2442         ["_D8bug100274mainFZ5localMFZi","int bug10027.main().local()"],
2443         ["_D8demangle4testFNhG16gZv", "void demangle.test(__vector(byte[16]))"],
2444         ["_D8demangle4testFNhG8sZv", "void demangle.test(__vector(short[8]))"],
2445         ["_D8demangle4testFNhG4iZv", "void demangle.test(__vector(int[4]))"],
2446         ["_D8demangle4testFNhG2lZv", "void demangle.test(__vector(long[2]))"],
2447         ["_D8demangle4testFNhG4fZv", "void demangle.test(__vector(float[4]))"],
2448         ["_D8demangle4testFNhG2dZv", "void demangle.test(__vector(double[2]))"],
2449         ["_D8demangle4testFNhG4fNhG4fZv", "void demangle.test(__vector(float[4]), __vector(float[4]))"],
2450         ["_D8bug1119234__T3fooS23_D8bug111924mainFZ3bariZ3fooMFZv","void bug11192.foo!(bug11192.main().bar).foo()"],
2451         ["_D13libd_demangle12__ModuleInfoZ", "libd_demangle.__ModuleInfo"],
2452         ["_D15TypeInfo_Struct6__vtblZ", "TypeInfo_Struct.__vtbl"],
2453         ["_D3std5stdio12__ModuleInfoZ", "std.stdio.__ModuleInfo"],
2454         ["_D3std6traits15__T8DemangleTkZ8Demangle6__initZ", "std.traits.Demangle!(uint).Demangle.__init"],
2455         ["_D3foo3Bar7__ClassZ", "foo.Bar.__Class"],
2456         ["_D3foo3Bar6__vtblZ", "foo.Bar.__vtbl"],
2457         ["_D3foo3Bar11__interfaceZ", "foo.Bar.__interface"],
2458         ["_D3foo7__arrayZ", "foo.__array"],
2459         ["_D8link657428__T3fooVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"],
2460         ["_D8link657429__T3fooHVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"],
2461         ["_D4test22__T4funcVAyaa3_610a62Z4funcFNaNbNiNmNfZAya", `pure nothrow @nogc @live @safe immutable(char)[] test.func!("a\x0ab").func()`],
2462         ["_D3foo3barFzkZzi", "cent foo.bar(ucent)"],
2463         ["_D5bug145Class3fooMFNlZPv", "scope void* bug14.Class.foo()"],
2464         ["_D5bug145Class3barMFNjZPv", "return void* bug14.Class.bar()"],
2465         ["_D5bug143fooFMPvZPv", "void* bug14.foo(scope void*)"],
2466         ["_D5bug143barFMNkPvZPv", "void* bug14.bar(scope return void*)"],
2467         ["_D3std5range15__T4iotaTtTtTtZ4iotaFtttZ6Result7opIndexMNgFNaNbNiNfmZNgt",
2468          "inout pure nothrow @nogc @safe inout(ushort) std.range.iota!(ushort, ushort, ushort).iota(ushort, ushort, ushort).Result.opIndex(ulong)"],
2469         ["_D3std6format77__T6getNthVAyaa13_696e7465676572207769647468S233std6traits10isIntegralTiTkTkZ6getNthFNaNfkkkZi",
2470          "pure @safe int std.format.getNth!(\"integer width\", std.traits.isIntegral, int, uint, uint).getNth(uint, uint, uint)"],
2471         ["_D3std11parallelism42__T16RoundRobinBufferTDFKAaZvTDxFNaNdNeZbZ16RoundRobinBuffer5primeMFZv",
2472          "void std.parallelism.RoundRobinBuffer!(void delegate(ref char[]), bool delegate() pure @property @trusted const).RoundRobinBuffer.prime()"],
2473         ["_D6mangle__T8fun21753VSQv6S21753S1f_DQBj10__lambda71MFNaNbNiNfZvZQCbQp",
2474         "void function() pure nothrow @nogc @safe mangle.fun21753!(mangle.S21753(mangle.__lambda71())).fun21753"],
2475         // Lname '0'
2476         ["_D3std9algorithm9iteration__T9MapResultSQBmQBlQBe005stripTAAyaZQBi7opSliceMFNaNbNiNfmmZSQDiQDhQDa__TQCtSQDyQDxQDq00QCmTQCjZQDq",
2477          "pure nothrow @nogc @safe std.algorithm.iteration.MapResult!(std.algorithm.iteration.__anonymous.strip, "
2478         ~"immutable(char)[][]).MapResult std.algorithm.iteration.MapResult!(std.algorithm.iteration.strip, immutable(char)[][]).MapResult.opSlice(ulong, ulong)"],
2479 
2480         // back references
2481         ["_D4core4stdc5errnoQgFZi", "int core.stdc.errno.errno()"], // identifier back reference
2482         ["_D4testFS10structnameQnZb", "bool test(structname, structname)"], // type back reference
2483         ["_D3std11parallelism__T4TaskS8unittest3cmpTAyaTQeZQBb6__dtorMFNfZv",
2484         "@safe void std.parallelism.Task!(unittest.cmp, immutable(char)[], immutable(char)[]).Task.__dtor()"],
2485         // 1.s.s.foo from https://issues.dlang.org/show_bug.cgi?id=15831
2486         ["_D13testexpansion44__T1sTS13testexpansion8__T1sTiZ1sFiZ6ResultZ1sFS13testexpansion8__T1sTiZ1sFiZ6ResultZ6Result3fooMFNaNfZv",
2487          "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
2488         ["_D13testexpansion__T1sTSQw__TQjTiZQoFiZ6ResultZQBbFQBcZQq3fooMFNaNfZv",
2489          "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
2490         // formerly ambiguous on 'V', template value argument or pascal function
2491         // pascal functions have now been removed (in v2.095.0)
2492         ["_D3std4conv__T7enumRepTyAaTEQBa12experimental9allocator15building_blocks15stats_collector7OptionsVQCti64ZQDnyQDh",
2493          "immutable(char[]) std.conv.enumRep!(immutable(char[]), std.experimental.allocator.building_blocks.stats_collector.Options, 64).enumRep"],
2494         // symbol back reference to location with symbol back reference
2495         ["_D3std12experimental9allocator6common__T10reallocateTSQCaQBzQBo15building_blocks17kernighan_ritchie__T8KRRegionTSQEhQEgQDvQCh14null_allocator13NullAllocatorZQCdZQErFNaNbNiKQEpKAvmZb",
2496          "pure nothrow @nogc bool std.experimental.allocator.common.reallocate!(std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!("
2497         ~"std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion).reallocate(ref "
2498         ~"std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!(std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion, ref void[], ulong)"],
2499         ["_D3std9exception__T11doesPointToTASQBh5regex8internal2ir10NamedGroupTQBkTvZQCeFNaNbNiNeKxASQDlQCeQCbQBvQBvKxQtZb",
2500          "pure nothrow @nogc @trusted bool std.exception.doesPointTo!(std.regex.internal.ir.NamedGroup[], "
2501         ~"std.regex.internal.ir.NamedGroup[], void).doesPointTo(ref const(std.regex.internal.ir.NamedGroup[]), ref const(std.regex.internal.ir.NamedGroup[]))"],
2502         ["_D3std9algorithm9iteration__T14SplitterResultS_DQBu3uni7isWhiteFNaNbNiNfwZbTAyaZQBz9__xtoHashFNbNeKxSQDvQDuQDn__TQDgS_DQEnQCtQCsQCnTQCeZQEdZm",
2503          "nothrow @trusted ulong std.algorithm.iteration.SplitterResult!(std.uni.isWhite(dchar), immutable(char)[]).SplitterResult."
2504         ~"__xtoHash(ref const(std.algorithm.iteration.SplitterResult!(std.uni.isWhite, immutable(char)[]).SplitterResult))"],
2505         ["_D3std8typecons__T7TypedefTCQBaQz19__unittestL6513_208FNfZ7MyClassVQBonVAyanZQCh6__ctorMFNaNbNcNiNfQCuZSQDyQDx__TQDrTQDmVQDqnVQCcnZQEj",
2506          "pure nothrow ref @nogc @safe std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef "
2507         ~"std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef.__ctor(std.typecons.__unittestL6513_208().MyClass)"],
2508         ["_D3std6getopt__TQkTAyaTDFNaNbNiNfQoZvTQtTDQsZQBnFNfKAQBiQBlQBkQBrQyZSQCpQCo12GetoptResult",
2509          "@safe std.getopt.GetoptResult std.getopt.getopt!(immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, "
2510         ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)."
2511         ~"getopt(ref immutable(char)[][], immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, "
2512         ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)"],
2513         ["_D3std5regex8internal9kickstart__T7ShiftOrTaZQl11ShiftThread__T3setS_DQCqQCpQCmQCg__TQBzTaZQCfQBv10setInvMaskMFNaNbNiNfkkZvZQCjMFNaNfwZv",
2514          "pure @safe void std.regex.internal.kickstart.ShiftOr!(char).ShiftOr.ShiftThread.set!(std.regex.internal.kickstart.ShiftOr!(char).ShiftOr.ShiftThread.setInvMask(uint, uint)).set(dchar)"],
2515         ["_D3std5stdio4File__T8lockImplX10LockFileExTykZQBaMFmmykZi", // C function as template alias parameter
2516          "int std.stdio.File.lockImpl!(LockFileEx, immutable(uint)).lockImpl(ulong, ulong, immutable(uint))"],
2517         // back reference for type in template AA parameter value
2518         ["_D3std9algorithm9iteration__T12FilterResultSQBq8typecons__T5TupleTiVAyaa1_61TiVQla1_62TiVQva1_63ZQBm__T6renameVHiQBtA2i0a1_63i2a1_61ZQBeMFNcZ9__lambda1TAiZQEw9__xtoHashFNbNeKxSQGsQGrQGk__TQGdSQHiQFs__TQFmTiVQFja1_61TiVQFua1_62TiVQGfa1_63ZQGx__TQFlVQFhA2i0a1_63i2a1_61ZQGjMFNcZQFfTQEyZQJvZm",
2519          `nothrow @trusted ulong std.algorithm.iteration.FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").`
2520         ~`Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult.__xtoHash(ref const(std.algorithm.iteration.`
2521         ~`FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult))`],
2522 
2523         ["_D4test4rrs1FKPiZv",    "void test.rrs1(ref int*)"],
2524         ["_D4test4rrs1FMNkJPiZv", "void test.rrs1(scope return out int*)"],
2525         ["_D4test4rrs1FMNkKPiZv", "void test.rrs1(scope return ref int*)"],
2526         ["_D4test4rrs1FNkJPiZv",  "void test.rrs1(return out int*)"],
2527         ["_D4test4rrs1FNkKPiZv",  "void test.rrs1(return ref int*)"],
2528         ["_D4test4rrs1FNkMJPiZv", "void test.rrs1(return scope out int*)"],
2529         ["_D4test4rrs1FNkMKPiZv", "void test.rrs1(return scope ref int*)"],
2530         ["_D4test4rrs1FNkMPiZv",  "void test.rrs1(return scope int*)"],
2531 
2532         // `scope` and `return` combinations
2533         ["_D3foo3Foo3barMNgFNjNlNfZNgPv", "inout return scope @safe inout(void*) foo.Foo.bar()"],
2534         ["_D3foo3FooQiMNgFNlNfZv",        "inout scope @safe void foo.Foo.foo()"],
2535         ["_D3foo3Foo4foorMNgFNjNfZv",     "inout return @safe void foo.Foo.foor()"],
2536         ["_D3foo3Foo3rabMNgFNlNjNfZv",    "inout scope return @safe void foo.Foo.rab()"],
2537     ];
2538 
2539 
2540     template staticIota(int x)
2541     {
2542         template Seq(T...){ alias Seq = T; }
2543 
2544         static if (x == 0)
2545             alias staticIota = Seq!();
2546         else
2547             alias staticIota = Seq!(staticIota!(x - 1), x - 1);
2548     }
2549     foreach ( i, name; table )
2550     {
2551         auto r = demangle( name[0] );
2552         assert( r == name[1],
2553                 "demangled `" ~ name[0] ~ "` as `" ~ r ~ "` but expected `" ~ name[1] ~ "`");
2554     }
2555     foreach ( i; staticIota!(table.length) )
2556     {
2557         enum r = demangle( table[i][0] );
2558         static assert( r == table[i][1],
2559                 "demangled `" ~ table[i][0] ~ "` as `" ~ r ~ "` but expected `" ~ table[i][1] ~ "`");
2560     }
2561 
2562     {
2563         // https://issues.dlang.org/show_bug.cgi?id=18531
2564         auto symbol = `_D3std3uni__T6toCaseS_DQvQt12toLowerIndexFNaNbNiNewZtVii1043S_DQCjQCi10toLowerTabFNaNbNiNemZwSQDo5ascii7toLowerTAyaZQDzFNaNeQmZ14__foreachbody2MFNaNeKmKwZ14__foreachbody3MFNaNeKwZi`;
2565         auto demangled = `pure @trusted int std.uni.toCase!(std.uni.toLowerIndex(dchar), 1043, std.uni.toLowerTab(ulong), std.ascii.toLower, immutable(char)[]).toCase(immutable(char)[]).__foreachbody2(ref ulong, ref dchar).__foreachbody3(ref dchar)`;
2566         auto dst = new char[200];
2567         auto ret = demangle( symbol, dst);
2568         assert( ret == demangled );
2569     }
2570 }
2571 
2572 unittest
2573 {
2574     // https://issues.dlang.org/show_bug.cgi?id=18300
2575     string s = demangle.mangleof;
2576     foreach (i; 1..77)
2577     {
2578         char[] buf = new char[i];
2579         auto ds = demangle(s, buf);
2580         assert(ds == "pure nothrow @safe char[] core.demangle.demangle(scope return const(char)[], scope return char[], extern (C) char* function(const(char*), char*, ulong*, int*) pure nothrow @trusted*, bool)" ||
2581                ds == "pure nothrow @safe char[] core.demangle.demangle(return scope const(char)[], return scope char[], extern (C) char* function(const(char*), char*, ulong*, int*) pure nothrow @trusted*, bool)" ||
2582                ds == "pure nothrow @safe char[] core.demangle.demangle(scope return const(char)[], scope return char[], extern (C) char* function(const(char*), char*, uint*, int*) pure nothrow @trusted*, bool)" ||
2583                ds == "pure nothrow @safe char[] core.demangle.demangle(return scope const(char)[], return scope char[], extern (C) char* function(const(char*), char*, uint*, int*) pure nothrow @trusted*, bool)", ds);
2584     }
2585 }
2586 
2587 unittest
2588 {
2589     // https://issues.dlang.org/show_bug.cgi?id=18300
2590     string s = "_D1";
2591     string expected = "int ";
2592     foreach (_; 0..10_000)
2593     {
2594         s ~= "a1";
2595         expected ~= "a.";
2596     }
2597     s ~= "FiZi";
2598     expected ~= "F";
2599     assert(s.demangle == expected);
2600 
2601     // https://issues.dlang.org/show_bug.cgi?id=23562
2602     assert(demangle("_Zv") == "_Zv");
2603 }
2604 
2605 // https://issues.dlang.org/show_bug.cgi?id=22235
2606 unittest
2607 {
2608     enum parent = __MODULE__ ~ '.' ~ __traits(identifier, __traits(parent, {}));
2609 
2610     static noreturn abort() { assert(false); }
2611     assert(demangle(abort.mangleof) == "pure nothrow @nogc @safe noreturn " ~ parent ~ "().abort()");
2612 
2613     version (LDC)
2614     {
2615         // FIXME: https://github.com/ldc-developers/ldc/issues/3853
2616     }
2617     else
2618     {
2619         static void accept(noreturn) {}
2620         assert(demangle(accept.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().accept(noreturn)");
2621 
2622         static void templ(T)(T, T) {}
2623         assert(demangle(templ!noreturn.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().templ!(noreturn).templ(noreturn, noreturn)");
2624     }
2625 
2626     static struct S(T) {}
2627     static void aggr(S!noreturn) { assert(0); }
2628     assert(demangle(aggr.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().aggr(" ~ parent ~ "().S!(noreturn).S)");
2629 }
2630 
2631 /*
2632  * Expand an OMF, DMD-generated compressed identifier into its full form
2633  *
2634  * This function only has a visible effect for OMF binaries (Win32),
2635  * as compression is otherwise not used.
2636  *
2637  * See_Also: `compiler/src/dmd/backend/compress.d`
2638  */
2639 string decodeDmdString( const(char)[] ln, ref size_t p ) nothrow pure @safe
2640 {
2641     string s;
2642     uint zlen, zpos;
2643 
2644     // decompress symbol
2645     while ( p < ln.length )
2646     {
2647         int ch = cast(ubyte) ln[p++];
2648         if ( (ch & 0xc0) == 0xc0 )
2649         {
2650             zlen = (ch & 0x7) + 1;
2651             zpos = ((ch >> 3) & 7) + 1; // + zlen;
2652             if ( zpos > s.length )
2653                 break;
2654             s ~= s[$ - zpos .. $ - zpos + zlen];
2655         }
2656         else if ( ch >= 0x80 )
2657         {
2658             if ( p >= ln.length )
2659                 break;
2660             int ch2 = cast(ubyte) ln[p++];
2661             zlen = (ch2 & 0x7f) | ((ch & 0x38) << 4);
2662             if ( p >= ln.length )
2663                 break;
2664             int ch3 = cast(ubyte) ln[p++];
2665             zpos = (ch3 & 0x7f) | ((ch & 7) << 7);
2666             if ( zpos > s.length )
2667                 break;
2668             s ~= s[$ - zpos .. $ - zpos + zlen];
2669         }
2670         else if ( Demangle!().isAlpha(cast(char)ch) || Demangle!().isDigit(cast(char)ch) || ch == '_' )
2671             s ~= cast(char) ch;
2672         else
2673         {
2674             p--;
2675             break;
2676         }
2677     }
2678     return s;
2679 }
2680 
2681 // locally purified for internal use here only
2682 extern (C) private
2683 {
2684     pure @trusted @nogc nothrow pragma(mangle, "fakePureReprintReal") void pureReprintReal(char[] nptr);
2685 
2686     void fakePureReprintReal(char[] nptr)
2687     {
2688         import core.stdc.stdlib : strtold;
2689         import core.stdc.stdio : snprintf;
2690         import core.stdc.errno : errno;
2691 
2692         const err = errno;
2693         real val = strtold(nptr.ptr, null);
2694         snprintf(nptr.ptr, nptr.length, "%#Lg", val);
2695         errno = err;
2696     }
2697 }
2698 
2699 private struct ManglingFlagInfo
2700 {
2701     /// The flag value to use
2702     ushort flag;
2703 
2704     /// Human-readable representation
2705     string value;
2706 }
2707 
2708 private enum TypeCtor : ushort {
2709     None      = 0,
2710     //// 'x'
2711     Const     = (1 << 1),
2712     /// 'y'
2713     Immutable = (1 << 2),
2714     /// 'O'
2715     Shared    = (1 << 3),
2716     ///
2717     InOut     = (1 << 4),
2718 }
2719 
2720 private immutable ManglingFlagInfo[] typeCtors = [
2721     ManglingFlagInfo(TypeCtor.Immutable, "immutable"),
2722     ManglingFlagInfo(TypeCtor.Shared,    "shared"),
2723     ManglingFlagInfo(TypeCtor.InOut,     "inout"),
2724     ManglingFlagInfo(TypeCtor.Const,     "const"),
2725 ];
2726 
2727 private enum FuncAttributes : ushort {
2728     None      = 0,
2729     //// 'a'
2730     Pure     = (1 << 1),
2731     //// 'b'
2732     Nothrow  = (1 << 2),
2733     //// 'c'
2734     Ref      = (1 << 3),
2735     //// 'd'
2736     Property = (1 << 4),
2737     //// 'e'
2738     Trusted  = (1 << 5),
2739     //// 'f'
2740     Safe     = (1 << 6),
2741     //// 'i'
2742     NoGC     = (1 << 7),
2743     //// 'j'
2744     Return   = (1 << 8),
2745     //// 'l'
2746     Scope    = (1 << 9),
2747     //// 'm'
2748     Live     = (1 << 10),
2749 
2750     /// Their order matter
2751     ReturnScope   = (1 << 11),
2752     ScopeReturn   = (1 << 12),
2753 }
2754 
2755 // The order in which we process is the same as in compiler/dmd/src/dmangle.d
2756 private immutable ManglingFlagInfo[] funcAttrs = [
2757     ManglingFlagInfo(FuncAttributes.Pure,     "pure"),
2758     ManglingFlagInfo(FuncAttributes.Nothrow,  "nothrow"),
2759     ManglingFlagInfo(FuncAttributes.Ref,      "ref"),
2760     ManglingFlagInfo(FuncAttributes.Property, "@property"),
2761     ManglingFlagInfo(FuncAttributes.NoGC,     "@nogc"),
2762 
2763     ManglingFlagInfo(FuncAttributes.ReturnScope, "return scope"),
2764     ManglingFlagInfo(FuncAttributes.ScopeReturn, "scope return"),
2765 
2766     ManglingFlagInfo(FuncAttributes.Return,   "return"),
2767     ManglingFlagInfo(FuncAttributes.Scope,    "scope"),
2768 
2769     ManglingFlagInfo(FuncAttributes.Live,     "@live"),
2770     ManglingFlagInfo(FuncAttributes.Trusted,  "@trusted"),
2771     ManglingFlagInfo(FuncAttributes.Safe,     "@safe"),
2772 ];
2773 
2774 private string toStringConsume (immutable ManglingFlagInfo[] infos, ref ushort base)
2775     @safe pure nothrow @nogc
2776 {
2777     foreach (const ref info; infos)
2778     {
2779         if ((base & info.flag) == info.flag)
2780         {
2781             base &= ~info.flag;
2782             return info.value;
2783         }
2784     }
2785     return null;
2786 }
2787 
2788 private shared CXX_DEMANGLER __cxa_demangle;
2789 
2790 /**
2791  * Returns:
2792  *  a CXX_DEMANGLER if a C++ stdlib is loaded
2793  */
2794 
2795 CXX_DEMANGLER getCXXDemangler() nothrow @trusted
2796 {
2797     import core.atomic : atomicLoad, atomicStore;
2798     if (__cxa_demangle is null)
2799     version (Posix)
2800     {
2801         import core.sys.posix.dlfcn : dlsym;
2802         version (DragonFlyBSD) import core.sys.dragonflybsd.dlfcn : RTLD_DEFAULT;
2803         version (FreeBSD) import core.sys.freebsd.dlfcn : RTLD_DEFAULT;
2804         version (linux) import core.sys.linux.dlfcn : RTLD_DEFAULT;
2805         version (NetBSD) import core.sys.netbsd.dlfcn : RTLD_DEFAULT;
2806         version (OpenBSD) import core.sys.openbsd.dlfcn : RTLD_DEFAULT;
2807         version (Darwin) import core.sys.darwin.dlfcn : RTLD_DEFAULT;
2808         version (Solaris) import core.sys.solaris.dlfcn : RTLD_DEFAULT;
2809 
2810         if (auto found = cast(CXX_DEMANGLER) dlsym(RTLD_DEFAULT, "__cxa_demangle"))
2811             atomicStore(__cxa_demangle, found);
2812     }
2813 
2814     if (__cxa_demangle is null)
2815     {
2816         static extern(C) char* _(const char* mangled_name, char* output_buffer,
2817              size_t* length, int* status) nothrow pure @trusted
2818         {
2819             *status = -1;
2820             return null;
2821         }
2822         atomicStore(__cxa_demangle, &_);
2823     }
2824 
2825     return atomicLoad(__cxa_demangle);
2826 }
2827 
2828 /**
2829  * Demangles C++ mangled names.  If it is not a C++ mangled name, it
2830  * returns its argument name.
2831  *
2832  * Params:
2833  *  buf = The string to demangle.
2834  *  __cxa_demangle = C++ demangler
2835  *  dst = An optional destination buffer.
2836  *
2837  * Returns:
2838  *  The demangled name or the original string if the name is not a mangled
2839  *  C++ name.
2840  */
2841 private char[] demangleCXX(return scope const(char)[] buf, CXX_DEMANGLER __cxa_demangle, return scope char[] dst = null,) nothrow pure @trusted
2842 {
2843     char[] c_string = dst; // temporarily use dst buffer if possible
2844     c_string.length = buf.length + 1;
2845     c_string[0 .. buf.length] = buf[0 .. buf.length];
2846     c_string[buf.length] = '\0';
2847 
2848     int status;
2849     size_t demangled_length;
2850     auto demangled = __cxa_demangle(&c_string[0], null, &demangled_length, &status);
2851     scope (exit) {
2852         import core.memory;
2853         pureFree(cast(void*) demangled);
2854     }
2855     if (status == 0)
2856     {
2857         dst.length = demangled_length;
2858         dst[] = demangled[0 .. demangled_length];
2859         return dst;
2860     }
2861 
2862     dst.length = buf.length;
2863     dst[] = buf[];
2864     return dst;
2865 }
2866 
2867 /**
2868  * Error handling through Exceptions
2869  *
2870  * The following types / functions are only used in this module,
2871  * hence why the functions are `@trusted`.
2872  * To make things `@nogc`, default-initialized instances are thrown.
2873  */
2874 private class ParseException : Exception
2875 {
2876     public this(string msg) @safe pure nothrow
2877     {
2878         super(msg);
2879     }
2880 }
2881 
2882 /// Ditto
2883 private class OverflowException : Exception
2884 {
2885     public this(string msg) @safe pure nothrow
2886     {
2887         super(msg);
2888     }
2889 }
2890 
2891 /// Ditto
2892 private noreturn error(string msg = "Invalid symbol") @trusted pure
2893 {
2894     version (DigitalMars) pragma(inline, false); // tame dmd inliner
2895 
2896     //throw new ParseException( msg );
2897     debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr );
2898     throw __ctfe ? new ParseException(msg)
2899         : cast(ParseException) __traits(initSymbol, ParseException).ptr;
2900 }
2901 
2902 /// Ditto
2903 private noreturn overflow(string msg = "Buffer overflow") @trusted pure
2904 {
2905     version (DigitalMars) pragma(inline, false); // tame dmd inliner
2906 
2907     //throw new OverflowException( msg );
2908     debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr );
2909     throw cast(OverflowException) __traits(initSymbol, OverflowException).ptr;
2910 }
2911 
2912 private struct Buffer
2913 {
2914     enum size_t minSize = 4000;
2915 
2916     @safe pure:
2917 
2918     private char[] dst;
2919     private size_t len;
2920 
2921     public alias opDollar = len;
2922 
2923     public size_t length () const scope @safe pure nothrow @nogc
2924     {
2925         return this.len;
2926     }
2927 
2928     public inout(char)[] opSlice (size_t from, size_t to)
2929         inout return scope @safe pure nothrow @nogc
2930     {
2931         assert(from <= to);
2932         assert(to <= len);
2933         return this.dst[from .. to];
2934     }
2935 
2936     static bool contains(scope const(char)[] a, scope const(char)[] b) @trusted
2937     {
2938         if (a.length && b.length)
2939         {
2940             auto bend = b.ptr + b.length;
2941             auto aend = a.ptr + a.length;
2942             return a.ptr <= b.ptr && bend <= aend;
2943         }
2944         return false;
2945     }
2946 
2947     char[] copyInput(scope const(char)[] buf)
2948         return scope nothrow
2949     {
2950         if (dst.length < buf.length)
2951             dst.length = buf.length;
2952         char[] r = dst[0 .. buf.length];
2953         r[] = buf[];
2954         return r;
2955     }
2956 
2957     // move val to the end of the dst buffer
2958     char[] shift(scope const(char)[] val) return scope
2959     {
2960         version (DigitalMars) pragma(inline, false); // tame dmd inliner
2961 
2962         if (val.length)
2963         {
2964             assert( contains( dst[0 .. len], val ) );
2965             debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr );
2966 
2967             if (len + val.length > dst.length)
2968                 overflow();
2969             size_t v = &val[0] - &dst[0];
2970             dst[len .. len + val.length] = val[];
2971             for (size_t p = v; p < len; p++)
2972                 dst[p] = dst[p + val.length];
2973 
2974             return dst[len - val.length .. len];
2975         }
2976         return null;
2977     }
2978 
2979     // remove val from dst buffer
2980     void remove(scope const(char)[] val) scope
2981     {
2982         version (DigitalMars) pragma(inline, false); // tame dmd inliner
2983 
2984         if ( val.length )
2985         {
2986             assert( contains( dst[0 .. len], val ) );
2987             debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr );
2988             size_t v = &val[0] - &dst[0];
2989             assert( len >= val.length && len <= dst.length );
2990             len -= val.length;
2991             for (size_t p = v; p < len; p++)
2992                 dst[p] = dst[p + val.length];
2993         }
2994     }
2995 
2996     char[] append(scope const(char)[] val) return scope
2997     {
2998         version (DigitalMars) pragma(inline, false); // tame dmd inliner
2999 
3000         if (val.length)
3001         {
3002             if ( !dst.length )
3003                 dst.length = minSize;
3004             assert( !contains( dst[0 .. len], val ) );
3005             debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr );
3006 
3007             if ( dst.length - len >= val.length && &dst[len] == &val[0] )
3008             {
3009                 // data is already in place
3010                 auto t = dst[len .. len + val.length];
3011                 len += val.length;
3012                 return t;
3013             }
3014             if ( dst.length - len >= val.length )
3015             {
3016                 dst[len .. len + val.length] = val[];
3017                 auto t = dst[len .. len + val.length];
3018                 len += val.length;
3019                 return t;
3020             }
3021             overflow();
3022         }
3023         return null;
3024     }
3025 }