Skip to content

Commit c7fb52e

Browse files
committed
Fix Issue 24082 - add Int128.toString that supports std.format
toString has template parameter FormatSpec instead of template parameter Char and argument std.format.FormatSpec!Char so importing std.int128 does not require importing std.format unless toString is actually used (pay as you go).
1 parent 41f6a45 commit c7fb52e

File tree

1 file changed

+144
-2
lines changed

1 file changed

+144
-2
lines changed

std/int128.d

Lines changed: 144 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ private import core.int128;
1818

1919
public struct Int128
2020
{
21-
@safe pure nothrow @nogc:
22-
21+
@safe pure nothrow @nogc
22+
{
2323
Cent data; /// core.int128.Cent
2424

2525
/****************
@@ -271,6 +271,148 @@ public struct Int128
271271
{
272272
return opCmp(Int128(0, op2));
273273
}
274+
} // @safe pure nothrow @nogc
275+
276+
/**
277+
* Formats `Int128` with either `%d`, `%x`, `%X`, or `%s` (same as `%d`).
278+
*
279+
* Params:
280+
* sink = $(REF_ALTTEXT Output range, isOutputRange, std, range, primitives)
281+
* to write to.
282+
* fmt = A $(REF FormatSpec, std,format) which controls how the number
283+
* is displayed.
284+
*
285+
* Throws:
286+
* $(REF FormatException, std,format) if the format specifier is
287+
* not one of 'd', 'x', 'X', 's'.
288+
*
289+
* See_Also: $(REF formatValue, std,format)
290+
*/
291+
void toString(Writer, FormatSpec)(scope ref Writer sink, scope const ref FormatSpec fmt) const
292+
{
293+
import std.range.primitives : put;
294+
import std.format : FormatException, Fmt = FormatSpec;
295+
296+
static if (is(FormatSpec == Fmt!Char, Char))
297+
{
298+
// Puts "Char" into scope if the pattern matches.
299+
}
300+
static assert(is(Char),
301+
"Expecting `FormatSpec` to be instantiation of `std.format.FormatSpec`");
302+
303+
Char[39] buf = void;
304+
size_t bufStart = void;
305+
Char signChar = 0;
306+
if (fmt.spec == 'd' || fmt.spec == 's')
307+
{
308+
const bool isNeg = 0 > cast(long) this.data.hi;
309+
Cent val = isNeg ? neg(this.data) : this.data;
310+
immutable Cent radix = { lo: 10, hi: 0 };
311+
Cent modulus;
312+
bufStart = buf.length;
313+
do
314+
{
315+
uint x = void;
316+
if (ugt(radix, val))
317+
{
318+
x = cast(uint) val.lo;
319+
val = Cent(0, 0);
320+
}
321+
else
322+
{
323+
val = udivmod(val, radix, modulus);
324+
x = cast(uint) modulus.lo;
325+
}
326+
buf[--bufStart] = cast(Char) ('0' + x);
327+
} while (tst(val));
328+
if (isNeg)
329+
signChar = '-';
330+
else if (fmt.flPlus)
331+
signChar = '+';
332+
else if (fmt.flSpace)
333+
signChar = ' ';
334+
}
335+
else if (fmt.spec == 'x' || fmt.spec == 'X')
336+
{
337+
immutable hexDigits = fmt.spec == 'X' ? "0123456789ABCDEF" : "0123456789abcdef";
338+
ulong a = data.lo;
339+
bufStart = buf.length - 1;
340+
size_t penPos = buf.length - 1;
341+
do
342+
{
343+
if ((buf[penPos] = hexDigits[0xF & cast(uint) a]) != '0')
344+
bufStart = penPos;
345+
a >>>= 4;
346+
} while (--penPos >= buf.length - 16);
347+
a = data.hi;
348+
do
349+
{
350+
if ((buf[penPos] = hexDigits[0xF & cast(uint) a]) != '0')
351+
bufStart = penPos;
352+
a >>>= 4;
353+
} while (--penPos >= buf.length - 32);
354+
}
355+
else
356+
{
357+
throw new FormatException("Format specifier not understood: %" ~ fmt.spec);
358+
}
359+
360+
const minw = (buf.length - bufStart) + int(signChar != 0);
361+
const maxw = minw < fmt.width ? fmt.width : minw;
362+
const difw = maxw - minw;
363+
364+
static void putRepeatedChars(Char c)(scope ref Writer sink, size_t n)
365+
{
366+
static immutable Char[8] array = [c, c, c, c, c, c, c, c];
367+
foreach (_; 0 .. n / 8)
368+
put(sink, array[0 .. 8]);
369+
if (n & 7)
370+
put(sink, array[0 .. n & 7]);
371+
}
372+
373+
if (!fmt.flDash && !fmt.flZero && difw)
374+
putRepeatedChars!' '(sink, difw);
375+
376+
if (signChar)
377+
{
378+
Char[1] signCharBuf = signChar;
379+
put(sink, signCharBuf[0 .. 1]);
380+
}
381+
382+
if (!fmt.flDash && fmt.flZero && difw)
383+
putRepeatedChars!'0'(sink, difw);
384+
385+
put(sink, buf[bufStart .. $]);
386+
387+
if (fmt.flDash && difw)
388+
putRepeatedChars!' '(sink, difw);
389+
}
390+
391+
/**
392+
`toString` is rarely directly invoked; the usual way of using it is via
393+
$(REF format, std, format):
394+
*/
395+
@safe unittest
396+
{
397+
import std.format : format;
398+
399+
assert(format("%s", Int128.max) == "170141183460469231731687303715884105727");
400+
assert(format("%s", Int128.min) == "-170141183460469231731687303715884105728");
401+
assert(format("%x", Int128.max) == "7fffffffffffffffffffffffffffffff");
402+
assert(format("%X", Int128.max) == "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
403+
assert(format("%032X", Int128(123L)) == "0000000000000000000000000000007B");
404+
assert(format("%+ 40d", Int128(123L)) == " +123");
405+
assert(format("%+-40d", Int128(123L)) == "+123 ");
406+
}
407+
408+
/// Also can format as `wchar` or `dchar`.
409+
@safe unittest
410+
{
411+
import std.conv : to;
412+
413+
assert(to!wstring(Int128.max) == "170141183460469231731687303715884105727"w);
414+
assert(to!dstring(Int128.max) == "170141183460469231731687303715884105727"d);
415+
}
274416

275417
enum min = Int128(long.min, 0); /// minimum value
276418
enum max = Int128(long.max, ulong.max); /// maximum value

0 commit comments

Comments
 (0)