Wolfgang Thallergcc16 merge934c3885 months agoHistory
3994 lines · 116.9 KB · D
RawDownload
// Written in the D programming language. /*   Copyright: Copyright The D Language Foundation 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/internal/write.d) */module std.format.internal.write; import std.format.spec : FormatSpec;import std.range.primitives : isInputRange;import std.traits; version (StdUnittest){    import std.exception : assertCTFEable;    import std.format : format;} package(std.format): /*    `bool`s are formatted as `"true"` or `"false"` with `%s` and as `1` or    `0` with integral-specific format specs. */void formatValueImpl(Writer, T, Char)(auto ref Writer w, const(T) obj, scope const ref FormatSpec!Char f)if (is(BooleanTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)){    BooleanTypeOf!T val = obj;     if (f.spec == 's')        writeAligned(w, val ? "true" : "false", f);    else        formatValueImpl(w, cast(byte) val, f);} @safe pure unittest{    assertCTFEable!(    {        formatTest(false, "false");        formatTest(true,  "true");    });} @safe unittest{    struct S1    {        bool val;        alias val this;    }     struct S2    {        bool val;        alias val this;        string toString() const { return "S"; }    }     formatTest(S1(false), "false");    formatTest(S1(true),  "true");    formatTest(S2(false), "S");    formatTest(S2(true),  "S");} @safe pure unittest{    string t1 = format("[%6s] [%6s] [%-6s]", true, false, true);    assert(t1 == "[  true] [ false] [true  ]");     string t2 = format("[%3s] [%-2s]", true, false);    assert(t2 == "[true] [false]");} // https://issues.dlang.org/show_bug.cgi?id=20534@safe pure unittest{    assert(format("%r",false) == "\0");} @safe pure unittest{    assert(format("%07s",true) == "   true");} @safe pure unittest{    assert(format("%=8s",true)    == "  true  ");    assert(format("%=9s",false)   == "  false  ");    assert(format("%=9s",true)    == "   true  ");    assert(format("%-=9s",true)   == "  true   ");    assert(format("%=10s",false)  == "   false  ");    assert(format("%-=10s",false) == "  false   ");} /*    `null` literal is formatted as `"null"` */void formatValueImpl(Writer, T, Char)(auto ref Writer w, const(T) obj, scope const ref FormatSpec!Char f)if (is(immutable T == immutable typeof(null)) && !is(T == enum) && !hasToString!(T, Char)){    import std.format : enforceFmt;     const spec = f.spec;    enforceFmt(spec == 's', "null literal cannot match %" ~ spec);     writeAligned(w, "null", f);} @safe pure unittest{    import std.exception : collectExceptionMsg;    import std.format : FormatException;    import std.range.primitives : back;     assert(collectExceptionMsg!FormatException(format("%p", null)).back == 'p');     assertCTFEable!(    {        formatTest(null, "null");    });} @safe pure unittest{    string t = format("[%6s] [%-6s]", null, null);    assert(t == "[  null] [null  ]");} /*    Integrals are formatted like $(REF printf, core, stdc, stdio). */void formatValueImpl(Writer, T, Char)(auto ref Writer w, const(T) obj, scope const ref FormatSpec!Char f)if (is(IntegralTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)){    alias U = IntegralTypeOf!T;    U val = obj;    // Extracting alias this may be impure/system/may-throw     if (f.spec == 'r')    {        // raw write, skip all else and write the thing        auto raw = (ref val) @trusted {            return (cast(const char*) &val)[0 .. val.sizeof];        }(val);        import std.range.primitives : put;        if (needToSwapEndianess(f))            foreach_reverse (c; raw)                put(w, c);        else            foreach (c; raw)                put(w, c);        return;    }     static if (isSigned!U)    {        const negative = val < 0 && f.spec != 'x' && f.spec != 'X' && f.spec != 'b' && f.spec != 'o' && f.spec != 'u';        ulong arg = negative ? -cast(ulong) val : val;    }    else    {        const negative = false;        ulong arg = val;    }    arg &= Unsigned!U.max;     formatValueImplUlong!(Writer, Char)(w, arg, negative, f);} // Helper function for `formatValueImpl` that avoids template bloatprivate void formatValueImplUlong(Writer, Char)(auto ref Writer w, ulong arg, in bool negative,                                                scope const ref FormatSpec!Char f){    immutable uint base = baseOfSpec(f.spec);     const bool zero = arg == 0;    char[64] digits = void;    size_t pos = digits.length - 1;    do    {        /* `cast(char)` is needed because value range propagation (VRP) cannot         * analyze `base` because it’s computed in a separate function         * (`baseOfSpec`). */        digits[pos--] = cast(char) ('0' + arg % base);        if (base > 10 && digits[pos + 1] > '9')            digits[pos + 1] += ((f.spec == 'x' || f.spec == 'a') ? 'a' : 'A') - '0' - 10;        arg /= base;    } while (arg > 0);     char[3] prefix = void;    size_t left = 2;    size_t right = 2;     // add sign    if (f.spec != 'x' && f.spec != 'X' && f.spec != 'b' && f.spec != 'o' && f.spec != 'u')    {        if (negative)            prefix[right++] = '-';        else if (f.flPlus)            prefix[right++] = '+';        else if (f.flSpace)            prefix[right++] = ' ';    }     // not a floating point like spec    if (f.spec == 'x' || f.spec == 'X' || f.spec == 'b' || f.spec == 'o' || f.spec == 'u'        || f.spec == 'd' || f.spec == 's')    {        if (f.flHash && (base == 16) && !zero)        {            prefix[--left] = f.spec;            prefix[--left] = '0';        }        if (f.flHash && (base == 8) && !zero            && (digits.length - (pos + 1) >= f.precision || f.precision == f.UNSPECIFIED))            prefix[--left] = '0';         writeAligned(w, prefix[left .. right], digits[pos + 1 .. $], "", f, true);        return;    }     FormatSpec!Char fs = f;    if (f.precision == f.UNSPECIFIED)        fs.precision = cast(typeof(fs.precision)) (digits.length - pos - 2);     // %f like output    if (f.spec == 'f' || f.spec == 'F'        || ((f.spec == 'g' || f.spec == 'G') && (fs.precision >= digits.length - pos - 2)))    {        if (f.precision == f.UNSPECIFIED)            fs.precision = 0;         writeAligned(w, prefix[left .. right], digits[pos + 1 .. $], ".", "", fs,                     (f.spec == 'g' || f.spec == 'G') ? PrecisionType.allDigits : PrecisionType.fractionalDigits);         return;    }     import std.algorithm.searching : all;     // at least one digit for %g    if ((f.spec == 'g' || f.spec == 'G') && fs.precision == 0)        fs.precision = 1;     // rounding    size_t digit_end = pos + fs.precision + ((f.spec == 'g' || f.spec == 'G') ? 1 : 2);    if (digit_end <= digits.length)    {        RoundingClass rt = RoundingClass.ZERO;        if (digit_end < digits.length)        {            auto tie = (f.spec == 'a' || f.spec == 'A') ? '8' : '5';            if (digits[digit_end] >= tie)            {                rt = RoundingClass.UPPER;                if (digits[digit_end] == tie && digits[digit_end + 1 .. $].all!(a => a == '0'))                    rt = RoundingClass.FIVE;            }            else            {                rt = RoundingClass.LOWER;                if (digits[digit_end .. $].all!(a => a == '0'))                    rt = RoundingClass.ZERO;            }        }         if (round(digits, pos + 1, digit_end, rt, negative,                  f.spec == 'a' ? 'f' : (f.spec == 'A' ? 'F' : '9')))        {            pos--;            digit_end--;        }    }     // convert to scientific notation    char[1] int_digit = void;    int_digit[0] = digits[pos + 1];    digits[pos + 1] = '.';     char[4] suffix = void;     if (f.spec == 'e' || f.spec == 'E' || f.spec == 'g' || f.spec == 'G')    {        suffix[0] = (f.spec == 'e' || f.spec == 'g') ? 'e' : 'E';        suffix[1] = '+';        suffix[2] = cast(char) ('0' + (digits.length - pos - 2) / 10);        suffix[3] = cast(char) ('0' + (digits.length - pos - 2) % 10);    }    else    {        if (right == 3)            prefix[0] = prefix[2];        prefix[1] = '0';        prefix[2] = f.spec == 'a' ? 'x' : 'X';         left = right == 3 ? 0 : 1;        right = 3;         suffix[0] = f.spec == 'a' ? 'p' : 'P';        suffix[1] = '+';        suffix[2] = cast(char) ('0' + ((digits.length - pos - 2) * 4) / 10);        suffix[3] = cast(char) ('0' + ((digits.length - pos - 2) * 4) % 10);    }     import std.algorithm.comparison : min;     // remove trailing zeros    if ((f.spec == 'g' || f.spec == 'G') && !f.flHash)    {        digit_end = min(digit_end, digits.length);        while (digit_end > pos + 1 &&               (digits[digit_end - 1] == '0' || digits[digit_end - 1] == '.'))            digit_end--;    }     writeAligned(w, prefix[left .. right], int_digit[0 .. $],                 digits[pos + 1 .. min(digit_end, $)],                 suffix[0 .. $], fs,                 (f.spec == 'g' || f.spec == 'G') ? PrecisionType.allDigits : PrecisionType.fractionalDigits);} private uint baseOfSpec(in char spec) @safe pure{    typeof(return) base =        spec == 'x' || spec == 'X' || spec == 'a' || spec == 'A' ? 16 :        spec == 'o' ? 8 :        spec == 'b' ? 2 :        spec == 's' || spec == 'd' || spec == 'u'        || spec == 'e' || spec == 'E' || spec == 'f' || spec == 'F'        || spec == 'g' || spec == 'G' ? 10 :        0;     import std.format : enforceFmt;    enforceFmt(base > 0,        "incompatible format character for integral argument: %" ~ spec);     return base;} @safe pure unittest{    assertCTFEable!(    {        formatTest(byte.min, "-128");        formatTest(byte.max, "127");        formatTest(short.min, "-32768");        formatTest(short.max, "32767");        formatTest(int.min, "-2147483648");        formatTest(int.max, "2147483647");        formatTest(long.min, "-9223372036854775808");        formatTest(long.max, "9223372036854775807");         formatTest(ubyte.min, "0");        formatTest(ubyte.max, "255");        formatTest(ushort.min, "0");        formatTest(ushort.max, "65535");        formatTest(uint.min, "0");        formatTest(uint.max, "4294967295");        formatTest(ulong.min, "0");        formatTest(ulong.max, "18446744073709551615");    });} // https://issues.dlang.org/show_bug.cgi?id=18838@safe pure unittest{    assert("%12,d".format(0) == "           0");} @safe pure unittest{    import std.exception : collectExceptionMsg;    import std.format : FormatException;    import std.range.primitives : back;     assert(collectExceptionMsg!FormatException(format("%c", 5)).back == 'c');     assertCTFEable!(    {        formatTest(9, "9");        formatTest(10, "10");    });} @safe unittest{    struct S1    {        long val;        alias val this;    }     struct S2    {        long val;        alias val this;        string toString() const { return "S"; }    }     formatTest(S1(10), "10");    formatTest(S2(10), "S");} // https://issues.dlang.org/show_bug.cgi?id=20064@safe unittest{    assert(format( "%03,d",  1234) ==              "1,234");    assert(format( "%04,d",  1234) ==              "1,234");    assert(format( "%05,d",  1234) ==              "1,234");    assert(format( "%06,d",  1234) ==             "01,234");    assert(format( "%07,d",  1234) ==            "001,234");    assert(format( "%08,d",  1234) ==          "0,001,234");    assert(format( "%09,d",  1234) ==          "0,001,234");    assert(format("%010,d",  1234) ==         "00,001,234");    assert(format("%011,d",  1234) ==        "000,001,234");    assert(format("%012,d",  1234) ==      "0,000,001,234");    assert(format("%013,d",  1234) ==      "0,000,001,234");    assert(format("%014,d",  1234) ==     "00,000,001,234");    assert(format("%015,d",  1234) ==    "000,000,001,234");    assert(format("%016,d",  1234) ==  "0,000,000,001,234");    assert(format("%017,d",  1234) ==  "0,000,000,001,234");     assert(format( "%03,d", -1234) ==             "-1,234");    assert(format( "%04,d", -1234) ==             "-1,234");    assert(format( "%05,d", -1234) ==             "-1,234");    assert(format( "%06,d", -1234) ==             "-1,234");    assert(format( "%07,d", -1234) ==            "-01,234");    assert(format( "%08,d", -1234) ==           "-001,234");    assert(format( "%09,d", -1234) ==         "-0,001,234");    assert(format("%010,d", -1234) ==         "-0,001,234");    assert(format("%011,d", -1234) ==        "-00,001,234");    assert(format("%012,d", -1234) ==       "-000,001,234");    assert(format("%013,d", -1234) ==     "-0,000,001,234");    assert(format("%014,d", -1234) ==     "-0,000,001,234");    assert(format("%015,d", -1234) ==    "-00,000,001,234");    assert(format("%016,d", -1234) ==   "-000,000,001,234");    assert(format("%017,d", -1234) == "-0,000,000,001,234");} @safe pure unittest{    string t1 = format("[%6s] [%-6s]", 123, 123);    assert(t1 == "[   123] [123   ]");     string t2 = format("[%6s] [%-6s]", -123, -123);    assert(t2 == "[  -123] [-123  ]");} @safe pure unittest{    formatTest(byte.min, "-128");    formatTest(short.min, "-32768");    formatTest(int.min, "-2147483648");    formatTest(long.min, "-9223372036854775808");} // https://issues.dlang.org/show_bug.cgi?id=21777@safe pure unittest{    assert(format!"%20.5,d"(cast(short) 120) == "              00,120");    assert(format!"%20.5,o"(cast(short) 120) == "              00,170");    assert(format!"%20.5,x"(cast(short) 120) == "              00,078");    assert(format!"%20.5,2d"(cast(short) 120) == "             0,01,20");    assert(format!"%20.5,2o"(cast(short) 120) == "             0,01,70");    assert(format!"%20.5,4d"(cast(short) 120) == "              0,0120");    assert(format!"%20.5,4o"(cast(short) 120) == "              0,0170");    assert(format!"%20.5,4x"(cast(short) 120) == "              0,0078");    assert(format!"%20.5,2x"(3000) == "             0,0b,b8");    assert(format!"%20.5,4d"(3000) == "              0,3000");    assert(format!"%20.5,4o"(3000) == "              0,5670");    assert(format!"%20.5,4x"(3000) == "              0,0bb8");    assert(format!"%20.5,d"(-400) == "             -00,400");    assert(format!"%20.30d"(-400) == "-000000000000000000000000000400");    assert(format!"%20.5,4d"(0) == "              0,0000");    assert(format!"%0#.8,2s"(12345) == "00,01,23,45");    assert(format!"%0#.9,3x"(55) == "0x000,000,037");} // https://issues.dlang.org/show_bug.cgi?id=21814@safe pure unittest{    assert(format("%,0d",1000) == "1000");} // https://issues.dlang.org/show_bug.cgi?id=21817@safe pure unittest{    assert(format!"%u"(-5) == "4294967291");} // https://issues.dlang.org/show_bug.cgi?id=21820@safe pure unittest{    assert(format!"%#.0o"(0) == "0");

Lines 1–500 of 3994.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t