diff --git a/source/tanya/format/package.d b/source/tanya/format/package.d index 44c079c..70b32c7 100644 --- a/source/tanya/format/package.d +++ b/source/tanya/format/package.d @@ -17,11 +17,138 @@ module tanya.format; import tanya.container.string; import tanya.encoding.ascii; public import tanya.format.conv; +import tanya.math; import tanya.memory.op; import tanya.meta.metafunction; import tanya.meta.trait; import tanya.range.array; +// Integer and floating point to string conversion is based on stb_sprintf +// written by Jeff Roberts. + +// Returns the last part of buffer with converted number. +package(tanya) char[] integral2String(T)(T number, return ref char[21] buffer) +if (isIntegral!T) +{ + // abs the integer. + ulong n64 = number < 0 ? -cast(long) number : number; + + char* start = buffer[].ptr + buffer.sizeof - 1; + + while (true) + { + // Do in 32-bit chunks (avoid lots of 64-bit divides even with constant + // denominators). + char* o = start - 8; + uint n; + if (n64 >= 100000000) + { + n = n64 % 100000000; + n64 /= 100000000; + } + else + { + n = cast(uint) n64; + n64 = 0; + } + + while (n) + { + *--start = cast(char) (n % 10) + '0'; + n /= 10; + } + // Ignore the leading zero if it was the last part of the integer. + if (n64 == 0) + { + if ((start[0] == '0') + && (start != (buffer[].ptr + buffer.sizeof -1))) + { + ++start; + } + break; + } + // Copy leading zeros if it wasn't the most significant part of the + // integer. + while (start != o) + { + *--start = '0'; + } + } + + // Get the length that we have copied. + uint l = cast(uint) ((buffer[].ptr + buffer.sizeof - 1) - start); + if (l == 0) + { + *--start = '0'; + l = 1; + } + else if (number < 0) // Set the sign. + { + *--start = '-'; + ++l; + } + + return buffer[$ - l - 1 .. $ - 1]; +} + +// Converting an integer to string. +@nogc nothrow pure @system unittest +{ + char[21] buf; + + assert(integral2String(80, buf) == "80"); + assert(integral2String(-80, buf) == "-80"); + assert(integral2String(0, buf) == "0"); + assert(integral2String(uint.max, buf) == "4294967295"); + assert(integral2String(int.min, buf) == "-2147483648"); +} + +/* + * Double-double high-precision floating point number. + * + * The first element is a base value corresponding to the nearest approximation + * of the target $(D_PSYMBOL HP) value, and the second element is an offset + * value corresponding to the difference between the target value and the base. + * Thus, the $(D_PSYMBOL HP) value represented is the sum of the base and the + * offset. + */ +private struct HP +{ + private double base; + private double offset = 0.0; + + private void normalize() @nogc nothrow pure @safe + { + const double target = this.base + this.offset; + this.offset -= target - this.base; + this.base = target; + } + + private void multiply(ref const HP x, ref const HP y) + @nogc nothrow pure @safe + { + HP a, b; + long bt; + + this.base = x.base * y.base; + copyFp(x.base, bt); + bt &= ulong.max << 27; + copyFp(bt, a.base); + + a.offset = x.base - a.base; + copyFp(y.base, bt); + bt &= ulong.max << 27; + copyFp(bt, b.base); + + b.offset = y.base - b.base; + this.offset = a.base * b.base - this.base + + a.base * b.offset + + a.offset * b.base + + a.offset * b.offset; + this.offset += x.base * y.offset + x.offset * y.base; + } +} + private enum special = 0x7000; private enum char period = '.'; @@ -48,145 +175,121 @@ private static const ulong[20] powersOf10 = [ 10000000000000000000UL, ]; -private static const char[201] digitpair = +private static const char[201] digitPairs = "0001020304050607080910111213141516171819202122232425262728293031323334353" ~ "6373839404142434445464748495051525354555657585960616263646566676869707172" ~ "737475767778798081828384858687888990919293949596979899"; -private static const double[23] bottom = [ - 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, - 1e+009, 1e+010, 1e+011, 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, - 1e+018, 1e+019, 1e+020, 1e+021, 1e+022, +private static const HP[23] bottom = [ + HP(1e+000), HP(1e+001), HP(1e+002), HP(1e+003), HP(1e+004), HP(1e+005), + HP(1e+006), HP(1e+007), HP(1e+008), HP(1e+009), HP(1e+010), HP(1e+011), + HP(1e+012), HP(1e+013), HP(1e+014), HP(1e+015), HP(1e+016), HP(1e+017), + HP(1e+018), HP(1e+019), HP(1e+020), HP(1e+021), HP(1e+022), ]; -private static const double[22] negativeBottom = [ - 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, - 1e-010, 1e-011, 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, - 1e-019, 1e-020, 1e-021, 1e-022, +private static const HP[22] negativeBottom = [ + HP(1e-001, -5.551115123125783e-018), + HP(1e-002, -2.0816681711721684e-019), + HP(1e-003, -2.0816681711721686e-020), + HP(1e-004, -4.7921736023859299e-021), + HP(1e-005, -8.1803053914031305e-022), + HP(1e-006, 4.5251888174113741e-023), + HP(1e-007, 4.5251888174113739e-024), + HP(1e-008, -2.0922560830128471e-025), + HP(1e-009, -6.2281591457779853e-026), + HP(1e-010, -3.6432197315497743e-027), + HP(1e-011, 6.0503030718060191e-028), + HP(1e-012, 2.0113352370744385e-029), + HP(1e-013, -3.0373745563400371e-030), + HP(1e-014, 1.1806906454401013e-032), + HP(1e-015, -7.7705399876661076e-032), + HP(1e-016, 2.0902213275965398e-033), + HP(1e-017, -7.1542424054621921e-034), + HP(1e-018, -7.1542424054621926e-035), + HP(1e-019, 2.4754073164739869e-036), + HP(1e-020, 5.4846728545790429e-037), + HP(1e-021, 9.2462547772103625e-038), + HP(1e-022, -4.8596774326570872e-039), ]; -private static const double[13] top = [ - 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, - 1e+230, 1e+253, 1e+276, 1e+299, +private static const HP[13] top = [ + HP(1e+023, 8388608), + HP(1e+046, 6.8601809640529717e+028), + HP(1e+069, -7.253143638152921e+052), + HP(1e+092, -4.3377296974619174e+075), + HP(1e+115, -1.5559416129466825e+098), + HP(1e+138, -3.2841562489204913e+121), + HP(1e+161, -3.7745893248228135e+144), + HP(1e+184, -1.7356668416969134e+167), + HP(1e+207, -3.8893577551088374e+190), + HP(1e+230, -9.9566444326005119e+213), + HP(1e+253, 6.3641293062232429e+236), + HP(1e+276, -5.2069140800249813e+259), + HP(1e+299, -5.2504760255204387e+282), ]; -private static const double[13] negativeTop = [ - 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, - 1e-230, 1e-253, 1e-276, 1e-299, -]; - -private static const double[13] topError = [ - 8388608, 6.8601809640529717e+028, -7.253143638152921e+052, - -4.3377296974619174e+075, -1.5559416129466825e+098, - -3.2841562489204913e+121, -3.7745893248228135e+144, - -1.7356668416969134e+167, -3.8893577551088374e+190, - -9.9566444326005119e+213, 6.3641293062232429e+236, - -5.2069140800249813e+259, -5.2504760255204387e+282, -]; - -private static const double[22] negativeBottomError = [ - -5.551115123125783e-018, -2.0816681711721684e-019, - -2.0816681711721686e-020, -4.7921736023859299e-021, - -8.1803053914031305e-022, 4.5251888174113741e-023, - 4.5251888174113739e-024, -2.0922560830128471e-025, - -6.2281591457779853e-026, -3.6432197315497743e-027, - 6.0503030718060191e-028, 2.0113352370744385e-029, -3.0373745563400371e-030, - 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, - -7.1542424054621921e-034, -7.1542424054621926e-035, - 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, - -4.8596774326570872e-039, -]; - -private static const double[13] negativeTopError = [ - 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, - 1.1875228833981544e-109, -5.0644902316928607e-132, - -6.7156837247865426e-155, -2.812077463003139e-178, - -5.7778912386589953e-201, 7.4997100559334532e-224, - -4.6439668915134491e-247, -6.3691100762962136e-270, - -9.436808465446358e-293, // 8.0970921678014997e-317, +private static const HP[13] negativeTop = [ + HP(1e-023, 3.9565301985100693e-040L), + HP(1e-046, -2.299904345391321e-063L), + HP(1e-069, 3.6506201437945798e-086L), + HP(1e-092, 1.1875228833981544e-109L), + HP(1e-115, -5.0644902316928607e-132L), + HP(1e-138, -6.7156837247865426e-155L), + HP(1e-161, -2.812077463003139e-178L), + HP(1e-184, -5.7778912386589953e-201L), + HP(1e-207, 7.4997100559334532e-224L), + HP(1e-230, -4.6439668915134491e-247L), + HP(1e-253, -6.3691100762962136e-270L), + HP(1e-276, -9.436808465446358e-293L), + HP(1e-299, 8.0970921678014997e-317L), ]; private enum ulong tenTo19th = 1000000000000000000UL; -private void ddmultlo(A, B, C, D, E, F)(ref A oh, - ref B ol, - ref C xh, - ref D xl, - ref E yh, - ref F yl) -{ - ol = ol + (xh * yl + xl * yh); -} - -private void ddmultlos(A, B, C, D)(ref A oh, ref B ol, ref C xh, ref D yl) -{ - ol = ol + (xh * yl); -} - -private void ddrenorm(T, U)(ref T oh, ref U ol) -{ - double s; - s = oh + ol; - ol = ol - (s - oh); - oh = s; -} - // Power can be -323 to +350. -private void raise2Power10(double* ohi, - double* olo, - double d, - int power) @nogc nothrow pure +private HP raise2Power10(const HP value, int power) +@nogc nothrow pure @safe { - double ph, pl; + HP result; if ((power >= 0) && (power <= 22)) { - ddmulthi(ph, pl, d, bottom[power]); + result.multiply(value, bottom[power]); } else { - int e, et, eb; - double p2h, p2l; + HP p2; + int e = power; - e = power; if (power < 0) { e = -e; } - et = (e * 0x2c9) >> 14; /* %23 */ + int et = (e * 0x2c9) >> 14; // % 23 if (et > 13) { et = 13; } - eb = e - (et * 23); + int eb = e - (et * 23); - ph = d; - pl = 0.0; + result = value; if (power < 0) { - if (eb) + if (eb != 0) { --eb; - ddmulthi(ph, pl, d, negativeBottom[eb]); - ddmultlos(ph, pl, d, negativeBottomError[eb]); + result.multiply(value, negativeBottom[eb]); } if (et) { - ddrenorm(ph, pl); + result.normalize(); --et; - ddmulthi(p2h, p2l, ph, negativeTop[et]); - ddmultlo(p2h, - p2l, - ph, - pl, - negativeTop[et], - negativeTopError[et]); - ph = p2h; - pl = p2l; + p2.multiply(result, negativeTop[et]); + result = p2; } } else { - if (eb) + if (eb != 0) { e = eb; if (eb > 22) @@ -194,168 +297,141 @@ private void raise2Power10(double* ohi, eb = 22; } e -= eb; - ddmulthi(ph, pl, d, bottom[eb]); + result.multiply(value, bottom[eb]); if (e) { - ddrenorm(ph, pl); - ddmulthi(p2h, p2l, ph, bottom[e]); - ddmultlos(p2h, p2l, bottom[e], pl); - ph = p2h; - pl = p2l; + result.normalize(); + p2.multiply(result, bottom[e]); + result = p2; } } - if (et) + if (et != 0) { - ddrenorm(ph, pl); + result.normalize(); --et; - ddmulthi(p2h, p2l, ph, top[et]); - ddmultlo(p2h, p2l, ph, pl, top[et], topError[et]); - ph = p2h; - pl = p2l; + p2.multiply(result, top[et]); + result = p2; } } } - ddrenorm(ph, pl); - *ohi = ph; - *olo = pl; + result.normalize(); + + return result; } /* * Given a float value, returns the significant bits in bits, and the position - * of the decimal point in decimalPos. +/-Inf and NaN are specified by special - * values returned in the decimalPos parameter. - * fracDigits is absolute normally, but if you want from first significant - * digits (got %g), or in 0x80000000 + * of the decimal point in $(D_PARAM exponent). +/-Inf and NaN are specified + * by special values returned in the $(D_PARAM exponent). Sing bit is set in + * $(D_PARAM sign). */ -private int real2String(ref const(char)* start, - ref uint len, - char* out_, - out int decimalPos, - double value, - uint fracDigits) @nogc nothrow pure +private const(char)[] real2String(double value, + ref char[512] buffer, + out int exponent, + out bool sign) @nogc nothrow pure @trusted { - long bits = 0; - int e, tens; + long bits; + copyFp(value, bits); - double d = value; - copyFp(bits, d); - auto exponent = cast(int) ((bits >> 52) & 2047); - auto ng = cast(int) (bits >> 63); - if (ng) + exponent = (bits >> 52) & 0x7ff; + sign = signBit(value); + if (sign) { - d = -d; + value = -value; } - if (exponent == 2047) // Is NaN or inf? + if (exponent == 2047) // Is NaN or Inf? { - start = (bits & (((cast(ulong) 1) << 52) - 1)) ? "NaN" : "Inf"; - decimalPos = special; - len = 3; - return ng; + exponent = special; + return (bits & ((1UL << 52) - 1)) != 0 ? "NaN" : "Inf"; } if (exponent == 0) // Is zero or denormal? { - if ((bits << 1) == 0) // Do zero. + if ((bits << 1) == 0) // Zero. { - decimalPos = 1; - start = out_; - out_[0] = '0'; - len = 1; - return ng; + exponent = 1; + buffer[0] = '0'; + return buffer[0 .. 1]; } + // Find the right exponent for denormals. + for (long cursor = 1UL << 51; (bits & cursor) == 0; cursor >>= 1) { - long v = (cast(ulong) 1) << 51; - while ((bits & v) == 0) - { - --exponent; - v >>= 1; - } + --exponent; } } - // Find the decimal exponent as well as the decimal bits of the value. + // "617 / 2048" and "1233 / 4096" are estimations for the common logarithm + // (log10) of 2. Multiplied by a binary number it tells how big the number + // is in decimals, so it translates the binary exponent into decimal + // format. The estimation is tweaked to hit or undershoot by no more than + // 1 of log10 of all exponents 1..2046. + int tens = exponent - 1023; // Bias. + if (tens < 0) { - double ph, pl; - - // log10 estimate - very specifically tweaked to hit or undershoot by - // no more than 1 of log10 of all exponents 1..2046. - tens = exponent - 1023; - if (tens < 0) - { - tens = (tens * 617) / 2048; - } - else - { - tens = ((tens * 1233) / 4096) + 1; - } - - // Move the significant bits into position and stick them into an int. - raise2Power10(&ph, &pl, d, 18 - tens); - - // Get full as much precision from double-double as possible. - bits = cast(long) ph; - double vh = cast(double) bits; - double ahi = (ph - vh); - double t = (ahi - ph); - double alo = (ph - (ahi - t)) - (vh + t); - bits += cast(long) (ahi + alo + pl); - - // Check if we undershot. - if ((cast(ulong) bits) >= tenTo19th) - { - ++tens; - } - } - - // Now do the rounding in integer land. - if (fracDigits & 0x80000000) - { - fracDigits = (fracDigits & 0x7ffffff) + 1; + tens = tens * 617 / 2048; } else { - fracDigits = tens + fracDigits; + tens = tens * 1233 / 4096 + 1; } - if ((fracDigits < 24)) + + // Move the significant bits into position and stick them into an int. + HP p = raise2Power10(HP(value), 18 - tens); + + // Get full as much precision from double-double as possible. + bits = cast(long) p.base; + double vh = cast(double) bits; + auto a = HP(p.base - vh); + double t = a.base - p.base; + a.offset = p.base - a.base + t - vh - t; + bits += cast(long) (a.base + a.offset + p.offset); + + // Check if we undershot (bits >= 10 ^ 19). + if ((cast(ulong) bits) >= 1000000000000000000UL) { - uint dg = 1; - if (cast(ulong) bits >= powersOf10[9]) - { - dg = 10; - } - while (cast(ulong) bits >= powersOf10[dg]) - { - ++dg; - if (dg == 20) - { - goto noround; - } - } - if (fracDigits < dg) - { - // Add 0.5 at the right position and round. - e = dg - fracDigits; - if (cast(uint) e >= 24) - { - goto noround; - } - ulong r = powersOf10[e]; - bits = bits + (r / 2); - if (cast(ulong) bits >= powersOf10[dg]) - { - ++tens; - } - bits /= r; - } - noround: + ++tens; } + // Now do the rounding in integer land. + enum uint fracDigits = 6; + + uint dg = 1; + if ((cast(ulong) bits) >= powersOf10[9]) + { + dg = 10; + } + uint length; + while ((cast(ulong) bits) >= powersOf10[dg]) + { + ++dg; + if (dg == 20) + { + goto NoRound; + } + } + if (fracDigits < dg) + { + // Add 0.5 at the right position and round. + length = dg - fracDigits; + if (length >= 24) + { + goto NoRound; + } + ulong r = powersOf10[length]; + bits = bits + (r / 2); + if ((cast(ulong) bits) >= powersOf10[dg]) + { + ++tens; + } + bits /= r; + } +NoRound: + // Kill long trailing runs of zeros. if (bits) { - uint n; for (;;) { if (bits <= 0xffffffff) @@ -364,26 +440,26 @@ private int real2String(ref const(char)* start, } if (bits % 1000) { - goto donez; + goto Zeroed; } bits /= 1000; } - n = cast(uint) bits; + auto n = cast(uint) bits; while ((n % 1000) == 0) { n /= 1000; } bits = n; - donez: } +Zeroed: // Convert to string. - out_ += 64; - e = 0; - for (;;) + auto result = buffer.ptr + 64; + length = 0; + while (true) { uint n; - char *o = out_ - 8; + char* o = result - 8; // Do the conversion in chunks of U32s (avoid most 64-bit divides, // worth it, constant denomiators be damned). if (bits >= 100000000) @@ -398,35 +474,31 @@ private int real2String(ref const(char)* start, } while (n) { - out_ -= 2; - *cast(ushort*) out_ = *cast(ushort*) &digitpair[(n % 100) * 2]; + result -= 2; + *cast(ushort*) result = *cast(ushort*) &digitPairs[(n % 100) * 2]; n /= 100; - e += 2; + length += 2; } if (bits == 0) { - if ((e) && (out_[0] == '0')) + if ((length != 0) && (result[0] == '0')) { - ++out_; - --e; + ++result; + --length; } break; } - while (out_ != o) + for (; result !is o; ++length, --result) { - *--out_ = '0'; - ++e; + *result = '0'; } } - - decimalPos = tens; - start = out_; - len = e; - return ng; + exponent = tens; + return result[0 .. length]; } private void leadSign(bool negative, ref char[8] sign) -@nogc nothrow pure +@nogc nothrow pure @safe { sign[0] = 0; if (negative) @@ -436,13 +508,13 @@ private void leadSign(bool negative, ref char[8] sign) } } -// Copies d to bits w/ strict aliasing (this compiles to nothing on /Ox). -private void copyFp(T, U)(ref T dest, ref U src) +/* + * Copies double into long and back bitwise. + */ +private void copyFp(T, U)(ref const U src, ref T dest) @trusted +if (T.sizeof == U.sizeof) { - for (int count = 0; count < 8; ++count) - { - (cast(char*) &dest)[count] = (cast(char*) &src)[count]; - } + copy((&src)[0 .. 1], (&dest)[0 .. 1]); } private void ddmulthi(ref double oh, @@ -453,13 +525,13 @@ private void ddmulthi(ref double oh, double ahi, bhi; long bt; oh = xh * yh; - copyFp(bt, xh); + copyFp(xh, bt); bt &= ((~cast(ulong) 0) << 27); - copyFp(ahi, bt); + copyFp(bt, ahi); double alo = xh - ahi; - copyFp(bt, yh); + copyFp(yh, bt); bt &= ((~cast(ulong) 0) << 27); - copyFp(bhi, bt); + copyFp(bt, bhi); double blo = yh - bhi; ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; } @@ -467,25 +539,6 @@ private void ddmulthi(ref double oh, package(tanya) String format(string fmt, Args...)(char[] buf, Args args) { String result; - char* bf = buf.ptr; - - // Ok, we have a percent, read the modifiers first. - int precision = -1; - int tz = 0; - bool negative; - - // Handle each replacement. - char[512] num; // Big enough for e308 (with commas) or e-307. - char[8] lead; - char[8] tail; - char *s; - uint l, n; - ulong n64; - - double fv; - - int decimalPos; - const(char)* sn; static if (isSomeString!(Args[0])) // String { @@ -504,28 +557,23 @@ package(tanya) String format(string fmt, Args...)(char[] buf, Args args) } else static if (isFloatingPoint!(Args[0])) // Float { - fv = args[0]; - if (precision == -1) - { - precision = 6; - } - else if (precision == 0) - { - precision = 1; // Default is 6. - } + char[512] num; // Big enough for e308 (with commas) or e-307. + char[8] lead; + char[8] tail = 0; + char *s; + int precision = 6; + int tz; + bool negative; + char* bf = buf.ptr; + int decimalPos; + // Read the double into a string. - if (real2String(sn, - l, - num.ptr, - decimalPos, - fv, - (precision - 1) | 0x80000000)) - { - negative = true; - } + auto fv = real2String(args[0], num, decimalPos, negative); + const(char)* sn = fv.ptr; + auto l = cast(uint) fv.length; // Clamp the precision and delete extra zeros after clamp. - n = precision; + uint n = precision; if (l > cast(uint) precision) { l = precision; @@ -570,7 +618,6 @@ package(tanya) String format(string fmt, Args...)(char[] buf, Args args) goto dofloatfromg; doexpfromg: - tail[0] = 0; leadSign(negative, lead); if (decimalPos == special) { @@ -628,7 +675,6 @@ package(tanya) String format(string fmt, Args...)(char[] buf, Args args) goto flt_lead; dofloatfromg: - tail[0] = 0; leadSign(negative, lead); if (decimalPos == special) { @@ -765,140 +811,67 @@ package(tanya) String format(string fmt, Args...)(char[] buf, Args args) // Get the length that we copied. l = cast(uint) (s - (num.ptr + 64)); s = num.ptr + 64; - } - else static if (isPointer!(Args[0])) // Pointer - { - // Get the number. - n64 = cast(size_t) args[0]; - size_t position = num.length; - do // Write at least "0" if the pointer is null. + scopy: + // Get fw=leading/trailing space, precision=leading zeros. + if (precision < cast(int) l) { - num[--position] = lowerHexDigits[cast(size_t) (n64 & 15)]; - n64 >>= 4; + precision = l; } - while (n64 != 0); + n = precision + lead[0] + tail[0] + tz; + precision -= l; - result.insertBack("0x"); - result.insertBack(num[position .. $]); - } - else static if (isIntegral!(Args[0])) // Integer - { - // Get the integer and abs it. - static if (Args[0].sizeof == 8) + // Copy the spaces and/or zeros. + if (precision) { - long k64 = args[0]; - n64 = cast(ulong) k64; - static if (isSigned!(Args[0])) + int i; + + // copy leader + sn = lead.ptr + 1; + while (lead[0]) { - if (k64 < 0) + i = lead[0]; + lead[0] -= cast(char) i; + while (i) { - n64 = cast(ulong) -k64; - negative = true; + *bf++ = *sn++; + --i; } } - } - else - { - int k = args[0]; - n64 = cast(uint) k; - static if (isSigned!(Args[0])) + + // Copy leading zeros. + while (precision > 0) { - if (k < 0) + i = precision; + precision -= i; + while (i) { - n64 = cast(uint) -k; - negative = true; + if (((cast(size_t) bf) & 3) == 0) + { + break; + } + *bf++ = '0'; + --i; + } + while (i >= 4) + { + *cast(uint*) bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) + { + *bf++ = '0'; + --i; } } } - // Convert to string. - s = num.ptr + num.length; - l = 0; - - for (;;) - { - // Do in 32-bit chunks (avoid lots of 64-bit divides even - // with constant denominators). - char *o = s - 8; - if (n64 >= 100000000) - { - n = cast(uint) (n64 % 100000000); - n64 /= 100000000; - } - else - { - n = cast(uint) n64; - n64 = 0; - } - while (n) - { - s -= 2; - *cast(ushort*) s = *cast(ushort*) &digitpair[(n % 100) * 2]; - n /= 100; - } - while (n) - { - *--s = cast(char) (n % 10) + '0'; - n /= 10; - } - if (n64 == 0) - { - if ((s[0] == '0') && (s != (num.ptr + num.length))) - { - ++s; - } - break; - } - while (s != o) - { - *--s = '0'; - } - } - - tail[0] = 0; - leadSign(negative, lead); - - // Get the length that we copied. - l = cast(uint) ((num.ptr + num.length) - s); - if (l == 0) - { - *--s = '0'; - l = 1; - } - if (precision < 0) - { - precision = 0; - } - } - else - { - static assert(false); - } - - if (result.length > 0) - { - return result; - } -scopy: - // Get fw=leading/trailing space, precision=leading zeros. - if (precision < cast(int) l) - { - precision = l; - } - n = precision + lead[0] + tail[0] + tz; - precision -= l; - - // Copy the spaces and/or zeros. - if (precision) - { - int i; - - // copy leader + // copy leader if there is still one sn = lead.ptr + 1; while (lead[0]) { - i = lead[0]; + int i = lead[0]; lead[0] -= cast(char) i; while (i) { @@ -907,11 +880,25 @@ scopy: } } - // Copy leading zeros. - while (precision > 0) + // Copy the string. + n = l; + while (n) { - i = precision; - precision -= i; + int i = n; + n -= i; + + while (i) + { + *bf++ = *s++; + --i; + } + } + + // Copy trailing zeros. + while (tz) + { + int i = tz; + tz -= i; while (i) { if (((cast(size_t) bf) & 3) == 0) @@ -933,77 +920,49 @@ scopy: --i; } } - } - // copy leader if there is still one - sn = lead.ptr + 1; - while (lead[0]) - { - int i = lead[0]; - lead[0] -= cast(char) i; - while (i) + // copy tail if there is one + sn = tail.ptr + 1; + while (tail[0]) { - *bf++ = *sn++; - --i; - } - } - - // Copy the string. - n = l; - while (n) - { - int i = n; - n -= i; - - while (i) - { - *bf++ = *s++; - --i; - } - } - - // Copy trailing zeros. - while (tz) - { - int i = tz; - tz -= i; - while (i) - { - if (((cast(size_t) bf) & 3) == 0) + int i = tail[0]; + tail[0] -= cast(char) i; + while (i) { - break; + *bf++ = *sn++; + --i; } - *bf++ = '0'; - --i; } - while (i >= 4) - { - *cast(uint*) bf = 0x30303030; - bf += 4; - i -= 4; - } - while (i) - { - *bf++ = '0'; - --i; - } - } - // copy tail if there is one - sn = tail.ptr + 1; - while (tail[0]) + *bf = 0; + result = String(buf[0 .. cast(int) (bf - buf.ptr)]); + } + else static if (isPointer!(Args[0])) // Pointer { - int i = tail[0]; - tail[0] -= cast(char) i; - while (i) + char[size_t.sizeof * 2] buffer; + size_t position = buffer.length; + auto address = cast(size_t) args[0]; + + do // Write at least "0" if the pointer is null. { - *bf++ = *sn++; - --i; + buffer[--position] = lowerHexDigits[cast(size_t) (address & 15)]; + address >>= 4; } + while (address != 0); + + result.insertBack("0x"); + result.insertBack(buffer[position .. $]); + } + else static if (isIntegral!(Args[0])) // Integer + { + char[21] buffer; + result.insertBack(integral2String(args[0], buffer)); + } + else + { + static assert(false); } - *bf = 0; - result = String(buf[0 .. cast(int) (bf - buf.ptr)]); return result; }