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