diff options
Diffstat (limited to 'src/undead/doformat.d')
-rw-r--r-- | src/undead/doformat.d | 1620 |
1 files changed, 0 insertions, 1620 deletions
diff --git a/src/undead/doformat.d b/src/undead/doformat.d deleted file mode 100644 index 4fc0daf..0000000 --- a/src/undead/doformat.d +++ /dev/null @@ -1,1620 +0,0 @@ -// 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"); -} |