1 // Written in the D programming language.
2 
3 /**
4 This is a submodule of $(MREF std, format).
5 
6 It provides two functions for writing formatted output: $(LREF
7 formatValue) and $(LREF formattedWrite). The former writes a single
8 value. The latter writes several values at once, interspersed with
9 unformatted text.
10 
11 The following combinations of format characters and types are
12 available:
13 
14 $(BOOKTABLE ,
15 $(TR $(TH) $(TH s) $(TH c) $(TH d, u, b, o) $(TH x, X) $(TH e, E, f, F, g, G, a, A) $(TH r) $(TH compound))
16 $(TR $(TD `bool`) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)))
17 $(TR $(TD `null`) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))
18 $(TR $(TD $(I integer)) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH)))
19 $(TR $(TD $(I floating point)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH)))
20 $(TR $(TD $(I character)) $(TD yes) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)))
21 $(TR $(TD $(I string)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes))
22 $(TR $(TD $(I array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes))
23 $(TR $(TD $(I associative array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes))
24 $(TR $(TD $(I pointer)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))
25 $(TR $(TD $(I SIMD vectors)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes))
26 $(TR $(TD $(I delegates)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes))
27 )
28 
29 Enums can be used with all format characters of the base type.
30 
31 $(H3 $(LNAME2 aggregates, Structs, Unions, Classes, and Interfaces))
32 
33 Aggregate types can define various `toString` functions. If this
34 function takes a $(REF_ALTTEXT FormatSpec, FormatSpec, std, format,
35 spec) or a $(I format string) as argument, the function decides
36 which format characters are accepted. If no `toString` is defined and
37 the aggregate is an $(REF_ALTTEXT input range, isInputRange, std,
38 range, primitives), it is treated like a range, that is $(B 's'), $(B
39 'r') and a compound specifier are accepted. In all other cases
40 aggregate types only accept $(B 's').
41 
42 `toString` should have one of the following signatures:
43 
44 ---
45 void toString(Writer, Char)(ref Writer w, const ref FormatSpec!Char fmt)
46 void toString(Writer)(ref Writer w)
47 string toString();
48 ---
49 
50 Where `Writer` is an $(REF_ALTTEXT output range, isOutputRange,
51 std,range,primitives) which accepts characters $(LPAREN)of type
52 `Char` in the first version$(RPAREN). The template type does not have
53 to be called `Writer`.
54 
55 Sometimes it's not possible to use a template, for example when
56 `toString` overrides `Object.toString`. In this case, the following
57 $(LPAREN)slower and less flexible$(RPAREN) functions can be used:
58 
59 ---
60 void toString(void delegate(const(char)[]) sink, const ref FormatSpec!char fmt);
61 void toString(void delegate(const(char)[]) sink, string fmt);
62 void toString(void delegate(const(char)[]) sink);
63 ---
64 
65 When several of the above `toString` versions are available, the
66 versions with `Writer` take precedence over the versions with a
67 `sink`. `string toString()` has the lowest priority.
68 
69 If none of the above mentioned `toString` versions are available, the
70 aggregates will be formatted by other means, in the following
71 order:
72 
73 If an aggregate is an $(REF_ALTTEXT input range, isInputRange, std,
74 range, primitives), it is formatted like an input range.
75 
76 If an aggregate is a builtin type (using `alias this`), it is formatted
77 like the builtin type.
78 
79 If all else fails, structs are formatted like `Type(field1, field2, ...)`,
80 classes and interfaces are formatted with their fully qualified name
81 and unions with their base name.
82 
83 Copyright: Copyright The D Language Foundation 2000-2013.
84 
85 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
86 
87 Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
88 Andrei Alexandrescu), and Kenji Hara
89 
90 Source: $(PHOBOSSRC std/format/write.d)
91  */
92 module std.format.write;
93 
94 /**
95 `bool`s are formatted as `"true"` or `"false"` with `%s` and like the
96 `byte`s 1 and 0 with all other format characters.
97  */
98 @safe pure unittest
99 {
100     import std.array : appender;
101     import std.format.spec : singleSpec;
102 
103     auto w1 = appender!string();
104     auto spec1 = singleSpec("%s");
105     formatValue(w1, true, spec1);
106 
107     assert(w1.data == "true");
108 
109     auto w2 = appender!string();
110     auto spec2 = singleSpec("%#x");
111     formatValue(w2, true, spec2);
112 
113     assert(w2.data == "0x1");
114 }
115 
116 /// The `null` literal is formatted as `"null"`.
117 @safe pure unittest
118 {
119     import std.array : appender;
120     import std.format.spec : singleSpec;
121 
122     auto w = appender!string();
123     auto spec = singleSpec("%s");
124     formatValue(w, null, spec);
125 
126     assert(w.data == "null");
127 }
128 
129 /**
130 Integrals are formatted in (signed) every day notation with `%s` and
131 `%d` and as an (unsigned) image of the underlying bit representation
132 with `%b` (binary), `%u` (decimal), `%o` (octal), and `%x` (hexadecimal).
133  */
134 @safe pure unittest
135 {
136     import std.array : appender;
137     import std.format.spec : singleSpec;
138 
139     auto w1 = appender!string();
140     auto spec1 = singleSpec("%d");
141     formatValue(w1, -1337, spec1);
142 
143     assert(w1.data == "-1337");
144 
145     auto w2 = appender!string();
146     auto spec2 = singleSpec("%x");
147     formatValue(w2, -1337, spec2);
148 
149     assert(w2.data == "fffffac7");
150 }
151 
152 /**
153 Floating-point values are formatted in natural notation with `%f`, in
154 scientific notation with `%e`, in short notation with `%g`, and in
155 hexadecimal scientific notation with `%a`. If a rounding mode is
156 available, they are rounded according to this rounding mode, otherwise
157 they are rounded to the nearest value, ties to even.
158  */
159 @safe unittest
160 {
161     import std.array : appender;
162     import std.format.spec : singleSpec;
163 
164     auto w1 = appender!string();
165     auto spec1 = singleSpec("%.3f");
166     formatValue(w1, 1337.7779, spec1);
167 
168     assert(w1.data == "1337.778");
169 
170     auto w2 = appender!string();
171     auto spec2 = singleSpec("%.3e");
172     formatValue(w2, 1337.7779, spec2);
173 
174     assert(w2.data == "1.338e+03");
175 
176     auto w3 = appender!string();
177     auto spec3 = singleSpec("%.3g");
178     formatValue(w3, 1337.7779, spec3);
179 
180     assert(w3.data == "1.34e+03");
181 
182     auto w4 = appender!string();
183     auto spec4 = singleSpec("%.3a");
184     formatValue(w4, 1337.7779, spec4);
185 
186     assert(w4.data == "0x1.4e7p+10");
187 }
188 
189 /**
190 Individual characters (`char`, `wchar`, or `dchar`) are formatted as
191 Unicode characters with `%s` and `%c` and as integers (`ubyte`,
192 `ushort`, `uint`) with all other format characters. With
193 $(MREF_ALTTEXT compound specifiers, std,format) characters are
194 treated differently.
195  */
196 @safe pure unittest
197 {
198     import std.array : appender;
199     import std.format.spec : singleSpec;
200 
201     auto w1 = appender!string();
202     auto spec1 = singleSpec("%c");
203     formatValue(w1, 'ì', spec1);
204 
205     assert(w1.data == "ì");
206 
207     auto w2 = appender!string();
208     auto spec2 = singleSpec("%#x");
209     formatValue(w2, 'ì', spec2);
210 
211     assert(w2.data == "0xec");
212 }
213 
214 /**
215 Strings are formatted as a sequence of characters with `%s`.
216 Non-printable characters are not escaped. With a compound specifier
217 the string is treated like a range of characters. With $(MREF_ALTTEXT
218 compound specifiers, std,format) strings are treated differently.
219  */
220 @safe pure unittest
221 {
222     import std.array : appender;
223     import std.format.spec : singleSpec;
224 
225     auto w1 = appender!string();
226     auto spec1 = singleSpec("%s");
227     formatValue(w1, "hello", spec1);
228 
229     assert(w1.data == "hello");
230 
231     auto w2 = appender!string();
232     auto spec2 = singleSpec("%(%#x%|/%)");
233     formatValue(w2, "hello", spec2);
234 
235     assert(w2.data == "0x68/0x65/0x6c/0x6c/0x6f");
236 }
237 
238 /// Static arrays are formatted as dynamic arrays.
239 @safe pure unittest
240 {
241     import std.array : appender;
242     import std.format.spec : singleSpec;
243 
244     auto w = appender!string();
245     auto spec = singleSpec("%s");
246     int[2] two = [1, 2];
247     formatValue(w, two, spec);
248 
249     assert(w.data == "[1, 2]");
250 }
251 
252 /**
253 Dynamic arrays are formatted as input ranges.
254  */
255 @safe pure unittest
256 {
257     import std.array : appender;
258     import std.format.spec : singleSpec;
259 
260     auto w1 = appender!string();
261     auto spec1 = singleSpec("%s");
262     auto two = [1, 2];
263     formatValue(w1, two, spec1);
264 
265     assert(w1.data == "[1, 2]");
266 
267     auto w2 = appender!string();
268     auto spec2 = singleSpec("%(%g%|, %)");
269     auto consts = [3.1415926, 299792458, 6.67430e-11];
270     formatValue(w2, consts, spec2);
271 
272     assert(w2.data == "3.14159, 2.99792e+08, 6.6743e-11");
273 
274     // void[] is treated like ubyte[]
275     auto w3 = appender!string();
276     auto spec3 = singleSpec("%s");
277     void[] val = cast(void[]) cast(ubyte[])[1, 2, 3];
278     formatValue(w3, val, spec3);
279 
280     assert(w3.data == "[1, 2, 3]");
281 }
282 
283 /**
284 Associative arrays are formatted by using `':'` and `", "` as
285 separators, enclosed by `'['` and `']'` when used with `%s`. It's
286 also possible to use a compound specifier for better control.
287 
288 Please note, that the order of the elements is not defined, therefore
289 the result of this function might differ.
290  */
291 @safe pure unittest
292 {
293     import std.array : appender;
294     import std.format.spec : singleSpec;
295 
296     auto aa = [10:17.5, 20:9.99];
297 
298     auto w1 = appender!string();
299     auto spec1 = singleSpec("%s");
300     formatValue(w1, aa, spec1);
301 
302     assert(w1.data == "[10:17.5, 20:9.99]" || w1.data == "[20:9.99, 10:17.5]");
303 
304     auto w2 = appender!string();
305     auto spec2 = singleSpec("%(%x = %.0e%| # %)");
306     formatValue(w2, aa, spec2);
307 
308     assert(w2.data == "a = 2e+01 # 14 = 1e+01" || w2.data == "14 = 1e+01 # a = 2e+01");
309 }
310 
311 /**
312 `enum`s are formatted as their name when used with `%s` and like
313 their base value else.
314  */
315 @safe pure unittest
316 {
317     import std.array : appender;
318     import std.format.spec : singleSpec;
319 
320     enum A { first, second, third }
321 
322     auto w1 = appender!string();
323     auto spec1 = singleSpec("%s");
324     formatValue(w1, A.second, spec1);
325 
326     assert(w1.data == "second");
327 
328     auto w2 = appender!string();
329     auto spec2 = singleSpec("%d");
330     formatValue(w2, A.second, spec2);
331 
332     assert(w2.data == "1");
333 
334     // values of an enum that have no name are formatted with %s using a cast
335     A a = A.third;
336     a++;
337 
338     auto w3 = appender!string();
339     auto spec3 = singleSpec("%s");
340     formatValue(w3, a, spec3);
341 
342     assert(w3.data == "cast(A)3");
343 }
344 
345 /**
346 `structs`, `unions`, `classes` and `interfaces` can be formatted in
347 several different ways. The following example highlights `struct`
348 formatting, however, it applies to other aggregates as well.
349  */
350 @safe unittest
351 {
352     import std.array : appender;
353     import std.format.spec : FormatSpec, singleSpec;
354 
355     // Using a `toString` with a writer
356     static struct Point1
357     {
358         import std.range.primitives : isOutputRange, put;
359 
360         int x, y;
361 
362         void toString(W)(ref W writer, scope const ref FormatSpec!char f)
363         if (isOutputRange!(W, char))
364         {
365             put(writer, "(");
366             formatValue(writer, x, f);
367             put(writer, ",");
368             formatValue(writer, y, f);
369             put(writer, ")");
370         }
371     }
372 
373     auto w1 = appender!string();
374     auto spec1 = singleSpec("%s");
375     auto p1 = Point1(16, 11);
376 
377     formatValue(w1, p1, spec1);
378     assert(w1.data == "(16,11)");
379 
380     // Using a `toString` with a sink
381     static struct Point2
382     {
383         int x, y;
384 
385         void toString(scope void delegate(scope const(char)[]) @safe sink,
386                       scope const FormatSpec!char fmt) const
387         {
388             sink("(");
389             sink.formatValue(x, fmt);
390             sink(",");
391             sink.formatValue(y, fmt);
392             sink(")");
393         }
394     }
395 
396     auto w2 = appender!string();
397     auto spec2 = singleSpec("%03d");
398     auto p2 = Point2(16,11);
399 
400     formatValue(w2, p2, spec2);
401     assert(w2.data == "(016,011)");
402 
403     // Using `string toString()`
404     static struct Point3
405     {
406         int x, y;
407 
408         string toString()
409         {
410             import std.conv : to;
411 
412             return "(" ~ to!string(x) ~ "," ~ to!string(y) ~ ")";
413         }
414     }
415 
416     auto w3 = appender!string();
417     auto spec3 = singleSpec("%s"); // has to be %s
418     auto p3 = Point3(16,11);
419 
420     formatValue(w3, p3, spec3);
421     assert(w3.data == "(16,11)");
422 
423     // without `toString`
424     static struct Point4
425     {
426         int x, y;
427     }
428 
429     auto w4 = appender!string();
430     auto spec4 = singleSpec("%s"); // has to be %s
431     auto p4 = Point4(16,11);
432 
433     formatValue(w4, p4, spec3);
434     assert(w4.data == "Point4(16, 11)");
435 }
436 
437 /// Pointers are formatted as hexadecimal integers.
438 @safe pure unittest
439 {
440     import std.array : appender;
441     import std.format.spec : singleSpec;
442 
443     auto w1 = appender!string();
444     auto spec1 = singleSpec("%s");
445     auto p1 = () @trusted { return cast(void*) 0xFFEECCAA; } ();
446     formatValue(w1, p1, spec1);
447 
448     assert(w1.data == "FFEECCAA");
449 
450     // null pointers are printed as `"null"` when used with `%s` and as hexadecimal integer else
451     auto w2 = appender!string();
452     auto spec2 = singleSpec("%s");
453     auto p2 = () @trusted { return cast(void*) 0x00000000; } ();
454     formatValue(w2, p2, spec2);
455 
456     assert(w2.data == "null");
457 
458     auto w3 = appender!string();
459     auto spec3 = singleSpec("%x");
460     formatValue(w3, p2, spec3);
461 
462     assert(w3.data == "0");
463 }
464 
465 /// SIMD vectors are formatted as arrays.
466 @safe unittest
467 {
468     import core.simd; // cannot be selective, because float4 might not be defined
469     import std.array : appender;
470     import std.format.spec : singleSpec;
471 
472     auto w = appender!string();
473     auto spec = singleSpec("%s");
474 
475     static if (is(float4))
476     {
477         version (X86) {}
478         else
479         {
480             float4 f4;
481             f4.array[0] = 1;
482             f4.array[1] = 2;
483             f4.array[2] = 3;
484             f4.array[3] = 4;
485 
486             formatValue(w, f4, spec);
487             assert(w.data == "[1, 2, 3, 4]");
488         }
489     }
490 }
491 
492 import std.format.internal.write;
493 
494 import std.format.spec : FormatSpec;
495 import std.traits : isSomeString;
496 
497 /**
498 Converts its arguments according to a format string and writes
499 the result to an output range.
500 
501 The second version of `formattedWrite` takes the format string as a
502 template argument. In this case, it is checked for consistency at
503 compile-time.
504 
505 Params:
506     w = an $(REF_ALTTEXT output range, isOutputRange, std, range, primitives),
507         where the formatted result is written to
508     fmt = a $(MREF_ALTTEXT format string, std,format)
509     args = a variadic list of arguments to be formatted
510     Writer = the type of the writer `w`
511     Char = character type of `fmt`
512     Args = a variadic list of types of the arguments
513 
514 Returns:
515     The index of the last argument that was formatted. If no positional
516     arguments are used, this is the number of arguments that where formatted.
517 
518 Throws:
519     A $(REF_ALTTEXT FormatException, FormatException, std, format)
520     if formatting did not succeed.
521 
522 Note:
523     In theory this function should be `@nogc`. But with the current
524     implementation there are some cases where allocations occur.
525     See $(REF_ALTTEXT $(D sformat), sformat, std, format) for more details.
526  */
527 uint formattedWrite(Writer, Char, Args...)(auto ref Writer w, const scope Char[] fmt, Args args)
528 {
529     import std.conv : text;
530     import std.format : enforceFmt, FormatException;
531     import std.traits : isSomeChar;
532 
533     auto spec = FormatSpec!Char(fmt);
534 
535     // Are we already done with formats? Then just dump each parameter in turn
536     uint currentArg = 0;
537     while (spec.writeUpToNextSpec(w))
538     {
539         if (currentArg == Args.length && !spec.indexStart)
540         {
541             // leftover spec?
542             enforceFmt(fmt.length == 0,
543                 text("Orphan format specifier: %", spec.spec));
544             break;
545         }
546 
547         if (spec.width == spec.DYNAMIC)
548         {
549             auto width = getNthInt!"integer width"(currentArg, args);
550             if (width < 0)
551             {
552                 spec.flDash = true;
553                 width = -width;
554             }
555             spec.width = width;
556             ++currentArg;
557         }
558         else if (spec.width < 0)
559         {
560             // means: get width as a positional parameter
561             auto index = cast(uint) -spec.width;
562             assert(index > 0, "The index must be greater than zero");
563             auto width = getNthInt!"integer width"(index - 1, args);
564             if (currentArg < index) currentArg = index;
565             if (width < 0)
566             {
567                 spec.flDash = true;
568                 width = -width;
569             }
570             spec.width = width;
571         }
572 
573         if (spec.precision == spec.DYNAMIC)
574         {
575             auto precision = getNthInt!"integer precision"(currentArg, args);
576             if (precision >= 0) spec.precision = precision;
577             // else negative precision is same as no precision
578             else spec.precision = spec.UNSPECIFIED;
579             ++currentArg;
580         }
581         else if (spec.precision < 0)
582         {
583             // means: get precision as a positional parameter
584             auto index = cast(uint) -spec.precision;
585             assert(index > 0, "The precision must be greater than zero");
586             auto precision = getNthInt!"integer precision"(index- 1, args);
587             if (currentArg < index) currentArg = index;
588             if (precision >= 0) spec.precision = precision;
589             // else negative precision is same as no precision
590             else spec.precision = spec.UNSPECIFIED;
591         }
592 
593         if (spec.separators == spec.DYNAMIC)
594         {
595             auto separators = getNthInt!"separator digit width"(currentArg, args);
596             spec.separators = separators;
597             ++currentArg;
598         }
599 
600         if (spec.dynamicSeparatorChar)
601         {
602             auto separatorChar =
603                 getNth!("separator character", isSomeChar, dchar)(currentArg, args);
604             spec.separatorChar = separatorChar;
605             spec.dynamicSeparatorChar = false;
606             ++currentArg;
607         }
608 
609         if (currentArg == Args.length && !spec.indexStart)
610         {
611             // leftover spec?
612             enforceFmt(fmt.length == 0,
613                 text("Orphan format specifier: %", spec.spec));
614             break;
615         }
616 
617         // Format an argument
618         // This switch uses a static foreach to generate a jump table.
619         // Currently `spec.indexStart` use the special value '0' to signal
620         // we should use the current argument. An enhancement would be to
621         // always store the index.
622         size_t index = currentArg;
623         if (spec.indexStart != 0)
624             index = spec.indexStart - 1;
625         else
626             ++currentArg;
627     SWITCH: switch (index)
628         {
629             foreach (i, Tunused; Args)
630             {
631             case i:
632                 formatValue(w, args[i], spec);
633                 if (currentArg < spec.indexEnd)
634                     currentArg = spec.indexEnd;
635                 // A little know feature of format is to format a range
636                 // of arguments, e.g. `%1:3$` will format the first 3
637                 // arguments. Since they have to be consecutive we can
638                 // just use explicit fallthrough to cover that case.
639                 if (i + 1 < spec.indexEnd)
640                 {
641                     // You cannot goto case if the next case is the default
642                     static if (i + 1 < Args.length)
643                         goto case;
644                     else
645                         goto default;
646                 }
647                 else
648                     break SWITCH;
649             }
650         default:
651             throw new FormatException(
652                 text("Positional specifier %", spec.indexStart, '$', spec.spec,
653                      " index exceeds ", Args.length));
654         }
655     }
656     return currentArg;
657 }
658 
659 ///
660 @safe pure unittest
661 {
662     import std.array : appender;
663 
664     auto writer1 = appender!string();
665     formattedWrite(writer1, "%s is the ultimate %s.", 42, "answer");
666     assert(writer1[] == "42 is the ultimate answer.");
667 
668     auto writer2 = appender!string();
669     formattedWrite(writer2, "Increase: %7.2f %%", 17.4285);
670     assert(writer2[] == "Increase:   17.43 %");
671 }
672 
673 /// ditto
674 uint formattedWrite(alias fmt, Writer, Args...)(auto ref Writer w, Args args)
675 if (isSomeString!(typeof(fmt)))
676 {
677     import std.format : checkFormatException;
678 
679     alias e = checkFormatException!(fmt, Args);
680     static assert(!e, e);
681     return .formattedWrite(w, fmt, args);
682 }
683 
684 /// The format string can be checked at compile-time:
685 @safe pure unittest
686 {
687     import std.array : appender;
688 
689     auto writer = appender!string();
690     writer.formattedWrite!"%d is the ultimate %s."(42, "answer");
691     assert(writer[] == "42 is the ultimate answer.");
692 
693     // This line doesn't compile, because 3.14 cannot be formatted with %d:
694     // writer.formattedWrite!"%d is the ultimate %s."(3.14, "answer");
695 }
696 
697 @safe pure unittest
698 {
699     import std.array : appender;
700 
701     auto stream = appender!string();
702     formattedWrite(stream, "%s", 1.1);
703     assert(stream.data == "1.1", stream.data);
704 }
705 
706 @safe pure unittest
707 {
708     import std.array;
709 
710     auto w = appender!string();
711     formattedWrite(w, "%s %d", "@safe/pure", 42);
712     assert(w.data == "@safe/pure 42");
713 }
714 
715 @safe pure unittest
716 {
717     char[20] buf;
718     auto w = buf[];
719     formattedWrite(w, "%s %d", "@safe/pure", 42);
720     assert(buf[0 .. $ - w.length] == "@safe/pure 42");
721 }
722 
723 @safe pure unittest
724 {
725     import std.algorithm.iteration : map;
726     import std.array : appender;
727 
728     auto stream = appender!string();
729     formattedWrite(stream, "%s", map!"a*a"([2, 3, 5]));
730     assert(stream.data == "[4, 9, 25]", stream.data);
731 
732     // Test shared data.
733     stream = appender!string();
734     shared int s = 6;
735     formattedWrite(stream, "%s", s);
736     assert(stream.data == "6");
737 }
738 
739 @safe pure unittest
740 {
741     // testing positional parameters
742     import std.array : appender;
743     import std.exception : collectExceptionMsg;
744     import std.format : FormatException;
745 
746     auto w = appender!(char[])();
747     formattedWrite(w,
748             "Numbers %2$s and %1$s are reversed and %1$s%2$s repeated",
749             42, 0);
750     assert(w.data == "Numbers 0 and 42 are reversed and 420 repeated",
751             w.data);
752     assert(collectExceptionMsg!FormatException(formattedWrite(w, "%1$s, %3$s", 1, 2))
753         == "Positional specifier %3$s index exceeds 2");
754 
755     w.clear();
756     formattedWrite(w, "asd%s", 23);
757     assert(w.data == "asd23", w.data);
758     w.clear();
759     formattedWrite(w, "%s%s", 23, 45);
760     assert(w.data == "2345", w.data);
761 }
762 
763 // https://issues.dlang.org/show_bug.cgi?id=3479
764 @safe unittest
765 {
766     import std.array : appender;
767 
768     auto stream = appender!(char[])();
769     formattedWrite(stream, "%2$.*1$d", 12, 10);
770     assert(stream.data == "000000000010", stream.data);
771 }
772 
773 // https://issues.dlang.org/show_bug.cgi?id=6893
774 @safe unittest
775 {
776     import std.array : appender;
777 
778     enum E : ulong { A, B, C }
779     auto stream = appender!(char[])();
780     formattedWrite(stream, "%s", E.C);
781     assert(stream.data == "C");
782 }
783 
784 @safe pure unittest
785 {
786     import std.array : appender;
787 
788     auto stream = appender!string();
789     formattedWrite(stream, "%u", 42);
790     assert(stream.data == "42", stream.data);
791 }
792 
793 @safe pure unittest
794 {
795     // testing raw writes
796     import std.array : appender;
797 
798     auto w = appender!(char[])();
799     uint a = 0x02030405;
800     formattedWrite(w, "%+r", a);
801     assert(w.data.length == 4 && w.data[0] == 2 && w.data[1] == 3
802         && w.data[2] == 4 && w.data[3] == 5);
803 
804     w.clear();
805     formattedWrite(w, "%-r", a);
806     assert(w.data.length == 4 && w.data[0] == 5 && w.data[1] == 4
807         && w.data[2] == 3 && w.data[3] == 2);
808 }
809 
810 @safe unittest
811 {
812     import std.array : appender;
813     import std.conv : text, octal;
814 
815     auto stream = appender!(char[])();
816 
817     formattedWrite(stream, "hello world! %s %s ", true, 57, 1_000_000_000, 'x', " foo");
818     assert(stream.data == "hello world! true 57 ", stream.data);
819     stream.clear();
820 
821     formattedWrite(stream, "%g %A %s", 1.67, -1.28, float.nan);
822     assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", stream.data);
823     stream.clear();
824 
825     formattedWrite(stream, "%x %X", 0x1234AF, 0xAFAFAFAF);
826     assert(stream.data == "1234af AFAFAFAF");
827     stream.clear();
828 
829     formattedWrite(stream, "%b %o", 0x1234AF, 0xAFAFAFAF);
830     assert(stream.data == "100100011010010101111 25753727657");
831     stream.clear();
832 
833     formattedWrite(stream, "%d %s", 0x1234AF, 0xAFAFAFAF);
834     assert(stream.data == "1193135 2947526575");
835     stream.clear();
836 
837     formattedWrite(stream, "%a %A", 1.32, 6.78f);
838     assert(stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB86P+2");
839     stream.clear();
840 
841     formattedWrite(stream, "%#06.*f", 2, 12.345);
842     assert(stream.data == "012.35");
843     stream.clear();
844 
845     formattedWrite(stream, "%#0*.*f", 6, 2, 12.345);
846     assert(stream.data == "012.35");
847     stream.clear();
848 
849     const real constreal = 1;
850     formattedWrite(stream, "%g",constreal);
851     assert(stream.data == "1");
852     stream.clear();
853 
854     formattedWrite(stream, "%7.4g:", 12.678);
855     assert(stream.data == "  12.68:");
856     stream.clear();
857 
858     formattedWrite(stream, "%7.4g:", 12.678L);
859     assert(stream.data == "  12.68:");
860     stream.clear();
861 
862     formattedWrite(stream, "%04f|%05d|%#05x|%#5x", -4.0, -10, 1, 1);
863     assert(stream.data == "-4.000000|-0010|0x001|  0x1", stream.data);
864     stream.clear();
865 
866     int i;
867     string s;
868 
869     i = -10;
870     formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
871     assert(stream.data == "-10|-10|-10|-10|-10.0000");
872     stream.clear();
873 
874     i = -5;
875     formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
876     assert(stream.data == "-5| -5|-05|-5|-5.0000");
877     stream.clear();
878 
879     i = 0;
880     formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
881     assert(stream.data == "0|  0|000|0|0.0000");
882     stream.clear();
883 
884     i = 5;
885     formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
886     assert(stream.data == "5|  5|005|5|5.0000");
887     stream.clear();
888 
889     i = 10;
890     formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
891     assert(stream.data == "10| 10|010|10|10.0000");
892     stream.clear();
893 
894     formattedWrite(stream, "%.0d", 0);
895     assert(stream.data == "0");
896     stream.clear();
897 
898     formattedWrite(stream, "%.g", .34);
899     assert(stream.data == "0.3");
900     stream.clear();
901 
902     stream.clear();
903     formattedWrite(stream, "%.0g", .34);
904     assert(stream.data == "0.3");
905 
906     stream.clear();
907     formattedWrite(stream, "%.2g", .34);
908     assert(stream.data == "0.34");
909 
910     stream.clear();
911     formattedWrite(stream, "%0.0008f", 1e-08);
912     assert(stream.data == "0.00000001");
913 
914     stream.clear();
915     formattedWrite(stream, "%0.0008f", 1e-05);
916     assert(stream.data == "0.00001000");
917 
918     s = "helloworld";
919     string r;
920     stream.clear();
921     formattedWrite(stream, "%.2s", s[0 .. 5]);
922     assert(stream.data == "he");
923     stream.clear();
924     formattedWrite(stream, "%.20s", s[0 .. 5]);
925     assert(stream.data == "hello");
926     stream.clear();
927     formattedWrite(stream, "%8s", s[0 .. 5]);
928     assert(stream.data == "   hello");
929 
930     byte[] arrbyte = new byte[4];
931     arrbyte[0] = 100;
932     arrbyte[1] = -99;
933     arrbyte[3] = 0;
934     stream.clear();
935     formattedWrite(stream, "%s", arrbyte);
936     assert(stream.data == "[100, -99, 0, 0]", stream.data);
937 
938     ubyte[] arrubyte = new ubyte[4];
939     arrubyte[0] = 100;
940     arrubyte[1] = 200;
941     arrubyte[3] = 0;
942     stream.clear();
943     formattedWrite(stream, "%s", arrubyte);
944     assert(stream.data == "[100, 200, 0, 0]", stream.data);
945 
946     short[] arrshort = new short[4];
947     arrshort[0] = 100;
948     arrshort[1] = -999;
949     arrshort[3] = 0;
950     stream.clear();
951     formattedWrite(stream, "%s", arrshort);
952     assert(stream.data == "[100, -999, 0, 0]");
953     stream.clear();
954     formattedWrite(stream, "%s", arrshort);
955     assert(stream.data == "[100, -999, 0, 0]");
956 
957     ushort[] arrushort = new ushort[4];
958     arrushort[0] = 100;
959     arrushort[1] = 20_000;
960     arrushort[3] = 0;
961     stream.clear();
962     formattedWrite(stream, "%s", arrushort);
963     assert(stream.data == "[100, 20000, 0, 0]");
964 
965     int[] arrint = new int[4];
966     arrint[0] = 100;
967     arrint[1] = -999;
968     arrint[3] = 0;
969     stream.clear();
970     formattedWrite(stream, "%s", arrint);
971     assert(stream.data == "[100, -999, 0, 0]");
972     stream.clear();
973     formattedWrite(stream, "%s", arrint);
974     assert(stream.data == "[100, -999, 0, 0]");
975 
976     long[] arrlong = new long[4];
977     arrlong[0] = 100;
978     arrlong[1] = -999;
979     arrlong[3] = 0;
980     stream.clear();
981     formattedWrite(stream, "%s", arrlong);
982     assert(stream.data == "[100, -999, 0, 0]");
983     stream.clear();
984     formattedWrite(stream, "%s",arrlong);
985     assert(stream.data == "[100, -999, 0, 0]");
986 
987     ulong[] arrulong = new ulong[4];
988     arrulong[0] = 100;
989     arrulong[1] = 999;
990     arrulong[3] = 0;
991     stream.clear();
992     formattedWrite(stream, "%s", arrulong);
993     assert(stream.data == "[100, 999, 0, 0]");
994 
995     string[] arr2 = new string[4];
996     arr2[0] = "hello";
997     arr2[1] = "world";
998     arr2[3] = "foo";
999     stream.clear();
1000     formattedWrite(stream, "%s", arr2);
1001     assert(stream.data == `["hello", "world", "", "foo"]`, stream.data);
1002 
1003     stream.clear();
1004     formattedWrite(stream, "%.8d", 7);
1005     assert(stream.data == "00000007");
1006 
1007     stream.clear();
1008     formattedWrite(stream, "%.8x", 10);
1009     assert(stream.data == "0000000a");
1010 
1011     stream.clear();
1012     formattedWrite(stream, "%-3d", 7);
1013     assert(stream.data == "7  ");
1014 
1015     stream.clear();
1016     formattedWrite(stream, "%*d", -3, 7);
1017     assert(stream.data == "7  ");
1018 
1019     stream.clear();
1020     formattedWrite(stream, "%.*d", -3, 7);
1021     assert(stream.data == "7");
1022 
1023     stream.clear();
1024     formattedWrite(stream, "%s", "abc"c);
1025     assert(stream.data == "abc");
1026     stream.clear();
1027     formattedWrite(stream, "%s", "def"w);
1028     assert(stream.data == "def", text(stream.data.length));
1029     stream.clear();
1030     formattedWrite(stream, "%s", "ghi"d);
1031     assert(stream.data == "ghi");
1032 
1033     @trusted void* deadBeef() { return cast(void*) 0xDEADBEEF; }
1034     stream.clear();
1035     formattedWrite(stream, "%s", deadBeef());
1036     assert(stream.data == "DEADBEEF", stream.data);
1037 
1038     stream.clear();
1039     formattedWrite(stream, "%#x", 0xabcd);
1040     assert(stream.data == "0xabcd");
1041     stream.clear();
1042     formattedWrite(stream, "%#X", 0xABCD);
1043     assert(stream.data == "0XABCD");
1044 
1045     stream.clear();
1046     formattedWrite(stream, "%#o", octal!12345);
1047     assert(stream.data == "012345");
1048     stream.clear();
1049     formattedWrite(stream, "%o", 9);
1050     assert(stream.data == "11");
1051 
1052     stream.clear();
1053     formattedWrite(stream, "%+d", 123);
1054     assert(stream.data == "+123");
1055     stream.clear();
1056     formattedWrite(stream, "%+d", -123);
1057     assert(stream.data == "-123");
1058     stream.clear();
1059     formattedWrite(stream, "% d", 123);
1060     assert(stream.data == " 123");
1061     stream.clear();
1062     formattedWrite(stream, "% d", -123);
1063     assert(stream.data == "-123");
1064 
1065     stream.clear();
1066     formattedWrite(stream, "%%");
1067     assert(stream.data == "%");
1068 
1069     stream.clear();
1070     formattedWrite(stream, "%d", true);
1071     assert(stream.data == "1");
1072     stream.clear();
1073     formattedWrite(stream, "%d", false);
1074     assert(stream.data == "0");
1075 
1076     stream.clear();
1077     formattedWrite(stream, "%d", 'a');
1078     assert(stream.data == "97", stream.data);
1079     wchar wc = 'a';
1080     stream.clear();
1081     formattedWrite(stream, "%d", wc);
1082     assert(stream.data == "97");
1083     dchar dc = 'a';
1084     stream.clear();
1085     formattedWrite(stream, "%d", dc);
1086     assert(stream.data == "97");
1087 
1088     byte b = byte.max;
1089     stream.clear();
1090     formattedWrite(stream, "%x", b);
1091     assert(stream.data == "7f");
1092     stream.clear();
1093     formattedWrite(stream, "%x", ++b);
1094     assert(stream.data == "80");
1095     stream.clear();
1096     formattedWrite(stream, "%x", ++b);
1097     assert(stream.data == "81");
1098 
1099     short sh = short.max;
1100     stream.clear();
1101     formattedWrite(stream, "%x", sh);
1102     assert(stream.data == "7fff");
1103     stream.clear();
1104     formattedWrite(stream, "%x", ++sh);
1105     assert(stream.data == "8000");
1106     stream.clear();
1107     formattedWrite(stream, "%x", ++sh);
1108     assert(stream.data == "8001");
1109 
1110     i = int.max;
1111     stream.clear();
1112     formattedWrite(stream, "%x", i);
1113     assert(stream.data == "7fffffff");
1114     stream.clear();
1115     formattedWrite(stream, "%x", ++i);
1116     assert(stream.data == "80000000");
1117     stream.clear();
1118     formattedWrite(stream, "%x", ++i);
1119     assert(stream.data == "80000001");
1120 
1121     stream.clear();
1122     formattedWrite(stream, "%x", 10);
1123     assert(stream.data == "a");
1124     stream.clear();
1125     formattedWrite(stream, "%X", 10);
1126     assert(stream.data == "A");
1127     stream.clear();
1128     formattedWrite(stream, "%x", 15);
1129     assert(stream.data == "f");
1130     stream.clear();
1131     formattedWrite(stream, "%X", 15);
1132     assert(stream.data == "F");
1133 
1134     @trusted void ObjectTest()
1135     {
1136         Object c = null;
1137         stream.clear();
1138         formattedWrite(stream, "%s", c);
1139         assert(stream.data == "null");
1140     }
1141     ObjectTest();
1142 
1143     enum TestEnum
1144     {
1145         Value1, Value2
1146     }
1147     stream.clear();
1148     formattedWrite(stream, "%s", TestEnum.Value2);
1149     assert(stream.data == "Value2", stream.data);
1150     stream.clear();
1151     formattedWrite(stream, "%s", cast(TestEnum) 5);
1152     assert(stream.data == "cast(TestEnum)5", stream.data);
1153 
1154     //immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
1155     //stream.clear();
1156     //formattedWrite(stream, "%s", aa.values);
1157     //assert(stream.data == "[[h,e,l,l,o],[b,e,t,t,y]]");
1158     //stream.clear();
1159     //formattedWrite(stream, "%s", aa);
1160     //assert(stream.data == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]");
1161 
1162     static const dchar[] ds = ['a','b'];
1163     for (int j = 0; j < ds.length; ++j)
1164     {
1165         stream.clear(); formattedWrite(stream, " %d", ds[j]);
1166         if (j == 0)
1167             assert(stream.data == " 97");
1168         else
1169             assert(stream.data == " 98");
1170     }
1171 
1172     stream.clear();
1173     formattedWrite(stream, "%.-3d", 7);
1174     assert(stream.data == "7", ">" ~ stream.data ~ "<");
1175 }
1176 
1177 @safe unittest
1178 {
1179     import std.array : appender;
1180     import std.meta : AliasSeq;
1181 
1182     immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
1183     assert(aa[3] == "hello");
1184     assert(aa[4] == "betty");
1185 
1186     auto stream = appender!(char[])();
1187     alias AllNumerics =
1188         AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong,
1189                   float, double, real);
1190     foreach (T; AllNumerics)
1191     {
1192         T value = 1;
1193         stream.clear();
1194         formattedWrite(stream, "%s", value);
1195         assert(stream.data == "1");
1196     }
1197 
1198     stream.clear();
1199     formattedWrite(stream, "%s", aa);
1200 }
1201 
1202 /**
1203 Formats a value of any type according to a format specifier and
1204 writes the result to an output range.
1205 
1206 More details about how types are formatted, and how the format
1207 specifier influences the outcome, can be found in the definition of a
1208 $(MREF_ALTTEXT format string, std,format).
1209 
1210 Params:
1211     w = an $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) where
1212         the formatted value is written to
1213     val = the value to write
1214     f = a $(REF_ALTTEXT FormatSpec, FormatSpec, std, format, spec) defining the
1215         format specifier
1216     Writer = the type of the output range `w`
1217     T = the type of value `val`
1218     Char = the character type used for `f`
1219 
1220 Throws:
1221     A $(LREF FormatException) if formatting did not succeed.
1222 
1223 Note:
1224     In theory this function should be `@nogc`. But with the current
1225     implementation there are some cases where allocations occur.
1226     See $(REF_ALTTEXT $(D sformat), sformat, std, format) for more details.
1227 
1228 See_Also:
1229     $(LREF formattedWrite) which formats several values at once.
1230  */
1231 void formatValue(Writer, T, Char)(auto ref Writer w, auto ref T val, scope const ref FormatSpec!Char f)
1232 {
1233     import std.format : enforceFmt;
1234 
1235     enforceFmt(f.width != f.DYNAMIC && f.precision != f.DYNAMIC
1236                && f.separators != f.DYNAMIC && !f.dynamicSeparatorChar,
1237                "Dynamic argument not allowed for `formatValue`");
1238 
1239     formatValueImpl(w, val, f);
1240 }
1241 
1242 ///
1243 @safe pure unittest
1244 {
1245     import std.array : appender;
1246     import std.format.spec : singleSpec;
1247 
1248     auto writer = appender!string();
1249     auto spec = singleSpec("%08b");
1250     writer.formatValue(42, spec);
1251     assert(writer.data == "00101010");
1252 
1253     spec = singleSpec("%2s");
1254     writer.formatValue('=', spec);
1255     assert(writer.data == "00101010 =");
1256 
1257     spec = singleSpec("%+14.6e");
1258     writer.formatValue(42.0, spec);
1259     assert(writer.data == "00101010 = +4.200000e+01");
1260 }
1261 
1262 // https://issues.dlang.org/show_bug.cgi?id=15386
1263 @safe pure unittest
1264 {
1265     import std.array : appender;
1266     import std.format.spec : FormatSpec;
1267     import std.format : FormatException;
1268     import std.exception : assertThrown;
1269 
1270     auto w = appender!(char[])();
1271     auto dor = appender!(char[])();
1272     auto fs = FormatSpec!char("%.*s");
1273     fs.writeUpToNextSpec(dor);
1274     assertThrown!FormatException(formatValue(w, 0, fs));
1275 
1276     fs = FormatSpec!char("%*s");
1277     fs.writeUpToNextSpec(dor);
1278     assertThrown!FormatException(formatValue(w, 0, fs));
1279 
1280     fs = FormatSpec!char("%,*s");
1281     fs.writeUpToNextSpec(dor);
1282     assertThrown!FormatException(formatValue(w, 0, fs));
1283 
1284     fs = FormatSpec!char("%,?s");
1285     fs.writeUpToNextSpec(dor);
1286     assertThrown!FormatException(formatValue(w, 0, fs));
1287 
1288     assertThrown!FormatException(formattedWrite(w, "%(%0*d%)", new int[1]));
1289 }
1290 
1291 // https://issues.dlang.org/show_bug.cgi?id=22609
1292 @safe pure unittest
1293 {
1294     static enum State: ubyte { INACTIVE }
1295     static struct S {
1296         State state = State.INACTIVE;
1297         int generation = 1;
1298         alias state this;
1299         // DMDBUG: https://issues.dlang.org/show_bug.cgi?id=16657
1300         auto opEquals(S other) const { return state == other.state && generation == other.generation; }
1301         auto opEquals(State other) const { return state == other; }
1302     }
1303 
1304     import std.array : appender;
1305     import std.format.spec : singleSpec;
1306 
1307     auto writer = appender!string();
1308     const spec = singleSpec("%s");
1309     S a;
1310     writer.formatValue(a, spec);
1311     assert(writer.data == "0");
1312 }
1313 
1314 // https://issues.dlang.org/show_bug.cgi?id=23400
1315 @safe pure unittest
1316 {
1317     import std.range : nullSink;
1318     import std.format.spec : singleSpec;
1319 
1320     static struct S
1321     {
1322         // non-const opEquals method
1323         bool opEquals(S rhs) { return false; }
1324     }
1325 
1326     enum E { a = S() }
1327 
1328     E e;
1329     auto writer = nullSink;
1330     const spec = singleSpec("%s");
1331     writer.formatValue(e, spec);
1332 }