@@ -18,8 +18,8 @@ private import core.int128;
1818
1919public 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