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 */
56module std.format;
57
58//debug=format;                // uncomment to turn on debugging printf's
59
60import core.vararg;
61import std.exception;
62import std.meta;
63import std.range.primitives;
64import std.traits;
65
66
67/**********************************************************************
68 * Signals a mismatch between a format and its corresponding argument.
69 */
70class FormatException : Exception
71{
72    @safe pure nothrow
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
85private 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
365My items are 1 2 3.
366My 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
384My 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
4081 2 3
4094 5 6
4107 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
436My friends are ["John", "Nancy"].
437My friends are "John", "Nancy".
438My friends are John, Nancy.
439)
440 */
441uint formattedWrite(alias fmt, Writer, A...)(auto ref Writer w, A args)
442if (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
466uint 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/**
618Reads characters from input range $(D r), converts them according
619to $(D fmt), and writes them to $(D args).
620
621Params:
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
626Returns:
627
628On success, the function returns the number of variables filled. This count
629can match the expected number of readings or fewer, even zero, if a
630matching failure happens.
631
632Throws:
633    An `Exception` if `S.length == 0` and `fmt` has format specifiers.
634 */
635uint formattedRead(alias fmt, R, S...)(ref R r, auto ref S args)
636if (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
644uint 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
982template FormatSpec(Char)
983if (!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 */
992struct FormatSpec(Char)
993if (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
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/**
1670Helper function that returns a $(D FormatSpec) for a single specifier given
1671in $(D fmt).
1672
1673Params:
1674    fmt = A format specifier.
1675
1676Returns:
1677    A $(D FormatSpec) with the specifier parsed.
1678Throws:
1679    An `Exception` when more than one specifier is given or the specifier
1680    is malformed.
1681  */
1682FormatSpec!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
1722Params:
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 */
1727void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
1728if (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
1804Params:
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 */
1809void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
1810if (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/**
1840Integrals are formatted like $(D printf) does.
1841
1842Params:
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 */
1847void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
1848if (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
1903private 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
1921private 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
2114private enum ctfpMessage = "Cannot format floating point types at compile-time";
2115
2116/**
2117Floating-point values are formatted like $(D printf) does.
2118
2119Params:
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 */
2124void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
2125if (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/*
2341Formatting a $(D creal) is deprecated but still kept around for a while.
2342
2343Params:
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 */
2348void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
2349if (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
2399Params:
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 */
2404void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
2405if (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/**
2442Individual characters ($(D char), $(D wchar), or $(D dchar)) are formatted as
2443Unicode characters with %s and as integers with integral-specific format
2444specs.
2445
2446Params:
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 */
2451void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
2452if (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/**
2517Strings are formatted like $(D printf) does.
2518
2519Params:
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 */
2524void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
2525if (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/**
2595Static-size arrays are formatted as dynamic arrays.
2596
2597Params:
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 */
2602void formatValue(Writer, T, Char)(auto ref Writer w, auto ref T obj, const ref FormatSpec!Char f)
2603if (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/**
2634Dynamic arrays are formatted as input ranges.
2635
2636Specializations:
2637    $(UL $(LI $(D void[]) is formatted like $(D ubyte[]).)
2638        $(LI Const array is converted to input range by removing its qualifier.))
2639
2640Params:
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 */
2645void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
2646if (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
2859private void formatRange(Writer, T, Char)(ref Writer w, ref T val, const ref FormatSpec!Char f)
2860if (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
3028private 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.
3066void formatElement(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
3067if (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.
3151void formatElement(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
3152if (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'.
3177void formatElement(Writer, T, Char)(auto ref Writer w, auto ref T val, const ref FormatSpec!Char f)
3178if (!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
3187Params:
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 */
3192void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
3193if (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
3306template 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
3336private void formatObject(Writer, T, Char)(ref Writer w, ref T val, const ref FormatSpec!Char f)
3337if (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
3359void 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---
3420const void toString(scope void delegate(const(char)[]) sink, FormatSpec fmt);
3421const void toString(scope void delegate(const(char)[]) sink, string fmt);
3422const void toString(scope void delegate(const(char)[]) sink);
3423const 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 */
3437void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
3438if (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
3586void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
3587if (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'.
3666void formatValue(Writer, T, Char)(auto ref Writer w, auto ref T val, const ref FormatSpec!Char f)
3667if ((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
3796Params:
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 */
3801void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
3802if (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 */
3870void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
3871if (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 */
3975void formatValue(Writer, T, Char)(auto ref Writer w, scope T, const ref FormatSpec!Char f)
3976if (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
4023private int getNthInt(string kind, A...)(uint index, A args)
4024{
4025    return getNth!(kind, isIntegral,int)(index, args);
4026}
4027
4028private 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
4082version (unittest)
4083void 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
4096version (unittest)
4097void 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
4109version (unittest)
4110void 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
4127version (unittest)
4128void 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
4462here:
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
4609version (unittest)
4610void 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
4651version (unittest)
4652void 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//------------------------------------------------------------------------------
4797private 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
4817private 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 */
4840T 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
4952private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
4953if (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
4966private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
4967if (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
4977private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
4978if (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
5011private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5012if (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
5035private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5036if (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
5060private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5061if (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
5113private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5114if (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
5128private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5129if (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 */
5146private T rawRead(T, Range)(ref Range input)
5147if (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
5176private T unformatRange(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5177in
5178{
5179    assert(spec.spec == '(');
5180}
5181body
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", cont);
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, sep);
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
5277T unformatElement(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
5278if (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
5302enum 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.
5349private 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
5384private 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
5726package 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 */
5741typeof(fmt) format(alias fmt, Args...)(Args args)
5742if (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
5761immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args)
5762if (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 */
5829char[] sformat(alias fmt, Args...)(char[] buf, Args args)
5830if (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
5838char[] 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