Skip to content

Commit 9e1e8e7

Browse files
authored
Merge pull request #8797 from n8sh/int128-toString
Fix Issue 24082 - add Int128.toString that supports std.format
2 parents 4773cdf + c7fb52e commit 9e1e8e7

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)