1 // Written in the D programming language.
2 
3 /**
4    This module implements the formatting functionality for strings and
5    I/O. It's comparable to C99's $(D vsprintf()) and uses a similar
6    _format encoding scheme.
7 
8    For an introductory look at $(B std._format)'s capabilities and how to use
9    this module see the dedicated
10    $(LINK2 http://wiki.dlang.org/Defining_custom_print_format_specifiers, DWiki article).
11 
12    This module centers around two functions:
13 
14 $(BOOKTABLE ,
15 $(TR $(TH Function Name) $(TH Description)
16 )
17     $(TR $(TD $(LREF formattedRead))
18         $(TD Reads values according to the _format string from an InputRange.
19     ))
20     $(TR $(TD $(LREF formattedWrite))
21         $(TD Formats its arguments according to the _format string and puts them
22         to an OutputRange.
23     ))
24 )
25 
26    Please see the documentation of function $(LREF formattedWrite) for a
27    description of the _format string.
28 
29    Two functions have been added for convenience:
30 
31 $(BOOKTABLE ,
32 $(TR $(TH Function Name) $(TH Description)
33 )
34     $(TR $(TD $(LREF _format))
35         $(TD Returns a GC-allocated string with the formatting result.
36     ))
37     $(TR $(TD $(LREF sformat))
38         $(TD Puts the formatting result into a preallocated array.
39     ))
40 )
41 
42    These two functions are publicly imported by $(MREF std, string)
43    to be easily available.
44 
45    The functions $(LREF formatValue) and $(LREF unformatValue) are
46    used for the plumbing.
47    Copyright: Copyright Digital Mars 2000-2013.
48 
49    License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
50 
51    Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
52    Andrei Alexandrescu), and Kenji Hara
53 
54    Source: $(PHOBOSSRC std/_format.d)
55  */
56 module std.format;
57 
58 //debug=format;                // uncomment to turn on debugging printf's
59 
60 import core.vararg;
61 import std.exception;
62 import std.meta;
63 import std.range.primitives;
64 import std.traits;
65 
66 
67 /**********************************************************************
68  * Signals a mismatch between a format and its corresponding argument.
69  */
70 class FormatException : Exception
71 {
72     @safe pure nothrow
this()73     this()
74     {
75         super("format error");
76     }
77 
78     @safe pure nothrow
79     this(string msg, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null)
80     {
81         super(msg, fn, ln, next);
82     }
83 }
84 
85 private alias enforceFmt = enforceEx!FormatException;
86 
87 
88 /**********************************************************************
89    Interprets variadic argument list $(D args), formats them according
90    to $(D fmt), and sends the resulting characters to $(D w). The
91    encoding of the output is the same as $(D Char). The type $(D Writer)
92    must satisfy $(D $(REF isOutputRange, std,range,primitives)!(Writer, Char)).
93 
94    The variadic arguments are normally consumed in order. POSIX-style
95    $(HTTP opengroup.org/onlinepubs/009695399/functions/printf.html,
96    positional parameter syntax) is also supported. Each argument is
97    formatted into a sequence of chars according to the format
98    specification, and the characters are passed to $(D w). As many
99    arguments as specified in the format string are consumed and
100    formatted. If there are fewer arguments than format specifiers, a
101    $(D FormatException) is thrown. If there are more remaining arguments
102    than needed by the format specification, they are ignored but only
103    if at least one argument was formatted.
104 
105    The format string supports the formatting of array and nested array elements
106    via the grouping format specifiers $(B %() and $(B %)). Each
107    matching pair of $(B %() and $(B %)) corresponds with a single array
108    argument. The enclosed sub-format string is applied to individual array
109    elements.  The trailing portion of the sub-format string following the
110    conversion specifier for the array element is interpreted as the array
111    delimiter, and is therefore omitted following the last array element. The
112    $(B %|) specifier may be used to explicitly indicate the start of the
113    delimiter, so that the preceding portion of the string will be included
114    following the last array element.  (See below for explicit examples.)
115 
116    Params:
117 
118    w = Output is sent to this writer. Typical output writers include
119    $(REF Appender!string, std,array) and $(REF LockingTextWriter, std,stdio).
120 
121    fmt = Format string.
122 
123    args = Variadic argument list.
124 
125    Returns: Formatted number of arguments.
126 
127    Throws: Mismatched arguments and formats result in a $(D
128    FormatException) being thrown.
129 
130    Format_String: <a name="format-string">$(I Format strings)</a>
131    consist of characters interspersed with $(I format
132    specifications). Characters are simply copied to the output (such
133    as putc) after any necessary conversion to the corresponding UTF-8
134    sequence.
135 
136    The format string has the following grammar:
137 
138 $(PRE
139 $(I FormatString):
140     $(I FormatStringItem)*
141 $(I FormatStringItem):
142     $(B '%%')
143     $(B '%') $(I Position) $(I Flags) $(I Width) $(I Separator) $(I Precision) $(I FormatChar)
144     $(B '%$(LPAREN)') $(I FormatString) $(B '%$(RPAREN)')
145     $(I OtherCharacterExceptPercent)
146 $(I Position):
147     $(I empty)
148     $(I Integer) $(B '$')
149 $(I Flags):
150     $(I empty)
151     $(B '-') $(I Flags)
152     $(B '+') $(I Flags)
153     $(B '#') $(I Flags)
154     $(B '0') $(I Flags)
155     $(B ' ') $(I Flags)
156 $(I Width):
157     $(I empty)
158     $(I Integer)
159     $(B '*')
160 $(I Separator):
161     $(I empty)
162     $(B ',')
163     $(B ',') $(B '?')
164     $(B ',') $(B '*') $(B '?')
165     $(B ',') $(I Integer) $(B '?')
166     $(B ',') $(B '*')
167     $(B ',') $(I Integer)
168 $(I Precision):
169     $(I empty)
170     $(B '.')
171     $(B '.') $(I Integer)
172     $(B '.*')
173 $(I Integer):
174     $(I Digit)
175     $(I Digit) $(I Integer)
176 $(I Digit):
177     $(B '0')|$(B '1')|$(B '2')|$(B '3')|$(B '4')|$(B '5')|$(B '6')|$(B '7')|$(B '8')|$(B '9')
178 $(I FormatChar):
179     $(B 's')|$(B 'c')|$(B 'b')|$(B 'd')|$(B 'o')|$(B 'x')|$(B 'X')|$(B 'e')|$(B 'E')|$(B 'f')|$(B 'F')|$(B 'g')|$(B 'G')|$(B 'a')|$(B 'A')|$(B '|')
180 )
181 
182     $(BOOKTABLE Flags affect formatting depending on the specifier as
183     follows., $(TR $(TH Flag) $(TH Types&nbsp;affected) $(TH Semantics))
184 
185     $(TR $(TD $(B '-')) $(TD numeric) $(TD Left justify the result in
186         the field.  It overrides any $(B 0) flag.))
187 
188     $(TR $(TD $(B '+')) $(TD numeric) $(TD Prefix positive numbers in
189     a signed conversion with a $(B +).  It overrides any $(I space)
190     flag.))
191 
192     $(TR $(TD $(B '#')) $(TD integral ($(B 'o'))) $(TD Add to
193     precision as necessary so that the first digit of the octal
194     formatting is a '0', even if both the argument and the $(I
195     Precision) are zero.))
196 
197     $(TR $(TD $(B '#')) $(TD integral ($(B 'x'), $(B 'X'))) $(TD If
198        non-zero, prefix result with $(B 0x) ($(B 0X)).))
199 
200     $(TR $(TD $(B '#')) $(TD floating) $(TD Always insert the decimal
201        point and print trailing zeros.))
202 
203     $(TR $(TD $(B '0')) $(TD numeric) $(TD Use leading
204     zeros to pad rather than spaces (except for the floating point
205     values $(D nan) and $(D infinity)).  Ignore if there's a $(I
206     Precision).))
207 
208     $(TR $(TD $(B ' ')) $(TD numeric) $(TD Prefix positive
209     numbers in a signed conversion with a space.)))
210 
211     $(DL
212         $(DT $(I Width))
213         $(DD
214         Specifies the minimum field width.
215         If the width is a $(B *), an additional argument of type $(B int),
216         preceding the actual argument, is taken as the width.
217         If the width is negative, it is as if the $(B -) was given
218         as a $(I Flags) character.)
219 
220         $(DT $(I Precision))
221         $(DD Gives the precision for numeric conversions.
222         If the precision is a $(B *), an additional argument of type $(B int),
223         preceding the actual argument, is taken as the precision.
224         If it is negative, it is as if there was no $(I Precision) specifier.)
225 
226         $(DT $(I Separator))
227         $(DD Inserts the separator symbols ',' every $(I X) digits, from right
228         to left, into numeric values to increase readability.
229         The fractional part of floating point values inserts the separator
230         from left to right.
231         Entering an integer after the ',' allows to specify $(I X).
232         If a '*' is placed after the ',' then $(I X) is specified by an
233         additional parameter to the format function.
234         Adding a '?' after the ',' or $(I X) specifier allows to specify
235         the separator character as an additional parameter.
236         )
237 
238         $(DT $(I FormatChar))
239         $(DD
240         $(DL
241             $(DT $(B 's'))
242             $(DD The corresponding argument is formatted in a manner consistent
243             with its type:
244             $(DL
245                 $(DT $(B bool))
246                 $(DD The result is $(D "true") or $(D "false").)
247                 $(DT integral types)
248                 $(DD The $(B %d) format is used.)
249                 $(DT floating point types)
250                 $(DD The $(B %g) format is used.)
251                 $(DT string types)
252                 $(DD The result is the string converted to UTF-8.
253                 A $(I Precision) specifies the maximum number of characters
254                 to use in the result.)
255                 $(DT structs)
256                 $(DD If the struct defines a $(B toString()) method the result is
257                 the string returned from this function. Otherwise the result is
258                 StructName(field<sub>0</sub>, field<sub>1</sub>, ...) where
259                 field<sub>n</sub> is the nth element formatted with the default
260                 format.)
261                 $(DT classes derived from $(B Object))
262                 $(DD The result is the string returned from the class instance's
263                 $(B .toString()) method.
264                 A $(I Precision) specifies the maximum number of characters
265                 to use in the result.)
266                 $(DT unions)
267                 $(DD If the union defines a $(B toString()) method the result is
268                 the string returned from this function. Otherwise the result is
269                 the name of the union, without its contents.)
270                 $(DT non-string static and dynamic arrays)
271                 $(DD The result is [s<sub>0</sub>, s<sub>1</sub>, ...]
272                 where s<sub>n</sub> is the nth element
273                 formatted with the default format.)
274                 $(DT associative arrays)
275                 $(DD The result is the equivalent of what the initializer
276                 would look like for the contents of the associative array,
277                 e.g.: ["red" : 10, "blue" : 20].)
278             ))
279 
280             $(DT $(B 'c'))
281             $(DD The corresponding argument must be a character type.)
282 
283             $(DT $(B 'b','d','o','x','X'))
284             $(DD The corresponding argument must be an integral type
285             and is formatted as an integer. If the argument is a signed type
286             and the $(I FormatChar) is $(B d) it is converted to
287             a signed string of characters, otherwise it is treated as
288             unsigned. An argument of type $(B bool) is formatted as '1'
289             or '0'. The base used is binary for $(B b), octal for $(B o),
290             decimal
291             for $(B d), and hexadecimal for $(B x) or $(B X).
292             $(B x) formats using lower case letters, $(B X) uppercase.
293             If there are fewer resulting digits than the $(I Precision),
294             leading zeros are used as necessary.
295             If the $(I Precision) is 0 and the number is 0, no digits
296             result.)
297 
298             $(DT $(B 'e','E'))
299             $(DD A floating point number is formatted as one digit before
300             the decimal point, $(I Precision) digits after, the $(I FormatChar),
301             &plusmn;, followed by at least a two digit exponent:
302             $(I d.dddddd)e$(I &plusmn;dd).
303             If there is no $(I Precision), six
304             digits are generated after the decimal point.
305             If the $(I Precision) is 0, no decimal point is generated.)
306 
307             $(DT $(B 'f','F'))
308             $(DD A floating point number is formatted in decimal notation.
309             The $(I Precision) specifies the number of digits generated
310             after the decimal point. It defaults to six. At least one digit
311             is generated before the decimal point. If the $(I Precision)
312             is zero, no decimal point is generated.)
313 
314             $(DT $(B 'g','G'))
315             $(DD A floating point number is formatted in either $(B e) or
316             $(B f) format for $(B g); $(B E) or $(B F) format for
317             $(B G).
318             The $(B f) format is used if the exponent for an $(B e) format
319             is greater than -5 and less than the $(I Precision).
320             The $(I Precision) specifies the number of significant
321             digits, and defaults to six.
322             Trailing zeros are elided after the decimal point, if the fractional
323             part is zero then no decimal point is generated.)
324 
325             $(DT $(B 'a','A'))
326             $(DD A floating point number is formatted in hexadecimal
327             exponential notation 0x$(I h.hhhhhh)p$(I &plusmn;d).
328             There is one hexadecimal digit before the decimal point, and as
329             many after as specified by the $(I Precision).
330             If the $(I Precision) is zero, no decimal point is generated.
331             If there is no $(I Precision), as many hexadecimal digits as
332             necessary to exactly represent the mantissa are generated.
333             The exponent is written in as few digits as possible,
334             but at least one, is in decimal, and represents a power of 2 as in
335             $(I h.hhhhhh)*2<sup>$(I &plusmn;d)</sup>.
336             The exponent for zero is zero.
337             The hexadecimal digits, x and p are in upper case if the
338             $(I FormatChar) is upper case.)
339         ))
340     )
341 
342     Floating point NaN's are formatted as $(B nan) if the
343     $(I FormatChar) is lower case, or $(B NAN) if upper.
344     Floating point infinities are formatted as $(B inf) or
345     $(B infinity) if the
346     $(I FormatChar) is lower case, or $(B INF) or $(B INFINITY) if upper.
347 
348     The positional and non-positional styles can be mixed in the same
349     format string. (POSIX leaves this behavior undefined.) The internal
350     counter for non-positional parameters tracks the next parameter after
351     the largest positional parameter already used.
352 
353     Example using array and nested array formatting:
354     -------------------------
355     import std.stdio;
356 
357     void main()
358     {
359         writefln("My items are %(%s %).", [1,2,3]);
360         writefln("My items are %(%s, %).", [1,2,3]);
361     }
362     -------------------------
363     The output is:
364 $(CONSOLE
365 My items are 1 2 3.
366 My items are 1, 2, 3.
367 )
368 
369     The trailing end of the sub-format string following the specifier for each
370     item is interpreted as the array delimiter, and is therefore omitted
371     following the last array item. The $(B %|) delimiter specifier may be used
372     to indicate where the delimiter begins, so that the portion of the format
373     string prior to it will be retained in the last array element:
374     -------------------------
375     import std.stdio;
376 
377     void main()
378     {
379         writefln("My items are %(-%s-%|, %).", [1,2,3]);
380     }
381     -------------------------
382     which gives the output:
383 $(CONSOLE
384 My items are -1-, -2-, -3-.
385 )
386 
387     These compound format specifiers may be nested in the case of a nested
388     array argument:
389     -------------------------
390     import std.stdio;
391     void main() {
392          auto mat = [[1, 2, 3],
393                      [4, 5, 6],
394                      [7, 8, 9]];
395 
396          writefln("%(%(%d %)\n%)", mat);
397          writeln();
398 
399          writefln("[%(%(%d %)\n %)]", mat);
400          writeln();
401 
402          writefln("[%([%(%d %)]%|\n %)]", mat);
403          writeln();
404     }
405     -------------------------
406     The output is:
407 $(CONSOLE
408 1 2 3
409 4 5 6
410 7 8 9
411 
412 [1 2 3
413  4 5 6
414  7 8 9]
415 
416 [[1 2 3]
417  [4 5 6]
418  [7 8 9]]
419 )
420 
421     Inside a compound format specifier, strings and characters are escaped
422     automatically. To avoid this behavior, add $(B '-') flag to
423     $(D "%$(LPAREN)").
424     -------------------------
425     import std.stdio;
426 
427     void main()
428     {
429         writefln("My friends are %s.", ["John", "Nancy"]);
430         writefln("My friends are %(%s, %).", ["John", "Nancy"]);
431         writefln("My friends are %-(%s, %).", ["John", "Nancy"]);
432     }
433     -------------------------
434    which gives the output:
435 $(CONSOLE
436 My friends are ["John", "Nancy"].
437 My friends are "John", "Nancy".
438 My friends are John, Nancy.
439 )
440  */
441 uint formattedWrite(alias fmt, Writer, A...)(auto ref Writer w, A args)
442 if (isSomeString!(typeof(fmt)))
443 {
444     alias e = checkFormatException!(fmt, A);
445     static assert(!e, e.msg);
446     return .formattedWrite(w, fmt, args);
447 }
448 
449 /// The format string can be checked at compile-time (see $(LREF format) for details):
450 @safe pure unittest
451 {
452     import std.array : appender;
453     import std.format : formattedWrite;
454 
455     auto writer = appender!string();
456     writer.formattedWrite!"%s is the ultimate %s."(42, "answer");
457     assert(writer.data == "42 is the ultimate answer.");
458 
459     // Clear the writer
460     writer = appender!string();
461     formattedWrite(writer, "Date: %2$s %1$s", "October", 5);
462     assert(writer.data == "Date: 5 October");
463 }
464 
465 /// ditto
formattedWrite(Writer,Char,A...)466 uint formattedWrite(Writer, Char, A...)(auto ref Writer w, in Char[] fmt, A args)
467 {
468     import std.conv : text;
469 
470     auto spec = FormatSpec!Char(fmt);
471 
472     // Are we already done with formats? Then just dump each parameter in turn
473     uint currentArg = 0;
474     while (spec.writeUpToNextSpec(w))
475     {
476         if (currentArg == A.length && !spec.indexStart)
477         {
478             // leftover spec?
479             enforceFmt(fmt.length == 0,
480                 text("Orphan format specifier: %", spec.spec));
481             break;
482         }
483 
484         if (spec.width == spec.DYNAMIC)
485         {
486             auto width = getNthInt!"integer width"(currentArg, args);
487             if (width < 0)
488             {
489                 spec.flDash = true;
490                 width = -width;
491             }
492             spec.width = width;
493             ++currentArg;
494         }
495         else if (spec.width < 0)
496         {
497             // means: get width as a positional parameter
498             auto index = cast(uint) -spec.width;
499             assert(index > 0);
500             auto width = getNthInt!"integer width"(index - 1, args);
501             if (currentArg < index) currentArg = index;
502             if (width < 0)
503             {
504                 spec.flDash = true;
505                 width = -width;
506             }
507             spec.width = width;
508         }
509 
510         if (spec.precision == spec.DYNAMIC)
511         {
512             auto precision = getNthInt!"integer precision"(currentArg, args);
513             if (precision >= 0) spec.precision = precision;
514             // else negative precision is same as no precision
515             else spec.precision = spec.UNSPECIFIED;
516             ++currentArg;
517         }
518         else if (spec.precision < 0)
519         {
520             // means: get precision as a positional parameter
521             auto index = cast(uint) -spec.precision;
522             assert(index > 0);
523             auto precision = getNthInt!"integer precision"(index- 1, args);
524             if (currentArg < index) currentArg = index;
525             if (precision >= 0) spec.precision = precision;
526             // else negative precision is same as no precision
527             else spec.precision = spec.UNSPECIFIED;
528         }
529 
530         if (spec.separators == spec.DYNAMIC)
531         {
532             auto separators = getNthInt!"separator digit width"(currentArg, args);
533             spec.separators = separators;
534             ++currentArg;
535         }
536 
537         if (spec.separatorCharPos == spec.DYNAMIC)
538         {
539             auto separatorChar =
540                 getNth!("separator character", isSomeChar, dchar)(currentArg, args);
541             spec.separatorChar = separatorChar;
542             ++currentArg;
543         }
544 
545         if (currentArg == A.length && !spec.indexStart)
546         {
547             // leftover spec?
548             enforceFmt(fmt.length == 0,
549                 text("Orphan format specifier: %", spec.spec));
550             break;
551         }
552 
553         // Format an argument
554         // This switch uses a static foreach to generate a jump table.
555         // Currently `spec.indexStart` use the special value '0' to signal
556         // we should use the current argument. An enhancement would be to
557         // always store the index.
558         size_t index = currentArg;
559         if (spec.indexStart != 0)
560             index = spec.indexStart - 1;
561         else
562             ++currentArg;
563     SWITCH: switch (index)
564         {
565             foreach (i, Tunused; A)
566             {
567             case i:
568                 formatValue(w, args[i], spec);
569                 if (currentArg < spec.indexEnd)
570                     currentArg = spec.indexEnd;
571                 // A little know feature of format is to format a range
572                 // of arguments, e.g. `%1:3$` will format the first 3
573                 // arguments. Since they have to be consecutive we can
574                 // just use explicit fallthrough to cover that case.
575                 if (i + 1 < spec.indexEnd)
576                 {
577                     // You cannot goto case if the next case is the default
578                     static if (i + 1 < A.length)
579                         goto case;
580                     else
581                         goto default;
582                 }
583                 else
584                     break SWITCH;
585             }
586         default:
587             throw new FormatException(
588                 text("Positional specifier %", spec.indexStart, '$', spec.spec,
589                      " index exceeds ", A.length));
590         }
591     }
592     return currentArg;
593 }
594 
595 ///
596 @safe unittest
597 {
598     assert(format("%,d", 1000) == "1,000");
599     assert(format("%,f", 1234567.891011) == "1,234,567.891,011");
600     assert(format("%,?d", '?', 1000) == "1?000");
601     assert(format("%,1d", 1000) == "1,0,0,0", format("%,1d", 1000));
602     assert(format("%,*d", 4, -12345) == "-1,2345");
603     assert(format("%,*?d", 4, '_', -12345) == "-1_2345");
604     assert(format("%,6?d", '_', -12345678) == "-12_345678");
605     assert(format("%12,3.3f", 1234.5678) == "   1,234.568", "'" ~
606             format("%12,3.3f", 1234.5678) ~ "'");
607 }
608 
609 @safe pure unittest
610 {
611     import std.array;
612     auto w = appender!string();
613     formattedWrite(w, "%s %d", "@safe/pure", 42);
614     assert(w.data == "@safe/pure 42");
615 }
616 
617 /**
618 Reads characters from input range $(D r), converts them according
619 to $(D fmt), and writes them to $(D args).
620 
621 Params:
622     r = The range to read from.
623     fmt = The format of the data to read.
624     args = The drain of the data read.
625 
626 Returns:
627 
628 On success, the function returns the number of variables filled. This count
629 can match the expected number of readings or fewer, even zero, if a
630 matching failure happens.
631 
632 Throws:
633     An `Exception` if `S.length == 0` and `fmt` has format specifiers.
634  */
635 uint formattedRead(alias fmt, R, S...)(ref R r, auto ref S args)
636 if (isSomeString!(typeof(fmt)))
637 {
638     alias e = checkFormatException!(fmt, S);
639     static assert(!e, e.msg);
640     return .formattedRead(r, fmt, args);
641 }
642 
643 /// ditto
formattedRead(R,Char,S...)644 uint formattedRead(R, Char, S...)(ref R r, const(Char)[] fmt, auto ref S args)
645 {
646     import std.typecons : isTuple;
647 
648     auto spec = FormatSpec!Char(fmt);
649     static if (!S.length)
650     {
651         spec.readUpToNextSpec(r);
652         enforce(spec.trailing.empty, "Trailing characters in formattedRead format string");
653         return 0;
654     }
655     else
656     {
657         enum hasPointer = isPointer!(typeof(args[0]));
658 
659         // The function below accounts for '*' == fields meant to be
660         // read and skipped
661         void skipUnstoredFields()
662         {
663             for (;;)
664             {
665                 spec.readUpToNextSpec(r);
666                 if (spec.width != spec.DYNAMIC) break;
667                 // must skip this field
668                 skipData(r, spec);
669             }
670         }
671 
672         skipUnstoredFields();
673         if (r.empty)
674         {
675             // Input is empty, nothing to read
676             return 0;
677         }
678         static if (hasPointer)
679             alias A = typeof(*args[0]);
680         else
681             alias A = typeof(args[0]);
682 
683         static if (isTuple!A)
684         {
685             foreach (i, T; A.Types)
686             {
687                 static if (hasPointer)
688                     (*args[0])[i] = unformatValue!(T)(r, spec);
689                 else
690                     args[0][i] = unformatValue!(T)(r, spec);
691                 skipUnstoredFields();
692             }
693         }
694         else
695         {
696             static if (hasPointer)
697                 *args[0] = unformatValue!(A)(r, spec);
698             else
699                 args[0] = unformatValue!(A)(r, spec);
700         }
701         return 1 + formattedRead(r, spec.trailing, args[1 .. $]);
702     }
703 }
704 
705 /// The format string can be checked at compile-time (see $(LREF format) for details):
706 @safe pure unittest
707 {
708     string s = "hello!124:34.5";
709     string a;
710     int b;
711     double c;
712     s.formattedRead!"%s!%s:%s"(a, b, c);
713     assert(a == "hello" && b == 124 && c == 34.5);
714 }
715 
716 @safe unittest
717 {
718     import std.math;
719     string s = " 1.2 3.4 ";
720     double x, y, z;
721     assert(formattedRead(s, " %s %s %s ", x, y, z) == 2);
722     assert(s.empty);
723     assert(approxEqual(x, 1.2));
724     assert(approxEqual(y, 3.4));
725     assert(isNaN(z));
726 }
727 
728 // for backwards compatibility
729 @system pure unittest
730 {
731     string s = "hello!124:34.5";
732     string a;
733     int b;
734     double c;
735     formattedRead(s, "%s!%s:%s", &a, &b, &c);
736     assert(a == "hello" && b == 124 && c == 34.5);
737 
738     // mix pointers and auto-ref
739     s = "world!200:42.25";
740     formattedRead(s, "%s!%s:%s", a, &b, &c);
741     assert(a == "world" && b == 200 && c == 42.25);
742 
743     s = "world1!201:42.5";
744     formattedRead(s, "%s!%s:%s", &a, &b, c);
745     assert(a == "world1" && b == 201 && c == 42.5);
746 
747     s = "world2!202:42.75";
748     formattedRead(s, "%s!%s:%s", a, b, &c);
749     assert(a == "world2" && b == 202 && c == 42.75);
750 }
751 
752 // for backwards compatibility
753 @system pure unittest
754 {
755     import std.math;
756     string s = " 1.2 3.4 ";
757     double x, y, z;
758     assert(formattedRead(s, " %s %s %s ", &x, &y, &z) == 2);
759     assert(s.empty);
760     assert(approxEqual(x, 1.2));
761     assert(approxEqual(y, 3.4));
762     assert(isNaN(z));
763 }
764 
765 @system pure unittest
766 {
767     string line;
768 
769     bool f1;
770 
771     line = "true";
772     formattedRead(line, "%s", &f1);
773     assert(f1);
774 
775     line = "TrUE";
776     formattedRead(line, "%s", &f1);
777     assert(f1);
778 
779     line = "false";
780     formattedRead(line, "%s", &f1);
781     assert(!f1);
782 
783     line = "fALsE";
784     formattedRead(line, "%s", &f1);
785     assert(!f1);
786 
787     line = "1";
788     formattedRead(line, "%d", &f1);
789     assert(f1);
790 
791     line = "-1";
792     formattedRead(line, "%d", &f1);
793     assert(f1);
794 
795     line = "0";
796     formattedRead(line, "%d", &f1);
797     assert(!f1);
798 
799     line = "-0";
800     formattedRead(line, "%d", &f1);
801     assert(!f1);
802 }
803 
804 @system pure unittest
805 {
806      union B
807      {
808          char[int.sizeof] untyped;
809          int typed;
810      }
811      B b;
812      b.typed = 5;
813      char[] input = b.untyped[];
814      int witness;
815      formattedRead(input, "%r", &witness);
816      assert(witness == b.typed);
817 }
818 
819 @system pure unittest
820 {
821     union A
822     {
823         char[float.sizeof] untyped;
824         float typed;
825     }
826     A a;
827     a.typed = 5.5;
828     char[] input = a.untyped[];
829     float witness;
830     formattedRead(input, "%r", &witness);
831     assert(witness == a.typed);
832 }
833 
834 @system pure unittest
835 {
836     import std.typecons;
837     char[] line = "1 2".dup;
838     int a, b;
839     formattedRead(line, "%s %s", &a, &b);
840     assert(a == 1 && b == 2);
841 
842     line = "10 2 3".dup;
843     formattedRead(line, "%d ", &a);
844     assert(a == 10);
845     assert(line == "2 3");
846 
847     Tuple!(int, float) t;
848     line = "1 2.125".dup;
849     formattedRead(line, "%d %g", &t);
850     assert(t[0] == 1 && t[1] == 2.125);
851 
852     line = "1 7643 2.125".dup;
853     formattedRead(line, "%s %*u %s", &t);
854     assert(t[0] == 1 && t[1] == 2.125);
855 }
856 
857 @system pure unittest
858 {
859     string line;
860 
861     char c1, c2;
862 
863     line = "abc";
864     formattedRead(line, "%s%c", &c1, &c2);
865     assert(c1 == 'a' && c2 == 'b');
866     assert(line == "c");
867 }
868 
869 @system pure unittest
870 {
871     string line;
872 
873     line = "[1,2,3]";
874     int[] s1;
875     formattedRead(line, "%s", &s1);
876     assert(s1 == [1,2,3]);
877 }
878 
879 @system pure unittest
880 {
881     string line;
882 
883     line = "[1,2,3]";
884     int[] s1;
885     formattedRead(line, "[%(%s,%)]", &s1);
886     assert(s1 == [1,2,3]);
887 
888     line = `["hello", "world"]`;
889     string[] s2;
890     formattedRead(line, "[%(%s, %)]", &s2);
891     assert(s2 == ["hello", "world"]);
892 
893     line = "123 456";
894     int[] s3;
895     formattedRead(line, "%(%s %)", &s3);
896     assert(s3 == [123, 456]);
897 
898     line = "h,e,l,l,o; w,o,r,l,d";
899     string[] s4;
900     formattedRead(line, "%(%(%c,%); %)", &s4);
901     assert(s4 == ["hello", "world"]);
902 }
903 
904 @system pure unittest
905 {
906     string line;
907 
908     int[4] sa1;
909     line = `[1,2,3,4]`;
910     formattedRead(line, "%s", &sa1);
911     assert(sa1 == [1,2,3,4]);
912 
913     int[4] sa2;
914     line = `[1,2,3]`;
915     assertThrown(formattedRead(line, "%s", &sa2));
916 
917     int[4] sa3;
918     line = `[1,2,3,4,5]`;
919     assertThrown(formattedRead(line, "%s", &sa3));
920 }
921 
922 @system pure unittest
923 {
924     string input;
925 
926     int[4] sa1;
927     input = `[1,2,3,4]`;
928     formattedRead(input, "[%(%s,%)]", &sa1);
929     assert(sa1 == [1,2,3,4]);
930 
931     int[4] sa2;
932     input = `[1,2,3]`;
933     assertThrown(formattedRead(input, "[%(%s,%)]", &sa2));
934 }
935 
936 @system pure unittest
937 {
938     string line;
939 
940     string s1, s2;
941 
942     line = "hello, world";
943     formattedRead(line, "%s", &s1);
944     assert(s1 == "hello, world", s1);
945 
946     line = "hello, world;yah";
947     formattedRead(line, "%s;%s", &s1, &s2);
948     assert(s1 == "hello, world", s1);
949     assert(s2 == "yah", s2);
950 
951     line = `['h','e','l','l','o']`;
952     string s3;
953     formattedRead(line, "[%(%s,%)]", &s3);
954     assert(s3 == "hello");
955 
956     line = `"hello"`;
957     string s4;
958     formattedRead(line, "\"%(%c%)\"", &s4);
959     assert(s4 == "hello");
960 }
961 
962 @system pure unittest
963 {
964     string line;
965 
966     string[int] aa1;
967     line = `[1:"hello", 2:"world"]`;
968     formattedRead(line, "%s", &aa1);
969     assert(aa1 == [1:"hello", 2:"world"]);
970 
971     int[string] aa2;
972     line = `{"hello"=1; "world"=2}`;
973     formattedRead(line, "{%(%s=%s; %)}", &aa2);
974     assert(aa2 == ["hello":1, "world":2]);
975 
976     int[string] aa3;
977     line = `{[hello=1]; [world=2]}`;
978     formattedRead(line, "{%([%(%c%)=%s]%|; %)}", &aa3);
979     assert(aa3 == ["hello":1, "world":2]);
980 }
981 
982 template FormatSpec(Char)
983 if (!is(Unqual!Char == Char))
984 {
985     alias FormatSpec = FormatSpec!(Unqual!Char);
986 }
987 
988 /**
989  * A General handler for $(D printf) style format specifiers. Used for building more
990  * specific formatting functions.
991  */
992 struct FormatSpec(Char)
993 if (is(Unqual!Char == Char))
994 {
995     import std.algorithm.searching : startsWith;
996     import std.ascii : isDigit, isPunctuation, isAlpha;
997     import std.conv : parse, text, to;
998 
999     /**
1000        Minimum _width, default $(D 0).
1001      */
1002     int width = 0;
1003 
1004     /**
1005        Precision. Its semantics depends on the argument type. For
1006        floating point numbers, _precision dictates the number of
1007        decimals printed.
1008      */
1009     int precision = UNSPECIFIED;
1010 
1011     /**
1012        Number of digits printed between _separators.
1013     */
1014     int separators = UNSPECIFIED;
1015 
1016     /**
1017        Set to `DYNAMIC` when the separator character is supplied at runtime.
1018     */
1019     int separatorCharPos = UNSPECIFIED;
1020 
1021     /**
1022        Character to insert between digits.
1023     */
1024     dchar separatorChar = ',';
1025 
1026     /**
1027        Special value for width and precision. $(D DYNAMIC) width or
1028        precision means that they were specified with $(D '*') in the
1029        format string and are passed at runtime through the varargs.
1030      */
1031     enum int DYNAMIC = int.max;
1032 
1033     /**
1034        Special value for precision, meaning the format specifier
1035        contained no explicit precision.
1036      */
1037     enum int UNSPECIFIED = DYNAMIC - 1;
1038 
1039     /**
1040        The actual format specifier, $(D 's') by default.
1041     */
1042     char spec = 's';
1043 
1044     /**
1045        Index of the argument for positional parameters, from $(D 1) to
1046        $(D ubyte.max). ($(D 0) means not used).
1047     */
1048     ubyte indexStart;
1049 
1050     /**
1051        Index of the last argument for positional parameter range, from
1052        $(D 1) to $(D ubyte.max). ($(D 0) means not used).
1053     */
1054     ubyte indexEnd;
1055 
version(StdDdoc)1056     version (StdDdoc)
1057     {
1058         /**
1059          The format specifier contained a $(D '-') ($(D printf)
1060          compatibility).
1061          */
1062         bool flDash;
1063 
1064         /**
1065          The format specifier contained a $(D '0') ($(D printf)
1066          compatibility).
1067          */
1068         bool flZero;
1069 
1070         /**
1071          The format specifier contained a $(D ' ') ($(D printf)
1072          compatibility).
1073          */
1074         bool flSpace;
1075 
1076         /**
1077          The format specifier contained a $(D '+') ($(D printf)
1078          compatibility).
1079          */
1080         bool flPlus;
1081 
1082         /**
1083          The format specifier contained a $(D '#') ($(D printf)
1084          compatibility).
1085          */
1086         bool flHash;
1087 
1088         /**
1089          The format specifier contained a $(D ',')
1090          */
1091         bool flSeparator;
1092 
1093         // Fake field to allow compilation
1094         ubyte allFlags;
1095     }
1096     else
1097     {
1098         union
1099         {
1100             import std.bitmanip : bitfields;
1101             mixin(bitfields!(
1102                         bool, "flDash", 1,
1103                         bool, "flZero", 1,
1104                         bool, "flSpace", 1,
1105                         bool, "flPlus", 1,
1106                         bool, "flHash", 1,
1107                         bool, "flSeparator", 1,
1108                         ubyte, "", 2));
1109             ubyte allFlags;
1110         }
1111     }
1112 
1113     /**
1114        In case of a compound format specifier starting with $(D
1115        "%$(LPAREN)") and ending with $(D "%$(RPAREN)"), $(D _nested)
1116        contains the string contained within the two separators.
1117      */
1118     const(Char)[] nested;
1119 
1120     /**
1121        In case of a compound format specifier, $(D _sep) contains the
1122        string positioning after $(D "%|").
1123        `sep is null` means no separator else `sep.empty` means 0 length
1124         separator.
1125      */
1126     const(Char)[] sep;
1127 
1128     /**
1129        $(D _trailing) contains the rest of the format string.
1130      */
1131     const(Char)[] trailing;
1132 
1133     /*
1134        This string is inserted before each sequence (e.g. array)
1135        formatted (by default $(D "[")).
1136      */
1137     enum immutable(Char)[] seqBefore = "[";
1138 
1139     /*
1140        This string is inserted after each sequence formatted (by
1141        default $(D "]")).
1142      */
1143     enum immutable(Char)[] seqAfter = "]";
1144 
1145     /*
1146        This string is inserted after each element keys of a sequence (by
1147        default $(D ":")).
1148      */
1149     enum immutable(Char)[] keySeparator = ":";
1150 
1151     /*
1152        This string is inserted in between elements of a sequence (by
1153        default $(D ", ")).
1154      */
1155     enum immutable(Char)[] seqSeparator = ", ";
1156 
1157     /**
1158        Construct a new $(D FormatSpec) using the format string $(D fmt), no
1159        processing is done until needed.
1160      */
1161     this(in Char[] fmt) @safe pure
1162     {
1163         trailing = fmt;
1164     }
1165 
1166     bool writeUpToNextSpec(OutputRange)(ref OutputRange writer)
1167     {
1168         if (trailing.empty)
1169             return false;
1170         for (size_t i = 0; i < trailing.length; ++i)
1171         {
1172             if (trailing[i] != '%') continue;
1173             put(writer, trailing[0 .. i]);
1174             trailing = trailing[i .. $];
1175             enforceFmt(trailing.length >= 2, `Unterminated format specifier: "%"`);
1176             trailing = trailing[1 .. $];
1177 
1178             if (trailing[0] != '%')
1179             {
1180                 // Spec found. Fill up the spec, and bailout
1181                 fillUp();
1182                 return true;
1183             }
1184             // Doubled! Reset and Keep going
1185             i = 0;
1186         }
1187         // no format spec found
1188         put(writer, trailing);
1189         trailing = null;
1190         return false;
1191     }
1192 
1193     @safe unittest
1194     {
1195         import std.array;
1196         auto w = appender!(char[])();
1197         auto f = FormatSpec("abc%sdef%sghi");
1198         f.writeUpToNextSpec(w);
1199         assert(w.data == "abc", w.data);
1200         assert(f.trailing == "def%sghi", text(f.trailing));
1201         f.writeUpToNextSpec(w);
1202         assert(w.data == "abcdef", w.data);
1203         assert(f.trailing == "ghi");
1204         // test with embedded %%s
1205         f = FormatSpec("ab%%cd%%ef%sg%%h%sij");
1206         w.clear();
1207         f.writeUpToNextSpec(w);
1208         assert(w.data == "ab%cd%ef" && f.trailing == "g%%h%sij", w.data);
1209         f.writeUpToNextSpec(w);
1210         assert(w.data == "ab%cd%efg%h" && f.trailing == "ij");
1211         // bug4775
1212         f = FormatSpec("%%%s");
1213         w.clear();
1214         f.writeUpToNextSpec(w);
1215         assert(w.data == "%" && f.trailing == "");
1216         f = FormatSpec("%%%%%s%%");
1217         w.clear();
1218         while (f.writeUpToNextSpec(w)) continue;
1219         assert(w.data == "%%%");
1220 
1221         f = FormatSpec("a%%b%%c%");
1222         w.clear();
1223         assertThrown!FormatException(f.writeUpToNextSpec(w));
1224         assert(w.data == "a%b%c" && f.trailing == "%");
1225     }
1226 
1227     private void fillUp()
1228     {
1229         // Reset content
1230         if (__ctfe)
1231         {
1232             flDash = false;
1233             flZero = false;
1234             flSpace = false;
1235             flPlus = false;
1236             flHash = false;
1237             flSeparator = false;
1238         }
1239         else
1240         {
1241             allFlags = 0;
1242         }
1243 
1244         width = 0;
1245         precision = UNSPECIFIED;
1246         nested = null;
1247         // Parse the spec (we assume we're past '%' already)
1248         for (size_t i = 0; i < trailing.length; )
1249         {
1250             switch (trailing[i])
1251             {
1252             case '(':
1253                 // Embedded format specifier.
1254                 auto j = i + 1;
1255                 // Get the matching balanced paren
1256                 for (uint innerParens;;)
1257                 {
1258                     enforceFmt(j + 1 < trailing.length,
1259                         text("Incorrect format specifier: %", trailing[i .. $]));
1260                     if (trailing[j++] != '%')
1261                     {
1262                         // skip, we're waiting for %( and %)
1263                         continue;
1264                     }
1265                     if (trailing[j] == '-') // for %-(
1266                     {
1267                         ++j;    // skip
1268                         enforceFmt(j < trailing.length,
1269                             text("Incorrect format specifier: %", trailing[i .. $]));
1270                     }
1271                     if (trailing[j] == ')')
1272                     {
1273                         if (innerParens-- == 0) break;
1274                     }
1275                     else if (trailing[j] == '|')
1276                     {
1277                         if (innerParens == 0) break;
1278                     }
1279                     else if (trailing[j] == '(')
1280                     {
1281                         ++innerParens;
1282                     }
1283                 }
1284                 if (trailing[j] == '|')
1285                 {
1286                     auto k = j;
1287                     for (++j;;)
1288                     {
1289                         if (trailing[j++] != '%')
1290                             continue;
1291                         if (trailing[j] == '%')
1292                             ++j;
1293                         else if (trailing[j] == ')')
1294                             break;
1295                         else
1296                             throw new Exception(
1297                                 text("Incorrect format specifier: %",
1298                                         trailing[j .. $]));
1299                     }
1300                     nested = trailing[i + 1 .. k - 1];
1301                     sep = trailing[k + 1 .. j - 1];
1302                 }
1303                 else
1304                 {
1305                     nested = trailing[i + 1 .. j - 1];
1306                     sep = null; // no separator
1307                 }
1308                 //this = FormatSpec(innerTrailingSpec);
1309                 spec = '(';
1310                 // We practically found the format specifier
1311                 trailing = trailing[j + 1 .. $];
1312                 return;
1313             case '-': flDash = true; ++i; break;
1314             case '+': flPlus = true; ++i; break;
1315             case '#': flHash = true; ++i; break;
1316             case '0': flZero = true; ++i; break;
1317             case ' ': flSpace = true; ++i; break;
1318             case '*':
1319                 if (isDigit(trailing[++i]))
1320                 {
1321                     // a '*' followed by digits and '$' is a
1322                     // positional format
1323                     trailing = trailing[1 .. $];
1324                     width = -parse!(typeof(width))(trailing);
1325                     i = 0;
1326                     enforceFmt(trailing[i++] == '$',
1327                         "$ expected");
1328                 }
1329                 else
1330                 {
1331                     // read result
1332                     width = DYNAMIC;
1333                 }
1334                 break;
1335             case '1': .. case '9':
1336                 auto tmp = trailing[i .. $];
1337                 const widthOrArgIndex = parse!uint(tmp);
1338                 enforceFmt(tmp.length,
1339                     text("Incorrect format specifier %", trailing[i .. $]));
1340                 i = arrayPtrDiff(tmp, trailing);
1341                 if (tmp.startsWith('$'))
1342                 {
1343                     // index of the form %n$
1344                     indexEnd = indexStart = to!ubyte(widthOrArgIndex);
1345                     ++i;
1346                 }
1347                 else if (tmp.startsWith(':'))
1348                 {
1349                     // two indexes of the form %m:n$, or one index of the form %m:$
1350                     indexStart = to!ubyte(widthOrArgIndex);
1351                     tmp = tmp[1 .. $];
1352                     if (tmp.startsWith('$'))
1353                     {
1354                         indexEnd = indexEnd.max;
1355                     }
1356                     else
1357                     {
1358                         indexEnd = parse!(typeof(indexEnd))(tmp);
1359                     }
1360                     i = arrayPtrDiff(tmp, trailing);
1361                     enforceFmt(trailing[i++] == '$',
1362                         "$ expected");
1363                 }
1364                 else
1365                 {
1366                     // width
1367                     width = to!int(widthOrArgIndex);
1368                 }
1369                 break;
1370             case ',':
1371                 // Precision
1372                 ++i;
1373                 flSeparator = true;
1374 
1375                 if (trailing[i] == '*')
1376                 {
1377                     ++i;
1378                     // read result
1379                     separators = DYNAMIC;
1380                 }
1381                 else if (isDigit(trailing[i]))
1382                 {
1383                     auto tmp = trailing[i .. $];
1384                     separators = parse!int(tmp);
1385                     i = arrayPtrDiff(tmp, trailing);
1386                 }
1387                 else
1388                 {
1389                     // "," was specified, but nothing after it
1390                     separators = 3;
1391                 }
1392 
1393                 if (trailing[i] == '?')
1394                 {
1395                     separatorCharPos = DYNAMIC;
1396                     ++i;
1397                 }
1398 
1399                 break;
1400             case '.':
1401                 // Precision
1402                 if (trailing[++i] == '*')
1403                 {
1404                     if (isDigit(trailing[++i]))
1405                     {
1406                         // a '.*' followed by digits and '$' is a
1407                         // positional precision
1408                         trailing = trailing[i .. $];
1409                         i = 0;
1410                         precision = -parse!int(trailing);
1411                         enforceFmt(trailing[i++] == '$',
1412                             "$ expected");
1413                     }
1414                     else
1415                     {
1416                         // read result
1417                         precision = DYNAMIC;
1418                     }
1419                 }
1420                 else if (trailing[i] == '-')
1421                 {
1422                     // negative precision, as good as 0
1423                     precision = 0;
1424                     auto tmp = trailing[i .. $];
1425                     parse!int(tmp); // skip digits
1426                     i = arrayPtrDiff(tmp, trailing);
1427                 }
1428                 else if (isDigit(trailing[i]))
1429                 {
1430                     auto tmp = trailing[i .. $];
1431                     precision = parse!int(tmp);
1432                     i = arrayPtrDiff(tmp, trailing);
1433                 }
1434                 else
1435                 {
1436                     // "." was specified, but nothing after it
1437                     precision = 0;
1438                 }
1439                 break;
1440             default:
1441                 // this is the format char
1442                 spec = cast(char) trailing[i++];
1443                 trailing = trailing[i .. $];
1444                 return;
1445             } // end switch
1446         } // end for
1447         throw new Exception(text("Incorrect format specifier: ", trailing));
1448     }
1449 
1450     //--------------------------------------------------------------------------
1451     private bool readUpToNextSpec(R)(ref R r)
1452     {
1453         import std.ascii : isLower, isWhite;
1454         import std.utf : stride;
1455 
1456         // Reset content
1457         if (__ctfe)
1458         {
1459             flDash = false;
1460             flZero = false;
1461             flSpace = false;
1462             flPlus = false;
1463             flHash = false;
1464             flSeparator = false;
1465         }
1466         else
1467         {
1468             allFlags = 0;
1469         }
1470         width = 0;
1471         precision = UNSPECIFIED;
1472         nested = null;
1473         // Parse the spec
1474         while (trailing.length)
1475         {
1476             const c = trailing[0];
1477             if (c == '%' && trailing.length > 1)
1478             {
1479                 const c2 = trailing[1];
1480                 if (c2 == '%')
1481                 {
1482                     assert(!r.empty);
1483                     // Require a '%'
1484                     if (r.front != '%') break;
1485                     trailing = trailing[2 .. $];
1486                     r.popFront();
1487                 }
1488                 else
1489                 {
1490                     enforce(isLower(c2) || c2 == '*' ||
1491                             c2 == '(',
1492                             text("'%", c2,
1493                                     "' not supported with formatted read"));
1494                     trailing = trailing[1 .. $];
1495                     fillUp();
1496                     return true;
1497                 }
1498             }
1499             else
1500             {
1501                 if (c == ' ')
1502                 {
1503                     while (!r.empty && isWhite(r.front)) r.popFront();
1504                     //r = std.algorithm.find!(not!(isWhite))(r);
1505                 }
1506                 else
1507                 {
1508                     enforce(!r.empty,
1509                             text("parseToFormatSpec: Cannot find character '",
1510                                     c, "' in the input string."));
1511                     if (r.front != trailing.front) break;
1512                     r.popFront();
1513                 }
1514                 trailing = trailing[stride(trailing, 0) .. $];
1515             }
1516         }
1517         return false;
1518     }
1519 
1520     private string getCurFmtStr() const
1521     {
1522         import std.array : appender;
1523         auto w = appender!string();
1524         auto f = FormatSpec!Char("%s"); // for stringnize
1525 
1526         put(w, '%');
1527         if (indexStart != 0)
1528         {
1529             formatValue(w, indexStart, f);
1530             put(w, '$');
1531         }
1532         if (flDash)  put(w, '-');
1533         if (flZero)  put(w, '0');
1534         if (flSpace) put(w, ' ');
1535         if (flPlus)  put(w, '+');
1536         if (flHash)  put(w, '#');
1537         if (flSeparator)  put(w, ',');
1538         if (width != 0)
1539             formatValue(w, width, f);
1540         if (precision != FormatSpec!Char.UNSPECIFIED)
1541         {
1542             put(w, '.');
1543             formatValue(w, precision, f);
1544         }
1545         put(w, spec);
1546         return w.data;
1547     }
1548 
1549     @safe unittest
1550     {
1551         // issue 5237
1552         import std.array;
1553         auto w = appender!string();
1554         auto f = FormatSpec!char("%.16f");
1555         f.writeUpToNextSpec(w); // dummy eating
1556         assert(f.spec == 'f');
1557         auto fmt = f.getCurFmtStr();
1558         assert(fmt == "%.16f");
1559     }
1560 
1561     private const(Char)[] headUpToNextSpec()
1562     {
1563         import std.array : appender;
1564         auto w = appender!(typeof(return))();
1565         auto tr = trailing;
1566 
1567         while (tr.length)
1568         {
1569             if (tr[0] == '%')
1570             {
1571                 if (tr.length > 1 && tr[1] == '%')
1572                 {
1573                     tr = tr[2 .. $];
1574                     w.put('%');
1575                 }
1576                 else
1577                     break;
1578             }
1579             else
1580             {
1581                 w.put(tr.front);
1582                 tr.popFront();
1583             }
1584         }
1585         return w.data;
1586     }
1587 
1588     string toString()
1589     {
1590         return text("address = ", cast(void*) &this,
1591                 "\nwidth = ", width,
1592                 "\nprecision = ", precision,
1593                 "\nspec = ", spec,
1594                 "\nindexStart = ", indexStart,
1595                 "\nindexEnd = ", indexEnd,
1596                 "\nflDash = ", flDash,
1597                 "\nflZero = ", flZero,
1598                 "\nflSpace = ", flSpace,
1599                 "\nflPlus = ", flPlus,
1600                 "\nflHash = ", flHash,
1601                 "\nflSeparator = ", flSeparator,
1602                 "\nnested = ", nested,
1603                 "\ntrailing = ", trailing, "\n");
1604     }
1605 }
1606 
1607 ///
1608 @safe pure unittest
1609 {
1610     import std.array;
1611     auto a = appender!(string)();
1612     auto fmt = "Number: %2.4e\nString: %s";
1613     auto f = FormatSpec!char(fmt);
1614 
1615     f.writeUpToNextSpec(a);
1616 
1617     assert(a.data == "Number: ");
1618     assert(f.trailing == "\nString: %s");
1619     assert(f.spec == 'e');
1620     assert(f.width == 2);
1621     assert(f.precision == 4);
1622 
1623     f.writeUpToNextSpec(a);
1624 
1625     assert(a.data == "Number: \nString: ");
1626     assert(f.trailing == "");
1627     assert(f.spec == 's');
1628 }
1629 
1630 // Issue 14059
1631 @safe unittest
1632 {
1633     import std.array : appender;
1634     auto a = appender!(string)();
1635 
1636     auto f = FormatSpec!char("%-(%s%"); // %)")
1637     assertThrown(f.writeUpToNextSpec(a));
1638 
1639     f = FormatSpec!char("%(%-"); // %)")
1640     assertThrown(f.writeUpToNextSpec(a));
1641 }
1642 
1643 @safe unittest
1644 {
1645     import std.array : appender;
1646     auto a = appender!(string)();
1647 
1648     auto f = FormatSpec!char("%,d");
1649     f.writeUpToNextSpec(a);
1650 
1651     assert(f.spec == 'd', format("%s", f.spec));
1652     assert(f.precision == FormatSpec!char.UNSPECIFIED);
1653     assert(f.separators == 3);
1654 
1655     f = FormatSpec!char("%5,10f");
1656     f.writeUpToNextSpec(a);
1657     assert(f.spec == 'f', format("%s", f.spec));
1658     assert(f.separators == 10);
1659     assert(f.width == 5);
1660 
1661     f = FormatSpec!char("%5,10.4f");
1662     f.writeUpToNextSpec(a);
1663     assert(f.spec == 'f', format("%s", f.spec));
1664     assert(f.separators == 10);
1665     assert(f.width == 5);
1666     assert(f.precision == 4);
1667 }
1668 
1669 /**
1670 Helper function that returns a $(D FormatSpec) for a single specifier given
1671 in $(D fmt).
1672 
1673 Params:
1674     fmt = A format specifier.
1675 
1676 Returns:
1677     A $(D FormatSpec) with the specifier parsed.
1678 Throws:
1679     An `Exception` when more than one specifier is given or the specifier
1680     is malformed.
1681   */
1682 FormatSpec!Char singleSpec(Char)(Char[] fmt)
1683 {
1684     import std.conv : text;
1685     enforce(fmt.length >= 2, "fmt must be at least 2 characters long");
1686     enforce(fmt.front == '%', "fmt must start with a '%' character");
1687 
1688     static struct DummyOutputRange {
1689         void put(C)(C[] buf) {} // eat elements
1690     }
1691     auto a = DummyOutputRange();
1692     auto spec = FormatSpec!Char(fmt);
1693     //dummy write
1694     spec.writeUpToNextSpec(a);
1695 
1696     enforce(spec.trailing.empty,
1697             text("Trailing characters in fmt string: '", spec.trailing));
1698 
1699     return spec;
1700 }
1701 
1702 ///
1703 @safe pure unittest
1704 {
1705     import std.exception : assertThrown;
1706     auto spec = singleSpec("%2.3e");
1707 
1708     assert(spec.trailing == "");
1709     assert(spec.spec == 'e');
1710     assert(spec.width == 2);
1711     assert(spec.precision == 3);
1712 
1713     assertThrown(singleSpec(""));
1714     assertThrown(singleSpec("2.3e"));
1715     assertThrown(singleSpec("%2.3eTest"));
1716 }
1717 
1718 /**
1719 $(D bool)s are formatted as "true" or "false" with %s and as "1" or
1720 "0" with integral-specific format specs.
1721 
1722 Params:
1723     w = The $(D OutputRange) to write to.
1724     obj = The value to write.
1725     f = The $(D FormatSpec) defining how to write the value.
1726  */
1727 void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
1728 if (is(BooleanTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
1729 {
1730     BooleanTypeOf!T val = obj;
1731 
1732     if (f.spec == 's')
1733     {
1734         string s = val ? "true" : "false";
1735         if (!f.flDash)
1736         {
1737             // right align
1738             if (f.width > s.length)
1739                 foreach (i ; 0 .. f.width - s.length) put(w, ' ');
1740             put(w, s);
1741         }
1742         else
1743         {
1744             // left align
1745             put(w, s);
1746             if (f.width > s.length)
1747                 foreach (i ; 0 .. f.width - s.length) put(w, ' ');
1748         }
1749     }
1750     else
1751         formatValue(w, cast(int) val, f);
1752 }
1753 
1754 ///
1755 @safe pure unittest
1756 {
1757     import std.array : appender;
1758     auto w = appender!string();
1759     auto spec = singleSpec("%s");
1760     formatValue(w, true, spec);
1761 
1762     assert(w.data == "true");
1763 }
1764 
1765 @safe pure unittest
1766 {
1767     assertCTFEable!(
1768     {
1769         formatTest( false, "false" );
1770         formatTest( true,  "true"  );
1771     });
1772 }
1773 @system unittest
1774 {
1775     class C1 { bool val; alias val this; this(bool v){ val = v; } }
1776     class C2 { bool val; alias val this; this(bool v){ val = v; }
1777                override string toString() const { return "C"; } }
1778     formatTest( new C1(false), "false" );
1779     formatTest( new C1(true),  "true" );
1780     formatTest( new C2(false), "C" );
1781     formatTest( new C2(true),  "C" );
1782 
1783     struct S1 { bool val; alias val this; }
1784     struct S2 { bool val; alias val this;
1785                 string toString() const { return "S"; } }
1786     formatTest( S1(false), "false" );
1787     formatTest( S1(true),  "true"  );
1788     formatTest( S2(false), "S" );
1789     formatTest( S2(true),  "S" );
1790 }
1791 
1792 @safe pure unittest
1793 {
1794     string t1 = format("[%6s] [%6s] [%-6s]", true, false, true);
1795     assert(t1 == "[  true] [ false] [true  ]");
1796 
1797     string t2 = format("[%3s] [%-2s]", true, false);
1798     assert(t2 == "[true] [false]");
1799 }
1800 
1801 /**
1802 $(D null) literal is formatted as $(D "null").
1803 
1804 Params:
1805     w = The $(D OutputRange) to write to.
1806     obj = The value to write.
1807     f = The $(D FormatSpec) defining how to write the value.
1808  */
1809 void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
1810 if (is(Unqual!T == typeof(null)) && !is(T == enum) && !hasToString!(T, Char))
1811 {
1812     enforceFmt(f.spec == 's',
1813         "null literal cannot match %" ~ f.spec);
1814 
1815     put(w, "null");
1816 }
1817 
1818 ///
1819 @safe pure unittest
1820 {
1821     import std.array : appender;
1822     auto w = appender!string();
1823     auto spec = singleSpec("%s");
1824     formatValue(w, null, spec);
1825 
1826     assert(w.data == "null");
1827 }
1828 
1829 @safe pure unittest
1830 {
1831     assert(collectExceptionMsg!FormatException(format("%p", null)).back == 'p');
1832 
1833     assertCTFEable!(
1834     {
1835         formatTest( null, "null" );
1836     });
1837 }
1838 
1839 /**
1840 Integrals are formatted like $(D printf) does.
1841 
1842 Params:
1843     w = The $(D OutputRange) to write to.
1844     obj = The value to write.
1845     f = The $(D FormatSpec) defining how to write the value.
1846  */
1847 void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
1848 if (is(IntegralTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
1849 {
1850     alias U = IntegralTypeOf!T;
1851     U val = obj;    // Extracting alias this may be impure/system/may-throw
1852 
1853     if (f.spec == 'r')
1854     {
1855         // raw write, skip all else and write the thing
1856         auto raw = (ref val)@trusted{
1857             return (cast(const char*) &val)[0 .. val.sizeof];
1858         }(val);
1859         if (needToSwapEndianess(f))
1860         {
1861             foreach_reverse (c; raw)
1862                 put(w, c);
1863         }
1864         else
1865         {
1866             foreach (c; raw)
1867                 put(w, c);
1868         }
1869         return;
1870     }
1871 
1872     immutable uint base =
1873         f.spec == 'x' || f.spec == 'X' ? 16 :
1874         f.spec == 'o' ? 8 :
1875         f.spec == 'b' ? 2 :
1876         f.spec == 's' || f.spec == 'd' || f.spec == 'u' ? 10 :
1877         0;
1878     enforceFmt(base > 0,
1879         "incompatible format character for integral argument: %" ~ f.spec);
1880 
1881     // Forward on to formatIntegral to handle both U and const(U)
1882     // Saves duplication of code for both versions.
1883     static if (is(ucent) && (is(U == cent) || is(U == ucent)))
1884         alias C = U;
1885     else static if (isSigned!U)
1886         alias C = long;
1887     else
1888         alias C = ulong;
1889     formatIntegral(w, cast(C) val, f, base, Unsigned!U.max);
1890 }
1891 
1892 ///
1893 @safe pure unittest
1894 {
1895     import std.array : appender;
1896     auto w = appender!string();
1897     auto spec = singleSpec("%d");
1898     formatValue(w, 1337, spec);
1899 
1900     assert(w.data == "1337");
1901 }
1902 
1903 private void formatIntegral(Writer, T, Char)(ref Writer w, const(T) val, const ref FormatSpec!Char fs,
1904     uint base, ulong mask)
1905 {
1906     T arg = val;
1907 
1908     immutable negative = (base == 10 && arg < 0);
1909     if (negative)
1910     {
1911         arg = -arg;
1912     }
1913 
1914     // All unsigned integral types should fit in ulong.
1915     static if (is(ucent) && is(typeof(arg) == ucent))
1916         formatUnsigned(w, (cast(ucent) arg) & mask, fs, base, negative);
1917     else
1918         formatUnsigned(w, (cast(ulong) arg) & mask, fs, base, negative);
1919 }
1920 
1921 private void formatUnsigned(Writer, T, Char)
1922 (ref Writer w, T arg, const ref FormatSpec!Char fs, uint base, bool negative)
1923 {
1924     /* Write string:
1925      *    leftpad prefix1 prefix2 zerofill digits rightpad
1926      */
1927 
1928     /* Convert arg to digits[].
1929      * Note that 0 becomes an empty digits[]
1930      */
1931     char[64] buffer = void; // 64 bits in base 2 at most
1932     char[] digits;
1933     if (arg < base && base <= 10 && arg)
1934     {
1935         // Most numbers are a single digit - avoid expensive divide
1936         buffer[0] = cast(char)(arg + '0');
1937         digits = buffer[0 .. 1];
1938     }
1939     else
1940     {
1941         size_t i = buffer.length;
1942         while (arg)
1943         {
1944             --i;
1945             char c = cast(char) (arg % base);
1946             arg /= base;
1947             if (c < 10)
1948                 buffer[i] = cast(char)(c + '0');
1949             else
1950                 buffer[i] = cast(char)(c + (fs.spec == 'x' ? 'a' - 10 : 'A' - 10));
1951         }
1952         digits = buffer[i .. $]; // got the digits without the sign
1953     }
1954 
1955 
1956     immutable precision = (fs.precision == fs.UNSPECIFIED) ? 1 : fs.precision;
1957 
1958     char padChar = 0;
1959     if (!fs.flDash)
1960     {
1961         padChar = (fs.flZero && fs.precision == fs.UNSPECIFIED) ? '0' : ' ';
1962     }
1963 
1964     // Compute prefix1 and prefix2
1965     char prefix1 = 0;
1966     char prefix2 = 0;
1967     if (base == 10)
1968     {
1969         if (negative)
1970             prefix1 = '-';
1971         else if (fs.flPlus)
1972             prefix1 = '+';
1973         else if (fs.flSpace)
1974             prefix1 = ' ';
1975     }
1976     else if (base == 16 && fs.flHash && digits.length)
1977     {
1978         prefix1 = '0';
1979         prefix2 = fs.spec == 'x' ? 'x' : 'X';
1980     }
1981     // adjust precision to print a '0' for octal if alternate format is on
1982     else if (base == 8 && fs.flHash &&
1983              (precision <= 1 || precision <= digits.length) && // too low precision
1984              digits.length > 0)
1985         prefix1 = '0';
1986 
1987     size_t zerofill = precision > digits.length ? precision - digits.length : 0;
1988     size_t leftpad = 0;
1989     size_t rightpad = 0;
1990 
1991     immutable ptrdiff_t spacesToPrint =
1992         fs.width - (
1993               (prefix1 != 0)
1994             + (prefix2 != 0)
1995             + zerofill
1996             + digits.length
1997             + ((fs.flSeparator != 0) * (digits.length / fs.separators))
1998         );
1999     if (spacesToPrint > 0) // need to do some padding
2000     {
2001         if (padChar == '0')
2002             zerofill += spacesToPrint;
2003         else if (padChar)
2004             leftpad = spacesToPrint;
2005         else
2006             rightpad = spacesToPrint;
2007     }
2008 
2009     // Print
2010     foreach (i ; 0 .. leftpad)
2011         put(w, ' ');
2012 
2013     if (prefix1) put(w, prefix1);
2014     if (prefix2) put(w, prefix2);
2015 
2016     foreach (i ; 0 .. zerofill)
2017         put(w, '0');
2018 
2019     if (fs.flSeparator)
2020     {
2021         for (size_t j = 0; j < digits.length; ++j)
2022         {
2023             if (j != 0 && (digits.length - j) % fs.separators == 0)
2024             {
2025                 put(w, fs.separatorChar);
2026             }
2027             put(w, digits[j]);
2028         }
2029     }
2030     else
2031     {
2032         put(w, digits);
2033     }
2034 
2035     foreach (i ; 0 .. rightpad)
2036         put(w, ' ');
2037 }
2038 
2039 @safe pure unittest
2040 {
2041     assert(collectExceptionMsg!FormatException(format("%c", 5)).back == 'c');
2042 
2043     assertCTFEable!(
2044     {
2045         formatTest(9, "9");
2046         formatTest( 10, "10" );
2047     });
2048 }
2049 
2050 @system unittest
2051 {
2052     class C1 { long val; alias val this; this(long v){ val = v; } }
2053     class C2 { long val; alias val this; this(long v){ val = v; }
2054                override string toString() const { return "C"; } }
2055     formatTest( new C1(10), "10" );
2056     formatTest( new C2(10), "C" );
2057 
2058     struct S1 { long val; alias val this; }
2059     struct S2 { long val; alias val this;
2060                 string toString() const { return "S"; } }
2061     formatTest( S1(10), "10" );
2062     formatTest( S2(10), "S" );
2063 }
2064 
2065 // bugzilla 9117
2066 @safe unittest
2067 {
2068     static struct Frop {}
2069 
2070     static struct Foo
2071     {
2072         int n = 0;
2073         alias n this;
2074         T opCast(T) () if (is(T == Frop))
2075         {
2076             return Frop();
2077         }
2078         string toString()
2079         {
2080             return "Foo";
2081         }
2082     }
2083 
2084     static struct Bar
2085     {
2086         Foo foo;
2087         alias foo this;
2088         string toString()
2089         {
2090             return "Bar";
2091         }
2092     }
2093 
2094     const(char)[] result;
2095     void put(const char[] s){ result ~= s; }
2096 
2097     Foo foo;
2098     formattedWrite(&put, "%s", foo);    // OK
2099     assert(result == "Foo");
2100 
2101     result = null;
2102 
2103     Bar bar;
2104     formattedWrite(&put, "%s", bar);    // NG
2105     assert(result == "Bar");
2106 
2107     result = null;
2108 
2109     int i = 9;
2110     formattedWrite(&put, "%s", 9);
2111     assert(result == "9");
2112 }
2113 
2114 private enum ctfpMessage = "Cannot format floating point types at compile-time";
2115 
2116 /**
2117 Floating-point values are formatted like $(D printf) does.
2118 
2119 Params:
2120     w = The $(D OutputRange) to write to.
2121     obj = The value to write.
2122     f = The $(D FormatSpec) defining how to write the value.
2123  */
2124 void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
2125 if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
2126 {
2127     import std.algorithm.comparison : min;
2128     import std.algorithm.searching : find;
2129     import std.string : indexOf, indexOfAny, indexOfNeither;
2130 
2131     FormatSpec!Char fs = f; // fs is copy for change its values.
2132     FloatingPointTypeOf!T val = obj;
2133 
2134     if (fs.spec == 'r')
2135     {
2136         // raw write, skip all else and write the thing
2137         auto raw = (ref val)@trusted{
2138             return (cast(const char*) &val)[0 .. val.sizeof];
2139         }(val);
2140         if (needToSwapEndianess(f))
2141         {
2142             foreach_reverse (c; raw)
2143                 put(w, c);
2144         }
2145         else
2146         {
2147             foreach (c; raw)
2148                 put(w, c);
2149         }
2150         return;
2151     }
2152     enforceFmt(find("fgFGaAeEs", fs.spec).length,
2153         "incompatible format character for floating point argument: %" ~ fs.spec);
2154     enforceFmt(!__ctfe, ctfpMessage);
2155 
2156     version (CRuntime_Microsoft)
2157     {
2158         import std.math : isNaN, isInfinity;
2159         immutable double tval = val; // convert early to get "inf" in case of overflow
2160         string s;
2161         if (isNaN(tval))
2162             s = "nan"; // snprintf writes 1.#QNAN
2163         else if (isInfinity(tval))
2164             s = val >= 0 ? "inf" : "-inf"; // snprintf writes 1.#INF
2165 
2166         if (s.length > 0)
2167         {
2168           version (none)
2169           {
2170             return formatValue(w, s, f);
2171           }
2172           else  // FIXME:workaround
2173           {
2174             s = s[0 .. f.precision < $ ? f.precision : $];
2175             if (!f.flDash)
2176             {
2177                 // right align
2178                 if (f.width > s.length)
2179                     foreach (j ; 0 .. f.width - s.length) put(w, ' ');
2180                 put(w, s);
2181             }
2182             else
2183             {
2184                 // left align
2185                 put(w, s);
2186                 if (f.width > s.length)
2187                     foreach (j ; 0 .. f.width - s.length) put(w, ' ');
2188             }
2189             return;
2190           }
2191         }
2192     }
2193     else
2194         alias tval = val;
2195     if (fs.spec == 's') fs.spec = 'g';
2196     char[1 /*%*/ + 5 /*flags*/ + 3 /*width.prec*/ + 2 /*format*/
2197                      + 1 /*\0*/] sprintfSpec = void;
2198     sprintfSpec[0] = '%';
2199     uint i = 1;
2200     if (fs.flDash) sprintfSpec[i++] = '-';
2201     if (fs.flPlus) sprintfSpec[i++] = '+';
2202     if (fs.flZero) sprintfSpec[i++] = '0';
2203     if (fs.flSpace) sprintfSpec[i++] = ' ';
2204     if (fs.flHash) sprintfSpec[i++] = '#';
2205     sprintfSpec[i .. i + 3] = "*.*";
2206     i += 3;
2207     if (is(Unqual!(typeof(val)) == real)) sprintfSpec[i++] = 'L';
2208     sprintfSpec[i++] = fs.spec;
2209     sprintfSpec[i] = 0;
2210     //printf("format: '%s'; geeba: %g\n", sprintfSpec.ptr, val);
2211     char[512] buf = void;
2212 
2213     immutable n = ()@trusted{
2214         import core.stdc.stdio : snprintf;
2215         return snprintf(buf.ptr, buf.length,
2216                         sprintfSpec.ptr,
2217                         fs.width,
2218                         // negative precision is same as no precision specified
2219                         fs.precision == fs.UNSPECIFIED ? -1 : fs.precision,
2220                         tval);
2221     }();
2222 
2223     enforceFmt(n >= 0,
2224         "floating point formatting failure");
2225 
2226     auto len = min(n, buf.length-1);
2227     ptrdiff_t dot = buf[0 .. len].indexOf('.');
2228     if (fs.flSeparator && dot != -1)
2229     {
2230         ptrdiff_t firstDigit = buf[0 .. len].indexOfAny("0123456789");
2231         ptrdiff_t ePos = buf[0 .. len].indexOf('e');
2232         size_t j;
2233 
2234         ptrdiff_t firstLen = dot - firstDigit;
2235 
2236         size_t separatorScoreCnt = firstLen / fs.separators;
2237 
2238         size_t afterDotIdx;
2239         if (ePos != -1)
2240         {
2241             afterDotIdx = ePos;
2242         }
2243         else
2244         {
2245             afterDotIdx = len;
2246         }
2247 
2248         if (dot != -1)
2249         {
2250             ptrdiff_t mantissaLen = afterDotIdx - (dot + 1);
2251             separatorScoreCnt += (mantissaLen > 0) ? (mantissaLen - 1) / fs.separators : 0;
2252         }
2253 
2254         // plus, minus prefix
2255         ptrdiff_t digitsBegin = buf[0 .. separatorScoreCnt].indexOfNeither(" ");
2256         if (digitsBegin == -1)
2257         {
2258             digitsBegin = separatorScoreCnt;
2259         }
2260         put(w, buf[digitsBegin .. firstDigit]);
2261 
2262         // digits until dot with separator
2263         for (j = 0; j < firstLen; ++j)
2264         {
2265             if (j > 0 && (firstLen - j) % fs.separators == 0)
2266             {
2267                 put(w, fs.separatorChar);
2268             }
2269             put(w, buf[j + firstDigit]);
2270         }
2271         put(w, '.');
2272 
2273         // digits after dot
2274         for (j = dot + 1; j < afterDotIdx; ++j)
2275         {
2276             auto realJ = (j - (dot + 1));
2277             if (realJ != 0 && realJ % fs.separators == 0)
2278             {
2279                 put(w, fs.separatorChar);
2280             }
2281             put(w, buf[j]);
2282         }
2283 
2284         // rest
2285         if (ePos != -1)
2286         {
2287             put(w, buf[afterDotIdx .. len]);
2288         }
2289     }
2290     else
2291     {
2292         put(w, buf[0 .. len]);
2293     }
2294 }
2295 
2296 ///
2297 @safe unittest
2298 {
2299     import std.array : appender;
2300     auto w = appender!string();
2301     auto spec = singleSpec("%.1f");
2302     formatValue(w, 1337.7, spec);
2303 
2304     assert(w.data == "1337.7");
2305 }
2306 
2307 @safe /*pure*/ unittest     // formatting floating point values is now impure
2308 {
2309     import std.conv : to;
2310 
2311     assert(collectExceptionMsg!FormatException(format("%d", 5.1)).back == 'd');
2312 
2313     foreach (T; AliasSeq!(float, double, real))
2314     {
2315         formatTest( to!(          T)(5.5), "5.5" );
2316         formatTest( to!(    const T)(5.5), "5.5" );
2317         formatTest( to!(immutable T)(5.5), "5.5" );
2318 
2319         formatTest( T.nan, "nan" );
2320     }
2321 }
2322 
2323 @system unittest
2324 {
2325     formatTest( 2.25, "2.25" );
2326 
2327     class C1 { double val; alias val this; this(double v){ val = v; } }
2328     class C2 { double val; alias val this; this(double v){ val = v; }
2329                override string toString() const { return "C"; } }
2330     formatTest( new C1(2.25), "2.25" );
2331     formatTest( new C2(2.25), "C" );
2332 
2333     struct S1 { double val; alias val this; }
2334     struct S2 { double val; alias val this;
2335                 string toString() const { return "S"; } }
2336     formatTest( S1(2.25), "2.25" );
2337     formatTest( S2(2.25), "S" );
2338 }
2339 
2340 /*
2341 Formatting a $(D creal) is deprecated but still kept around for a while.
2342 
2343 Params:
2344     w = The $(D OutputRange) to write to.
2345     obj = The value to write.
2346     f = The $(D FormatSpec) defining how to write the value.
2347  */
2348 void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
2349 if (is(Unqual!T : creal) && !is(T == enum) && !hasToString!(T, Char))
2350 {
2351     immutable creal val = obj;
2352 
2353     formatValue(w, val.re, f);
2354     if (val.im >= 0)
2355     {
2356         put(w, '+');
2357     }
2358     formatValue(w, val.im, f);
2359     put(w, 'i');
2360 }
2361 
2362 @safe /*pure*/ unittest     // formatting floating point values is now impure
2363 {
2364     import std.conv : to;
2365     foreach (T; AliasSeq!(cfloat, cdouble, creal))
2366     {
2367         formatTest( to!(          T)(1 + 1i), "1+1i" );
2368         formatTest( to!(    const T)(1 + 1i), "1+1i" );
2369         formatTest( to!(immutable T)(1 + 1i), "1+1i" );
2370     }
2371     foreach (T; AliasSeq!(cfloat, cdouble, creal))
2372     {
2373         formatTest( to!(          T)(0 - 3i), "0-3i" );
2374         formatTest( to!(    const T)(0 - 3i), "0-3i" );
2375         formatTest( to!(immutable T)(0 - 3i), "0-3i" );
2376     }
2377 }
2378 
2379 @system unittest
2380 {
2381     formatTest( 3+2.25i, "3+2.25i" );
2382 
2383     class C1 { cdouble val; alias val this; this(cdouble v){ val = v; } }
2384     class C2 { cdouble val; alias val this; this(cdouble v){ val = v; }
2385                override string toString() const { return "C"; } }
2386     formatTest( new C1(3+2.25i), "3+2.25i" );
2387     formatTest( new C2(3+2.25i), "C" );
2388 
2389     struct S1 { cdouble val; alias val this; }
2390     struct S2 { cdouble val; alias val this;
2391                 string toString() const { return "S"; } }
2392     formatTest( S1(3+2.25i), "3+2.25i" );
2393     formatTest( S2(3+2.25i), "S" );
2394 }
2395 
2396 /*
2397    Formatting an $(D ireal) is deprecated but still kept around for a while.
2398 
2399 Params:
2400     w = The $(D OutputRange) to write to.
2401     obj = The value to write.
2402     f = The $(D FormatSpec) defining how to write the value.
2403  */
2404 void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
2405 if (is(Unqual!T : ireal) && !is(T == enum) && !hasToString!(T, Char))
2406 {
2407     immutable ireal val = obj;
2408 
2409     formatValue(w, val.im, f);
2410     put(w, 'i');
2411 }
2412 
2413 @safe /*pure*/ unittest     // formatting floating point values is now impure
2414 {
2415     import std.conv : to;
2416     foreach (T; AliasSeq!(ifloat, idouble, ireal))
2417     {
2418         formatTest( to!(          T)(1i), "1i" );
2419         formatTest( to!(    const T)(1i), "1i" );
2420         formatTest( to!(immutable T)(1i), "1i" );
2421     }
2422 }
2423 
2424 @system unittest
2425 {
2426     formatTest( 2.25i, "2.25i" );
2427 
2428     class C1 { idouble val; alias val this; this(idouble v){ val = v; } }
2429     class C2 { idouble val; alias val this; this(idouble v){ val = v; }
2430                override string toString() const { return "C"; } }
2431     formatTest( new C1(2.25i), "2.25i" );
2432     formatTest( new C2(2.25i), "C" );
2433 
2434     struct S1 { idouble val; alias val this; }
2435     struct S2 { idouble val; alias val this;
2436                 string toString() const { return "S"; } }
2437     formatTest( S1(2.25i), "2.25i" );
2438     formatTest( S2(2.25i), "S" );
2439 }
2440 
2441 /**
2442 Individual characters ($(D char), $(D wchar), or $(D dchar)) are formatted as
2443 Unicode characters with %s and as integers with integral-specific format
2444 specs.
2445 
2446 Params:
2447     w = The $(D OutputRange) to write to.
2448     obj = The value to write.
2449     f = The $(D FormatSpec) defining how to write the value.
2450  */
2451 void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
2452 if (is(CharTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
2453 {
2454     CharTypeOf!T val = obj;
2455 
2456     if (f.spec == 's' || f.spec == 'c')
2457     {
2458         put(w, val);
2459     }
2460     else
2461     {
2462         alias U = AliasSeq!(ubyte, ushort, uint)[CharTypeOf!T.sizeof/2];
2463         formatValue(w, cast(U) val, f);
2464     }
2465 }
2466 
2467 ///
2468 @safe pure unittest
2469 {
2470     import std.array : appender;
2471     auto w = appender!string();
2472     auto spec = singleSpec("%c");
2473     formatValue(w, 'a', spec);
2474 
2475     assert(w.data == "a");
2476 }
2477 
2478 @safe pure unittest
2479 {
2480     assertCTFEable!(
2481     {
2482         formatTest( 'c', "c" );
2483     });
2484 }
2485 
2486 @system unittest
2487 {
2488     class C1 { char val; alias val this; this(char v){ val = v; } }
2489     class C2 { char val; alias val this; this(char v){ val = v; }
2490                override string toString() const { return "C"; } }
2491     formatTest( new C1('c'), "c" );
2492     formatTest( new C2('c'), "C" );
2493 
2494     struct S1 { char val; alias val this; }
2495     struct S2 { char val; alias val this;
2496                 string toString() const { return "S"; } }
2497     formatTest( S1('c'), "c" );
2498     formatTest( S2('c'), "S" );
2499 }
2500 
2501 @safe unittest
2502 {
2503     //Little Endian
2504     formatTest( "%-r", cast( char)'c', ['c'         ] );
2505     formatTest( "%-r", cast(wchar)'c', ['c', 0      ] );
2506     formatTest( "%-r", cast(dchar)'c', ['c', 0, 0, 0] );
2507     formatTest( "%-r", '本', ['\x2c', '\x67'] );
2508 
2509     //Big Endian
2510     formatTest( "%+r", cast( char)'c', [         'c'] );
2511     formatTest( "%+r", cast(wchar)'c', [0,       'c'] );
2512     formatTest( "%+r", cast(dchar)'c', [0, 0, 0, 'c'] );
2513     formatTest( "%+r", '本', ['\x67', '\x2c'] );
2514 }
2515 
2516 /**
2517 Strings are formatted like $(D printf) does.
2518 
2519 Params:
2520     w = The $(D OutputRange) to write to.
2521     obj = The value to write.
2522     f = The $(D FormatSpec) defining how to write the value.
2523  */
2524 void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
2525 if (is(StringTypeOf!T) && !is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
2526 {
2527     Unqual!(StringTypeOf!T) val = obj;  // for `alias this`, see bug5371
2528     formatRange(w, val, f);
2529 }
2530 
2531 ///
2532 @safe pure unittest
2533 {
2534     import std.array : appender;
2535     auto w = appender!string();
2536     auto spec = singleSpec("%s");
2537     formatValue(w, "hello", spec);
2538 
2539     assert(w.data == "hello");
2540 }
2541 
2542 @safe unittest
2543 {
2544     formatTest( "abc", "abc" );
2545 }
2546 
2547 @system unittest
2548 {
2549     // Test for bug 5371 for classes
2550     class C1 { const string var; alias var this; this(string s){ var = s; } }
2551     class C2 {       string var; alias var this; this(string s){ var = s; } }
2552     formatTest( new C1("c1"), "c1" );
2553     formatTest( new C2("c2"), "c2" );
2554 
2555     // Test for bug 5371 for structs
2556     struct S1 { const string var; alias var this; }
2557     struct S2 {       string var; alias var this; }
2558     formatTest( S1("s1"), "s1" );
2559     formatTest( S2("s2"), "s2" );
2560 }
2561 
2562 @system unittest
2563 {
2564     class  C3 { string val; alias val this; this(string s){ val = s; }
2565                 override string toString() const { return "C"; } }
2566     formatTest( new C3("c3"), "C" );
2567 
2568     struct S3 { string val; alias val this;
2569                 string toString() const { return "S"; } }
2570     formatTest( S3("s3"), "S" );
2571 }
2572 
2573 @safe pure unittest
2574 {
2575     //Little Endian
2576     formatTest( "%-r", "ab"c, ['a'         , 'b'         ] );
2577     formatTest( "%-r", "ab"w, ['a', 0      , 'b', 0      ] );
2578     formatTest( "%-r", "ab"d, ['a', 0, 0, 0, 'b', 0, 0, 0] );
2579     formatTest( "%-r", "日本語"c, ['\xe6', '\x97', '\xa5', '\xe6', '\x9c', '\xac', '\xe8', '\xaa', '\x9e'] );
2580     formatTest( "%-r", "日本語"w, ['\xe5', '\x65', '\x2c', '\x67', '\x9e', '\x8a']);
2581     formatTest( "%-r", "日本語"d, ['\xe5', '\x65', '\x00', '\x00', '\x2c', '\x67',
2582         '\x00', '\x00', '\x9e', '\x8a', '\x00', '\x00'] );
2583 
2584     //Big Endian
2585     formatTest( "%+r", "ab"c, [         'a',          'b'] );
2586     formatTest( "%+r", "ab"w, [      0, 'a',       0, 'b'] );
2587     formatTest( "%+r", "ab"d, [0, 0, 0, 'a', 0, 0, 0, 'b'] );
2588     formatTest( "%+r", "日本語"c, ['\xe6', '\x97', '\xa5', '\xe6', '\x9c', '\xac', '\xe8', '\xaa', '\x9e'] );
2589     formatTest( "%+r", "日本語"w, ['\x65', '\xe5', '\x67', '\x2c', '\x8a', '\x9e'] );
2590     formatTest( "%+r", "日本語"d, ['\x00', '\x00', '\x65', '\xe5', '\x00', '\x00',
2591         '\x67', '\x2c', '\x00', '\x00', '\x8a', '\x9e'] );
2592 }
2593 
2594 /**
2595 Static-size arrays are formatted as dynamic arrays.
2596 
2597 Params:
2598     w = The $(D OutputRange) to write to.
2599     obj = The value to write.
2600     f = The $(D FormatSpec) defining how to write the value.
2601  */
2602 void formatValue(Writer, T, Char)(auto ref Writer w, auto ref T obj, const ref FormatSpec!Char f)
2603 if (is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
2604 {
2605     formatValue(w, obj[], f);
2606 }
2607 
2608 ///
2609 @safe pure unittest
2610 {
2611     import std.array : appender;
2612     auto w = appender!string();
2613     auto spec = singleSpec("%s");
2614     char[2] two = ['a', 'b'];
2615     formatValue(w, two, spec);
2616 
2617     assert(w.data == "ab");
2618 }
2619 
2620 @safe unittest    // Test for issue 8310
2621 {
2622     import std.array : appender;
2623     FormatSpec!char f;
2624     auto w = appender!string();
2625 
2626     char[2] two = ['a', 'b'];
2627     formatValue(w, two, f);
2628 
2629     char[2] getTwo(){ return two; }
2630     formatValue(w, getTwo(), f);
2631 }
2632 
2633 /**
2634 Dynamic arrays are formatted as input ranges.
2635 
2636 Specializations:
2637     $(UL $(LI $(D void[]) is formatted like $(D ubyte[]).)
2638         $(LI Const array is converted to input range by removing its qualifier.))
2639 
2640 Params:
2641     w = The $(D OutputRange) to write to.
2642     obj = The value to write.
2643     f = The $(D FormatSpec) defining how to write the value.
2644  */
2645 void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
2646 if (is(DynamicArrayTypeOf!T) && !is(StringTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
2647 {
2648     static if (is(const(ArrayTypeOf!T) == const(void[])))
2649     {
2650         formatValue(w, cast(const ubyte[]) obj, f);
2651     }
2652     else static if (!isInputRange!T)
2653     {
2654         alias U = Unqual!(ArrayTypeOf!T);
2655         static assert(isInputRange!U);
2656         U val = obj;
2657         formatValue(w, val, f);
2658     }
2659     else
2660     {
2661         formatRange(w, obj, f);
2662     }
2663 }
2664 
2665 ///
2666 @safe pure unittest
2667 {
2668     import std.array : appender;
2669     auto w = appender!string();
2670     auto spec = singleSpec("%s");
2671     auto two = [1, 2];
2672     formatValue(w, two, spec);
2673 
2674     assert(w.data == "[1, 2]");
2675 }
2676 
2677 // alias this, input range I/F, and toString()
2678 @system unittest
2679 {
2680     struct S(int flags)
2681     {
2682         int[] arr;
2683       static if (flags & 1)
2684         alias arr this;
2685 
2686       static if (flags & 2)
2687       {
2688         @property bool empty() const { return arr.length == 0; }
2689         @property int front() const { return arr[0] * 2; }
2690         void popFront() { arr = arr[1..$]; }
2691       }
2692 
2693       static if (flags & 4)
2694         string toString() const { return "S"; }
2695     }
2696     formatTest(S!0b000([0, 1, 2]), "S!0([0, 1, 2])");
2697     formatTest(S!0b001([0, 1, 2]), "[0, 1, 2]");        // Test for bug 7628
2698     formatTest(S!0b010([0, 1, 2]), "[0, 2, 4]");
2699     formatTest(S!0b011([0, 1, 2]), "[0, 2, 4]");
2700     formatTest(S!0b100([0, 1, 2]), "S");
2701     formatTest(S!0b101([0, 1, 2]), "S");                // Test for bug 7628
2702     formatTest(S!0b110([0, 1, 2]), "S");
2703     formatTest(S!0b111([0, 1, 2]), "S");
2704 
2705     class C(uint flags)
2706     {
2707         int[] arr;
2708       static if (flags & 1)
2709         alias arr this;
2710 
2711         this(int[] a) { arr = a; }
2712 
2713       static if (flags & 2)
2714       {
2715         @property bool empty() const { return arr.length == 0; }
2716         @property int front() const { return arr[0] * 2; }
2717         void popFront() { arr = arr[1..$]; }
2718       }
2719 
2720       static if (flags & 4)
2721         override string toString() const { return "C"; }
2722     }
2723     formatTest(new C!0b000([0, 1, 2]), (new C!0b000([])).toString());
2724     formatTest(new C!0b001([0, 1, 2]), "[0, 1, 2]");    // Test for bug 7628
2725     formatTest(new C!0b010([0, 1, 2]), "[0, 2, 4]");
2726     formatTest(new C!0b011([0, 1, 2]), "[0, 2, 4]");
2727     formatTest(new C!0b100([0, 1, 2]), "C");
2728     formatTest(new C!0b101([0, 1, 2]), "C");            // Test for bug 7628
2729     formatTest(new C!0b110([0, 1, 2]), "C");
2730     formatTest(new C!0b111([0, 1, 2]), "C");
2731 }
2732 
2733 @system unittest
2734 {
2735     // void[]
2736     void[] val0;
2737     formatTest( val0, "[]" );
2738 
2739     void[] val = cast(void[]) cast(ubyte[])[1, 2, 3];
2740     formatTest( val, "[1, 2, 3]" );
2741 
2742     void[0] sval0 = [];
2743     formatTest( sval0, "[]");
2744 
2745     void[3] sval = cast(void[3]) cast(ubyte[3])[1, 2, 3];
2746     formatTest( sval, "[1, 2, 3]" );
2747 }
2748 
2749 @safe unittest
2750 {
2751     // const(T[]) -> const(T)[]
2752     const short[] a = [1, 2, 3];
2753     formatTest( a, "[1, 2, 3]" );
2754 
2755     struct S { const(int[]) arr; alias arr this; }
2756     auto s = S([1,2,3]);
2757     formatTest( s, "[1, 2, 3]" );
2758 }
2759 
2760 @safe unittest
2761 {
2762     // 6640
2763     struct Range
2764     {
2765       @safe:
2766         string value;
2767         @property bool empty() const { return !value.length; }
2768         @property dchar front() const { return value.front; }
2769         void popFront() { value.popFront(); }
2770 
2771         @property size_t length() const { return value.length; }
2772     }
2773     immutable table =
2774     [
2775         ["[%s]", "[string]"],
2776         ["[%10s]", "[    string]"],
2777         ["[%-10s]", "[string    ]"],
2778         ["[%(%02x %)]", "[73 74 72 69 6e 67]"],
2779         ["[%(%c %)]", "[s t r i n g]"],
2780     ];
2781     foreach (e; table)
2782     {
2783         formatTest(e[0], "string", e[1]);
2784         formatTest(e[0], Range("string"), e[1]);
2785     }
2786 }
2787 
2788 @system unittest
2789 {
2790     // string literal from valid UTF sequence is encoding free.
2791     foreach (StrType; AliasSeq!(string, wstring, dstring))
2792     {
2793         // Valid and printable (ASCII)
2794         formatTest( [cast(StrType)"hello"],
2795                     `["hello"]` );
2796 
2797         // 1 character escape sequences (' is not escaped in strings)
2798         formatTest( [cast(StrType)"\"'\0\\\a\b\f\n\r\t\v"],
2799                     `["\"'\0\\\a\b\f\n\r\t\v"]` );
2800 
2801         // 1 character optional escape sequences
2802         formatTest( [cast(StrType)"\'\?"],
2803                     `["'?"]` );
2804 
2805         // Valid and non-printable code point (<= U+FF)
2806         formatTest( [cast(StrType)"\x10\x1F\x20test"],
2807                     `["\x10\x1F test"]` );
2808 
2809         // Valid and non-printable code point (<= U+FFFF)
2810         formatTest( [cast(StrType)"\u200B..\u200F"],
2811                     `["\u200B..\u200F"]` );
2812 
2813         // Valid and non-printable code point (<= U+10FFFF)
2814         formatTest( [cast(StrType)"\U000E0020..\U000E007F"],
2815                     `["\U000E0020..\U000E007F"]` );
2816     }
2817 
2818     // invalid UTF sequence needs hex-string literal postfix (c/w/d)
2819     {
2820         // U+FFFF with UTF-8 (Invalid code point for interchange)
2821         formatTest( [cast(string)[0xEF, 0xBF, 0xBF]],
2822                     `[x"EF BF BF"c]` );
2823 
2824         // U+FFFF with UTF-16 (Invalid code point for interchange)
2825         formatTest( [cast(wstring)[0xFFFF]],
2826                     `[x"FFFF"w]` );
2827 
2828         // U+FFFF with UTF-32 (Invalid code point for interchange)
2829         formatTest( [cast(dstring)[0xFFFF]],
2830                     `[x"FFFF"d]` );
2831     }
2832 }
2833 
2834 @safe unittest
2835 {
2836     // nested range formatting with array of string
2837     formatTest( "%({%(%02x %)}%| %)", ["test", "msg"],
2838                 `{74 65 73 74} {6d 73 67}` );
2839 }
2840 
2841 @safe unittest
2842 {
2843     // stop auto escaping inside range formatting
2844     auto arr = ["hello", "world"];
2845     formatTest( "%(%s, %)",  arr, `"hello", "world"` );
2846     formatTest( "%-(%s, %)", arr, `hello, world` );
2847 
2848     auto aa1 = [1:"hello", 2:"world"];
2849     formatTest( "%(%s:%s, %)",  aa1, [`1:"hello", 2:"world"`, `2:"world", 1:"hello"`] );
2850     formatTest( "%-(%s:%s, %)", aa1, [`1:hello, 2:world`, `2:world, 1:hello`] );
2851 
2852     auto aa2 = [1:["ab", "cd"], 2:["ef", "gh"]];
2853     formatTest( "%-(%s:%s, %)",        aa2, [`1:["ab", "cd"], 2:["ef", "gh"]`, `2:["ef", "gh"], 1:["ab", "cd"]`] );
2854     formatTest( "%-(%s:%(%s%), %)",    aa2, [`1:"ab""cd", 2:"ef""gh"`, `2:"ef""gh", 1:"ab""cd"`] );
2855     formatTest( "%-(%s:%-(%s%)%|, %)", aa2, [`1:abcd, 2:efgh`, `2:efgh, 1:abcd`] );
2856 }
2857 
2858 // input range formatting
2859 private void formatRange(Writer, T, Char)(ref Writer w, ref T val, const ref FormatSpec!Char f)
2860 if (isInputRange!T)
2861 {
2862     import std.conv : text;
2863 
2864     // Formatting character ranges like string
2865     if (f.spec == 's')
2866     {
2867         alias E = ElementType!T;
2868 
2869         static if (!is(E == enum) && is(CharTypeOf!E))
2870         {
2871             static if (is(StringTypeOf!T))
2872             {
2873                 auto s = val[0 .. f.precision < $ ? f.precision : $];
2874                 if (!f.flDash)
2875                 {
2876                     // right align
2877                     if (f.width > s.length)
2878                         foreach (i ; 0 .. f.width - s.length) put(w, ' ');
2879                     put(w, s);
2880                 }
2881                 else
2882                 {
2883                     // left align
2884                     put(w, s);
2885                     if (f.width > s.length)
2886                         foreach (i ; 0 .. f.width - s.length) put(w, ' ');
2887                 }
2888             }
2889             else
2890             {
2891                 if (!f.flDash)
2892                 {
2893                     static if (hasLength!T)
2894                     {
2895                         // right align
2896                         auto len = val.length;
2897                     }
2898                     else static if (isForwardRange!T && !isInfinite!T)
2899                     {
2900                         auto len = walkLength(val.save);
2901                     }
2902                     else
2903                     {
2904                         enforce(f.width == 0, "Cannot right-align a range without length");
2905                         size_t len = 0;
2906                     }
2907                     if (f.precision != f.UNSPECIFIED && len > f.precision)
2908                         len = f.precision;
2909 
2910                     if (f.width > len)
2911                         foreach (i ; 0 .. f.width - len)
2912                             put(w, ' ');
2913                     if (f.precision == f.UNSPECIFIED)
2914                         put(w, val);
2915                     else
2916                     {
2917                         size_t printed = 0;
2918                         for (; !val.empty && printed < f.precision; val.popFront(), ++printed)
2919                             put(w, val.front);
2920                     }
2921                 }
2922                 else
2923                 {
2924                     size_t printed = void;
2925 
2926                     // left align
2927                     if (f.precision == f.UNSPECIFIED)
2928                     {
2929                         static if (hasLength!T)
2930                         {
2931                             printed = val.length;
2932                             put(w, val);
2933                         }
2934                         else
2935                         {
2936                             printed = 0;
2937                             for (; !val.empty; val.popFront(), ++printed)
2938                                 put(w, val.front);
2939                         }
2940                     }
2941                     else
2942                     {
2943                         printed = 0;
2944                         for (; !val.empty && printed < f.precision; val.popFront(), ++printed)
2945                             put(w, val.front);
2946                     }
2947 
2948                     if (f.width > printed)
2949                         foreach (i ; 0 .. f.width - printed)
2950                             put(w, ' ');
2951                 }
2952             }
2953         }
2954         else
2955         {
2956             put(w, f.seqBefore);
2957             if (!val.empty)
2958             {
2959                 formatElement(w, val.front, f);
2960                 val.popFront();
2961                 for (size_t i; !val.empty; val.popFront(), ++i)
2962                 {
2963                     put(w, f.seqSeparator);
2964                     formatElement(w, val.front, f);
2965                 }
2966             }
2967             static if (!isInfinite!T) put(w, f.seqAfter);
2968         }
2969     }
2970     else if (f.spec == 'r')
2971     {
2972         static if (is(DynamicArrayTypeOf!T))
2973         {
2974             alias ARR = DynamicArrayTypeOf!T;
2975             foreach (e ; cast(ARR) val)
2976             {
2977                 formatValue(w, e, f);
2978             }
2979         }
2980         else
2981         {
2982             for (size_t i; !val.empty; val.popFront(), ++i)
2983             {
2984                 formatValue(w, val.front, f);
2985             }
2986         }
2987     }
2988     else if (f.spec == '(')
2989     {
2990         if (val.empty)
2991             return;
2992         // Nested specifier is to be used
2993         for (;;)
2994         {
2995             auto fmt = FormatSpec!Char(f.nested);
2996             fmt.writeUpToNextSpec(w);
2997             if (f.flDash)
2998                 formatValue(w, val.front, fmt);
2999             else
3000                 formatElement(w, val.front, fmt);
3001             if (f.sep !is null)
3002             {
3003                 put(w, fmt.trailing);
3004                 val.popFront();
3005                 if (val.empty)
3006                     break;
3007                 put(w, f.sep);
3008             }
3009             else
3010             {
3011                 val.popFront();
3012                 if (val.empty)
3013                     break;
3014                 put(w, fmt.trailing);
3015             }
3016         }
3017     }
3018     else
3019         throw new Exception(text("Incorrect format specifier for range: %", f.spec));
3020 }
3021 
3022 @safe pure unittest
3023 {
3024     assert(collectExceptionMsg(format("%d", "hi")).back == 'd');
3025 }
3026 
3027 // character formatting with ecaping
3028 private void formatChar(Writer)(ref Writer w, in dchar c, in char quote)
3029 {
3030     import std.uni : isGraphical;
3031 
3032     string fmt;
3033     if (isGraphical(c))
3034     {
3035         if (c == quote || c == '\\')
3036             put(w, '\\');
3037         put(w, c);
3038         return;
3039     }
3040     else if (c <= 0xFF)
3041     {
3042         if (c < 0x20)
3043         {
3044             foreach (i, k; "\n\r\t\a\b\f\v\0")
3045             {
3046                 if (c == k)
3047                 {
3048                     put(w, '\\');
3049                     put(w, "nrtabfv0"[i]);
3050                     return;
3051                 }
3052             }
3053         }
3054         fmt = "\\x%02X";
3055     }
3056     else if (c <= 0xFFFF)
3057         fmt = "\\u%04X";
3058     else
3059         fmt = "\\U%08X";
3060 
3061     formattedWrite(w, fmt, cast(uint) c);
3062 }
3063 
3064 // undocumented because of deprecation
3065 // string elements are formatted like UTF-8 string literals.
3066 void formatElement(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
3067 if (is(StringTypeOf!T) && !is(T == enum))
3068 {
3069     import std.array : appender;
3070     import std.utf : UTFException;
3071 
3072     StringTypeOf!T str = val;   // bug 8015
3073 
3074     if (f.spec == 's')
3075     {
3076         try
3077         {
3078             // ignore other specifications and quote
3079             auto app = appender!(typeof(val[0])[])();
3080             put(app, '\"');
3081             for (size_t i = 0; i < str.length; )
3082             {
3083                 import std.utf : decode;
3084 
3085                 auto c = decode(str, i);
3086                 // \uFFFE and \uFFFF are considered valid by isValidDchar,
3087                 // so need checking for interchange.
3088                 if (c == 0xFFFE || c == 0xFFFF)
3089                     goto LinvalidSeq;
3090                 formatChar(app, c, '"');
3091             }
3092             put(app, '\"');
3093             put(w, app.data);
3094             return;
3095         }
3096         catch (UTFException)
3097         {
3098         }
3099 
3100         // If val contains invalid UTF sequence, formatted like HexString literal
3101     LinvalidSeq:
3102         static if (is(typeof(str[0]) : const(char)))
3103         {
3104             enum postfix = 'c';
3105             alias IntArr = const(ubyte)[];
3106         }
3107         else static if (is(typeof(str[0]) : const(wchar)))
3108         {
3109             enum postfix = 'w';
3110             alias IntArr = const(ushort)[];
3111         }
3112         else static if (is(typeof(str[0]) : const(dchar)))
3113         {
3114             enum postfix = 'd';
3115             alias IntArr = const(uint)[];
3116         }
3117         formattedWrite(w, "x\"%(%02X %)\"%s", cast(IntArr) str, postfix);
3118     }
3119     else
3120         formatValue(w, str, f);
3121 }
3122 
3123 @safe pure unittest
3124 {
3125     import std.array : appender;
3126     auto w = appender!string();
3127     auto spec = singleSpec("%s");
3128     formatElement(w, "Hello World", spec);
3129 
3130     assert(w.data == "\"Hello World\"");
3131 }
3132 
3133 @safe unittest
3134 {
3135     // Test for bug 8015
3136     import std.typecons;
3137 
3138     struct MyStruct {
3139         string str;
3140         @property string toStr() {
3141             return str;
3142         }
3143         alias toStr this;
3144     }
3145 
3146     Tuple!(MyStruct) t;
3147 }
3148 
3149 // undocumented because of deprecation
3150 // Character elements are formatted like UTF-8 character literals.
3151 void formatElement(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
3152 if (is(CharTypeOf!T) && !is(T == enum))
3153 {
3154     if (f.spec == 's')
3155     {
3156         put(w, '\'');
3157         formatChar(w, val, '\'');
3158         put(w, '\'');
3159     }
3160     else
3161         formatValue(w, val, f);
3162 }
3163 
3164 ///
3165 @safe unittest
3166 {
3167     import std.array : appender;
3168     auto w = appender!string();
3169     auto spec = singleSpec("%s");
3170     formatElement(w, "H", spec);
3171 
3172     assert(w.data == "\"H\"", w.data);
3173 }
3174 
3175 // undocumented
3176 // Maybe T is noncopyable struct, so receive it by 'auto ref'.
3177 void formatElement(Writer, T, Char)(auto ref Writer w, auto ref T val, const ref FormatSpec!Char f)
3178 if (!is(StringTypeOf!T) && !is(CharTypeOf!T) || is(T == enum))
3179 {
3180     formatValue(w, val, f);
3181 }
3182 
3183 /**
3184    Associative arrays are formatted by using $(D ':') and $(D ", ") as
3185    separators, and enclosed by $(D '[') and $(D ']').
3186 
3187 Params:
3188     w = The $(D OutputRange) to write to.
3189     obj = The value to write.
3190     f = The $(D FormatSpec) defining how to write the value.
3191  */
3192 void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
3193 if (is(AssocArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
3194 {
3195     AssocArrayTypeOf!T val = obj;
3196 
3197     enforceFmt(f.spec == 's' || f.spec == '(',
3198         "incompatible format character for associative array argument: %" ~ f.spec);
3199 
3200     enum const(Char)[] defSpec = "%s" ~ f.keySeparator ~ "%s" ~ f.seqSeparator;
3201     auto fmtSpec = f.spec == '(' ? f.nested : defSpec;
3202 
3203     size_t i = 0;
3204     immutable end = val.length;
3205 
3206     if (f.spec == 's')
3207         put(w, f.seqBefore);
3208     foreach (k, ref v; val)
3209     {
3210         auto fmt = FormatSpec!Char(fmtSpec);
3211         fmt.writeUpToNextSpec(w);
3212         if (f.flDash)
3213         {
3214             formatValue(w, k, fmt);
3215             fmt.writeUpToNextSpec(w);
3216             formatValue(w, v, fmt);
3217         }
3218         else
3219         {
3220             formatElement(w, k, fmt);
3221             fmt.writeUpToNextSpec(w);
3222             formatElement(w, v, fmt);
3223         }
3224         if (f.sep !is null)
3225         {
3226             fmt.writeUpToNextSpec(w);
3227             if (++i != end)
3228                 put(w, f.sep);
3229         }
3230         else
3231         {
3232             if (++i != end)
3233                 fmt.writeUpToNextSpec(w);
3234         }
3235     }
3236     if (f.spec == 's')
3237         put(w, f.seqAfter);
3238 }
3239 
3240 ///
3241 @safe pure unittest
3242 {
3243     import std.array : appender;
3244     auto w = appender!string();
3245     auto spec = singleSpec("%s");
3246     auto aa = ["H":"W"];
3247     formatElement(w, aa, spec);
3248 
3249     assert(w.data == "[\"H\":\"W\"]", w.data);
3250 }
3251 
3252 @safe unittest
3253 {
3254     assert(collectExceptionMsg!FormatException(format("%d", [0:1])).back == 'd');
3255 
3256     int[string] aa0;
3257     formatTest( aa0, `[]` );
3258 
3259     // elements escaping
3260     formatTest(  ["aaa":1, "bbb":2],
3261                [`["aaa":1, "bbb":2]`, `["bbb":2, "aaa":1]`] );
3262     formatTest(  ['c':"str"],
3263                 `['c':"str"]` );
3264     formatTest(  ['"':"\"", '\'':"'"],
3265                [`['"':"\"", '\'':"'"]`, `['\'':"'", '"':"\""]`] );
3266 
3267     // range formatting for AA
3268     auto aa3 = [1:"hello", 2:"world"];
3269     // escape
3270     formatTest( "{%(%s:%s $ %)}", aa3,
3271                [`{1:"hello" $ 2:"world"}`, `{2:"world" $ 1:"hello"}`]);
3272     // use range formatting for key and value, and use %|
3273     formatTest( "{%([%04d->%(%c.%)]%| $ %)}", aa3,
3274                [`{[0001->h.e.l.l.o] $ [0002->w.o.r.l.d]}`, `{[0002->w.o.r.l.d] $ [0001->h.e.l.l.o]}`] );
3275 
3276     // issue 12135
3277     formatTest("%(%s:<%s>%|,%)", [1:2], "1:<2>");
3278     formatTest("%(%s:<%s>%|%)" , [1:2], "1:<2>");
3279 }
3280 
3281 @system unittest
3282 {
3283     class C1 { int[char] val; alias val this; this(int[char] v){ val = v; } }
3284     class C2 { int[char] val; alias val this; this(int[char] v){ val = v; }
3285                override string toString() const { return "C"; } }
3286     formatTest( new C1(['c':1, 'd':2]), [`['c':1, 'd':2]`, `['d':2, 'c':1]`] );
3287     formatTest( new C2(['c':1, 'd':2]), "C" );
3288 
3289     struct S1 { int[char] val; alias val this; }
3290     struct S2 { int[char] val; alias val this;
3291                 string toString() const { return "S"; } }
3292     formatTest( S1(['c':1, 'd':2]), [`['c':1, 'd':2]`, `['d':2, 'c':1]`] );
3293     formatTest( S2(['c':1, 'd':2]), "S" );
3294 }
3295 
3296 @safe unittest  // Issue 8921
3297 {
3298     enum E : char { A = 'a', B = 'b', C = 'c' }
3299     E[3] e = [E.A, E.B, E.C];
3300     formatTest(e, "[A, B, C]");
3301 
3302     E[] e2 = [E.A, E.B, E.C];
3303     formatTest(e2, "[A, B, C]");
3304 }
3305 
3306 template hasToString(T, Char)
3307 {
3308     static if (isPointer!T && !isAggregateType!T)
3309     {
3310         // X* does not have toString, even if X is aggregate type has toString.
3311         enum hasToString = 0;
3312     }
3313     else static if (is(typeof({ T val = void; FormatSpec!Char f; val.toString((const(char)[] s){}, f); })))
3314     {
3315         enum hasToString = 4;
3316     }
3317     else static if (is(typeof({ T val = void; val.toString((const(char)[] s){}, "%s"); })))
3318     {
3319         enum hasToString = 3;
3320     }
3321     else static if (is(typeof({ T val = void; val.toString((const(char)[] s){}); })))
3322     {
3323         enum hasToString = 2;
3324     }
3325     else static if (is(typeof({ T val = void; return val.toString(); }()) S) && isSomeString!S)
3326     {
3327         enum hasToString = 1;
3328     }
3329     else
3330     {
3331         enum hasToString = 0;
3332     }
3333 }
3334 
3335 // object formatting with toString
3336 private void formatObject(Writer, T, Char)(ref Writer w, ref T val, const ref FormatSpec!Char f)
3337 if (hasToString!(T, Char))
3338 {
3339     static if (is(typeof(val.toString((const(char)[] s){}, f))))
3340     {
3341         val.toString((const(char)[] s) { put(w, s); }, f);
3342     }
3343     else static if (is(typeof(val.toString((const(char)[] s){}, "%s"))))
3344     {
3345         val.toString((const(char)[] s) { put(w, s); }, f.getCurFmtStr());
3346     }
3347     else static if (is(typeof(val.toString((const(char)[] s){}))))
3348     {
3349         val.toString((const(char)[] s) { put(w, s); });
3350     }
3351     else static if (is(typeof(val.toString()) S) && isSomeString!S)
3352     {
3353         put(w, val.toString());
3354     }
3355     else
3356         static assert(0);
3357 }
3358 
3359 void enforceValidFormatSpec(T, Char)(const ref FormatSpec!Char f)
3360 {
3361     static if (!isInputRange!T && hasToString!(T, Char) != 4)
3362     {
3363         enforceFmt(f.spec == 's',
3364             "Expected '%s' format specifier for type '" ~ T.stringof ~ "'");
3365     }
3366 }
3367 
3368 @system unittest
3369 {
3370     static interface IF1 { }
3371     class CIF1 : IF1 { }
3372     static struct SF1 { }
3373     static union UF1 { }
3374     static class CF1 { }
3375 
3376     static interface IF2 { string toString(); }
3377     static class CIF2 : IF2 { override string toString() { return ""; } }
3378     static struct SF2 { string toString() { return ""; } }
3379     static union UF2 { string toString() { return ""; } }
3380     static class CF2 { override string toString() { return ""; } }
3381 
3382     static interface IK1 { void toString(scope void delegate(const(char)[]) sink,
3383                            FormatSpec!char) const; }
3384     static class CIK1 : IK1 { override void toString(scope void delegate(const(char)[]) sink,
3385                               FormatSpec!char) const { sink("CIK1"); } }
3386     static struct KS1 { void toString(scope void delegate(const(char)[]) sink,
3387                         FormatSpec!char) const { sink("KS1"); } }
3388 
3389     static union KU1 { void toString(scope void delegate(const(char)[]) sink,
3390                        FormatSpec!char) const { sink("KU1"); } }
3391 
3392     static class KC1 { void toString(scope void delegate(const(char)[]) sink,
3393                        FormatSpec!char) const { sink("KC1"); } }
3394 
3395     IF1 cif1 = new CIF1;
3396     assertThrown!FormatException(format("%f", cif1));
3397     assertThrown!FormatException(format("%f", SF1()));
3398     assertThrown!FormatException(format("%f", UF1()));
3399     assertThrown!FormatException(format("%f", new CF1()));
3400 
3401     IF2 cif2 = new CIF2;
3402     assertThrown!FormatException(format("%f", cif2));
3403     assertThrown!FormatException(format("%f", SF2()));
3404     assertThrown!FormatException(format("%f", UF2()));
3405     assertThrown!FormatException(format("%f", new CF2()));
3406 
3407     IK1 cik1 = new CIK1;
3408     assert(format("%f", cik1) == "CIK1");
3409     assert(format("%f", KS1()) == "KS1");
3410     assert(format("%f", KU1()) == "KU1");
3411     assert(format("%f", new KC1()) == "KC1");
3412 }
3413 
3414 /**
3415    Aggregates ($(D struct), $(D union), $(D class), and $(D interface)) are
3416    basically formatted by calling $(D toString).
3417    $(D toString) should have one of the following signatures:
3418 
3419 ---
3420 const void toString(scope void delegate(const(char)[]) sink, FormatSpec fmt);
3421 const void toString(scope void delegate(const(char)[]) sink, string fmt);
3422 const void toString(scope void delegate(const(char)[]) sink);
3423 const string toString();
3424 ---
3425 
3426    For the class objects which have input range interface,
3427    $(UL $(LI If the instance $(D toString) has overridden
3428              $(D Object.toString), it is used.)
3429         $(LI Otherwise, the objects are formatted as input range.))
3430 
3431    For the struct and union objects which does not have $(D toString),
3432    $(UL $(LI If they have range interface, formatted as input range.)
3433         $(LI Otherwise, they are formatted like $(D Type(field1, filed2, ...)).))
3434 
3435    Otherwise, are formatted just as their type name.
3436  */
3437 void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
3438 if (is(T == class) && !is(T == enum))
3439 {
3440     enforceValidFormatSpec!(T, Char)(f);
3441     // TODO: Change this once toString() works for shared objects.
3442     static assert(!is(T == shared), "unable to format shared objects");
3443 
3444     if (val is null)
3445         put(w, "null");
3446     else
3447     {
3448         static if (hasToString!(T, Char) > 1 || (!isInputRange!T && !is(BuiltinTypeOf!T)))
3449         {
3450             formatObject!(Writer, T, Char)(w, val, f);
3451         }
3452         else
3453         {
3454           //string delegate() dg = &val.toString;
3455             Object o = val;     // workaround
3456             string delegate() dg = &o.toString;
3457             if (dg.funcptr != &Object.toString) // toString is overridden
3458             {
3459                 formatObject(w, val, f);
3460             }
3461             else static if (isInputRange!T)
3462             {
3463                 formatRange(w, val, f);
3464             }
3465             else static if (is(BuiltinTypeOf!T X))
3466             {
3467                 X x = val;
3468                 formatValue(w, x, f);
3469             }
3470             else
3471             {
3472                 formatObject(w, val, f);
3473             }
3474         }
3475     }
3476 }
3477 
3478 /++
3479    $(D formatValue) allows to reuse existing format specifiers:
3480  +/
3481 @system unittest
3482 {
3483    import std.format;
3484 
3485    struct Point
3486    {
3487        int x, y;
3488 
3489        void toString(scope void delegate(const(char)[]) sink,
3490                      FormatSpec!char fmt) const
3491        {
3492            sink("(");
3493            sink.formatValue(x, fmt);
3494            sink(",");
3495            sink.formatValue(y, fmt);
3496            sink(")");
3497        }
3498    }
3499 
3500    auto p = Point(16,11);
3501    assert(format("%03d", p) == "(016,011)");
3502    assert(format("%02x", p) == "(10,0b)");
3503 }
3504 
3505 /++
3506    The following code compares the use of $(D formatValue) and $(D formattedWrite).
3507  +/
3508 @safe pure unittest
3509 {
3510    import std.array : appender;
3511    import std.format;
3512 
3513    auto writer1 = appender!string();
3514    writer1.formattedWrite("%08b", 42);
3515 
3516    auto writer2 = appender!string();
3517    auto f = singleSpec("%08b");
3518    writer2.formatValue(42, f);
3519 
3520    assert(writer1.data == writer2.data && writer1.data == "00101010");
3521 }
3522 
3523 @system unittest
3524 {
3525     import std.array : appender;
3526     import std.range.interfaces;
3527     // class range (issue 5154)
3528     auto c = inputRangeObject([1,2,3,4]);
3529     formatTest( c, "[1, 2, 3, 4]" );
3530     assert(c.empty);
3531     c = null;
3532     formatTest( c, "null" );
3533 }
3534 
3535 @system unittest
3536 {
3537     // 5354
3538     // If the class has both range I/F and custom toString, the use of custom
3539     // toString routine is prioritized.
3540 
3541     // Enable the use of custom toString that gets a sink delegate
3542     // for class formatting.
3543 
3544     enum inputRangeCode =
3545     q{
3546         int[] arr;
3547         this(int[] a){ arr = a; }
3548         @property int front() const { return arr[0]; }
3549         @property bool empty() const { return arr.length == 0; }
3550         void popFront(){ arr = arr[1..$]; }
3551     };
3552 
3553     class C1
3554     {
3555         mixin(inputRangeCode);
3556         void toString(scope void delegate(const(char)[]) dg, const ref FormatSpec!char f) const { dg("[012]"); }
3557     }
3558     class C2
3559     {
3560         mixin(inputRangeCode);
3561         void toString(scope void delegate(const(char)[]) dg, string f) const { dg("[012]"); }
3562     }
3563     class C3
3564     {
3565         mixin(inputRangeCode);
3566         void toString(scope void delegate(const(char)[]) dg) const { dg("[012]"); }
3567     }
3568     class C4
3569     {
3570         mixin(inputRangeCode);
3571         override string toString() const { return "[012]"; }
3572     }
3573     class C5
3574     {
3575         mixin(inputRangeCode);
3576     }
3577 
3578     formatTest( new C1([0, 1, 2]), "[012]" );
3579     formatTest( new C2([0, 1, 2]), "[012]" );
3580     formatTest( new C3([0, 1, 2]), "[012]" );
3581     formatTest( new C4([0, 1, 2]), "[012]" );
3582     formatTest( new C5([0, 1, 2]), "[0, 1, 2]" );
3583 }
3584 
3585 /// ditto
3586 void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
3587 if (is(T == interface) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is(T == enum))
3588 {
3589     enforceValidFormatSpec!(T, Char)(f);
3590     if (val is null)
3591         put(w, "null");
3592     else
3593     {
3594         static if (hasToString!(T, Char))
3595         {
3596             formatObject(w, val, f);
3597         }
3598         else static if (isInputRange!T)
3599         {
3600             formatRange(w, val, f);
3601         }
3602         else
3603         {
3604             version (Windows)
3605             {
3606                 import core.sys.windows.com : IUnknown;
3607                 static if (is(T : IUnknown))
3608                 {
3609                     formatValue(w, *cast(void**)&val, f);
3610                 }
3611                 else
3612                 {
3613                     formatValue(w, cast(Object) val, f);
3614                 }
3615             }
3616             else
3617             {
3618                 formatValue(w, cast(Object) val, f);
3619             }
3620         }
3621     }
3622 }
3623 
3624 @system unittest
3625 {
3626     // interface
3627     import std.range.interfaces;
3628     InputRange!int i = inputRangeObject([1,2,3,4]);
3629     formatTest( i, "[1, 2, 3, 4]" );
3630     assert(i.empty);
3631     i = null;
3632     formatTest( i, "null" );
3633 
3634     // interface (downcast to Object)
3635     interface Whatever {}
3636     class C : Whatever
3637     {
3638         override @property string toString() const { return "ab"; }
3639     }
3640     Whatever val = new C;
3641     formatTest( val, "ab" );
3642 
3643     // Issue 11175
3644     version (Windows)
3645     {
3646         import core.sys.windows.com : IUnknown, IID;
3647         import core.sys.windows.windows : HRESULT;
3648 
3649         interface IUnknown2 : IUnknown { }
3650 
3651         class D : IUnknown2
3652         {
3653             extern(Windows) HRESULT QueryInterface(const(IID)* riid, void** pvObject) { return typeof(return).init; }
3654             extern(Windows) uint AddRef() { return 0; }
3655             extern(Windows) uint Release() { return 0; }
3656         }
3657 
3658         IUnknown2 d = new D;
3659         string expected = format("%X", cast(void*) d);
3660         formatTest(d, expected);
3661     }
3662 }
3663 
3664 /// ditto
3665 // Maybe T is noncopyable struct, so receive it by 'auto ref'.
3666 void formatValue(Writer, T, Char)(auto ref Writer w, auto ref T val, const ref FormatSpec!Char f)
3667 if ((is(T == struct) || is(T == union)) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is(T == enum))
3668 {
3669     enforceValidFormatSpec!(T, Char)(f);
3670     static if (hasToString!(T, Char))
3671     {
3672         formatObject(w, val, f);
3673     }
3674     else static if (isInputRange!T)
3675     {
3676         formatRange(w, val, f);
3677     }
3678     else static if (is(T == struct))
3679     {
3680         enum left = T.stringof~"(";
3681         enum separator = ", ";
3682         enum right = ")";
3683 
3684         put(w, left);
3685         foreach (i, e; val.tupleof)
3686         {
3687             static if (0 < i && val.tupleof[i-1].offsetof == val.tupleof[i].offsetof)
3688             {
3689                 static if (i == val.tupleof.length - 1 || val.tupleof[i].offsetof != val.tupleof[i+1].offsetof)
3690                     put(w, separator~val.tupleof[i].stringof[4..$]~"}");
3691                 else
3692                     put(w, separator~val.tupleof[i].stringof[4..$]);
3693             }
3694             else
3695             {
3696                 static if (i+1 < val.tupleof.length && val.tupleof[i].offsetof == val.tupleof[i+1].offsetof)
3697                     put(w, (i > 0 ? separator : "")~"#{overlap "~val.tupleof[i].stringof[4..$]);
3698                 else
3699                 {
3700                     static if (i > 0)
3701                         put(w, separator);
3702                     formatElement(w, e, f);
3703                 }
3704             }
3705         }
3706         put(w, right);
3707     }
3708     else
3709     {
3710         put(w, T.stringof);
3711     }
3712 }
3713 
3714 @safe unittest
3715 {
3716     // bug 4638
3717     struct U8  {  string toString() const { return "blah"; } }
3718     struct U16 { wstring toString() const { return "blah"; } }
3719     struct U32 { dstring toString() const { return "blah"; } }
3720     formatTest( U8(), "blah" );
3721     formatTest( U16(), "blah" );
3722     formatTest( U32(), "blah" );
3723 }
3724 
3725 @safe unittest
3726 {
3727     // 3890
3728     struct Int{ int n; }
3729     struct Pair{ string s; Int i; }
3730     formatTest( Pair("hello", Int(5)),
3731                 `Pair("hello", Int(5))` );
3732 }
3733 
3734 @system unittest
3735 {
3736     // union formatting without toString
3737     union U1
3738     {
3739         int n;
3740         string s;
3741     }
3742     U1 u1;
3743     formatTest( u1, "U1" );
3744 
3745     // union formatting with toString
3746     union U2
3747     {
3748         int n;
3749         string s;
3750         string toString() const { return s; }
3751     }
3752     U2 u2;
3753     u2.s = "hello";
3754     formatTest( u2, "hello" );
3755 }
3756 
3757 @system unittest
3758 {
3759     import std.array;
3760     // 7230
3761     static struct Bug7230
3762     {
3763         string s = "hello";
3764         union {
3765             string a;
3766             int b;
3767             double c;
3768         }
3769         long x = 10;
3770     }
3771 
3772     Bug7230 bug;
3773     bug.b = 123;
3774 
3775     FormatSpec!char f;
3776     auto w = appender!(char[])();
3777     formatValue(w, bug, f);
3778     assert(w.data == `Bug7230("hello", #{overlap a, b, c}, 10)`);
3779 }
3780 
3781 @safe unittest
3782 {
3783     import std.array;
3784     static struct S{ @disable this(this); }
3785     S s;
3786 
3787     FormatSpec!char f;
3788     auto w = appender!string();
3789     formatValue(w, s, f);
3790     assert(w.data == "S()");
3791 }
3792 
3793 /**
3794 $(D enum) is formatted like its base value.
3795 
3796 Params:
3797     w = The $(D OutputRange) to write to.
3798     val = The value to write.
3799     f = The $(D FormatSpec) defining how to write the value.
3800  */
3801 void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
3802 if (is(T == enum))
3803 {
3804     if (f.spec == 's')
3805     {
3806         foreach (i, e; EnumMembers!T)
3807         {
3808             if (val == e)
3809             {
3810                 formatValue(w, __traits(allMembers, T)[i], f);
3811                 return;
3812             }
3813         }
3814 
3815         // val is not a member of T, output cast(T) rawValue instead.
3816         put(w, "cast(" ~ T.stringof ~ ")");
3817         static assert(!is(OriginalType!T == T));
3818     }
3819     formatValue(w, cast(OriginalType!T) val, f);
3820 }
3821 
3822 ///
3823 @safe pure unittest
3824 {
3825     import std.array : appender;
3826     auto w = appender!string();
3827     auto spec = singleSpec("%s");
3828 
3829     enum A { first, second, third }
3830 
3831     formatElement(w, A.second, spec);
3832 
3833     assert(w.data == "second");
3834 }
3835 
3836 @safe unittest
3837 {
3838     enum A { first, second, third }
3839     formatTest( A.second, "second" );
3840     formatTest( cast(A) 72, "cast(A)72" );
3841 }
3842 @safe unittest
3843 {
3844     enum A : string { one = "uno", two = "dos", three = "tres" }
3845     formatTest( A.three, "three" );
3846     formatTest( cast(A)"mill\&oacute;n", "cast(A)mill\&oacute;n" );
3847 }
3848 @safe unittest
3849 {
3850     enum A : bool { no, yes }
3851     formatTest( A.yes, "yes" );
3852     formatTest( A.no, "no" );
3853 }
3854 @safe unittest
3855 {
3856     // Test for bug 6892
3857     enum Foo { A = 10 }
3858     formatTest("%s",    Foo.A, "A");
3859     formatTest(">%4s<", Foo.A, ">   A<");
3860     formatTest("%04d",  Foo.A, "0010");
3861     formatTest("%+2u",  Foo.A, "+10");
3862     formatTest("%02x",  Foo.A, "0a");
3863     formatTest("%3o",   Foo.A, " 12");
3864     formatTest("%b",    Foo.A, "1010");
3865 }
3866 
3867 /**
3868    Pointers are formatted as hex integers.
3869  */
3870 void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
3871 if (isPointer!T && !is(T == enum) && !hasToString!(T, Char))
3872 {
3873     static if (isInputRange!T)
3874     {
3875         if (val !is null)
3876         {
3877             formatRange(w, *val, f);
3878             return;
3879         }
3880     }
3881 
3882     static if (is(typeof({ shared const void* p = val; })))
3883         alias SharedOf(T) = shared(T);
3884     else
3885         alias SharedOf(T) = T;
3886 
3887     const SharedOf!(void*) p = val;
3888     const pnum = ()@trusted{ return cast(ulong) p; }();
3889 
3890     if (f.spec == 's')
3891     {
3892         if (p is null)
3893         {
3894             put(w, "null");
3895             return;
3896         }
3897         FormatSpec!Char fs = f; // fs is copy for change its values.
3898         fs.spec = 'X';
3899         formatValue(w, pnum, fs);
3900     }
3901     else
3902     {
3903         enforceFmt(f.spec == 'X' || f.spec == 'x',
3904            "Expected one of %s, %x or %X for pointer type.");
3905         formatValue(w, pnum, f);
3906     }
3907 }
3908 
3909 @safe pure unittest
3910 {
3911     // pointer
3912     import std.range;
3913     auto r = retro([1,2,3,4]);
3914     auto p = ()@trusted{ auto p = &r; return p; }();
3915     formatTest( p, "[4, 3, 2, 1]" );
3916     assert(p.empty);
3917     p = null;
3918     formatTest( p, "null" );
3919 
3920     auto q = ()@trusted{ return cast(void*) 0xFFEECCAA; }();
3921     formatTest( q, "FFEECCAA" );
3922 }
3923 
3924 @system pure unittest
3925 {
3926     // Test for issue 7869
3927     struct S
3928     {
3929         string toString() const { return ""; }
3930     }
3931     S* p = null;
3932     formatTest( p, "null" );
3933 
3934     S* q = cast(S*) 0xFFEECCAA;
3935     formatTest( q, "FFEECCAA" );
3936 }
3937 
3938 @system unittest
3939 {
3940     // Test for issue 8186
3941     class B
3942     {
3943         int*a;
3944         this(){ a = new int; }
3945         alias a this;
3946     }
3947     formatTest( B.init, "null" );
3948 }
3949 
3950 @system pure unittest
3951 {
3952     // Test for issue 9336
3953     shared int i;
3954     format("%s", &i);
3955 }
3956 
3957 @system pure unittest
3958 {
3959     // Test for issue 11778
3960     int* p = null;
3961     assertThrown(format("%d", p));
3962     assertThrown(format("%04d", p + 2));
3963 }
3964 
3965 @safe pure unittest
3966 {
3967     // Test for issue 12505
3968     void* p = null;
3969     formatTest( "%08X", p, "00000000" );
3970 }
3971 
3972 /**
3973    Delegates are formatted by 'ReturnType delegate(Parameters) FunctionAttributes'
3974  */
3975 void formatValue(Writer, T, Char)(auto ref Writer w, scope T, const ref FormatSpec!Char f)
3976 if (isDelegate!T)
3977 {
3978     formatValue(w, T.stringof, f);
3979 }
3980 
3981 ///
3982 @safe pure unittest
3983 {
3984     import std.conv : to;
3985 
3986     int i;
3987 
3988     int foo(short k) @nogc
3989     {
3990         return i + k;
3991     }
3992 
3993     @system int delegate(short) @nogc bar() nothrow pure
3994     {
3995         int* p = new int;
3996         return &foo;
3997     }
3998 
3999     assert(to!string(&bar) == "int delegate(short) @nogc delegate() pure nothrow @system");
4000 }
4001 
4002 @safe unittest
4003 {
4004     void func() @system { __gshared int x; ++x; throw new Exception("msg"); }
4005     version (linux) formatTest( &func, "void delegate() @system" );
4006 }
4007 
4008 @safe pure unittest
4009 {
4010     int[] a = [ 1, 3, 2 ];
4011     formatTest( "testing %(%s & %) embedded", a,
4012                 "testing 1 & 3 & 2 embedded");
4013     formatTest( "testing %((%s) %)) wyda3", a,
4014                 "testing (1) (3) (2) wyda3" );
4015 
4016     int[0] empt = [];
4017     formatTest( "(%s)", empt,
4018                 "([])" );
4019 }
4020 
4021 //------------------------------------------------------------------------------
4022 // Fix for issue 1591
4023 private int getNthInt(string kind, A...)(uint index, A args)
4024 {
4025     return getNth!(kind, isIntegral,int)(index, args);
4026 }
4027 
4028 private T getNth(string kind, alias Condition, T, A...)(uint index, A args)
4029 {
4030     import std.conv : text, to;
4031 
4032     switch (index)
4033     {
4034         foreach (n, _; A)
4035         {
4036             case n:
4037                 static if (Condition!(typeof(args[n])))
4038                 {
4039                     return to!T(args[n]);
4040                 }
4041                 else
4042                 {
4043                     throw new FormatException(
4044                         text(kind, " expected, not ", typeof(args[n]).stringof,
4045                             " for argument #", index + 1));
4046                 }
4047         }
4048         default:
4049             throw new FormatException(
4050                 text("Missing ", kind, " argument"));
4051     }
4052 }
4053 
4054 @safe unittest
4055 {
4056     // width/precision
4057     assert(collectExceptionMsg!FormatException(format("%*.d", 5.1, 2))
4058         == "integer width expected, not double for argument #1");
4059     assert(collectExceptionMsg!FormatException(format("%-1*.d", 5.1, 2))
4060         == "integer width expected, not double for argument #1");
4061 
4062     assert(collectExceptionMsg!FormatException(format("%.*d", '5', 2))
4063         == "integer precision expected, not char for argument #1");
4064     assert(collectExceptionMsg!FormatException(format("%-1.*d", 4.7, 3))
4065         == "integer precision expected, not double for argument #1");
4066     assert(collectExceptionMsg!FormatException(format("%.*d", 5))
4067         == "Orphan format specifier: %d");
4068     assert(collectExceptionMsg!FormatException(format("%*.*d", 5))
4069         == "Missing integer precision argument");
4070 
4071     // separatorCharPos
4072     assert(collectExceptionMsg!FormatException(format("%,?d", 5))
4073         == "separator character expected, not int for argument #1");
4074     assert(collectExceptionMsg!FormatException(format("%,?d", '?'))
4075         == "Orphan format specifier: %d");
4076     assert(collectExceptionMsg!FormatException(format("%.*,*?d", 5))
4077         == "Missing separator digit width argument");
4078 }
4079 
4080 /* ======================== Unit Tests ====================================== */
4081 
4082 version (unittest)
4083 void formatTest(T)(T val, string expected, size_t ln = __LINE__, string fn = __FILE__)
4084 {
4085     import core.exception : AssertError;
4086     import std.array : appender;
4087     import std.conv : text;
4088     FormatSpec!char f;
4089     auto w = appender!string();
4090     formatValue(w, val, f);
4091     enforce!AssertError(
4092             w.data == expected,
4093             text("expected = `", expected, "`, result = `", w.data, "`"), fn, ln);
4094 }
4095 
4096 version (unittest)
4097 void formatTest(T)(string fmt, T val, string expected, size_t ln = __LINE__, string fn = __FILE__) @safe
4098 {
4099     import core.exception : AssertError;
4100     import std.array : appender;
4101     import std.conv : text;
4102     auto w = appender!string();
4103     formattedWrite(w, fmt, val);
4104     enforce!AssertError(
4105             w.data == expected,
4106             text("expected = `", expected, "`, result = `", w.data, "`"), fn, ln);
4107 }
4108 
4109 version (unittest)
4110 void formatTest(T)(T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__)
4111 {
4112     import core.exception : AssertError;
4113     import std.array : appender;
4114     import std.conv : text;
4115     FormatSpec!char f;
4116     auto w = appender!string();
4117     formatValue(w, val, f);
4118     foreach (cur; expected)
4119     {
4120         if (w.data == cur) return;
4121     }
4122     enforce!AssertError(
4123             false,
4124             text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln);
4125 }
4126 
4127 version (unittest)
4128 void formatTest(T)(string fmt, T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__) @safe
4129 {
4130     import core.exception : AssertError;
4131     import std.array : appender;
4132     import std.conv : text;
4133     auto w = appender!string();
4134     formattedWrite(w, fmt, val);
4135     foreach (cur; expected)
4136     {
4137         if (w.data == cur) return;
4138     }
4139     enforce!AssertError(
4140             false,
4141             text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln);
4142 }
4143 
4144 @safe /*pure*/ unittest     // formatting floating point values is now impure
4145 {
4146     import std.array;
4147 
4148     auto stream = appender!string();
4149     formattedWrite(stream, "%s", 1.1);
4150     assert(stream.data == "1.1", stream.data);
4151 }
4152 
4153 @safe pure unittest
4154 {
4155     import std.algorithm;
4156     import std.array;
4157 
4158     auto stream = appender!string();
4159     formattedWrite(stream, "%s", map!"a*a"([2, 3, 5]));
4160     assert(stream.data == "[4, 9, 25]", stream.data);
4161 
4162     // Test shared data.
4163     stream = appender!string();
4164     shared int s = 6;
4165     formattedWrite(stream, "%s", s);
4166     assert(stream.data == "6");
4167 }
4168 
4169 @safe pure unittest
4170 {
4171     import std.array;
4172     auto stream = appender!string();
4173     formattedWrite(stream, "%u", 42);
4174     assert(stream.data == "42", stream.data);
4175 }
4176 
4177 @safe pure unittest
4178 {
4179     // testing raw writes
4180     import std.array;
4181     auto w = appender!(char[])();
4182     uint a = 0x02030405;
4183     formattedWrite(w, "%+r", a);
4184     assert(w.data.length == 4 && w.data[0] == 2 && w.data[1] == 3
4185         && w.data[2] == 4 && w.data[3] == 5);
4186     w.clear();
4187     formattedWrite(w, "%-r", a);
4188     assert(w.data.length == 4 && w.data[0] == 5 && w.data[1] == 4
4189         && w.data[2] == 3 && w.data[3] == 2);
4190 }
4191 
4192 @safe pure unittest
4193 {
4194     // testing positional parameters
4195     import std.array;
4196     auto w = appender!(char[])();
4197     formattedWrite(w,
4198             "Numbers %2$s and %1$s are reversed and %1$s%2$s repeated",
4199             42, 0);
4200     assert(w.data == "Numbers 0 and 42 are reversed and 420 repeated",
4201             w.data);
4202     assert(collectExceptionMsg!FormatException(formattedWrite(w, "%1$s, %3$s", 1, 2))
4203         == "Positional specifier %3$s index exceeds 2");
4204 
4205     w.clear();
4206     formattedWrite(w, "asd%s", 23);
4207     assert(w.data == "asd23", w.data);
4208     w.clear();
4209     formattedWrite(w, "%s%s", 23, 45);
4210     assert(w.data == "2345", w.data);
4211 }
4212 
4213 @safe unittest
4214 {
4215     import core.stdc.string : strlen;
4216     import std.array : appender;
4217     import std.conv : text, octal;
4218     import core.stdc.stdio : snprintf;
4219 
4220     debug(format) printf("std.format.format.unittest\n");
4221 
4222     auto stream = appender!(char[])();
4223     //goto here;
4224 
4225     formattedWrite(stream,
4226             "hello world! %s %s ", true, 57, 1_000_000_000, 'x', " foo");
4227     assert(stream.data == "hello world! true 57 ",
4228         stream.data);
4229 
4230     stream.clear();
4231     formattedWrite(stream, "%g %A %s", 1.67, -1.28, float.nan);
4232     // core.stdc.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr);
4233 
4234     /* The host C library is used to format floats.  C99 doesn't
4235     * specify what the hex digit before the decimal point is for
4236     * %A.  */
4237 
4238     version (CRuntime_Glibc)
4239     {
4240         assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan",
4241                 stream.data);
4242     }
4243     else version (OSX)
4244     {
4245         assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan",
4246                 stream.data);
4247     }
4248     else version (MinGW)
4249     {
4250         assert(stream.data == "1.67 -0XA.3D70A3D70A3D8P-3 nan",
4251                 stream.data);
4252     }
4253     else version (CRuntime_Microsoft)
4254     {
4255         assert(stream.data == "1.67 -0X1.47AE14P+0 nan"
4256             || stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", // MSVCRT 14+ (VS 2015)
4257                 stream.data);
4258     }
4259     else
4260     {
4261         assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan",
4262                 stream.data);
4263     }
4264     stream.clear();
4265 
4266     formattedWrite(stream, "%x %X", 0x1234AF, 0xAFAFAFAF);
4267     assert(stream.data == "1234af AFAFAFAF");
4268     stream.clear();
4269 
4270     formattedWrite(stream, "%b %o", 0x1234AF, 0xAFAFAFAF);
4271     assert(stream.data == "100100011010010101111 25753727657");
4272     stream.clear();
4273 
4274     formattedWrite(stream, "%d %s", 0x1234AF, 0xAFAFAFAF);
4275     assert(stream.data == "1193135 2947526575");
4276     stream.clear();
4277 
4278     // formattedWrite(stream, "%s", 1.2 + 3.4i);
4279     // assert(stream.data == "1.2+3.4i");
4280     // stream.clear();
4281 
4282     formattedWrite(stream, "%a %A", 1.32, 6.78f);
4283     //formattedWrite(stream, "%x %X", 1.32);
4284     version (CRuntime_Microsoft)
4285         assert(stream.data == "0x1.51eb85p+0 0X1.B1EB86P+2"
4286             || stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB860000000P+2"); // MSVCRT 14+ (VS 2015)
4287     else
4288         assert(stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB86P+2");
4289     stream.clear();
4290 
4291     formattedWrite(stream, "%#06.*f",2,12.345);
4292     assert(stream.data == "012.35");
4293     stream.clear();
4294 
4295     formattedWrite(stream, "%#0*.*f",6,2,12.345);
4296     assert(stream.data == "012.35");
4297     stream.clear();
4298 
4299     const real constreal = 1;
4300     formattedWrite(stream, "%g",constreal);
4301     assert(stream.data == "1");
4302     stream.clear();
4303 
4304     formattedWrite(stream, "%7.4g:", 12.678);
4305     assert(stream.data == "  12.68:");
4306     stream.clear();
4307 
4308     formattedWrite(stream, "%7.4g:", 12.678L);
4309     assert(stream.data == "  12.68:");
4310     stream.clear();
4311 
4312     formattedWrite(stream, "%04f|%05d|%#05x|%#5x",-4.0,-10,1,1);
4313     assert(stream.data == "-4.000000|-0010|0x001|  0x1",
4314             stream.data);
4315     stream.clear();
4316 
4317     int i;
4318     string s;
4319 
4320     i = -10;
4321     formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
4322     assert(stream.data == "-10|-10|-10|-10|-10.0000");
4323     stream.clear();
4324 
4325     i = -5;
4326     formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
4327     assert(stream.data == "-5| -5|-05|-5|-5.0000");
4328     stream.clear();
4329 
4330     i = 0;
4331     formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
4332     assert(stream.data == "0|  0|000|0|0.0000");
4333     stream.clear();
4334 
4335     i = 5;
4336     formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
4337     assert(stream.data == "5|  5|005|5|5.0000");
4338     stream.clear();
4339 
4340     i = 10;
4341     formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
4342     assert(stream.data == "10| 10|010|10|10.0000");
4343     stream.clear();
4344 
4345     formattedWrite(stream, "%.0d", 0);
4346     assert(stream.data == "");
4347     stream.clear();
4348 
4349     formattedWrite(stream, "%.g", .34);
4350     assert(stream.data == "0.3");
4351     stream.clear();
4352 
4353     stream.clear(); formattedWrite(stream, "%.0g", .34);
4354     assert(stream.data == "0.3");
4355 
4356     stream.clear(); formattedWrite(stream, "%.2g", .34);
4357     assert(stream.data == "0.34");
4358 
4359     stream.clear(); formattedWrite(stream, "%0.0008f", 1e-08);
4360     assert(stream.data == "0.00000001");
4361 
4362     stream.clear(); formattedWrite(stream, "%0.0008f", 1e-05);
4363     assert(stream.data == "0.00001000");
4364 
4365     //return;
4366     //core.stdc.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr);
4367 
4368     s = "helloworld";
4369     string r;
4370     stream.clear(); formattedWrite(stream, "%.2s", s[0 .. 5]);
4371     assert(stream.data == "he");
4372     stream.clear(); formattedWrite(stream, "%.20s", s[0 .. 5]);
4373     assert(stream.data == "hello");
4374     stream.clear(); formattedWrite(stream, "%8s", s[0 .. 5]);
4375     assert(stream.data == "   hello");
4376 
4377     byte[] arrbyte = new byte[4];
4378     arrbyte[0] = 100;
4379     arrbyte[1] = -99;
4380     arrbyte[3] = 0;
4381     stream.clear(); formattedWrite(stream, "%s", arrbyte);
4382     assert(stream.data == "[100, -99, 0, 0]", stream.data);
4383 
4384     ubyte[] arrubyte = new ubyte[4];
4385     arrubyte[0] = 100;
4386     arrubyte[1] = 200;
4387     arrubyte[3] = 0;
4388     stream.clear(); formattedWrite(stream, "%s", arrubyte);
4389     assert(stream.data == "[100, 200, 0, 0]", stream.data);
4390 
4391     short[] arrshort = new short[4];
4392     arrshort[0] = 100;
4393     arrshort[1] = -999;
4394     arrshort[3] = 0;
4395     stream.clear(); formattedWrite(stream, "%s", arrshort);
4396     assert(stream.data == "[100, -999, 0, 0]");
4397     stream.clear(); formattedWrite(stream, "%s",arrshort);
4398     assert(stream.data == "[100, -999, 0, 0]");
4399 
4400     ushort[] arrushort = new ushort[4];
4401     arrushort[0] = 100;
4402     arrushort[1] = 20_000;
4403     arrushort[3] = 0;
4404     stream.clear(); formattedWrite(stream, "%s", arrushort);
4405     assert(stream.data == "[100, 20000, 0, 0]");
4406 
4407     int[] arrint = new int[4];
4408     arrint[0] = 100;
4409     arrint[1] = -999;
4410     arrint[3] = 0;
4411     stream.clear(); formattedWrite(stream, "%s", arrint);
4412     assert(stream.data == "[100, -999, 0, 0]");
4413     stream.clear(); formattedWrite(stream, "%s",arrint);
4414     assert(stream.data == "[100, -999, 0, 0]");
4415 
4416     long[] arrlong = new long[4];
4417     arrlong[0] = 100;
4418     arrlong[1] = -999;
4419     arrlong[3] = 0;
4420     stream.clear(); formattedWrite(stream, "%s", arrlong);
4421     assert(stream.data == "[100, -999, 0, 0]");
4422     stream.clear(); formattedWrite(stream, "%s",arrlong);
4423     assert(stream.data == "[100, -999, 0, 0]");
4424 
4425     ulong[] arrulong = new ulong[4];
4426     arrulong[0] = 100;
4427     arrulong[1] = 999;
4428     arrulong[3] = 0;
4429     stream.clear(); formattedWrite(stream, "%s", arrulong);
4430     assert(stream.data == "[100, 999, 0, 0]");
4431 
4432     string[] arr2 = new string[4];
4433     arr2[0] = "hello";
4434     arr2[1] = "world";
4435     arr2[3] = "foo";
4436     stream.clear(); formattedWrite(stream, "%s", arr2);
4437     assert(stream.data == `["hello", "world", "", "foo"]`, stream.data);
4438 
4439     stream.clear(); formattedWrite(stream, "%.8d", 7);
4440     assert(stream.data == "00000007");
4441 
4442     stream.clear(); formattedWrite(stream, "%.8x", 10);
4443     assert(stream.data == "0000000a");
4444 
4445     stream.clear(); formattedWrite(stream, "%-3d", 7);
4446     assert(stream.data == "7  ");
4447 
4448     stream.clear(); formattedWrite(stream, "%*d", -3, 7);
4449     assert(stream.data == "7  ");
4450 
4451     stream.clear(); formattedWrite(stream, "%.*d", -3, 7);
4452     //writeln(stream.data);
4453     assert(stream.data == "7");
4454 
4455     stream.clear(); formattedWrite(stream, "%s", "abc"c);
4456     assert(stream.data == "abc");
4457     stream.clear(); formattedWrite(stream, "%s", "def"w);
4458     assert(stream.data == "def", text(stream.data.length));
4459     stream.clear(); formattedWrite(stream, "%s", "ghi"d);
4460     assert(stream.data == "ghi");
4461 
4462 here:
4463     @trusted void* deadBeef() { return cast(void*) 0xDEADBEEF; }
4464     stream.clear(); formattedWrite(stream, "%s", deadBeef());
4465     assert(stream.data == "DEADBEEF", stream.data);
4466 
4467     stream.clear(); formattedWrite(stream, "%#x", 0xabcd);
4468     assert(stream.data == "0xabcd");
4469     stream.clear(); formattedWrite(stream, "%#X", 0xABCD);
4470     assert(stream.data == "0XABCD");
4471 
4472     stream.clear(); formattedWrite(stream, "%#o", octal!12345);
4473     assert(stream.data == "012345");
4474     stream.clear(); formattedWrite(stream, "%o", 9);
4475     assert(stream.data == "11");
4476 
4477     stream.clear(); formattedWrite(stream, "%+d", 123);
4478     assert(stream.data == "+123");
4479     stream.clear(); formattedWrite(stream, "%+d", -123);
4480     assert(stream.data == "-123");
4481     stream.clear(); formattedWrite(stream, "% d", 123);
4482     assert(stream.data == " 123");
4483     stream.clear(); formattedWrite(stream, "% d", -123);
4484     assert(stream.data == "-123");
4485 
4486     stream.clear(); formattedWrite(stream, "%%");
4487     assert(stream.data == "%");
4488 
4489     stream.clear(); formattedWrite(stream, "%d", true);
4490     assert(stream.data == "1");
4491     stream.clear(); formattedWrite(stream, "%d", false);
4492     assert(stream.data == "0");
4493 
4494     stream.clear(); formattedWrite(stream, "%d", 'a');
4495     assert(stream.data == "97", stream.data);
4496     wchar wc = 'a';
4497     stream.clear(); formattedWrite(stream, "%d", wc);
4498     assert(stream.data == "97");
4499     dchar dc = 'a';
4500     stream.clear(); formattedWrite(stream, "%d", dc);
4501     assert(stream.data == "97");
4502 
4503     byte b = byte.max;
4504     stream.clear(); formattedWrite(stream, "%x", b);
4505     assert(stream.data == "7f");
4506     stream.clear(); formattedWrite(stream, "%x", ++b);
4507     assert(stream.data == "80");
4508     stream.clear(); formattedWrite(stream, "%x", ++b);
4509     assert(stream.data == "81");
4510 
4511     short sh = short.max;
4512     stream.clear(); formattedWrite(stream, "%x", sh);
4513     assert(stream.data == "7fff");
4514     stream.clear(); formattedWrite(stream, "%x", ++sh);
4515     assert(stream.data == "8000");
4516     stream.clear(); formattedWrite(stream, "%x", ++sh);
4517     assert(stream.data == "8001");
4518 
4519     i = int.max;
4520     stream.clear(); formattedWrite(stream, "%x", i);
4521     assert(stream.data == "7fffffff");
4522     stream.clear(); formattedWrite(stream, "%x", ++i);
4523     assert(stream.data == "80000000");
4524     stream.clear(); formattedWrite(stream, "%x", ++i);
4525     assert(stream.data == "80000001");
4526 
4527     stream.clear(); formattedWrite(stream, "%x", 10);
4528     assert(stream.data == "a");
4529     stream.clear(); formattedWrite(stream, "%X", 10);
4530     assert(stream.data == "A");
4531     stream.clear(); formattedWrite(stream, "%x", 15);
4532     assert(stream.data == "f");
4533     stream.clear(); formattedWrite(stream, "%X", 15);
4534     assert(stream.data == "F");
4535 
4536     @trusted void ObjectTest()
4537     {
4538         Object c = null;
4539         stream.clear(); formattedWrite(stream, "%s", c);
4540         assert(stream.data == "null");
4541     }
4542     ObjectTest();
4543 
4544     enum TestEnum
4545     {
4546         Value1, Value2
4547     }
4548     stream.clear(); formattedWrite(stream, "%s", TestEnum.Value2);
4549     assert(stream.data == "Value2", stream.data);
4550     stream.clear(); formattedWrite(stream, "%s", cast(TestEnum) 5);
4551     assert(stream.data == "cast(TestEnum)5", stream.data);
4552 
4553     //immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
4554     //stream.clear(); formattedWrite(stream, "%s", aa.values);
4555     //core.stdc.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr);
4556     //assert(stream.data == "[[h,e,l,l,o],[b,e,t,t,y]]");
4557     //stream.clear(); formattedWrite(stream, "%s", aa);
4558     //assert(stream.data == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]");
4559 
4560     static const dchar[] ds = ['a','b'];
4561     for (int j = 0; j < ds.length; ++j)
4562     {
4563         stream.clear(); formattedWrite(stream, " %d", ds[j]);
4564         if (j == 0)
4565             assert(stream.data == " 97");
4566         else
4567             assert(stream.data == " 98");
4568     }
4569 
4570     stream.clear(); formattedWrite(stream, "%.-3d", 7);
4571     assert(stream.data == "7", ">" ~ stream.data ~ "<");
4572 }
4573 
4574 @safe unittest
4575 {
4576     import std.array;
4577     import std.stdio;
4578 
4579     immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
4580     assert(aa[3] == "hello");
4581     assert(aa[4] == "betty");
4582 
4583     auto stream = appender!(char[])();
4584     alias AllNumerics =
4585         AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong,
4586                   float, double, real);
4587     foreach (T; AllNumerics)
4588     {
4589         T value = 1;
4590         stream.clear();
4591         formattedWrite(stream, "%s", value);
4592         assert(stream.data == "1");
4593     }
4594 
4595     stream.clear();
4596     formattedWrite(stream, "%s", aa);
4597 }
4598 
4599 @system unittest
4600 {
4601     string s = "hello!124:34.5";
4602     string a;
4603     int b;
4604     double c;
4605     formattedRead(s, "%s!%s:%s", &a, &b, &c);
4606     assert(a == "hello" && b == 124 && c == 34.5);
4607 }
4608 
4609 version (unittest)
4610 void formatReflectTest(T)(ref T val, string fmt, string formatted, string fn = __FILE__, size_t ln = __LINE__)
4611 {
4612     import core.exception : AssertError;
4613     import std.array : appender;
4614     auto w = appender!string();
4615     formattedWrite(w, fmt, val);
4616 
4617     auto input = w.data;
4618     enforce!AssertError(
4619             input == formatted,
4620             input, fn, ln);
4621 
4622     T val2;
4623     formattedRead(input, fmt, &val2);
4624     static if (isAssociativeArray!T)
4625     if (__ctfe)
4626     {
4627         alias aa1 = val;
4628         alias aa2 = val2;
4629         assert(aa1 == aa2);
4630 
4631         assert(aa1.length == aa2.length);
4632 
4633         assert(aa1.keys == aa2.keys);
4634 
4635         assert(aa1.values == aa2.values);
4636         assert(aa1.values.length == aa2.values.length);
4637         foreach (i; 0 .. aa1.values.length)
4638             assert(aa1.values[i] == aa2.values[i]);
4639 
4640         foreach (i, key; aa1.keys)
4641             assert(aa1.values[i] == aa1[key]);
4642         foreach (i, key; aa2.keys)
4643             assert(aa2.values[i] == aa2[key]);
4644         return;
4645     }
4646     enforce!AssertError(
4647             val == val2,
4648             input, fn, ln);
4649 }
4650 
4651 version (unittest)
4652 void formatReflectTest(T)(ref T val, string fmt, string[] formatted, string fn = __FILE__, size_t ln = __LINE__)
4653 {
4654     import core.exception : AssertError;
4655     import std.array : appender;
4656     auto w = appender!string();
4657     formattedWrite(w, fmt, val);
4658 
4659     auto input = w.data;
4660 
4661     foreach (cur; formatted)
4662     {
4663         if (input == cur) return;
4664     }
4665     enforce!AssertError(
4666             false,
4667             input,
4668             fn,
4669             ln);
4670 
4671     T val2;
4672     formattedRead(input, fmt, &val2);
4673     static if (isAssociativeArray!T)
4674     if (__ctfe)
4675     {
4676         alias aa1 = val;
4677         alias aa2 = val2;
4678         assert(aa1 == aa2);
4679 
4680         assert(aa1.length == aa2.length);
4681 
4682         assert(aa1.keys == aa2.keys);
4683 
4684         assert(aa1.values == aa2.values);
4685         assert(aa1.values.length == aa2.values.length);
4686         foreach (i; 0 .. aa1.values.length)
4687             assert(aa1.values[i] == aa2.values[i]);
4688 
4689         foreach (i, key; aa1.keys)
4690             assert(aa1.values[i] == aa1[key]);
4691         foreach (i, key; aa2.keys)
4692             assert(aa2.values[i] == aa2[key]);
4693         return;
4694     }
4695     enforce!AssertError(
4696             val == val2,
4697             input, fn, ln);
4698 }
4699 
4700 @system unittest
4701 {
4702     void booleanTest()
4703     {
4704         auto b = true;
4705         formatReflectTest(b, "%s",  `true`);
4706         formatReflectTest(b, "%b",  `1`);
4707         formatReflectTest(b, "%o",  `1`);
4708         formatReflectTest(b, "%d",  `1`);
4709         formatReflectTest(b, "%u",  `1`);
4710         formatReflectTest(b, "%x",  `1`);
4711     }
4712 
4713     void integerTest()
4714     {
4715         auto n = 127;
4716         formatReflectTest(n, "%s",  `127`);
4717         formatReflectTest(n, "%b",  `1111111`);
4718         formatReflectTest(n, "%o",  `177`);
4719         formatReflectTest(n, "%d",  `127`);
4720         formatReflectTest(n, "%u",  `127`);
4721         formatReflectTest(n, "%x",  `7f`);
4722     }
4723 
4724     void floatingTest()
4725     {
4726         auto f = 3.14;
4727         formatReflectTest(f, "%s",  `3.14`);
4728         version (MinGW)
4729             formatReflectTest(f, "%e",  `3.140000e+000`);
4730         else
4731             formatReflectTest(f, "%e",  `3.140000e+00`);
4732         formatReflectTest(f, "%f",  `3.140000`);
4733         formatReflectTest(f, "%g",  `3.14`);
4734     }
4735 
4736     void charTest()
4737     {
4738         auto c = 'a';
4739         formatReflectTest(c, "%s",  `a`);
4740         formatReflectTest(c, "%c",  `a`);
4741         formatReflectTest(c, "%b",  `1100001`);
4742         formatReflectTest(c, "%o",  `141`);
4743         formatReflectTest(c, "%d",  `97`);
4744         formatReflectTest(c, "%u",  `97`);
4745         formatReflectTest(c, "%x",  `61`);
4746     }
4747 
4748     void strTest()
4749     {
4750         auto s = "hello";
4751         formatReflectTest(s, "%s",                      `hello`);
4752         formatReflectTest(s, "%(%c,%)",                 `h,e,l,l,o`);
4753         formatReflectTest(s, "%(%s,%)",                 `'h','e','l','l','o'`);
4754         formatReflectTest(s, "[%(<%c>%| $ %)]",         `[<h> $ <e> $ <l> $ <l> $ <o>]`);
4755     }
4756 
4757     void daTest()
4758     {
4759         auto a = [1,2,3,4];
4760         formatReflectTest(a, "%s",                      `[1, 2, 3, 4]`);
4761         formatReflectTest(a, "[%(%s; %)]",              `[1; 2; 3; 4]`);
4762         formatReflectTest(a, "[%(<%s>%| $ %)]",         `[<1> $ <2> $ <3> $ <4>]`);
4763     }
4764 
4765     void saTest()
4766     {
4767         int[4] sa = [1,2,3,4];
4768         formatReflectTest(sa, "%s",                     `[1, 2, 3, 4]`);
4769         formatReflectTest(sa, "[%(%s; %)]",             `[1; 2; 3; 4]`);
4770         formatReflectTest(sa, "[%(<%s>%| $ %)]",        `[<1> $ <2> $ <3> $ <4>]`);
4771     }
4772 
4773     void aaTest()
4774     {
4775         auto aa = [1:"hello", 2:"world"];
4776         formatReflectTest(aa, "%s",                     [`[1:"hello", 2:"world"]`, `[2:"world", 1:"hello"]`]);
4777         formatReflectTest(aa, "[%(%s->%s, %)]",         [`[1->"hello", 2->"world"]`, `[2->"world", 1->"hello"]`]);
4778         formatReflectTest(aa, "{%([%s=%(%c%)]%|; %)}",  [`{[1=hello]; [2=world]}`, `{[2=world]; [1=hello]}`]);
4779     }
4780 
4781     import std.exception;
4782     assertCTFEable!(
4783     {
4784         booleanTest();
4785         integerTest();
4786         if (!__ctfe) floatingTest();    // snprintf
4787         charTest();
4788         strTest();
4789         daTest();
4790         saTest();
4791         aaTest();
4792         return true;
4793     });
4794 }
4795 
4796 //------------------------------------------------------------------------------
4797 private void skipData(Range, Char)(ref Range input, const ref FormatSpec!Char spec)
4798 {
4799     import std.ascii : isDigit;
4800     import std.conv : text;
4801 
4802     switch (spec.spec)
4803     {
4804         case 'c': input.popFront(); break;
4805         case 'd':
4806             if (input.front == '+' || input.front == '-') input.popFront();
4807             goto case 'u';
4808         case 'u':
4809             while (!input.empty && isDigit(input.front)) input.popFront();
4810             break;
4811         default:
4812             assert(false,
4813                     text("Format specifier not understood: %", spec.spec));
4814     }
4815 }
4816 
4817 private template acceptedSpecs(T)
4818 {
4819          static if (isIntegral!T)       enum acceptedSpecs = "bdosuxX";
4820     else static if (isFloatingPoint!T)  enum acceptedSpecs = "seEfgG";
4821     else static if (isSomeChar!T)       enum acceptedSpecs = "bcdosuxX";    // integral + 'c'
4822     else                                enum acceptedSpecs = "";
4823 }
4824 
4825 /**
4826  * Reads a value from the given _input range according to spec
4827  * and returns it as type `T`.
4828  *
4829  * Params:
4830  *     T = the type to return
4831  *     input = the _input range to read from
4832  *     spec = the `FormatSpec` to use when reading from `input`
4833  * Returns:
4834  *     A value from `input` of type `T`
4835  * Throws:
4836  *     An `Exception` if `spec` cannot read a type `T`
4837  * See_Also:
4838  *     $(REF parse, std, conv) and $(REF to, std, conv)
4839  */
4840 T unformatValue(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
4841 {
4842     return unformatValueImpl!T(input, spec);
4843 }
4844 
4845 /// Booleans
4846 @safe pure unittest
4847 {
4848     auto str = "false";
4849     auto spec = singleSpec("%s");
4850     assert(unformatValue!bool(str, spec) == false);
4851 
4852     str = "1";
4853     spec = singleSpec("%d");
4854     assert(unformatValue!bool(str, spec));
4855 }
4856 
4857 /// Null values
4858 @safe pure unittest
4859 {
4860     auto str = "null";
4861     auto spec = singleSpec("%s");
4862     assert(str.unformatValue!(typeof(null))(spec) == null);
4863 }
4864 
4865 /// Integrals
4866 @safe pure unittest
4867 {
4868     auto str = "123";
4869     auto spec = singleSpec("%s");
4870     assert(str.unformatValue!int(spec) == 123);
4871 
4872     str = "ABC";
4873     spec = singleSpec("%X");
4874     assert(str.unformatValue!int(spec) == 2748);
4875 
4876     str = "11610";
4877     spec = singleSpec("%o");
4878     assert(str.unformatValue!int(spec) == 5000);
4879 }
4880 
4881 /// Floating point numbers
4882 @safe pure unittest
4883 {
4884     import std.math : approxEqual;
4885 
4886     auto str = "123.456";
4887     auto spec = singleSpec("%s");
4888     assert(str.unformatValue!double(spec).approxEqual(123.456));
4889 }
4890 
4891 /// Character input ranges
4892 @safe pure unittest
4893 {
4894     auto str = "aaa";
4895     auto spec = singleSpec("%s");
4896     assert(str.unformatValue!char(spec) == 'a');
4897 
4898     // Using a numerical format spec reads a Unicode value from a string
4899     str = "65";
4900     spec = singleSpec("%d");
4901     assert(str.unformatValue!char(spec) == 'A');
4902 
4903     str = "41";
4904     spec = singleSpec("%x");
4905     assert(str.unformatValue!char(spec) == 'A');
4906 
4907     str = "10003";
4908     spec = singleSpec("%d");
4909     assert(str.unformatValue!dchar(spec) == '✓');
4910 }
4911 
4912 /// Arrays and static arrays
4913 @safe pure unittest
4914 {
4915     string str = "aaa";
4916     auto spec = singleSpec("%s");
4917     assert(str.unformatValue!(dchar[])(spec) == "aaa"d);
4918 
4919     str = "aaa";
4920     spec = singleSpec("%s");
4921     dchar[3] ret = ['a', 'a', 'a'];
4922     assert(str.unformatValue!(dchar[3])(spec) == ret);
4923 
4924     str = "[1, 2, 3, 4]";
4925     spec = singleSpec("%s");
4926     assert(str.unformatValue!(int[])(spec) == [1, 2, 3, 4]);
4927 
4928     str = "[1, 2, 3, 4]";
4929     spec = singleSpec("%s");
4930     int[4] ret2 = [1, 2, 3, 4];
4931     assert(str.unformatValue!(int[4])(spec) == ret2);
4932 }
4933 
4934 /// Associative arrays
4935 @safe pure unittest
4936 {
4937     auto str = `["one": 1, "two": 2]`;
4938     auto spec = singleSpec("%s");
4939     assert(str.unformatValue!(int[string])(spec) == ["one": 1, "two": 2]);
4940 }
4941 
4942 @safe pure unittest
4943 {
4944     // 7241
4945     string input = "a";
4946     auto spec = FormatSpec!char("%s");
4947     spec.readUpToNextSpec(input);
4948     auto result = unformatValue!(dchar[1])(input, spec);
4949     assert(result[0] == 'a');
4950 }
4951 
4952 private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
4953 if (isInputRange!Range && is(Unqual!T == bool))
4954 {
4955     import std.algorithm.searching : find;
4956     import std.conv : parse, text;
4957 
4958     if (spec.spec == 's') return parse!T(input);
4959 
4960     enforce(find(acceptedSpecs!long, spec.spec).length,
4961             text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
4962 
4963     return unformatValue!long(input, spec) != 0;
4964 }
4965 
4966 private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
4967 if (isInputRange!Range && is(T == typeof(null)))
4968 {
4969     import std.conv : parse, text;
4970     enforce(spec.spec == 's',
4971             text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
4972 
4973     return parse!T(input);
4974 }
4975 
4976 /// ditto
4977 private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
4978 if (isInputRange!Range && isIntegral!T && !is(T == enum) && isSomeChar!(ElementType!Range))
4979 {
4980 
4981     import std.algorithm.searching : find;
4982     import std.conv : parse, text;
4983 
4984     if (spec.spec == 'r')
4985     {
4986         static if (is(Unqual!(ElementEncodingType!Range) == char)
4987                 || is(Unqual!(ElementEncodingType!Range) == byte)
4988                 || is(Unqual!(ElementEncodingType!Range) == ubyte))
4989             return rawRead!T(input);
4990         else
4991             throw new Exception("The raw read specifier %r may only be used with narrow strings and ranges of bytes.");
4992     }
4993 
4994     enforce(find(acceptedSpecs!T, spec.spec).length,
4995             text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
4996 
4997     enforce(spec.width == 0, "Parsing integers with a width specification is not implemented");   // TODO
4998 
4999     immutable uint base =
5000         spec.spec == 'x' || spec.spec == 'X' ? 16 :
5001         spec.spec == 'o' ? 8 :
5002         spec.spec == 'b' ? 2 :
5003         spec.spec == 's' || spec.spec == 'd' || spec.spec == 'u' ? 10 : 0;
5004     assert(base != 0);
5005 
5006     return parse!T(input, base);
5007 
5008 }
5009 
5010 /// ditto
5011 private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5012 if (isFloatingPoint!T && !is(T == enum) && isInputRange!Range
5013     && isSomeChar!(ElementType!Range)&& !is(Range == enum))
5014 {
5015     import std.algorithm.searching : find;
5016     import std.conv : parse, text;
5017 
5018     if (spec.spec == 'r')
5019     {
5020         static if (is(Unqual!(ElementEncodingType!Range) == char)
5021                 || is(Unqual!(ElementEncodingType!Range) == byte)
5022                 || is(Unqual!(ElementEncodingType!Range) == ubyte))
5023             return rawRead!T(input);
5024         else
5025             throw new Exception("The raw read specifier %r may only be used with narrow strings and ranges of bytes.");
5026     }
5027 
5028     enforce(find(acceptedSpecs!T, spec.spec).length,
5029             text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
5030 
5031     return parse!T(input);
5032 }
5033 
5034 /// ditto
5035 private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5036 if (isInputRange!Range && isSomeChar!T && !is(T == enum) && isSomeChar!(ElementType!Range))
5037 {
5038     import std.algorithm.searching : find;
5039     import std.conv : to, text;
5040     if (spec.spec == 's' || spec.spec == 'c')
5041     {
5042         auto result = to!T(input.front);
5043         input.popFront();
5044         return result;
5045     }
5046     enforce(find(acceptedSpecs!T, spec.spec).length,
5047             text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
5048 
5049     static if (T.sizeof == 1)
5050         return unformatValue!ubyte(input, spec);
5051     else static if (T.sizeof == 2)
5052         return unformatValue!ushort(input, spec);
5053     else static if (T.sizeof == 4)
5054         return unformatValue!uint(input, spec);
5055     else
5056         static assert(0);
5057 }
5058 
5059 /// ditto
5060 private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5061 if (isInputRange!Range && is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum))
5062 {
5063     import std.conv : text;
5064 
5065     if (spec.spec == '(')
5066     {
5067         return unformatRange!T(input, spec);
5068     }
5069     enforce(spec.spec == 's',
5070             text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
5071 
5072     static if (isStaticArray!T)
5073     {
5074         T result;
5075         auto app = result[];
5076     }
5077     else
5078     {
5079         import std.array : appender;
5080         auto app = appender!T();
5081     }
5082     if (spec.trailing.empty)
5083     {
5084         for (; !input.empty; input.popFront())
5085         {
5086             static if (isStaticArray!T)
5087                 if (app.empty)
5088                     break;
5089             app.put(input.front);
5090         }
5091     }
5092     else
5093     {
5094         immutable end = spec.trailing.front;
5095         for (; !input.empty && input.front != end; input.popFront())
5096         {
5097             static if (isStaticArray!T)
5098                 if (app.empty)
5099                     break;
5100             app.put(input.front);
5101         }
5102     }
5103     static if (isStaticArray!T)
5104     {
5105         enforce(app.empty, "need more input");
5106         return result;
5107     }
5108     else
5109         return app.data;
5110 }
5111 
5112 /// ditto
5113 private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5114 if (isInputRange!Range && isArray!T && !is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum))
5115 {
5116     import std.conv : parse, text;
5117     if (spec.spec == '(')
5118     {
5119         return unformatRange!T(input, spec);
5120     }
5121     enforce(spec.spec == 's',
5122             text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
5123 
5124     return parse!T(input);
5125 }
5126 
5127 /// ditto
5128 private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5129 if (isInputRange!Range && isAssociativeArray!T && !is(T == enum))
5130 {
5131     import std.conv : parse, text;
5132     if (spec.spec == '(')
5133     {
5134         return unformatRange!T(input, spec);
5135     }
5136     enforce(spec.spec == 's',
5137             text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
5138 
5139     return parse!T(input);
5140 }
5141 
5142 /**
5143  * Function that performs raw reading. Used by unformatValue
5144  * for integral and float types.
5145  */
5146 private T rawRead(T, Range)(ref Range input)
5147 if (is(Unqual!(ElementEncodingType!Range) == char)
5148     || is(Unqual!(ElementEncodingType!Range) == byte)
5149     || is(Unqual!(ElementEncodingType!Range) == ubyte))
5150 {
5151     union X
5152     {
5153         ubyte[T.sizeof] raw;
5154         T typed;
5155     }
5156     X x;
5157     foreach (i; 0 .. T.sizeof)
5158     {
5159         static if (isSomeString!Range)
5160         {
5161             x.raw[i] = input[0];
5162             input = input[1 .. $];
5163         }
5164         else
5165         {
5166             // TODO: recheck this
5167             x.raw[i] = input.front;
5168             input.popFront();
5169         }
5170     }
5171     return x.typed;
5172 }
5173 
5174 //debug = unformatRange;
5175 
5176 private T unformatRange(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5177 in
5178 {
5179     assert(spec.spec == '(');
5180 }
5181 body
5182 {
5183     debug (unformatRange) printf("unformatRange:\n");
5184 
5185     T result;
5186     static if (isStaticArray!T)
5187     {
5188         size_t i;
5189     }
5190 
5191     const(Char)[] cont = spec.trailing;
5192     for (size_t j = 0; j < spec.trailing.length; ++j)
5193     {
5194         if (spec.trailing[j] == '%')
5195         {
5196             cont = spec.trailing[0 .. j];
5197             break;
5198         }
5199     }
5200     debug (unformatRange) printf("\t");
5201     debug (unformatRange) if (!input.empty) printf("input.front = %c, ", input.front);
5202     debug (unformatRange) printf("cont = %.*s\n", cast(int) cont.length, cont.ptr);
5203 
5204     bool checkEnd()
5205     {
5206         return input.empty || !cont.empty && input.front == cont.front;
5207     }
5208 
5209     if (!checkEnd())
5210     {
5211         for (;;)
5212         {
5213             auto fmt = FormatSpec!Char(spec.nested);
5214             fmt.readUpToNextSpec(input);
5215             enforce(!input.empty, "Unexpected end of input when parsing range");
5216 
5217             debug (unformatRange) printf("\t) spec = %c, front = %c ", fmt.spec, input.front);
5218             static if (isStaticArray!T)
5219             {
5220                 result[i++] = unformatElement!(typeof(T.init[0]))(input, fmt);
5221             }
5222             else static if (isDynamicArray!T)
5223             {
5224                 result ~= unformatElement!(ElementType!T)(input, fmt);
5225             }
5226             else static if (isAssociativeArray!T)
5227             {
5228                 auto key = unformatElement!(typeof(T.init.keys[0]))(input, fmt);
5229                 fmt.readUpToNextSpec(input);        // eat key separator
5230 
5231                 result[key] = unformatElement!(typeof(T.init.values[0]))(input, fmt);
5232             }
5233             debug (unformatRange) {
5234             if (input.empty) printf("-> front = [empty] ");
5235             else             printf("-> front = %c ", input.front);
5236             }
5237 
5238             static if (isStaticArray!T)
5239             {
5240                 debug (unformatRange) printf("i = %u < %u\n", i, T.length);
5241                 enforce(i <= T.length, "Too many format specifiers for static array of length %d".format(T.length));
5242             }
5243 
5244             if (spec.sep !is null)
5245                 fmt.readUpToNextSpec(input);
5246             auto sep = spec.sep !is null ? spec.sep
5247                          : fmt.trailing;
5248             debug (unformatRange) {
5249             if (!sep.empty && !input.empty) printf("-> %c, sep = %.*s\n", input.front, cast(int) sep.length, sep.ptr);
5250             else                            printf("\n");
5251             }
5252 
5253             if (checkEnd())
5254                 break;
5255 
5256             if (!sep.empty && input.front == sep.front)
5257             {
5258                 while (!sep.empty)
5259                 {
5260                     enforce(!input.empty, "Unexpected end of input when parsing range separator");
5261                     enforce(input.front == sep.front, "Unexpected character when parsing range separator");
5262                     input.popFront();
5263                     sep.popFront();
5264                 }
5265                 debug (unformatRange) printf("input.front = %c\n", input.front);
5266             }
5267         }
5268     }
5269     static if (isStaticArray!T)
5270     {
5271         enforce(i == T.length, "Too few (%d) format specifiers for static array of length %d".format(i, T.length));
5272     }
5273     return result;
5274 }
5275 
5276 // Undocumented
5277 T unformatElement(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5278 if (isInputRange!Range)
5279 {
5280     import std.conv : parseElement;
5281     static if (isSomeString!T)
5282     {
5283         if (spec.spec == 's')
5284         {
5285             return parseElement!T(input);
5286         }
5287     }
5288     else static if (isSomeChar!T)
5289     {
5290         if (spec.spec == 's')
5291         {
5292             return parseElement!T(input);
5293         }
5294     }
5295 
5296     return unformatValue!T(input, spec);
5297 }
5298 
5299 
5300 // Legacy implementation
5301 
5302 enum Mangle : char
5303 {
5304     Tvoid     = 'v',
5305     Tbool     = 'b',
5306     Tbyte     = 'g',
5307     Tubyte    = 'h',
5308     Tshort    = 's',
5309     Tushort   = 't',
5310     Tint      = 'i',
5311     Tuint     = 'k',
5312     Tlong     = 'l',
5313     Tulong    = 'm',
5314     Tfloat    = 'f',
5315     Tdouble   = 'd',
5316     Treal     = 'e',
5317 
5318     Tifloat   = 'o',
5319     Tidouble  = 'p',
5320     Tireal    = 'j',
5321     Tcfloat   = 'q',
5322     Tcdouble  = 'r',
5323     Tcreal    = 'c',
5324 
5325     Tchar     = 'a',
5326     Twchar    = 'u',
5327     Tdchar    = 'w',
5328 
5329     Tarray    = 'A',
5330     Tsarray   = 'G',
5331     Taarray   = 'H',
5332     Tpointer  = 'P',
5333     Tfunction = 'F',
5334     Tident    = 'I',
5335     Tclass    = 'C',
5336     Tstruct   = 'S',
5337     Tenum     = 'E',
5338     Ttypedef  = 'T',
5339     Tdelegate = 'D',
5340 
5341     Tconst    = 'x',
5342     Timmutable = 'y',
5343 }
5344 
5345 // return the TypeInfo for a primitive type and null otherwise.  This
5346 // is required since for arrays of ints we only have the mangled char
5347 // to work from. If arrays always subclassed TypeInfo_Array this
5348 // routine could go away.
5349 private TypeInfo primitiveTypeInfo(Mangle m)
5350 {
5351     // BUG: should fix this in static this() to avoid double checked locking bug
5352     __gshared TypeInfo[Mangle] dic;
5353     if (!dic.length)
5354     {
5355         dic = [
5356             Mangle.Tvoid : typeid(void),
5357             Mangle.Tbool : typeid(bool),
5358             Mangle.Tbyte : typeid(byte),
5359             Mangle.Tubyte : typeid(ubyte),
5360             Mangle.Tshort : typeid(short),
5361             Mangle.Tushort : typeid(ushort),
5362             Mangle.Tint : typeid(int),
5363             Mangle.Tuint : typeid(uint),
5364             Mangle.Tlong : typeid(long),
5365             Mangle.Tulong : typeid(ulong),
5366             Mangle.Tfloat : typeid(float),
5367             Mangle.Tdouble : typeid(double),
5368             Mangle.Treal : typeid(real),
5369             Mangle.Tifloat : typeid(ifloat),
5370             Mangle.Tidouble : typeid(idouble),
5371             Mangle.Tireal : typeid(ireal),
5372             Mangle.Tcfloat : typeid(cfloat),
5373             Mangle.Tcdouble : typeid(cdouble),
5374             Mangle.Tcreal : typeid(creal),
5375             Mangle.Tchar : typeid(char),
5376             Mangle.Twchar : typeid(wchar),
5377             Mangle.Tdchar : typeid(dchar)
5378             ];
5379     }
5380     auto p = m in dic;
5381     return p ? *p : null;
5382 }
5383 
5384 private bool needToSwapEndianess(Char)(const ref FormatSpec!Char f)
5385 {
5386     import std.system : endian, Endian;
5387 
5388     return endian == Endian.littleEndian && f.flPlus
5389         || endian == Endian.bigEndian && f.flDash;
5390 }
5391 
5392 /* ======================== Unit Tests ====================================== */
5393 
5394 @system unittest
5395 {
5396     import std.conv : octal;
5397 
5398     int i;
5399     string s;
5400 
5401     debug(format) printf("std.format.format.unittest\n");
5402 
5403     s = format("hello world! %s %s %s%s%s", true, 57, 1_000_000_000, 'x', " foo");
5404     assert(s == "hello world! true 57 1000000000x foo");
5405 
5406     s = format("%s %A %s", 1.67, -1.28, float.nan);
5407     /* The host C library is used to format floats.
5408      * C99 doesn't specify what the hex digit before the decimal point
5409      * is for %A.
5410      */
5411     //version (linux)
5412     //    assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan");
5413     //else version (OSX)
5414     //    assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan", s);
5415     //else
5416     version (MinGW)
5417         assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan", s);
5418     else version (CRuntime_Microsoft)
5419         assert(s == "1.67 -0X1.47AE14P+0 nan"
5420             || s == "1.67 -0X1.47AE147AE147BP+0 nan", s); // MSVCRT 14+ (VS 2015)
5421     else
5422         assert(s == "1.67 -0X1.47AE147AE147BP+0 nan", s);
5423 
5424     s = format("%x %X", 0x1234AF, 0xAFAFAFAF);
5425     assert(s == "1234af AFAFAFAF");
5426 
5427     s = format("%b %o", 0x1234AF, 0xAFAFAFAF);
5428     assert(s == "100100011010010101111 25753727657");
5429 
5430     s = format("%d %s", 0x1234AF, 0xAFAFAFAF);
5431     assert(s == "1193135 2947526575");
5432 
5433     //version (X86_64)
5434     //{
5435     //    pragma(msg, "several format tests disabled on x86_64 due to bug 5625");
5436     //}
5437     //else
5438     //{
5439         s = format("%s", 1.2 + 3.4i);
5440         assert(s == "1.2+3.4i", s);
5441 
5442         //s = format("%x %X", 1.32, 6.78f);
5443         //assert(s == "3ff51eb851eb851f 40D8F5C3");
5444 
5445     //}
5446 
5447     s = format("%#06.*f",2,12.345);
5448     assert(s == "012.35");
5449 
5450     s = format("%#0*.*f",6,2,12.345);
5451     assert(s == "012.35");
5452 
5453     s = format("%7.4g:", 12.678);
5454     assert(s == "  12.68:");
5455 
5456     s = format("%7.4g:", 12.678L);
5457     assert(s == "  12.68:");
5458 
5459     s = format("%04f|%05d|%#05x|%#5x",-4.0,-10,1,1);
5460     assert(s == "-4.000000|-0010|0x001|  0x1");
5461 
5462     i = -10;
5463     s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
5464     assert(s == "-10|-10|-10|-10|-10.0000");
5465 
5466     i = -5;
5467     s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
5468     assert(s == "-5| -5|-05|-5|-5.0000");
5469 
5470     i = 0;
5471     s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
5472     assert(s == "0|  0|000|0|0.0000");
5473 
5474     i = 5;
5475     s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
5476     assert(s == "5|  5|005|5|5.0000");
5477 
5478     i = 10;
5479     s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
5480     assert(s == "10| 10|010|10|10.0000");
5481 
5482     s = format("%.0d", 0);
5483     assert(s == "");
5484 
5485     s = format("%.g", .34);
5486     assert(s == "0.3");
5487 
5488     s = format("%.0g", .34);
5489     assert(s == "0.3");
5490 
5491     s = format("%.2g", .34);
5492     assert(s == "0.34");
5493 
5494     s = format("%0.0008f", 1e-08);
5495     assert(s == "0.00000001");
5496 
5497     s = format("%0.0008f", 1e-05);
5498     assert(s == "0.00001000");
5499 
5500     s = "helloworld";
5501     string r;
5502     r = format("%.2s", s[0 .. 5]);
5503     assert(r == "he");
5504     r = format("%.20s", s[0 .. 5]);
5505     assert(r == "hello");
5506     r = format("%8s", s[0 .. 5]);
5507     assert(r == "   hello");
5508 
5509     byte[] arrbyte = new byte[4];
5510     arrbyte[0] = 100;
5511     arrbyte[1] = -99;
5512     arrbyte[3] = 0;
5513     r = format("%s", arrbyte);
5514     assert(r == "[100, -99, 0, 0]");
5515 
5516     ubyte[] arrubyte = new ubyte[4];
5517     arrubyte[0] = 100;
5518     arrubyte[1] = 200;
5519     arrubyte[3] = 0;
5520     r = format("%s", arrubyte);
5521     assert(r == "[100, 200, 0, 0]");
5522 
5523     short[] arrshort = new short[4];
5524     arrshort[0] = 100;
5525     arrshort[1] = -999;
5526     arrshort[3] = 0;
5527     r = format("%s", arrshort);
5528     assert(r == "[100, -999, 0, 0]");
5529 
5530     ushort[] arrushort = new ushort[4];
5531     arrushort[0] = 100;
5532     arrushort[1] = 20_000;
5533     arrushort[3] = 0;
5534     r = format("%s", arrushort);
5535     assert(r == "[100, 20000, 0, 0]");
5536 
5537     int[] arrint = new int[4];
5538     arrint[0] = 100;
5539     arrint[1] = -999;
5540     arrint[3] = 0;
5541     r = format("%s", arrint);
5542     assert(r == "[100, -999, 0, 0]");
5543 
5544     long[] arrlong = new long[4];
5545     arrlong[0] = 100;
5546     arrlong[1] = -999;
5547     arrlong[3] = 0;
5548     r = format("%s", arrlong);
5549     assert(r == "[100, -999, 0, 0]");
5550 
5551     ulong[] arrulong = new ulong[4];
5552     arrulong[0] = 100;
5553     arrulong[1] = 999;
5554     arrulong[3] = 0;
5555     r = format("%s", arrulong);
5556     assert(r == "[100, 999, 0, 0]");
5557 
5558     string[] arr2 = new string[4];
5559     arr2[0] = "hello";
5560     arr2[1] = "world";
5561     arr2[3] = "foo";
5562     r = format("%s", arr2);
5563     assert(r == `["hello", "world", "", "foo"]`);
5564 
5565     r = format("%.8d", 7);
5566     assert(r == "00000007");
5567     r = format("%.8x", 10);
5568     assert(r == "0000000a");
5569 
5570     r = format("%-3d", 7);
5571     assert(r == "7  ");
5572 
5573     r = format("%-1*d", 4, 3);
5574     assert(r == "3   ");
5575 
5576     r = format("%*d", -3, 7);
5577     assert(r == "7  ");
5578 
5579     r = format("%.*d", -3, 7);
5580     assert(r == "7");
5581 
5582     r = format("%-1.*f", 2, 3.1415);
5583     assert(r == "3.14");
5584 
5585     r = format("abc"c);
5586     assert(r == "abc");
5587 
5588     //format() returns the same type as inputted.
5589     wstring wr;
5590     wr = format("def"w);
5591     assert(wr == "def"w);
5592 
5593     dstring dr;
5594     dr = format("ghi"d);
5595     assert(dr == "ghi"d);
5596 
5597     void* p = cast(void*) 0xDEADBEEF;
5598     r = format("%s", p);
5599     assert(r == "DEADBEEF");
5600 
5601     r = format("%#x", 0xabcd);
5602     assert(r == "0xabcd");
5603     r = format("%#X", 0xABCD);
5604     assert(r == "0XABCD");
5605 
5606     r = format("%#o", octal!12345);
5607     assert(r == "012345");
5608     r = format("%o", 9);
5609     assert(r == "11");
5610     r = format("%#o", 0);   // issue 15663
5611     assert(r == "0");
5612 
5613     r = format("%+d", 123);
5614     assert(r == "+123");
5615     r = format("%+d", -123);
5616     assert(r == "-123");
5617     r = format("% d", 123);
5618     assert(r == " 123");
5619     r = format("% d", -123);
5620     assert(r == "-123");
5621 
5622     r = format("%%");
5623     assert(r == "%");
5624 
5625     r = format("%d", true);
5626     assert(r == "1");
5627     r = format("%d", false);
5628     assert(r == "0");
5629 
5630     r = format("%d", 'a');
5631     assert(r == "97");
5632     wchar wc = 'a';
5633     r = format("%d", wc);
5634     assert(r == "97");
5635     dchar dc = 'a';
5636     r = format("%d", dc);
5637     assert(r == "97");
5638 
5639     byte b = byte.max;
5640     r = format("%x", b);
5641     assert(r == "7f");
5642     r = format("%x", ++b);
5643     assert(r == "80");
5644     r = format("%x", ++b);
5645     assert(r == "81");
5646 
5647     short sh = short.max;
5648     r = format("%x", sh);
5649     assert(r == "7fff");
5650     r = format("%x", ++sh);
5651     assert(r == "8000");
5652     r = format("%x", ++sh);
5653     assert(r == "8001");
5654 
5655     i = int.max;
5656     r = format("%x", i);
5657     assert(r == "7fffffff");
5658     r = format("%x", ++i);
5659     assert(r == "80000000");
5660     r = format("%x", ++i);
5661     assert(r == "80000001");
5662 
5663     r = format("%x", 10);
5664     assert(r == "a");
5665     r = format("%X", 10);
5666     assert(r == "A");
5667     r = format("%x", 15);
5668     assert(r == "f");
5669     r = format("%X", 15);
5670     assert(r == "F");
5671 
5672     Object c = null;
5673     r = format("%s", c);
5674     assert(r == "null");
5675 
5676     enum TestEnum
5677     {
5678         Value1, Value2
5679     }
5680     r = format("%s", TestEnum.Value2);
5681     assert(r == "Value2");
5682 
5683     immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
5684     r = format("%s", aa.values);
5685     assert(r == `["hello", "betty"]` || r == `["betty", "hello"]`);
5686     r = format("%s", aa);
5687     assert(r == `[3:"hello", 4:"betty"]` || r == `[4:"betty", 3:"hello"]`);
5688 
5689     static const dchar[] ds = ['a','b'];
5690     for (int j = 0; j < ds.length; ++j)
5691     {
5692         r = format(" %d", ds[j]);
5693         if (j == 0)
5694             assert(r == " 97");
5695         else
5696             assert(r == " 98");
5697     }
5698 
5699     r = format(">%14d<, %s", 15, [1,2,3]);
5700     assert(r == ">            15<, [1, 2, 3]");
5701 
5702     assert(format("%8s", "bar") == "     bar");
5703     assert(format("%8s", "b\u00e9ll\u00f4") == " b\u00e9ll\u00f4");
5704 }
5705 
5706 @safe unittest
5707 {
5708     // bugzilla 3479
5709     import std.array;
5710     auto stream = appender!(char[])();
5711     formattedWrite(stream, "%2$.*1$d", 12, 10);
5712     assert(stream.data == "000000000010", stream.data);
5713 }
5714 
5715 @safe unittest
5716 {
5717     // bug 6893
5718     import std.array;
5719     enum E : ulong { A, B, C }
5720     auto stream = appender!(char[])();
5721     formattedWrite(stream, "%s", E.C);
5722     assert(stream.data == "C");
5723 }
5724 
5725 // Used to check format strings are compatible with argument types
5726 package static const checkFormatException(alias fmt, Args...) =
5727 {
5728     try
5729         .format(fmt, Args.init);
5730     catch (Exception e)
5731         return (e.msg == ctfpMessage) ? null : e;
5732     return null;
5733 }();
5734 
5735 /*****************************************************
5736  * Format arguments into a string.
5737  *
5738  * Params: fmt  = Format string. For detailed specification, see $(LREF formattedWrite).
5739  *         args = Variadic list of arguments to _format into returned string.
5740  */
5741 typeof(fmt) format(alias fmt, Args...)(Args args)
5742 if (isSomeString!(typeof(fmt)))
5743 {
5744     alias e = checkFormatException!(fmt, Args);
5745     static assert(!e, e.msg);
5746     return .format(fmt, args);
5747 }
5748 
5749 /// Type checking can be done when fmt is known at compile-time:
5750 @safe unittest
5751 {
5752     auto s = format!"%s is %s"("Pi", 3.14);
5753     assert(s == "Pi is 3.14");
5754 
5755     static assert(!__traits(compiles, {s = format!"%l"();}));     // missing arg
5756     static assert(!__traits(compiles, {s = format!""(404);}));    // surplus arg
5757     static assert(!__traits(compiles, {s = format!"%d"(4.03);})); // incompatible arg
5758 }
5759 
5760 /// ditto
5761 immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args)
5762 if (isSomeChar!Char)
5763 {
5764     import std.array : appender;
5765     import std.format : formattedWrite, FormatException;
5766     auto w = appender!(immutable(Char)[]);
5767     auto n = formattedWrite(w, fmt, args);
5768     version (all)
5769     {
5770         // In the future, this check will be removed to increase consistency
5771         // with formattedWrite
5772         import std.conv : text;
5773         import std.exception : enforce;
5774         enforce(n == args.length, new FormatException(
5775             text("Orphan format arguments: args[", n, "..", args.length, "]")));
5776     }
5777     return w.data;
5778 }
5779 
5780 @safe pure unittest
5781 {
5782     import core.exception;
5783     import std.exception;
5784     import std.format;
5785     assertCTFEable!(
5786     {
5787 //  assert(format(null) == "");
5788     assert(format("foo") == "foo");
5789     assert(format("foo%%") == "foo%");
5790     assert(format("foo%s", 'C') == "fooC");
5791     assert(format("%s foo", "bar") == "bar foo");
5792     assert(format("%s foo %s", "bar", "abc") == "bar foo abc");
5793     assert(format("foo %d", -123) == "foo -123");
5794     assert(format("foo %d", 123) == "foo 123");
5795 
5796     assertThrown!FormatException(format("foo %s"));
5797     assertThrown!FormatException(format("foo %s", 123, 456));
5798 
5799     assert(format("hel%slo%s%s%s", "world", -138, 'c', true) ==
5800                   "helworldlo-138ctrue");
5801     });
5802 
5803     assert(is(typeof(format("happy")) == string));
5804     assert(is(typeof(format("happy"w)) == wstring));
5805     assert(is(typeof(format("happy"d)) == dstring));
5806 }
5807 
5808 // https://issues.dlang.org/show_bug.cgi?id=16661
5809 @safe unittest
5810 {
5811     assert(format("%.2f"d, 0.4) == "0.40");
5812     assert("%02d"d.format(1) == "01"d);
5813 }
5814 
5815 /*****************************************************
5816  * Format arguments into buffer $(I buf) which must be large
5817  * enough to hold the result.
5818  *
5819  * Returns:
5820  *     The slice of `buf` containing the formatted string.
5821  *
5822  * Throws:
5823  *     A `RangeError` if `buf` isn't large enough to hold the
5824  *     formatted string.
5825  *
5826  *     A $(LREF FormatException) if the length of `args` is different
5827  *     than the number of format specifiers in `fmt`.
5828  */
5829 char[] sformat(alias fmt, Args...)(char[] buf, Args args)
5830 if (isSomeString!(typeof(fmt)))
5831 {
5832     alias e = checkFormatException!(fmt, Args);
5833     static assert(!e, e.msg);
5834     return .sformat(buf, fmt, args);
5835 }
5836 
5837 /// ditto
5838 char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args)
5839 {
5840     import core.exception : RangeError;
5841     import std.format : formattedWrite, FormatException;
5842     import std.utf : encode;
5843 
5844     size_t i;
5845 
5846     struct Sink
5847     {
5848         void put(dchar c)
5849         {
5850             char[4] enc;
5851             auto n = encode(enc, c);
5852 
5853             if (buf.length < i + n)
5854                 throw new RangeError(__FILE__, __LINE__);
5855 
5856             buf[i .. i + n] = enc[0 .. n];
5857             i += n;
5858         }
5859         void put(const(char)[] s)
5860         {
5861             if (buf.length < i + s.length)
5862                 throw new RangeError(__FILE__, __LINE__);
5863 
5864             buf[i .. i + s.length] = s[];
5865             i += s.length;
5866         }
5867         void put(const(wchar)[] s)
5868         {
5869             for (; !s.empty; s.popFront())
5870                 put(s.front);
5871         }
5872         void put(const(dchar)[] s)
5873         {
5874             for (; !s.empty; s.popFront())
5875                 put(s.front);
5876         }
5877     }
5878     auto n = formattedWrite(Sink(), fmt, args);
5879     version (all)
5880     {
5881         // In the future, this check will be removed to increase consistency
5882         // with formattedWrite
5883         import std.conv : text;
5884         import std.exception : enforce;
5885         enforce!FormatException(
5886             n == args.length,
5887             text("Orphan format arguments: args[", n, " .. ", args.length, "]")
5888         );
5889     }
5890     return buf[0 .. i];
5891 }
5892 
5893 /// The format string can be checked at compile-time (see $(LREF format) for details):
5894 @system unittest
5895 {
5896     char[10] buf;
5897 
5898     assert(buf[].sformat!"foo%s"('C') == "fooC");
5899     assert(sformat(buf[], "%s foo", "bar") == "bar foo");
5900 }
5901 
5902 @system unittest
5903 {
5904     import core.exception;
5905     import std.format;
5906 
5907     debug(string) trustedPrintf("std.string.sformat.unittest\n");
5908 
5909     import std.exception;
5910     assertCTFEable!(
5911     {
5912     char[10] buf;
5913 
5914     assert(sformat(buf[], "foo") == "foo");
5915     assert(sformat(buf[], "foo%%") == "foo%");
5916     assert(sformat(buf[], "foo%s", 'C') == "fooC");
5917     assert(sformat(buf[], "%s foo", "bar") == "bar foo");
5918     assertThrown!RangeError(sformat(buf[], "%s foo %s", "bar", "abc"));
5919     assert(sformat(buf[], "foo %d", -123) == "foo -123");
5920     assert(sformat(buf[], "foo %d", 123) == "foo 123");
5921 
5922     assertThrown!FormatException(sformat(buf[], "foo %s"));
5923     assertThrown!FormatException(sformat(buf[], "foo %s", 123, 456));
5924 
5925     assert(sformat(buf[], "%s %s %s", "c"c, "w"w, "d"d) == "c w d");
5926     });
5927 }
5928 
5929 /*****************************
5930  * The .ptr is unsafe because it could be dereferenced and the length of the array may be 0.
5931  * Returns:
5932  *      the difference between the starts of the arrays
5933  */
5934 @trusted private pure nothrow @nogc
5935     ptrdiff_t arrayPtrDiff(T)(const T[] array1, const T[] array2)
5936 {
5937     return array1.ptr - array2.ptr;
5938 }
5939 
5940 @safe unittest
5941 {
5942     assertCTFEable!({
5943     auto tmp = format("%,d", 1000);
5944     assert(tmp == "1,000", "'" ~ tmp ~ "'");
5945 
5946     tmp = format("%,?d", 'z', 1234567);
5947     assert(tmp == "1z234z567", "'" ~ tmp ~ "'");
5948 
5949     tmp = format("%10,?d", 'z', 1234567);
5950     assert(tmp == " 1z234z567", "'" ~ tmp ~ "'");
5951 
5952     tmp = format("%11,2?d", 'z', 1234567);
5953     assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'");
5954 
5955     tmp = format("%11,*?d", 2, 'z', 1234567);
5956     assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'");
5957 
5958     tmp = format("%11,*d", 2, 1234567);
5959     assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'");
5960 
5961     tmp = format("%11,2d", 1234567);
5962     assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'");
5963     });
5964 }
5965 
5966 @safe unittest
5967 {
5968     auto tmp = format("%,f", 1000.0);
5969     assert(tmp == "1,000.000,000", "'" ~ tmp ~ "'");
5970 
5971     tmp = format("%,f", 1234567.891011);
5972     assert(tmp == "1,234,567.891,011", "'" ~ tmp ~ "'");
5973 
5974     tmp = format("%,f", -1234567.891011);
5975     assert(tmp == "-1,234,567.891,011", "'" ~ tmp ~ "'");
5976 
5977     tmp = format("%,2f", 1234567.891011);
5978     assert(tmp == "1,23,45,67.89,10,11", "'" ~ tmp ~ "'");
5979 
5980     tmp = format("%18,f", 1234567.891011);
5981     assert(tmp == " 1,234,567.891,011", "'" ~ tmp ~ "'");
5982 
5983     tmp = format("%18,?f", '.', 1234567.891011);
5984     assert(tmp == " 1.234.567.891.011", "'" ~ tmp ~ "'");
5985 
5986     tmp = format("%,?.3f", 'ä', 1234567.891011);
5987     assert(tmp == "1ä234ä567.891", "'" ~ tmp ~ "'");
5988 
5989     tmp = format("%,*?.3f", 1, 'ä', 1234567.891011);
5990     assert(tmp == "1ä2ä3ä4ä5ä6ä7.8ä9ä1", "'" ~ tmp ~ "'");
5991 
5992     tmp = format("%,4?.3f", '_', 1234567.891011);
5993     assert(tmp == "123_4567.891", "'" ~ tmp ~ "'");
5994 
5995     tmp = format("%12,3.3f", 1234.5678);
5996     assert(tmp == "   1,234.568", "'" ~ tmp ~ "'");
5997 
5998     tmp = format("%,e", 3.141592653589793238462);
5999     assert(tmp == "3.141,593e+00", "'" ~ tmp ~ "'");
6000 
6001     tmp = format("%15,e", 3.141592653589793238462);
6002     assert(tmp == "  3.141,593e+00", "'" ~ tmp ~ "'");
6003 
6004     tmp = format("%15,e", -3.141592653589793238462);
6005     assert(tmp == " -3.141,593e+00", "'" ~ tmp ~ "'");
6006 
6007     tmp = format("%.4,*e", 2, 3.141592653589793238462);
6008     assert(tmp == "3.14,16e+00", "'" ~ tmp ~ "'");
6009 
6010     tmp = format("%13.4,*e", 2, 3.141592653589793238462);
6011     assert(tmp == "  3.14,16e+00", "'" ~ tmp ~ "'");
6012 
6013     tmp = format("%,.0f", 3.14);
6014     assert(tmp == "3", "'" ~ tmp ~ "'");
6015 
6016     tmp = format("%3,g", 1_000_000.123456);
6017     assert(tmp == "1e+06", "'" ~ tmp ~ "'");
6018 
6019     tmp = format("%19,?f", '.', -1234567.891011);
6020     assert(tmp == " -1.234.567.891.011", "'" ~ tmp ~ "'");
6021 }
6022 
6023 // Test for multiple indexes
6024 @safe unittest
6025 {
6026     auto tmp = format("%2:5$s", 1, 2, 3, 4, 5);
6027     assert(tmp == "2345", tmp);
6028 }
6029