Reduce template bloat in IntegralTypeOf-overload of formatValueImpl

This commit is contained in:
Per Nordlöw 2021-10-15 00:36:44 +02:00 committed by The Dlang Bot
parent e2279cac12
commit 4b5785c4e3

View file

@ -160,8 +160,6 @@ if (is(immutable T == immutable typeof(null)) && !is(T == enum) && !hasToString!
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))
{
import std.range.primitives : put;
alias U = IntegralTypeOf!T;
U val = obj; // Extracting alias this may be impure/system/may-throw
@ -171,56 +169,46 @@ if (is(IntegralTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
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;
}
immutable uint base =
f.spec == 'x' || f.spec == 'X' || f.spec == 'a' || f.spec == 'A' ? 16 :
f.spec == 'o' ? 8 :
f.spec == 'b' ? 2 :
f.spec == 's' || f.spec == 'd' || f.spec == 'u'
|| f.spec == 'e' || f.spec == 'E' || f.spec == 'f' || f.spec == 'F'
|| f.spec == 'g' || f.spec == 'G' ? 10 :
0;
import std.format : enforceFmt;
enforceFmt(base > 0,
"incompatible format character for integral argument: %" ~ f.spec);
import std.math.algebraic : abs;
bool negative = false;
ulong arg = val;
static if (isSigned!U)
{
if (f.spec != 'x' && f.spec != 'X' && f.spec != 'b' && f.spec != 'o' && f.spec != 'u')
{
if (val < 0)
{
negative = true;
arg = cast(ulong) abs(val);
}
}
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 bloat
private 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
{
digits[pos--] = '0' + arg % base;
/* `cast(char)` is needed because value range propagation (VRP) cannot
* analyze `base` because its 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;
@ -245,12 +233,12 @@ if (is(IntegralTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
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) && obj != 0)
if (f.flHash && (base == 16) && !zero)
{
prefix[--left] = f.spec;
prefix[--left] = '0';
}
if (f.flHash && (base == 8) && obj != 0
if (f.flHash && (base == 8) && !zero
&& (digits.length - (pos + 1) >= f.precision || f.precision == f.UNSPECIFIED))
prefix[--left] = '0';
@ -358,6 +346,48 @@ if (is(IntegralTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
(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
{