diff options
Diffstat (limited to 'src/undead/doformat.d')
| -rw-r--r-- | src/undead/doformat.d | 1620 | 
1 files changed, 1620 insertions, 0 deletions
diff --git a/src/undead/doformat.d b/src/undead/doformat.d new file mode 100644 index 0000000..4fc0daf --- /dev/null +++ b/src/undead/doformat.d @@ -0,0 +1,1620 @@ +// Written in the D programming language. + +/** +   Copyright: Copyright Digital Mars 2000-2013. + +   License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). + +   Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com, +   Andrei Alexandrescu), and Kenji Hara + +   Source: $(PHOBOSSRC std/_format.d) + */ +module undead.doformat; + +//debug=format;                // uncomment to turn on debugging printf's + +import core.vararg; +import std.exception; +import std.meta; +import std.range.primitives; +import std.traits; +import std.format; + +version(CRuntime_DigitalMars) +{ +    version = DigitalMarsC; +} + +version (DigitalMarsC) +{ +    // This is DMC's internal floating point formatting function +    extern (C) +    { +        extern shared char* function(int c, int flags, int precision, +                in real* pdval, +                char* buf, size_t* psl, int width) __pfloatfmt; +    } +} + +/********************************************************************** + * Signals a mismatch between a format and its corresponding argument. + */ +class FormatException : Exception +{ +    @safe pure nothrow +    this() +    { +        super("format error"); +    } + +    @safe pure nothrow +    this(string msg, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) +    { +        super(msg, fn, ln, next); +    } +} + + +// Legacy implementation + +enum Mangle : char +{ +    Tvoid     = 'v', +    Tbool     = 'b', +    Tbyte     = 'g', +    Tubyte    = 'h', +    Tshort    = 's', +    Tushort   = 't', +    Tint      = 'i', +    Tuint     = 'k', +    Tlong     = 'l', +    Tulong    = 'm', +    Tfloat    = 'f', +    Tdouble   = 'd', +    Treal     = 'e', + +    Tifloat   = 'o', +    Tidouble  = 'p', +    Tireal    = 'j', +    Tcfloat   = 'q', +    Tcdouble  = 'r', +    Tcreal    = 'c', + +    Tchar     = 'a', +    Twchar    = 'u', +    Tdchar    = 'w', + +    Tarray    = 'A', +    Tsarray   = 'G', +    Taarray   = 'H', +    Tpointer  = 'P', +    Tfunction = 'F', +    Tident    = 'I', +    Tclass    = 'C', +    Tstruct   = 'S', +    Tenum     = 'E', +    Ttypedef  = 'T', +    Tdelegate = 'D', + +    Tconst    = 'x', +    Timmutable = 'y', +} + +// return the TypeInfo for a primitive type and null otherwise.  This +// is required since for arrays of ints we only have the mangled char +// to work from. If arrays always subclassed TypeInfo_Array this +// routine could go away. +private TypeInfo primitiveTypeInfo(Mangle m) +{ +    // BUG: should fix this in static this() to avoid double checked locking bug +    __gshared TypeInfo[Mangle] dic; +    if (!dic.length) +    { +        dic = [ +            Mangle.Tvoid : typeid(void), +            Mangle.Tbool : typeid(bool), +            Mangle.Tbyte : typeid(byte), +            Mangle.Tubyte : typeid(ubyte), +            Mangle.Tshort : typeid(short), +            Mangle.Tushort : typeid(ushort), +            Mangle.Tint : typeid(int), +            Mangle.Tuint : typeid(uint), +            Mangle.Tlong : typeid(long), +            Mangle.Tulong : typeid(ulong), +            Mangle.Tfloat : typeid(float), +            Mangle.Tdouble : typeid(double), +            Mangle.Treal : typeid(real), +            Mangle.Tifloat : typeid(ifloat), +            Mangle.Tidouble : typeid(idouble), +            Mangle.Tireal : typeid(ireal), +            Mangle.Tcfloat : typeid(cfloat), +            Mangle.Tcdouble : typeid(cdouble), +            Mangle.Tcreal : typeid(creal), +            Mangle.Tchar : typeid(char), +            Mangle.Twchar : typeid(wchar), +            Mangle.Tdchar : typeid(dchar) +            ]; +    } +    auto p = m in dic; +    return p ? *p : null; +} + +// This stuff has been removed from the docs and is planned for deprecation. +/* + * Interprets variadic argument list pointed to by argptr whose types + * are given by arguments[], formats them according to embedded format + * strings in the variadic argument list, and sends the resulting + * characters to putc. + * + * The variadic arguments are consumed in order.  Each is formatted + * into a sequence of chars, using the default format specification + * for its type, and the characters are sequentially passed to putc. + * If a $(D char[]), $(D wchar[]), or $(D dchar[]) argument is + * encountered, it is interpreted as a format string. As many + * arguments as specified in the format string are consumed and + * formatted according to the format specifications in that string and + * passed to putc. If there are too few remaining arguments, a + * $(D FormatException) is thrown. If there are more remaining arguments than + * needed by the format specification, the default processing of + * arguments resumes until they are all consumed. + * + * Params: + *        putc =        Output is sent do this delegate, character by character. + *        arguments = Array of $(D TypeInfo)s, one for each argument to be formatted. + *        argptr = Points to variadic argument list. + * + * Throws: + *        Mismatched arguments and formats result in a $(D FormatException) being thrown. + * + * Format_String: + *        <a name="format-string">$(I Format strings)</a> + *        consist of characters interspersed with + *        $(I format specifications). Characters are simply copied + *        to the output (such as putc) after any necessary conversion + *        to the corresponding UTF-8 sequence. + * + *        A $(I format specification) starts with a '%' character, + *        and has the following grammar: + +$(CONSOLE +$(I FormatSpecification): +    $(B '%%') +    $(B '%') $(I Flags) $(I Width) $(I Precision) $(I FormatChar) + +$(I Flags): +    $(I empty) +    $(B '-') $(I Flags) +    $(B '+') $(I Flags) +    $(B '#') $(I Flags) +    $(B '0') $(I Flags) +    $(B ' ') $(I Flags) + +$(I Width): +    $(I empty) +    $(I Integer) +    $(B '*') + +$(I Precision): +    $(I empty) +    $(B '.') +    $(B '.') $(I Integer) +    $(B '.*') + +$(I Integer): +    $(I Digit) +    $(I Digit) $(I Integer) + +$(I Digit): +    $(B '0') +    $(B '1') +    $(B '2') +    $(B '3') +    $(B '4') +    $(B '5') +    $(B '6') +    $(B '7') +    $(B '8') +    $(B '9') + +$(I FormatChar): +    $(B 's') +    $(B 'b') +    $(B 'd') +    $(B 'o') +    $(B 'x') +    $(B 'X') +    $(B 'e') +    $(B 'E') +    $(B 'f') +    $(B 'F') +    $(B 'g') +    $(B 'G') +    $(B 'a') +    $(B 'A') +) +    $(DL +    $(DT $(I Flags)) +    $(DL +        $(DT $(B '-')) +        $(DD +        Left justify the result in the field. +        It overrides any $(B 0) flag.) + +        $(DT $(B '+')) +        $(DD Prefix positive numbers in a signed conversion with a $(B +). +        It overrides any $(I space) flag.) + +        $(DT $(B '#')) +        $(DD Use alternative formatting: +        $(DL +            $(DT For $(B 'o'):) +            $(DD Add to precision as necessary so that the first digit +            of the octal formatting is a '0', even if both the argument +            and the $(I Precision) are zero.) +            $(DT For $(B 'x') ($(B 'X')):) +            $(DD If non-zero, prefix result with $(B 0x) ($(B 0X)).) +            $(DT For floating point formatting:) +            $(DD Always insert the decimal point.) +            $(DT For $(B 'g') ($(B 'G')):) +            $(DD Do not elide trailing zeros.) +        )) + +        $(DT $(B '0')) +        $(DD For integer and floating point formatting when not nan or +        infinity, use leading zeros +        to pad rather than spaces. +        Ignore if there's a $(I Precision).) + +        $(DT $(B ' ')) +        $(DD Prefix positive numbers in a signed conversion with a space.) +    ) + +    $(DT $(I Width)) +    $(DD +    Specifies the minimum field width. +    If the width is a $(B *), the next argument, which must be +    of type $(B int), is taken as the width. +    If the width is negative, it is as if the $(B -) was given +    as a $(I Flags) character.) + +    $(DT $(I Precision)) +    $(DD Gives the precision for numeric conversions. +    If the precision is a $(B *), the next argument, which must be +    of type $(B int), is taken as the precision. If it is negative, +    it is as if there was no $(I Precision).) + +    $(DT $(I FormatChar)) +    $(DD +    $(DL +        $(DT $(B 's')) +        $(DD The corresponding argument is formatted in a manner consistent +        with its type: +        $(DL +            $(DT $(B bool)) +            $(DD The result is <tt>'true'</tt> or <tt>'false'</tt>.) +            $(DT integral types) +            $(DD The $(B %d) format is used.) +            $(DT floating point types) +            $(DD The $(B %g) format is used.) +            $(DT string types) +            $(DD The result is the string converted to UTF-8.) +            A $(I Precision) specifies the maximum number of characters +            to use in the result. +            $(DT classes derived from $(B Object)) +            $(DD The result is the string returned from the class instance's +            $(B .toString()) method. +            A $(I Precision) specifies the maximum number of characters +            to use in the result.) +            $(DT non-string static and dynamic arrays) +            $(DD The result is [s<sub>0</sub>, s<sub>1</sub>, ...] +            where s<sub>k</sub> is the kth element +            formatted with the default format.) +        )) + +        $(DT $(B 'b','d','o','x','X')) +        $(DD The corresponding argument must be an integral type +        and is formatted as an integer. If the argument is a signed type +        and the $(I FormatChar) is $(B d) it is converted to +        a signed string of characters, otherwise it is treated as +        unsigned. An argument of type $(B bool) is formatted as '1' +        or '0'. The base used is binary for $(B b), octal for $(B o), +        decimal +        for $(B d), and hexadecimal for $(B x) or $(B X). +        $(B x) formats using lower case letters, $(B X) uppercase. +        If there are fewer resulting digits than the $(I Precision), +        leading zeros are used as necessary. +        If the $(I Precision) is 0 and the number is 0, no digits +        result.) + +        $(DT $(B 'e','E')) +        $(DD A floating point number is formatted as one digit before +        the decimal point, $(I Precision) digits after, the $(I FormatChar), +        ±, followed by at least a two digit exponent: $(I d.dddddd)e$(I ±dd). +        If there is no $(I Precision), six +        digits are generated after the decimal point. +        If the $(I Precision) is 0, no decimal point is generated.) + +        $(DT $(B 'f','F')) +        $(DD A floating point number is formatted in decimal notation. +        The $(I Precision) specifies the number of digits generated +        after the decimal point. It defaults to six. At least one digit +        is generated before the decimal point. If the $(I Precision) +        is zero, no decimal point is generated.) + +        $(DT $(B 'g','G')) +        $(DD A floating point number is formatted in either $(B e) or +        $(B f) format for $(B g); $(B E) or $(B F) format for +        $(B G). +        The $(B f) format is used if the exponent for an $(B e) format +        is greater than -5 and less than the $(I Precision). +        The $(I Precision) specifies the number of significant +        digits, and defaults to six. +        Trailing zeros are elided after the decimal point, if the fractional +        part is zero then no decimal point is generated.) + +        $(DT $(B 'a','A')) +        $(DD A floating point number is formatted in hexadecimal +        exponential notation 0x$(I h.hhhhhh)p$(I ±d). +        There is one hexadecimal digit before the decimal point, and as +        many after as specified by the $(I Precision). +        If the $(I Precision) is zero, no decimal point is generated. +        If there is no $(I Precision), as many hexadecimal digits as +        necessary to exactly represent the mantissa are generated. +        The exponent is written in as few digits as possible, +        but at least one, is in decimal, and represents a power of 2 as in +        $(I h.hhhhhh)*2<sup>$(I ±d)</sup>. +        The exponent for zero is zero. +        The hexadecimal digits, x and p are in upper case if the +        $(I FormatChar) is upper case.) +    ) + +    Floating point NaN's are formatted as $(B nan) if the +    $(I FormatChar) is lower case, or $(B NAN) if upper. +    Floating point infinities are formatted as $(B inf) or +    $(B infinity) if the +    $(I FormatChar) is lower case, or $(B INF) or $(B INFINITY) if upper. +    )) + +Example: + +------------------------- +import core.stdc.stdio; +import std.format; + +void myPrint(...) +{ +    void putc(dchar c) +    { +        fputc(c, stdout); +    } + +    std.format.doFormat(&putc, _arguments, _argptr); +} + +void main() +{ +    int x = 27; + +    // prints 'The answer is 27:6' +    myPrint("The answer is %s:", x, 6); +} +------------------------ + */ +void doFormat()(scope void delegate(dchar) putc, TypeInfo[] arguments, va_list ap) +{ +    import std.utf : toUCSindex, isValidDchar, UTFException, toUTF8; +    import core.stdc.string : strlen; +    import core.stdc.stdlib : alloca, malloc, realloc, free; +    import core.stdc.stdio : snprintf; + +    size_t bufLength = 1024; +    void* argBuffer = malloc(bufLength); +    scope(exit) free(argBuffer); + +    size_t bufUsed = 0; +    foreach (ti; arguments) +    { +        // Ensure the required alignment +        bufUsed += ti.talign - 1; +        bufUsed -= (cast(size_t)argBuffer + bufUsed) & (ti.talign - 1); +        auto pos = bufUsed; +        // Align to next word boundary +        bufUsed += ti.tsize + size_t.sizeof - 1; +        bufUsed -= (cast(size_t)argBuffer + bufUsed) & (size_t.sizeof - 1); +        // Resize buffer if necessary +        while (bufUsed > bufLength) +        { +            bufLength *= 2; +            argBuffer = realloc(argBuffer, bufLength); +        } +        // Copy argument into buffer +        va_arg(ap, ti, argBuffer + pos); +    } + +    auto argptr = argBuffer; +    void* skipArg(TypeInfo ti) +    { +        // Ensure the required alignment +        argptr += ti.talign - 1; +        argptr -= cast(size_t)argptr & (ti.talign - 1); +        auto p = argptr; +        // Align to next word boundary +        argptr += ti.tsize + size_t.sizeof - 1; +        argptr -= cast(size_t)argptr & (size_t.sizeof - 1); +        return p; +    } +    auto getArg(T)() +    { +        return *cast(T*)skipArg(typeid(T)); +    } + +    TypeInfo ti; +    Mangle m; +    uint flags; +    int field_width; +    int precision; + +    enum : uint +    { +        FLdash = 1, +        FLplus = 2, +        FLspace = 4, +        FLhash = 8, +        FLlngdbl = 0x20, +        FL0pad = 0x40, +        FLprecision = 0x80, +    } + +    static TypeInfo skipCI(TypeInfo valti) +    { +        for (;;) +        { +            if (typeid(valti).name.length == 18 && +                    typeid(valti).name[9..18] == "Invariant") +                valti = (cast(TypeInfo_Invariant)valti).base; +            else if (typeid(valti).name.length == 14 && +                    typeid(valti).name[9..14] == "Const") +                valti = (cast(TypeInfo_Const)valti).base; +            else +                break; +        } + +        return valti; +    } + +    void formatArg(char fc) +    { +        bool vbit; +        ulong vnumber; +        char vchar; +        dchar vdchar; +        Object vobject; +        real vreal; +        creal vcreal; +        Mangle m2; +        int signed = 0; +        uint base = 10; +        int uc; +        char[ulong.sizeof * 8] tmpbuf; // long enough to print long in binary +        const(char)* prefix = ""; +        string s; + +        void putstr(const char[] s) +        { +            //printf("putstr: s = %.*s, flags = x%x\n", s.length, s.ptr, flags); +            ptrdiff_t padding = field_width - +                (strlen(prefix) + toUCSindex(s, s.length)); +            ptrdiff_t prepad = 0; +            ptrdiff_t postpad = 0; +            if (padding > 0) +            { +                if (flags & FLdash) +                    postpad = padding; +                else +                    prepad = padding; +            } + +            if (flags & FL0pad) +            { +                while (*prefix) +                    putc(*prefix++); +                while (prepad--) +                    putc('0'); +            } +            else +            { +                while (prepad--) +                    putc(' '); +                while (*prefix) +                    putc(*prefix++); +            } + +            foreach (dchar c; s) +                putc(c); + +            while (postpad--) +                putc(' '); +        } + +        void putreal(real v) +        { +            //printf("putreal %Lg\n", vreal); + +            switch (fc) +            { +                case 's': +                    fc = 'g'; +                    break; + +                case 'f', 'F', 'e', 'E', 'g', 'G', 'a', 'A': +                    break; + +                default: +                    //printf("fc = '%c'\n", fc); +                Lerror: +                    throw new FormatException("incompatible format character for floating point type"); +            } +            version (DigitalMarsC) +            { +                uint sl; +                char[] fbuf = tmpbuf; +                if (!(flags & FLprecision)) +                    precision = 6; +                while (1) +                { +                    sl = fbuf.length; +                    prefix = (*__pfloatfmt)(fc, flags | FLlngdbl, +                            precision, &v, cast(char*)fbuf, &sl, field_width); +                    if (sl != -1) +                        break; +                    sl = fbuf.length * 2; +                    fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; +                } +                putstr(fbuf[0 .. sl]); +            } +            else +            { +                ptrdiff_t sl; +                char[] fbuf = tmpbuf; +                char[12] format; +                format[0] = '%'; +                int i = 1; +                if (flags & FLdash) +                    format[i++] = '-'; +                if (flags & FLplus) +                    format[i++] = '+'; +                if (flags & FLspace) +                    format[i++] = ' '; +                if (flags & FLhash) +                    format[i++] = '#'; +                if (flags & FL0pad) +                    format[i++] = '0'; +                format[i + 0] = '*'; +                format[i + 1] = '.'; +                format[i + 2] = '*'; +                format[i + 3] = 'L'; +                format[i + 4] = fc; +                format[i + 5] = 0; +                if (!(flags & FLprecision)) +                    precision = -1; +                while (1) +                { +                    sl = fbuf.length; +                    int n; +                    version (CRuntime_Microsoft) +                    { +                        import std.math : isNaN, isInfinity; +                        if (isNaN(v)) // snprintf writes 1.#QNAN +                            n = snprintf(fbuf.ptr, sl, "nan"); +                        else if (isInfinity(v)) // snprintf writes 1.#INF +                            n = snprintf(fbuf.ptr, sl, v < 0 ? "-inf" : "inf"); +                        else +                            n = snprintf(fbuf.ptr, sl, format.ptr, field_width, +                                         precision, cast(double)v); +                    } +                    else +                        n = snprintf(fbuf.ptr, sl, format.ptr, field_width, +                                precision, v); +                    //printf("format = '%s', n = %d\n", cast(char*)format, n); +                    if (n >= 0 && n < sl) +                    {        sl = n; +                        break; +                    } +                    if (n < 0) +                        sl = sl * 2; +                    else +                        sl = n + 1; +                    fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; +                } +                putstr(fbuf[0 .. sl]); +            } +            return; +        } + +        static Mangle getMan(TypeInfo ti) +        { +          auto m = cast(Mangle)typeid(ti).name[9]; +          if (typeid(ti).name.length == 20 && +              typeid(ti).name[9..20] == "StaticArray") +                m = cast(Mangle)'G'; +          return m; +        } + +        /* p = pointer to the first element in the array +         * len = number of elements in the array +         * valti = type of the elements +         */ +        void putArray(void* p, size_t len, TypeInfo valti) +        { +          //printf("\nputArray(len = %u), tsize = %u\n", len, valti.tsize); +          putc('['); +          valti = skipCI(valti); +          size_t tsize = valti.tsize; +          auto argptrSave = argptr; +          auto tiSave = ti; +          auto mSave = m; +          ti = valti; +          //printf("\n%.*s\n", typeid(valti).name.length, typeid(valti).name.ptr); +          m = getMan(valti); +          while (len--) +          { +            //doFormat(putc, (&valti)[0 .. 1], p); +            argptr = p; +            formatArg('s'); +            p += tsize; +            if (len > 0) putc(','); +          } +          m = mSave; +          ti = tiSave; +          argptr = argptrSave; +          putc(']'); +        } + +        void putAArray(ubyte[long] vaa, TypeInfo valti, TypeInfo keyti) +        { +            putc('['); +            bool comma=false; +            auto argptrSave = argptr; +            auto tiSave = ti; +            auto mSave = m; +            valti = skipCI(valti); +            keyti = skipCI(keyti); +            foreach (ref fakevalue; vaa) +            { +                if (comma) putc(','); +                comma = true; +                void *pkey = &fakevalue; +                version (D_LP64) +                    pkey -= (long.sizeof + 15) & ~(15); +                else +                    pkey -= (long.sizeof + size_t.sizeof - 1) & ~(size_t.sizeof - 1); + +                // the key comes before the value +                auto keysize = keyti.tsize; +                version (D_LP64) +                    auto keysizet = (keysize + 15) & ~(15); +                else +                    auto keysizet = (keysize + size_t.sizeof - 1) & ~(size_t.sizeof - 1); + +                void* pvalue = pkey + keysizet; + +                //doFormat(putc, (&keyti)[0..1], pkey); +                m = getMan(keyti); +                argptr = pkey; + +                ti = keyti; +                formatArg('s'); + +                putc(':'); +                //doFormat(putc, (&valti)[0..1], pvalue); +                m = getMan(valti); +                argptr = pvalue; + +                ti = valti; +                formatArg('s'); +            } +            m = mSave; +            ti = tiSave; +            argptr = argptrSave; +            putc(']'); +        } + +        //printf("formatArg(fc = '%c', m = '%c')\n", fc, m); +        int mi; +        switch (m) +        { +            case Mangle.Tbool: +                vbit = getArg!(bool)(); +                if (fc != 's') +                {   vnumber = vbit; +                    goto Lnumber; +                } +                putstr(vbit ? "true" : "false"); +                return; + +            case Mangle.Tchar: +                vchar = getArg!(char)(); +                if (fc != 's') +                {   vnumber = vchar; +                    goto Lnumber; +                } +            L2: +                putstr((&vchar)[0 .. 1]); +                return; + +            case Mangle.Twchar: +                vdchar = getArg!(wchar)(); +                goto L1; + +            case Mangle.Tdchar: +                vdchar = getArg!(dchar)(); +            L1: +                if (fc != 's') +                {   vnumber = vdchar; +                    goto Lnumber; +                } +                if (vdchar <= 0x7F) +                {   vchar = cast(char)vdchar; +                    goto L2; +                } +                else +                {   if (!isValidDchar(vdchar)) +                        throw new UTFException("invalid dchar in format"); +                    char[4] vbuf; +                    putstr(toUTF8(vbuf, vdchar)); +                } +                return; + +            case Mangle.Tbyte: +                signed = 1; +                vnumber = getArg!(byte)(); +                goto Lnumber; + +            case Mangle.Tubyte: +                vnumber = getArg!(ubyte)(); +                goto Lnumber; + +            case Mangle.Tshort: +                signed = 1; +                vnumber = getArg!(short)(); +                goto Lnumber; + +            case Mangle.Tushort: +                vnumber = getArg!(ushort)(); +                goto Lnumber; + +            case Mangle.Tint: +                signed = 1; +                vnumber = getArg!(int)(); +                goto Lnumber; + +            case Mangle.Tuint: +            Luint: +                vnumber = getArg!(uint)(); +                goto Lnumber; + +            case Mangle.Tlong: +                signed = 1; +                vnumber = cast(ulong)getArg!(long)(); +                goto Lnumber; + +            case Mangle.Tulong: +            Lulong: +                vnumber = getArg!(ulong)(); +                goto Lnumber; + +            case Mangle.Tclass: +                vobject = getArg!(Object)(); +                if (vobject is null) +                    s = "null"; +                else +                    s = vobject.toString(); +                goto Lputstr; + +            case Mangle.Tpointer: +                vnumber = cast(ulong)getArg!(void*)(); +                if (fc != 'x')  uc = 1; +                flags |= FL0pad; +                if (!(flags & FLprecision)) +                {   flags |= FLprecision; +                    precision = (void*).sizeof; +                } +                base = 16; +                goto Lnumber; + +            case Mangle.Tfloat: +            case Mangle.Tifloat: +                if (fc == 'x' || fc == 'X') +                    goto Luint; +                vreal = getArg!(float)(); +                goto Lreal; + +            case Mangle.Tdouble: +            case Mangle.Tidouble: +                if (fc == 'x' || fc == 'X') +                    goto Lulong; +                vreal = getArg!(double)(); +                goto Lreal; + +            case Mangle.Treal: +            case Mangle.Tireal: +                vreal = getArg!(real)(); +                goto Lreal; + +            case Mangle.Tcfloat: +                vcreal = getArg!(cfloat)(); +                goto Lcomplex; + +            case Mangle.Tcdouble: +                vcreal = getArg!(cdouble)(); +                goto Lcomplex; + +            case Mangle.Tcreal: +                vcreal = getArg!(creal)(); +                goto Lcomplex; + +            case Mangle.Tsarray: +                putArray(argptr, (cast(TypeInfo_StaticArray)ti).len, (cast(TypeInfo_StaticArray)ti).next); +                return; + +            case Mangle.Tarray: +                mi = 10; +                if (typeid(ti).name.length == 14 && +                    typeid(ti).name[9..14] == "Array") +                { // array of non-primitive types +                  TypeInfo tn = (cast(TypeInfo_Array)ti).next; +                  tn = skipCI(tn); +                  switch (cast(Mangle)typeid(tn).name[9]) +                  { +                    case Mangle.Tchar:  goto LarrayChar; +                    case Mangle.Twchar: goto LarrayWchar; +                    case Mangle.Tdchar: goto LarrayDchar; +                    default: +                        break; +                  } +                  void[] va = getArg!(void[])(); +                  putArray(va.ptr, va.length, tn); +                  return; +                } +                if (typeid(ti).name.length == 25 && +                    typeid(ti).name[9..25] == "AssociativeArray") +                { // associative array +                  ubyte[long] vaa = getArg!(ubyte[long])(); +                  putAArray(vaa, +                        (cast(TypeInfo_AssociativeArray)ti).next, +                        (cast(TypeInfo_AssociativeArray)ti).key); +                  return; +                } + +                while (1) +                { +                    m2 = cast(Mangle)typeid(ti).name[mi]; +                    switch (m2) +                    { +                        case Mangle.Tchar: +                        LarrayChar: +                            s = getArg!(string)(); +                            goto Lputstr; + +                        case Mangle.Twchar: +                        LarrayWchar: +                            wchar[] sw = getArg!(wchar[])(); +                            s = toUTF8(sw); +                            goto Lputstr; + +                        case Mangle.Tdchar: +                        LarrayDchar: +                            s = toUTF8(getArg!(dstring)()); +                        Lputstr: +                            if (fc != 's') +                                throw new FormatException("string"); +                            if (flags & FLprecision && precision < s.length) +                                s = s[0 .. precision]; +                            putstr(s); +                            break; + +                        case Mangle.Tconst: +                        case Mangle.Timmutable: +                            mi++; +                            continue; + +                        default: +                            TypeInfo ti2 = primitiveTypeInfo(m2); +                            if (!ti2) +                              goto Lerror; +                            void[] va = getArg!(void[])(); +                            putArray(va.ptr, va.length, ti2); +                    } +                    return; +                } +                assert(0); + +            case Mangle.Ttypedef: +                ti = (cast(TypeInfo_Typedef)ti).base; +                m = cast(Mangle)typeid(ti).name[9]; +                formatArg(fc); +                return; + +            case Mangle.Tenum: +                ti = (cast(TypeInfo_Enum)ti).base; +                m = cast(Mangle)typeid(ti).name[9]; +                formatArg(fc); +                return; + +            case Mangle.Tstruct: +            {        TypeInfo_Struct tis = cast(TypeInfo_Struct)ti; +                if (tis.xtoString is null) +                    throw new FormatException("Can't convert " ~ tis.toString() +                            ~ " to string: \"string toString()\" not defined"); +                s = tis.xtoString(skipArg(tis)); +                goto Lputstr; +            } + +            default: +                goto Lerror; +        } + +    Lnumber: +        switch (fc) +        { +            case 's': +            case 'd': +                if (signed) +                {   if (cast(long)vnumber < 0) +                    {        prefix = "-"; +                        vnumber = -vnumber; +                    } +                    else if (flags & FLplus) +                        prefix = "+"; +                    else if (flags & FLspace) +                        prefix = " "; +                } +                break; + +            case 'b': +                signed = 0; +                base = 2; +                break; + +            case 'o': +                signed = 0; +                base = 8; +                break; + +            case 'X': +                uc = 1; +                if (flags & FLhash && vnumber) +                    prefix = "0X"; +                signed = 0; +                base = 16; +                break; + +            case 'x': +                if (flags & FLhash && vnumber) +                    prefix = "0x"; +                signed = 0; +                base = 16; +                break; + +            default: +                goto Lerror; +        } + +        if (!signed) +        { +            switch (m) +            { +                case Mangle.Tbyte: +                    vnumber &= 0xFF; +                    break; + +                case Mangle.Tshort: +                    vnumber &= 0xFFFF; +                    break; + +                case Mangle.Tint: +                    vnumber &= 0xFFFFFFFF; +                    break; + +                default: +                    break; +            } +        } + +        if (flags & FLprecision && fc != 'p') +            flags &= ~FL0pad; + +        if (vnumber < base) +        { +            if (vnumber == 0 && precision == 0 && flags & FLprecision && +                !(fc == 'o' && flags & FLhash)) +            { +                putstr(null); +                return; +            } +            if (precision == 0 || !(flags & FLprecision)) +            {        vchar = cast(char)('0' + vnumber); +                if (vnumber < 10) +                    vchar = cast(char)('0' + vnumber); +                else +                    vchar = cast(char)((uc ? 'A' - 10 : 'a' - 10) + vnumber); +                goto L2; +            } +        } + +        { +            ptrdiff_t n = tmpbuf.length; +            char c; +            int hexoffset = uc ? ('A' - ('9' + 1)) : ('a' - ('9' + 1)); + +            while (vnumber) +            { +                c = cast(char)((vnumber % base) + '0'); +                if (c > '9') +                    c += hexoffset; +                vnumber /= base; +                tmpbuf[--n] = c; +            } +            if (tmpbuf.length - n < precision && precision < tmpbuf.length) +            { +                ptrdiff_t m = tmpbuf.length - precision; +                tmpbuf[m .. n] = '0'; +                n = m; +            } +            else if (flags & FLhash && fc == 'o') +                prefix = "0"; +            putstr(tmpbuf[n .. tmpbuf.length]); +            return; +        } + +    Lreal: +        putreal(vreal); +        return; + +    Lcomplex: +        putreal(vcreal.re); +        if (vcreal.im >= 0) +        { +            putc('+'); +        } +        putreal(vcreal.im); +        putc('i'); +        return; + +    Lerror: +        throw new FormatException("formatArg"); +    } + +    for (int j = 0; j < arguments.length; ) +    { +        ti = arguments[j++]; +        //printf("arg[%d]: '%.*s' %d\n", j, typeid(ti).name.length, typeid(ti).name.ptr, typeid(ti).name.length); +        //ti.print(); + +        flags = 0; +        precision = 0; +        field_width = 0; + +        ti = skipCI(ti); +        int mi = 9; +        do +        { +            if (typeid(ti).name.length <= mi) +                goto Lerror; +            m = cast(Mangle)typeid(ti).name[mi++]; +        } while (m == Mangle.Tconst || m == Mangle.Timmutable); + +        if (m == Mangle.Tarray) +        { +            if (typeid(ti).name.length == 14 && +                    typeid(ti).name[9..14] == "Array") +            { +                TypeInfo tn = (cast(TypeInfo_Array)ti).next; +                tn = skipCI(tn); +                switch (cast(Mangle)typeid(tn).name[9]) +                { +                case Mangle.Tchar: +                case Mangle.Twchar: +                case Mangle.Tdchar: +                    ti = tn; +                    mi = 9; +                    break; +                default: +                    break; +                } +            } +          L1: +            Mangle m2 = cast(Mangle)typeid(ti).name[mi]; +            string  fmt;                        // format string +            wstring wfmt; +            dstring dfmt; + +            /* For performance reasons, this code takes advantage of the +             * fact that most format strings will be ASCII, and that the +             * format specifiers are always ASCII. This means we only need +             * to deal with UTF in a couple of isolated spots. +             */ + +            switch (m2) +            { +            case Mangle.Tchar: +                fmt = getArg!(string)(); +                break; + +            case Mangle.Twchar: +                wfmt = getArg!(wstring)(); +                fmt = toUTF8(wfmt); +                break; + +            case Mangle.Tdchar: +                dfmt = getArg!(dstring)(); +                fmt = toUTF8(dfmt); +                break; + +            case Mangle.Tconst: +            case Mangle.Timmutable: +                mi++; +                goto L1; + +            default: +                formatArg('s'); +                continue; +            } + +            for (size_t i = 0; i < fmt.length; ) +            {        dchar c = fmt[i++]; + +                dchar getFmtChar() +                {   // Valid format specifier characters will never be UTF +                    if (i == fmt.length) +                        throw new FormatException("invalid specifier"); +                    return fmt[i++]; +                } + +                int getFmtInt() +                {   int n; + +                    while (1) +                    { +                        n = n * 10 + (c - '0'); +                        if (n < 0)        // overflow +                            throw new FormatException("int overflow"); +                        c = getFmtChar(); +                        if (c < '0' || c > '9') +                            break; +                    } +                    return n; +                } + +                int getFmtStar() +                {   Mangle m; +                    TypeInfo ti; + +                    if (j == arguments.length) +                        throw new FormatException("too few arguments"); +                    ti = arguments[j++]; +                    m = cast(Mangle)typeid(ti).name[9]; +                    if (m != Mangle.Tint) +                        throw new FormatException("int argument expected"); +                    return getArg!(int)(); +                } + +                if (c != '%') +                { +                    if (c > 0x7F)        // if UTF sequence +                    { +                        i--;                // back up and decode UTF sequence +                        import std.utf : decode; +                        c = decode(fmt, i); +                    } +                  Lputc: +                    putc(c); +                    continue; +                } + +                // Get flags {-+ #} +                flags = 0; +                while (1) +                { +                    c = getFmtChar(); +                    switch (c) +                    { +                    case '-':        flags |= FLdash;        continue; +                    case '+':        flags |= FLplus;        continue; +                    case ' ':        flags |= FLspace;        continue; +                    case '#':        flags |= FLhash;        continue; +                    case '0':        flags |= FL0pad;        continue; + +                    case '%':        if (flags == 0) +                                          goto Lputc; +                                     break; + +                    default:         break; +                    } +                    break; +                } + +                // Get field width +                field_width = 0; +                if (c == '*') +                { +                    field_width = getFmtStar(); +                    if (field_width < 0) +                    {   flags |= FLdash; +                        field_width = -field_width; +                    } + +                    c = getFmtChar(); +                } +                else if (c >= '0' && c <= '9') +                    field_width = getFmtInt(); + +                if (flags & FLplus) +                    flags &= ~FLspace; +                if (flags & FLdash) +                    flags &= ~FL0pad; + +                // Get precision +                precision = 0; +                if (c == '.') +                {   flags |= FLprecision; +                    //flags &= ~FL0pad; + +                    c = getFmtChar(); +                    if (c == '*') +                    { +                        precision = getFmtStar(); +                        if (precision < 0) +                        {   precision = 0; +                            flags &= ~FLprecision; +                        } + +                        c = getFmtChar(); +                    } +                    else if (c >= '0' && c <= '9') +                        precision = getFmtInt(); +                } + +                if (j == arguments.length) +                    goto Lerror; +                ti = arguments[j++]; +                ti = skipCI(ti); +                mi = 9; +                do +                { +                    m = cast(Mangle)typeid(ti).name[mi++]; +                } while (m == Mangle.Tconst || m == Mangle.Timmutable); + +                if (c > 0x7F)                // if UTF sequence +                    goto Lerror;        // format specifiers can't be UTF +                formatArg(cast(char)c); +            } +        } +        else +        { +            formatArg('s'); +        } +    } +    return; + +  Lerror: +    throw new FormatException(); +} + + +private bool needToSwapEndianess(Char)(ref FormatSpec!Char f) +{ +    import std.system : endian, Endian; + +    return endian == Endian.littleEndian && f.flPlus +        || endian == Endian.bigEndian && f.flDash; +} + +/* ======================== Unit Tests ====================================== */ + +unittest +{ +    import std.conv : octal; + +    int i; +    string s; + +    debug(format) printf("std.format.format.unittest\n"); + +    s = format("hello world! %s %s %s%s%s", true, 57, 1_000_000_000, 'x', " foo"); +    assert(s == "hello world! true 57 1000000000x foo"); + +    s = format("%s %A %s", 1.67, -1.28, float.nan); +    /* The host C library is used to format floats. +     * C99 doesn't specify what the hex digit before the decimal point +     * is for %A. +     */ +    //version (linux) +    //    assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan"); +    //else version (OSX) +    //    assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan", s); +    //else +    version (MinGW) +        assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan", s); +    else version (CRuntime_Microsoft) +        assert(s == "1.67 -0X1.47AE14P+0 nan" +            || s == "1.67 -0X1.47AE147AE147BP+0 nan", s); // MSVCRT 14+ (VS 2015) +    else +        assert(s == "1.67 -0X1.47AE147AE147BP+0 nan", s); + +    s = format("%x %X", 0x1234AF, 0xAFAFAFAF); +    assert(s == "1234af AFAFAFAF"); + +    s = format("%b %o", 0x1234AF, 0xAFAFAFAF); +    assert(s == "100100011010010101111 25753727657"); + +    s = format("%d %s", 0x1234AF, 0xAFAFAFAF); +    assert(s == "1193135 2947526575"); + +    //version(X86_64) +    //{ +    //    pragma(msg, "several format tests disabled on x86_64 due to bug 5625"); +    //} +    //else +    //{ +        s = format("%s", 1.2 + 3.4i); +        assert(s == "1.2+3.4i", s); + +        //s = format("%x %X", 1.32, 6.78f); +        //assert(s == "3ff51eb851eb851f 40D8F5C3"); + +    //} + +    s = format("%#06.*f",2,12.345); +    assert(s == "012.35"); + +    s = format("%#0*.*f",6,2,12.345); +    assert(s == "012.35"); + +    s = format("%7.4g:", 12.678); +    assert(s == "  12.68:"); + +    s = format("%7.4g:", 12.678L); +    assert(s == "  12.68:"); + +    s = format("%04f|%05d|%#05x|%#5x",-4.0,-10,1,1); +    assert(s == "-4.000000|-0010|0x001|  0x1"); + +    i = -10; +    s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); +    assert(s == "-10|-10|-10|-10|-10.0000"); + +    i = -5; +    s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); +    assert(s == "-5| -5|-05|-5|-5.0000"); + +    i = 0; +    s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); +    assert(s == "0|  0|000|0|0.0000"); + +    i = 5; +    s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); +    assert(s == "5|  5|005|5|5.0000"); + +    i = 10; +    s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); +    assert(s == "10| 10|010|10|10.0000"); + +    s = format("%.0d", 0); +    assert(s == ""); + +    s = format("%.g", .34); +    assert(s == "0.3"); + +    s = format("%.0g", .34); +    assert(s == "0.3"); + +    s = format("%.2g", .34); +    assert(s == "0.34"); + +    s = format("%0.0008f", 1e-08); +    assert(s == "0.00000001"); + +    s = format("%0.0008f", 1e-05); +    assert(s == "0.00001000"); + +    s = "helloworld"; +    string r; +    r = format("%.2s", s[0..5]); +    assert(r == "he"); +    r = format("%.20s", s[0..5]); +    assert(r == "hello"); +    r = format("%8s", s[0..5]); +    assert(r == "   hello"); + +    byte[] arrbyte = new byte[4]; +    arrbyte[0] = 100; +    arrbyte[1] = -99; +    arrbyte[3] = 0; +    r = format("%s", arrbyte); +    assert(r == "[100, -99, 0, 0]"); + +    ubyte[] arrubyte = new ubyte[4]; +    arrubyte[0] = 100; +    arrubyte[1] = 200; +    arrubyte[3] = 0; +    r = format("%s", arrubyte); +    assert(r == "[100, 200, 0, 0]"); + +    short[] arrshort = new short[4]; +    arrshort[0] = 100; +    arrshort[1] = -999; +    arrshort[3] = 0; +    r = format("%s", arrshort); +    assert(r == "[100, -999, 0, 0]"); + +    ushort[] arrushort = new ushort[4]; +    arrushort[0] = 100; +    arrushort[1] = 20_000; +    arrushort[3] = 0; +    r = format("%s", arrushort); +    assert(r == "[100, 20000, 0, 0]"); + +    int[] arrint = new int[4]; +    arrint[0] = 100; +    arrint[1] = -999; +    arrint[3] = 0; +    r = format("%s", arrint); +    assert(r == "[100, -999, 0, 0]"); + +    long[] arrlong = new long[4]; +    arrlong[0] = 100; +    arrlong[1] = -999; +    arrlong[3] = 0; +    r = format("%s", arrlong); +    assert(r == "[100, -999, 0, 0]"); + +    ulong[] arrulong = new ulong[4]; +    arrulong[0] = 100; +    arrulong[1] = 999; +    arrulong[3] = 0; +    r = format("%s", arrulong); +    assert(r == "[100, 999, 0, 0]"); + +    string[] arr2 = new string[4]; +    arr2[0] = "hello"; +    arr2[1] = "world"; +    arr2[3] = "foo"; +    r = format("%s", arr2); +    assert(r == `["hello", "world", "", "foo"]`); + +    r = format("%.8d", 7); +    assert(r == "00000007"); +    r = format("%.8x", 10); +    assert(r == "0000000a"); + +    r = format("%-3d", 7); +    assert(r == "7  "); + +    r = format("%*d", -3, 7); +    assert(r == "7  "); + +    r = format("%.*d", -3, 7); +    assert(r == "7"); + +    r = format("abc"c); +    assert(r == "abc"); + +    //format() returns the same type as inputted. +    wstring wr; +    wr = format("def"w); +    assert(wr == "def"w); + +    dstring dr; +    dr = format("ghi"d); +    assert(dr == "ghi"d); + +    void* p = cast(void*)0xDEADBEEF; +    r = format("%s", p); +    assert(r == "DEADBEEF"); + +    r = format("%#x", 0xabcd); +    assert(r == "0xabcd"); +    r = format("%#X", 0xABCD); +    assert(r == "0XABCD"); + +    r = format("%#o", octal!12345); +    assert(r == "012345"); +    r = format("%o", 9); +    assert(r == "11"); +    r = format("%#o", 0);   // issue 15663 +    assert(r == "0"); + +    r = format("%+d", 123); +    assert(r == "+123"); +    r = format("%+d", -123); +    assert(r == "-123"); +    r = format("% d", 123); +    assert(r == " 123"); +    r = format("% d", -123); +    assert(r == "-123"); + +    r = format("%%"); +    assert(r == "%"); + +    r = format("%d", true); +    assert(r == "1"); +    r = format("%d", false); +    assert(r == "0"); + +    r = format("%d", 'a'); +    assert(r == "97"); +    wchar wc = 'a'; +    r = format("%d", wc); +    assert(r == "97"); +    dchar dc = 'a'; +    r = format("%d", dc); +    assert(r == "97"); + +    byte b = byte.max; +    r = format("%x", b); +    assert(r == "7f"); +    r = format("%x", ++b); +    assert(r == "80"); +    r = format("%x", ++b); +    assert(r == "81"); + +    short sh = short.max; +    r = format("%x", sh); +    assert(r == "7fff"); +    r = format("%x", ++sh); +    assert(r == "8000"); +    r = format("%x", ++sh); +    assert(r == "8001"); + +    i = int.max; +    r = format("%x", i); +    assert(r == "7fffffff"); +    r = format("%x", ++i); +    assert(r == "80000000"); +    r = format("%x", ++i); +    assert(r == "80000001"); + +    r = format("%x", 10); +    assert(r == "a"); +    r = format("%X", 10); +    assert(r == "A"); +    r = format("%x", 15); +    assert(r == "f"); +    r = format("%X", 15); +    assert(r == "F"); + +    Object c = null; +    r = format("%s", c); +    assert(r == "null"); + +    enum TestEnum +    { +        Value1, Value2 +    } +    r = format("%s", TestEnum.Value2); +    assert(r == "Value2"); + +    immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]); +    r = format("%s", aa.values); +    assert(r == `["hello", "betty"]` || r == `["betty", "hello"]`); +    r = format("%s", aa); +    assert(r == `[3:"hello", 4:"betty"]` || r == `[4:"betty", 3:"hello"]`); + +    static const dchar[] ds = ['a','b']; +    for (int j = 0; j < ds.length; ++j) +    { +        r = format(" %d", ds[j]); +        if (j == 0) +            assert(r == " 97"); +        else +            assert(r == " 98"); +    } + +    r = format(">%14d<, %s", 15, [1,2,3]); +    assert(r == ">            15<, [1, 2, 3]"); + +    assert(format("%8s", "bar") == "     bar"); +    assert(format("%8s", "b\u00e9ll\u00f4") == " b\u00e9ll\u00f4"); +}  | 
