1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2005 Kris. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         version:        Initial release: 2005
8 
9         author:         Kris, Keinfarbton
10 
11         This module provides a general-purpose formatting system to
12         convert values to text suitable for display. There is support
13         for alignment, justification, and common format specifiers for
14         numbers.
15 
16         Layout can be customized via configuring various handlers and
17         associated meta-data. This is utilized to plug in text.locale
18         for handling custom formats, date/time and culture-specific
19         conversions.
20 
21         The format notation is influenced by that used by the .NET
22         and ICU frameworks, rather than C-style printf or D-style
23         writef notation.
24 
25 ******************************************************************************/
26 
27 module tango.text.convert.Layout;
28 
29 private import  tango.core.Exception;
30 
31 private import  Utf = tango.text.convert.Utf;
32 
33 private import  Float = tango.text.convert.Float,
34                 Integer = tango.text.convert.Integer;
35 
36 private import  tango.io.model.IConduit;
37 
38 version(WithVariant)
39         private import tango.core.Variant;
40 
41 version(WithExtensions)
42         private import tango.text.convert.Extensions;
43 else
44 version (WithDateTime)
45         {
46         private import tango.time.Time;
47         private import tango.text.convert.DateTime;
48         }
49 
50 
51 /*******************************************************************************
52 
53         Platform issues ...
54 
55 *******************************************************************************/
56 
57 version (GNU)
58         {
59         private import tango.core.Vararg;
60         alias void* Arg;
61         alias va_list ArgList;
62         }
63 else version(LDC)
64         {
65         private import tango.core.Vararg;
66         alias void* Arg;
67         alias va_list ArgList;
68         }
69 else version(DigitalMars)
70         {
71         private import tango.core.Vararg;
72         alias void* Arg;
73         alias va_list ArgList;
74 
75         version(X86_64) version = DigitalMarsX64;
76         }
77      else
78         {
79         alias void* Arg;
80         alias void* ArgList;
81         }
82 
83 private alias aaApplyDg = int delegate(void*, void*);
84 private extern(C) int _aaApply(void* aa, in size_t ignored, aaApplyDg dg);
85 /*******************************************************************************
86 
87         Contains methods for replacing format items in a string with string
88         equivalents of each argument.
89 
90 *******************************************************************************/
91 
92 class Layout(T)
93 {
94         public alias convert opCall;
95         public alias scope size_t delegate (const(T)[]) Sink;
96        
97         static if (is (DateTimeLocale))
98                    private DateTimeLocale* dateTime = &DateTimeDefault;
99 
100         /**********************************************************************
101 
102                 Return shared instance
103 
104                 Note that this is not threadsafe, and that static-ctor
105                 usage doesn't get invoked appropriately (compiler bug)
106 
107         **********************************************************************/
108 
109         @property static Layout instance ()
110         {
111                 static __gshared Layout common;
112 
113                 if (common is null)
114                     common = new Layout!(T);
115                 return common;
116         }
117 
118         /**********************************************************************
119 
120         **********************************************************************/
121 
122         public final T[] sprint (T[] result, const(T)[] formatStr, ...)
123         {
124                 version (DigitalMarsX64)
125                 {
126                     va_list ap;
127 
128                     va_start(ap, formatStr);
129 
130                     scope(exit) va_end(ap);
131 
132                     return vprint (result, formatStr, _arguments, ap);
133                 }
134                 else
135                     return vprint (result, formatStr, _arguments, _argptr);
136         }
137 
138         /**********************************************************************
139 
140         **********************************************************************/
141 
142         public final T[] vprint (T[] result, const(T)[] formatStr, TypeInfo[] arguments, ArgList args)
143         {
144                 T*  p = result.ptr;
145                 auto available = result.length;
146 
147                 size_t sink (const(T)[] s)
148                 {
149                         auto len = s.length;
150                         if (len > available)
151                             len = available;
152 
153                         available -= len;
154                         p [0..len] = s[0..len];
155                         p += len;
156                         return len;
157                 }
158 
159                 convert (&sink, arguments, args, formatStr);
160                 return result [0 .. cast(size_t) (p-result.ptr)];
161         }
162 
163         /**********************************************************************
164 
165                 Replaces the _format item in a string with the string
166                 equivalent of each argument.
167 
168                 Params:
169                   formatStr  = A string containing _format items.
170                   args       = A list of arguments.
171 
172                 Returns: A copy of formatStr in which the items have been
173                 replaced by the string equivalent of the arguments.
174 
175                 Remarks: The formatStr parameter is embedded with _format
176                 items of the form: $(BR)$(BR)
177                   {index[,alignment][:_format-string]}$(BR)$(BR)
178                   $(UL $(LI index $(BR)
179                     An integer indicating the element in a list to _format.)
180                   $(LI alignment $(BR)
181                     An optional integer indicating the minimum width. The
182                     result is padded with spaces if the length of the value
183                     is less than alignment.)
184                   $(LI _format-string $(BR)
185                     An optional string of formatting codes.)
186                 )$(BR)
187 
188                 The leading and trailing braces are required. To include a
189                 literal brace character, use two leading or trailing brace
190                 characters.$(BR)$(BR)
191                 If formatStr is "{0} bottles of beer on the wall" and the
192                 argument is an int with the value of 99, the return value
193                 will be:$(BR) "99 bottles of beer on the wall".
194 
195         **********************************************************************/
196 
197         public final T[] convert (const(T)[] formatStr, ...)
198         {
199                 version (DigitalMarsX64)
200                 {
201                     va_list ap;
202 
203                     va_start(ap, formatStr);
204 
205                     scope(exit) va_end(ap);
206 
207                     return convert (_arguments, ap, formatStr);
208                 }
209                 else
210                     return convert (_arguments, _argptr, formatStr);
211         }
212 
213         /**********************************************************************
214 
215         **********************************************************************/
216 
217         public final uint convert (Sink sink, const(T)[] formatStr, ...)
218         {
219                 version (DigitalMarsX64)
220                 {
221                     va_list ap;
222 
223                     va_start(ap, formatStr);
224 
225                     scope(exit) va_end(ap);
226 
227                     return convert (sink, _arguments, ap, formatStr);
228                 }
229                 else
230                     return convert (sink, _arguments, _argptr, formatStr);
231         }
232 
233         /**********************************************************************
234 
235             Tentative convert using an OutputStream as sink - may still be
236             removed.
237 
238             Since: 0.99.7
239 
240         **********************************************************************/
241 
242         public final uint convert (OutputStream output, const(T)[] formatStr, ...)
243         {
244                 size_t sink (const(T)[] s)
245                 {
246                         return output.write(s);
247                 }
248 
249 
250                 version (DigitalMarsX64)
251                 {
252                     va_list ap;
253 
254                     va_start(ap, formatStr);
255 
256                     scope(exit) va_end(ap);
257 
258                     return convert (&sink, _arguments, ap, formatStr);
259                 }
260                 else
261                     return convert (&sink, _arguments, _argptr, formatStr);
262         }
263 
264         /**********************************************************************
265 
266         **********************************************************************/
267 
268         public final T[] convert (TypeInfo[] arguments, ArgList args, const(T)[] formatStr)
269         {
270                 T[] output;
271 
272                 size_t sink (const(T)[] s)
273                 {
274                         output ~= s;
275                         return s.length;
276                 }
277 
278                 convert (&sink, arguments, args, formatStr);
279                 return output;
280         }
281 
282         /**********************************************************************
283 
284         **********************************************************************/
285 
286         version (old) public final T[] convertOne (T[] result, TypeInfo ti, Arg arg)
287         {
288                 return dispatch (result, null, ti, arg);
289         }
290 
291         /**********************************************************************
292 
293         **********************************************************************/
294 
295         public final uint convert (Sink sink, TypeInfo[] arguments, ArgList args, const(T)[] formatStr)
296         {
297                 assert (formatStr, "null format specifier");
298                 assert (arguments.length < 64, "too many args in Layout.convert");
299 
300                 version (GNU)
301                         {
302                         union ArgU {int i; byte b; long l; short s; void[] a;
303                                     real r; float f; double d;
304                                     cfloat cf; cdouble cd; creal cr;}
305 
306                         Arg[64] arglist = void;
307                         ArgU[64] storedArgs = void;
308 
309                         foreach (i, arg; arguments)
310                                 {
311                                 static if (is(typeof(args.ptr)))
312                                     arglist[i] = args.ptr;
313                                 else
314                                    arglist[i] = args;
315 
316                                 /* Since floating point types don't live on
317                                  * the stack, they must be accessed by the
318                                  * correct type. */
319                                 bool converted = false;
320                                 switch (arg.classinfo.name[9])
321                                        {
322                                        case TypeCode.FLOAT, TypeCode.IFLOAT:
323                                             storedArgs[i].f = va_arg!(float)(args);
324                                             arglist[i] = &(storedArgs[i].f);
325                                             converted = true;
326                                             break;
327 
328                                        case TypeCode.CFLOAT:
329                                             storedArgs[i].cf = va_arg!(cfloat)(args);
330                                             arglist[i] = &(storedArgs[i].cf);
331                                             converted = true;
332                                             break;
333 
334                                        case TypeCode.DOUBLE, TypeCode.IDOUBLE:
335                                             storedArgs[i].d = va_arg!(double)(args);
336                                             arglist[i] = &(storedArgs[i].d);
337                                             converted = true;
338                                             break;
339 
340                                        case TypeCode.CDOUBLE:
341                                             storedArgs[i].cd = va_arg!(cdouble)(args);
342                                             arglist[i] = &(storedArgs[i].cd);
343                                             converted = true;
344                                             break;
345 
346                                        case TypeCode.REAL, TypeCode.IREAL:
347                                             storedArgs[i].r = va_arg!(real)(args);
348                                             arglist[i] = &(storedArgs[i].r);
349                                             converted = true;
350                                             break;
351 
352                                        case TypeCode.CREAL:
353                                             storedArgs[i].cr = va_arg!(creal)(args);
354                                             arglist[i] = &(storedArgs[i].cr);
355                                             converted = true;
356                                             break;
357 
358                                        default:
359                                             break;
360                                         }
361                                 if (! converted)
362                                    {
363                                    switch (arg.tsize)
364                                           {
365                                           case 1:
366                                                storedArgs[i].b = va_arg!(byte)(args);
367                                                arglist[i] = &(storedArgs[i].b);
368                                                break;
369                                           case 2:
370                                                storedArgs[i].s = va_arg!(short)(args);
371                                                arglist[i] = &(storedArgs[i].s);
372                                                break;
373                                           case 4:
374                                                storedArgs[i].i = va_arg!(int)(args);
375                                                arglist[i] = &(storedArgs[i].i);
376                                                break;
377                                           case 8:
378                                                storedArgs[i].l = va_arg!(long)(args);
379                                                arglist[i] = &(storedArgs[i].l);
380                                                break;
381                                           case 16:
382                                                assert((void[]).sizeof==16,"Structure size not supported");
383                                                storedArgs[i].a = va_arg!(void[])(args);
384                                                arglist[i] = &(storedArgs[i].a);
385                                                break;
386                                           default:
387                                                assert (false, "Unknown size: " ~ Integer.toString (arg.tsize));
388                                           }
389                                    }
390                                 }
391                         }
392                     else version(DigitalMarsX64)
393                     {
394                         Arg[64] arglist = void;
395                         void[] buffer;
396                         uint len = 0;
397 
398                         foreach(i, argType; arguments)
399                             len +=  (argType.tsize + size_t.sizeof - 1) & ~ (size_t.sizeof - 1);
400 
401                         buffer.length = len;
402                         len = 0;
403                         foreach(i, argType; arguments)
404                         {
405                             //printf("type: %s\n", argType.classinfo.name.ptr);
406 
407                             va_arg(args, argType, buffer.ptr+len);
408 
409                             if(argType.classinfo.name.length != 25 && argType.classinfo.name[9] == TypeCode.ARRAY &&
410                                 (argType.classinfo.name[10] == TypeCode.USHORT ||
411                                 argType.classinfo.name[10] == TypeCode.SHORT))
412                                 {
413                                     printf("Warning: (u)short[] is broken for varargs in x86_64");
414                                     // simply disable the array for now
415                                     (cast(short[]*) (buffer.ptr+len)).length = 0;
416                                 }
417 
418                             arglist[i] = &buffer[len];
419 
420                             len+= (argType.tsize + size_t.sizeof - 1) & ~ (size_t.sizeof - 1);
421                         }
422 
423                         scope (exit) buffer.destroy;
424                     }
425                     else 
426                     {
427                         Arg[64] arglist = void;
428                         foreach (i, arg; arguments)
429                                 {
430                                 arglist[i] = args;
431                                 args += (arg.tsize + size_t.sizeof - 1) & ~ (size_t.sizeof - 1);
432                                 }
433                     }
434                 return parse (formatStr, arguments, arglist, sink);
435         }
436 
437         /**********************************************************************
438 
439                 Parse the format-string, emitting formatted args and text
440                 fragments as we go
441 
442         **********************************************************************/
443 
444         private uint parse (const(T)[] layout, TypeInfo[] ti, Arg[] args, Sink sink)
445         {
446                 T[512] result = void;
447                 int length, nextIndex;
448 
449 
450                 const(T)* s = layout.ptr;
451                 const(T)* fragment = s;
452                 const(T)* end = s + layout.length;
453 
454                 while (true)
455                       {
456                       while (s < end && *s != '{')
457                              ++s;
458 
459                       // emit fragment
460                       length += sink (fragment [0 .. cast(size_t) (s - fragment)]);
461 
462                       // all done?
463                       if (s is end)
464                           break;
465 
466                       // check for "{{" and skip if so
467                       if (*++s is '{')
468                          {
469                          fragment = s++;
470                          continue;
471                          }
472 
473                       int index = 0;
474                       bool indexed = false;
475 
476                       // extract index
477                       while (*s >= '0' && *s <= '9')
478                             {
479                             index = index * 10 + *s++ -'0';
480                             indexed = true;
481                             }
482 
483                       // skip spaces
484                       while (s < end && *s is ' ')
485                              ++s;
486 
487                       bool crop;
488                       bool left;
489                       bool right;
490                       int  width;
491 
492                       // has minimum or maximum width?
493                       if (*s is ',' || *s is '.')
494                          {
495                          if (*s is '.')
496                              crop = true;
497 
498                          while (++s < end && *s is ' ') {}
499                          if (*s is '-')
500                             {
501                             left = true;
502                             ++s;
503                             }
504                          else
505                             right = true;
506 
507                          // get width
508                          while (*s >= '0' && *s <= '9')
509                                 width = width * 10 + *s++ -'0';
510 
511                          // skip spaces
512                          while (s < end && *s is ' ')
513                                 ++s;
514                          }
515 
516                       const(T)[] format;
517 
518                       // has a format string?
519                       if (*s is ':' && s < end)
520                          {
521                          const(T)* fs = ++s;
522 
523                          // eat everything up to closing brace
524                          while (s < end && *s != '}')
525                                 ++s;
526                          format = fs [0 .. cast(size_t) (s - fs)];
527                          }
528 
529                       // insist on a closing brace
530                       if (*s != '}')
531                          {
532                          length += sink ("{malformed format}");
533                          continue;
534                          }
535 
536                       // check for default index & set next default counter
537                       if (! indexed)
538                             index = nextIndex;
539                       nextIndex = index + 1;
540 
541                       // next char is start of following fragment
542                       fragment = ++s;
543 
544                       // handle alignment
545                       void emit (const(T)[] str)
546                       {
547                                 int padding = width - cast(int)str.length;
548 
549                                 if (crop)
550                                    {
551                                    if (padding < 0)
552                                       {
553                                       if (left)
554                                          {
555                                          length += sink ("...");
556                                          length += sink (Utf.cropLeft (str[-padding..$]));
557                                          }
558                                       else
559                                          {
560                                          length += sink (Utf.cropRight (str[0..width]));
561                                          length += sink ("...");
562                                          }
563                                       }
564                                    else
565                                        length += sink (str);
566                                    }
567                                 else
568                                    {
569                                    // if right aligned, pad out with spaces
570                                    if (right && padding > 0)
571                                        length += spaces (sink, padding);
572 
573                                    // emit formatted argument
574                                    length += sink (str);
575 
576                                    // finally, pad out on right
577                                    if (left && padding > 0)
578                                        length += spaces (sink, padding);
579                                    }
580                       }
581 
582                       // an astonishing number of typehacks needed to handle arrays :(
583                       void process (const(TypeInfo) _ti, Arg _arg)
584                       {
585                           assert(_ti !is null, "typeinfo can't be null");
586                                 if ((_ti.classinfo.name.length is 14  && _ti.classinfo.name[9..$] == "Const") ||
587                                     (_ti.classinfo.name.length is 18  && _ti.classinfo.name[9..$] == "Invariant") ||
588                                     (_ti.classinfo.name.length is 15  && _ti.classinfo.name[9..$] == "Shared") ||
589                                     (_ti.classinfo.name.length is 14  && _ti.classinfo.name[9..$] == "Inout"))
590                                 {
591                                     process((cast(TypeInfo_Const)_ti).base, _arg);
592                                     return;
593                                 }
594                                 // Because Variants can contain AAs (and maybe
595                                 // even static arrays someday), we need to
596                                 // process them here.
597 version (WithVariant)
598 {
599                                 if (_ti is typeid(Variant))
600                                    {
601                                    // Unpack the variant and forward
602                                    auto vptr = cast(Variant*)_arg;
603                                    auto innerTi = vptr.type;
604                                    auto innerArg = vptr.ptr;
605                                    process (innerTi, innerArg);
606                                    }
607 }
608                                 if (_ti.classinfo.name.length is 20 && _ti.classinfo.name[9..$] == "StaticArray" )
609                                    {
610                                    auto tiStat = cast(TypeInfo_StaticArray)_ti;
611                                    auto p = _arg;
612                                    length += sink ("[");
613                                    for (int i = 0; i < tiStat.len; i++)
614                                        {
615                                        if (p !is _arg )
616                                            length += sink (", ");
617                                        process (tiStat.value, p);
618                                        p += tiStat.tsize/tiStat.len;
619                                        }
620                                    length += sink ("]");
621                                    }
622                                 else
623                                 if (_ti.classinfo.name.length is 25 && _ti.classinfo.name[9..$] == "AssociativeArray")
624                                    {
625                                    auto tiAsso = cast(TypeInfo_AssociativeArray)_ti;
626                                    auto tiKey = tiAsso.key;
627                                    auto tiVal = tiAsso.next();
628                                    bool first = true;
629 
630                                    length += sink ("{");
631 
632                                    // The argument is a pointer to a struct. We need to get that
633                                    // struct itself.
634                                    _aaApply(*cast(void**)_arg, 0, delegate int(void* pk, void* pv)
635                                    {
636                                        if (!first)
637                                             length += sink (", ");
638                                        process (tiKey, pk);
639                                        length += sink (" => ");
640                                        process (tiVal, pv);
641                                        first = false;
642                                        return 0;
643                                    });
644                                    length += sink ("}");
645                                    }
646                                 else
647                                 if (_ti.classinfo.name[9] is TypeCode.ARRAY)
648                                    {
649                                    if (_ti is typeid(char[]) || _ti is typeid(immutable(char)[]) || _ti is typeid(const(char)[]))
650                                        emit (Utf.fromString8 (*cast(char[]*) _arg, result));
651                                    else
652                                    if (_ti is typeid(wchar[]) || _ti is typeid(immutable(wchar)[]) || _ti is typeid(const(wchar)[]))
653                                        emit (Utf.fromString16 (*cast(wchar[]*) _arg, result));
654                                    else
655                                    if (_ti is typeid(dchar[]) || _ti is typeid(immutable(dchar)[]) || _ti is typeid(const(dchar)[]))
656                                        emit (Utf.fromString32 (*cast(dchar[]*) _arg, result));
657                                    else
658                                       {
659                                       // for all non string array types (including char[][])
660                                       auto arr = *cast(void[]*)_arg;
661                                       auto len = arr.length;
662                                       auto ptr = cast(Arg) arr.ptr;
663                                       auto elTi = (cast()_ti).next(); /* Cast courtesy of D2 */
664                                       auto size = elTi.tsize();
665                                       length += sink ("[");
666                                       while (len > 0)
667                                             {
668                                             if (ptr !is arr.ptr)
669                                                 length += sink (", ");
670                                             process (elTi, ptr);
671                                             len -= 1;
672                                             ptr += size;
673                                             }
674                                       length += sink ("]");
675                                       }
676                                    }
677                                 else
678                                    // the standard processing
679                                    emit (dispatch (result, format, _ti, _arg));
680                       }
681 
682 
683                       // process this argument
684                       if (index >= ti.length)
685                           emit ("{invalid index}");
686                       else
687                          process (ti[index], args[index]);
688                       }
689                 return length;
690         }
691 
692         /***********************************************************************
693 
694         ***********************************************************************/
695 
696         private T[] dispatch (T[] result, const(T)[] format, const(TypeInfo) type, Arg p)
697         {
698                 switch (type.classinfo.name[9])
699                        {
700                        case TypeCode.BOOL:
701                             __gshared immutable T[] t = cast(immutable T[])"true";
702                             __gshared immutable T[] f = cast(immutable T[])"false";
703                             /* Bad dup? */
704                             return (*cast(bool*) p) ? t.dup : f.dup;
705 
706                        case TypeCode.BYTE:
707                             return integer (result, *cast(byte*) p, format, ubyte.max);
708 
709                        case TypeCode.VOID:
710                        case TypeCode.UBYTE:
711                             return integer (result, *cast(ubyte*) p, format, ubyte.max, "u");
712 
713                        case TypeCode.SHORT:
714                             return integer (result, *cast(short*) p, format, ushort.max);
715 
716                        case TypeCode.USHORT:
717                             return integer (result, *cast(ushort*) p, format, ushort.max, "u");
718 
719                        case TypeCode.INT:
720                             return integer (result, *cast(int*) p, format, uint.max);
721 
722                        case TypeCode.UINT:
723                             return integer (result, *cast(uint*) p, format, uint.max, "u");
724 
725                        case TypeCode.ULONG:
726                             return integer (result, *cast(long*) p, format, ulong.max, "u");
727 
728                        case TypeCode.LONG:
729                             return integer (result, *cast(long*) p, format, ulong.max);
730 
731                        case TypeCode.FLOAT:
732                             return floater (result, *cast(float*) p, format);
733 
734                        case TypeCode.IFLOAT:
735                             return imaginary (result, *cast(ifloat*) p, format);
736 
737                        case TypeCode.IDOUBLE:
738                             return imaginary (result, *cast(idouble*) p, format);
739 
740                        case TypeCode.IREAL:
741                            return imaginary (result, *cast(ireal*) p, format);
742 
743                        case TypeCode.CFLOAT:
744                             return complex (result, *cast(cfloat*) p, format);
745 
746                        case TypeCode.CDOUBLE:
747                             return complex (result, *cast(cdouble*) p, format);
748 
749                        case TypeCode.CREAL:
750                             return complex (result, *cast(creal*) p, format);
751 
752                        case TypeCode.DOUBLE:
753                             return floater (result, *cast(double*) p, format);
754 
755                        case TypeCode.REAL:
756                             return floater (result, *cast(real*) p, format);
757 
758                        case TypeCode.CHAR:
759                             return Utf.fromString8 ((cast(char*) p)[0..1], result);
760 
761                        case TypeCode.WCHAR:
762                             return Utf.fromString16 ((cast(wchar*) p)[0..1], result);
763 
764                        case TypeCode.DCHAR:
765                             return Utf.fromString32 ((cast(dchar*) p)[0..1], result);
766 
767                        case TypeCode.POINTER:
768                             return integer (result, *cast(size_t*) p, format, size_t.max, "x");
769 
770                        case TypeCode.CLASS:
771                             auto c = *cast(Object*) p;
772                             if (c)
773                                 return cast(T[])Utf.fromString8 (c.toString(), result);
774                             break;
775 
776                        case TypeCode.STRUCT:
777                             auto s = cast(TypeInfo_Struct) type;
778                             if (s.xtoString)
779                                {
780                                char[] delegate() toString;
781                                toString.ptr = p;
782                                toString.funcptr = cast(char[] function())s.xtoString;
783                                return Utf.fromString8 (toString(), result);
784                                }
785                             goto default;
786 
787                        case TypeCode.INTERFACE:
788                             auto x = *cast(void**) p;
789                             if (x)
790                                {
791                                auto pi = **cast(Interface ***) x;
792                                auto o = cast(Object)(*cast(void**)p - pi.offset);
793                                return cast(T[])Utf.fromString8 (o.toString(), result);
794                                }
795                             break;
796 
797                        case TypeCode.ENUM:
798                             return dispatch (result, format, (cast(TypeInfo_Enum) type).base, p);
799 
800                        //case TypeCode.TYPEDEF:
801                        //     return dispatch (result, format, (cast(TypeInfo_Typedef) type).base, p);
802 
803                        default:
804                             return unknown (result, format, type, p);
805                        }
806 
807                 return cast(T[]) "{null}";
808         }
809 
810         /**********************************************************************
811 
812                 handle "unknown-type" errors
813 
814         **********************************************************************/
815 
816         protected T[] unknown (T[] result, const(T)[] format, const(TypeInfo) type, Arg p)
817         {
818         version (WithExtensions)
819                 {
820                 result = Extensions!(T).run (type, result, p, format);
821                 return (result) ? result :
822                        "{unhandled argument type: " ~ Utf.fromString8 (type.toString, result) ~ "}";
823                 }
824              else
825                 version (WithDateTime)
826                 {
827                 if (type is typeid(Time))
828                    {
829                    static if (is (T == char))
830                               return dateTime.format(result, *cast(Time*) p, format);
831                           else
832                              {
833                              // TODO: this needs to be cleaned up
834                              char[128] tmp0 = void;
835                              char[128] tmp1 = void;
836                              return Utf.fromString8(dateTime.format(tmp0, *cast(Time*) p, Utf.toString(format, tmp1)), result);
837                              }
838                    }
839                 }
840                 return cast(T[])"{unhandled argument type: " ~ cast(T[])Utf.fromString8 ((cast()type).toString(), result) ~ cast(T[])"}";/* Cast courtesy of D2 */
841         }
842 
843         /**********************************************************************
844 
845                 Format an integer value
846 
847         **********************************************************************/
848 
849         protected T[] integer (T[] output, long v, const(T)[] format, ulong mask = ulong.max, const(T)[] def="d")
850         {
851                 if (format.length is 0)
852                     format = def;
853                 if (format[0] != 'd')
854                     v &= mask;
855 
856                 return Integer.format (output, v, format);
857         }
858 
859         /**********************************************************************
860 
861                 format a floating-point value. Defaults to 2 decimal places
862 
863         **********************************************************************/
864 
865         protected T[] floater (T[] output, real v, const(T)[] format)
866         {
867                 uint dec = 2,
868                      exp = 10;
869                 bool pad = true;
870 
871                 for (auto p=format.ptr, e=p+format.length; p < e; ++p)
872                      switch (*p)
873                             {
874                             case '.':
875                                  pad = false;
876                                  break;
877                             case 'e':
878                             case 'E':
879                                  exp = 0;
880                                  break;
881                             case 'x':
882                             case 'X':
883                                  double d = v;
884                                  return integer (output, *cast(long*) &d, "x#");
885                             default:
886                                  auto c = cast(T)*p;
887                                  if (c >= '0' && c <= '9')
888                                     {
889                                     dec = c - '0', c = p[1];
890                                     if (c >= '0' && c <= '9' && ++p < e)
891                                         dec = dec * 10 + c - '0';
892                                     }
893                                  break;
894                             }
895 
896                 return Float.format (output, v, dec, exp, pad);
897         }
898 
899         /**********************************************************************
900 
901         **********************************************************************/
902 
903         private void error (char[] msg)
904         {
905                 throw new IllegalArgumentException (cast(immutable(char)[])msg);
906         }
907 
908         /**********************************************************************
909 
910         **********************************************************************/
911 
912         private size_t spaces (Sink sink, int count)
913         {
914                 size_t ret;
915 
916                 __gshared immutable immutable(T)[] Spaces = "                                ";
917                 while (count > Spaces.length)
918                       {
919                       ret += sink (Spaces);
920                       count -= Spaces.length;
921                       }
922                 return ret + sink (Spaces[0..count]);
923         }
924 
925         /**********************************************************************
926 
927                 format an imaginary value
928 
929         **********************************************************************/
930 
931         private T[] imaginary (T[] result, ireal val, const(T)[] format)
932         {
933                 return floatingTail (result, val.im, format, "*1i");
934         }
935 
936         /**********************************************************************
937 
938                 format a complex value
939 
940         **********************************************************************/
941 
942         private T[] complex (T[] result, creal val, const(T)[] format)
943         {
944                 static bool signed (real x)
945                 {
946                         static if (real.sizeof is 4)
947                                    return ((*cast(uint *)&x) & 0x8000_0000) != 0;
948                         else
949                         static if (real.sizeof is 8)
950                                    return ((*cast(ulong *)&x) & 0x8000_0000_0000_0000) != 0;
951                                else
952                                   {
953                                   auto pe = cast(ubyte *)&x;
954                                   return (pe[9] & 0x80) != 0;
955                                   }
956                 }
957                 __gshared immutable immutable(T)[] plus = "+";
958 
959                 auto len = floatingTail (result, val.re, format, signed(val.im) ? null : plus).length;
960                 return result [0 .. len + floatingTail (result[len..$], val.im, format, "*1i").length];
961         }
962 
963         /**********************************************************************
964 
965                 formats a floating-point value, and appends a tail to it
966 
967         **********************************************************************/
968 
969         private T[] floatingTail (T[] result, real val, const(T)[] format, const(T)[] tail)
970         {
971                 assert (result.length > tail.length);
972 
973                 auto res = floater (result[0..$-tail.length], val, format);
974                 auto len=res.length;
975                 if (res.ptr!is result.ptr)
976                     result[0..len]=res[];
977                 result [len .. len + tail.length] = tail[];
978                 return result [0 .. len + tail.length];
979         }
980 }
981 
982 
983 /*******************************************************************************
984 
985 *******************************************************************************/
986 
987 enum TypeCode
988 {
989         EMPTY = 0,
990         VOID = 'v',
991         BOOL = 'b',
992         UBYTE = 'h',
993         BYTE = 'g',
994         USHORT = 't',
995         SHORT = 's',
996         UINT = 'k',
997         INT = 'i',
998         ULONG = 'm',
999         LONG = 'l',
1000         REAL = 'e',
1001         FLOAT = 'f',
1002         DOUBLE = 'd',
1003         CHAR = 'a',
1004         WCHAR = 'u',
1005         DCHAR = 'w',
1006         ARRAY = 'A',
1007         CLASS = 'C',
1008         STRUCT = 'S',
1009         ENUM = 'E',
1010         CONST = 'x',
1011         INVARIANT = 'y',
1012         DELEGATE = 'D',
1013         FUNCTION = 'F',
1014         POINTER = 'P',
1015         //TYPEDEF = 'T',
1016         INTERFACE = 'I',
1017         CFLOAT = 'q',
1018         CDOUBLE = 'r',
1019         CREAL = 'c',
1020         IFLOAT = 'o',
1021         IDOUBLE = 'p',
1022         IREAL = 'j'
1023 }
1024 
1025 
1026 
1027 /*******************************************************************************
1028 
1029 *******************************************************************************/
1030 import tango.stdc.stdio : printf;
1031 debug (UnitTest)
1032 {
1033         unittest
1034         {
1035         auto Formatter = Layout!(char).instance;
1036 
1037         // basic layout tests
1038         assert( Formatter( "abc" ) == "abc" );
1039         assert( Formatter( "{0}", 1 ) == "1" );
1040         assert( Formatter( "{0}", -1 ) == "-1" );
1041 
1042         assert( Formatter( "{}", 1 ) == "1" );
1043         assert( Formatter( "{} {}", 1, 2) == "1 2" );
1044         assert( Formatter( "{} {0} {}", 1, 3) == "1 1 3" );
1045         assert( Formatter( "{} {0} {} {}", 1, 3) == "1 1 3 {invalid index}" );
1046         assert( Formatter( "{} {0} {} {:x}", 1, 3) == "1 1 3 {invalid index}" );
1047 
1048         assert( Formatter( "{0}", true ) == "true" , Formatter( "{0}", true ));
1049         assert( Formatter( "{0}", false ) == "false" );
1050 
1051         assert( Formatter( "{0}", cast(byte)-128 ) == "-128" );
1052         assert( Formatter( "{0}", cast(byte)127 ) == "127" );
1053         assert( Formatter( "{0}", cast(ubyte)255 ) == "255" );
1054 
1055         assert( Formatter( "{0}", cast(short)-32768  ) == "-32768" );
1056         assert( Formatter( "{0}", cast(short)32767 ) == "32767" );
1057         assert( Formatter( "{0}", cast(ushort)65535 ) == "65535" );
1058         assert( Formatter( "{0:x4}", cast(ushort)0xafe ) == "0afe" );
1059         assert( Formatter( "{0:X4}", cast(ushort)0xafe ) == "0AFE" );
1060 
1061         assert( Formatter( "{0}", -2147483648 ) == "-2147483648" );
1062         assert( Formatter( "{0}", 2147483647 ) == "2147483647" );
1063         assert( Formatter( "{0}", 4294967295 ) == "4294967295" );
1064 
1065         // large integers
1066         assert( Formatter( "{0}", -9223372036854775807L) == "-9223372036854775807" );
1067         assert( Formatter( "{0}", 0x8000_0000_0000_0000L) == "9223372036854775808" );
1068         assert( Formatter( "{0}", 9223372036854775807L ) == "9223372036854775807" );
1069         assert( Formatter( "{0:X}", 0xFFFF_FFFF_FFFF_FFFF) == "FFFFFFFFFFFFFFFF" );
1070         assert( Formatter( "{0:x}", 0xFFFF_FFFF_FFFF_FFFF) == "ffffffffffffffff" );
1071         assert( Formatter( "{0:x}", 0xFFFF_1234_FFFF_FFFF) == "ffff1234ffffffff" );
1072         assert( Formatter( "{0:x19}", 0x1234_FFFF_FFFF) == "00000001234ffffffff" );
1073         assert( Formatter( "{0}", 18446744073709551615UL ) == "18446744073709551615" );
1074         assert( Formatter( "{0}", 18446744073709551615UL ) == "18446744073709551615" );
1075 
1076         // fragments before and after
1077         assert( Formatter( "d{0}d", "s" ) == "dsd" );
1078         assert( Formatter( "d{0}d", "1234567890" ) == "d1234567890d" );
1079 
1080         // brace escaping
1081         assert( Formatter( "d{0}d", "<string>" ) == "d<string>d");
1082         assert( Formatter( "d{{0}d", "<string>" ) == "d{0}d");
1083         assert( Formatter( "d{{{0}d", "<string>" ) == "d{<string>d");
1084         assert( Formatter( "d{0}}d", "<string>" ) == "d<string>}d");
1085 
1086         // hex conversions, where width indicates leading zeroes
1087         assert( Formatter( "{0:x}", 0xafe0000 ) == "afe0000" );
1088         assert( Formatter( "{0:x7}", 0xafe0000 ) == "afe0000" );
1089         assert( Formatter( "{0:x8}", 0xafe0000 ) == "0afe0000" );
1090         assert( Formatter( "{0:X8}", 0xafe0000 ) == "0AFE0000" );
1091         assert( Formatter( "{0:X9}", 0xafe0000 ) == "00AFE0000" );
1092         assert( Formatter( "{0:X13}", 0xafe0000 ) == "000000AFE0000" );
1093         assert( Formatter( "{0:x13}", 0xafe0000 ) == "000000afe0000" );
1094 
1095         // decimal width
1096         assert( Formatter( "{0:d6}", 123 ) == "000123" );
1097         assert( Formatter( "{0,7:d6}", 123 ) == " 000123" );
1098         assert( Formatter( "{0,-7:d6}", 123 ) == "000123 " );
1099 
1100         // width & sign combinations
1101         assert( Formatter( "{0:d7}", -123 ) == "-0000123" );
1102         assert( Formatter( "{0,7:d6}", 123 ) == " 000123" );
1103         assert( Formatter( "{0,7:d7}", -123 ) == "-0000123" );
1104         assert( Formatter( "{0,8:d7}", -123 ) == "-0000123" );
1105         assert( Formatter( "{0,5:d7}", -123 ) == "-0000123" );
1106 
1107         // Negative numbers in various bases
1108         assert( Formatter( "{:b}", cast(byte) -1 ) == "11111111" );
1109         assert( Formatter( "{:b}", cast(short) -1 ) == "1111111111111111" );
1110         assert( Formatter( "{:b}", cast(int) -1 )
1111                 == "11111111111111111111111111111111" );
1112         assert( Formatter( "{:b}", cast(long) -1 )
1113                 == "1111111111111111111111111111111111111111111111111111111111111111" );
1114 
1115         assert( Formatter( "{:o}", cast(byte) -1 ) == "377" );
1116         assert( Formatter( "{:o}", cast(short) -1 ) == "177777" );
1117         assert( Formatter( "{:o}", cast(int) -1 ) == "37777777777" );
1118         assert( Formatter( "{:o}", cast(long) -1 ) == "1777777777777777777777" );
1119 
1120         assert( Formatter( "{:d}", cast(byte) -1 ) == "-1" );
1121         assert( Formatter( "{:d}", cast(short) -1 ) == "-1" );
1122         assert( Formatter( "{:d}", cast(int) -1 ) == "-1" );
1123         assert( Formatter( "{:d}", cast(long) -1 ) == "-1" );
1124 
1125         assert( Formatter( "{:x}", cast(byte) -1 ) == "ff" );
1126         assert( Formatter( "{:x}", cast(short) -1 ) == "ffff" );
1127         assert( Formatter( "{:x}", cast(int) -1 ) == "ffffffff" );
1128         assert( Formatter( "{:x}", cast(long) -1 ) == "ffffffffffffffff" );
1129 
1130         // argument index
1131         assert( Formatter( "a{0}b{1}c{2}", "x", "y", "z" ) == "axbycz" );
1132         assert( Formatter( "a{2}b{1}c{0}", "x", "y", "z" ) == "azbycx" );
1133         assert( Formatter( "a{1}b{1}c{1}", "x", "y", "z" ) == "aybycy" );
1134 
1135         // alignment does not restrict the length
1136         assert( Formatter( "{0,5}", "hellohello" ) == "hellohello" );
1137 
1138         // alignment fills with spaces
1139         assert( Formatter( "->{0,-10}<-", "hello" ) == "->hello     <-" );
1140         assert( Formatter( "->{0,10}<-", "hello" ) == "->     hello<-" );
1141         assert( Formatter( "->{0,-10}<-", 12345 ) == "->12345     <-" );
1142         assert( Formatter( "->{0,10}<-", 12345 ) == "->     12345<-" );
1143 
1144         // chop at maximum specified length; insert ellipses when chopped
1145         assert( Formatter( "->{.5}<-", "hello" ) == "->hello<-" );
1146         assert( Formatter( "->{.4}<-", "hello" ) == "->hell...<-" );
1147         assert( Formatter( "->{.-3}<-", "hello" ) == "->...llo<-" );
1148 
1149         // width specifier indicates number of decimal places
1150         assert( Formatter( "{0:f}", 1.23f ) == "1.23" );
1151         assert( Formatter( "{0:f4}", 1.23456789L ) == "1.2346" );
1152         assert( Formatter( "{0:e4}", 0.0001) == "1.0000e-04");
1153 
1154         assert( Formatter( "{0:f}", 1.23f*1i ) == "1.23*1i");
1155         assert( Formatter( "{0:f4}", 1.23456789L*1i ) == "1.2346*1i" );
1156         assert( Formatter( "{0:e4}", 0.0001*1i) == "1.0000e-04*1i");
1157 
1158         assert( Formatter( "{0:f}", 1.23f+1i ) == "1.23+1.00*1i" );
1159         assert( Formatter( "{0:f4}", 1.23456789L+1i ) == "1.2346+1.0000*1i" );
1160         assert( Formatter( "{0:e4}", 0.0001+1i) == "1.0000e-04+1.0000e+00*1i");
1161         assert( Formatter( "{0:f}", 1.23f-1i ) == "1.23-1.00*1i" );
1162         assert( Formatter( "{0:f4}", 1.23456789L-1i ) == "1.2346-1.0000*1i" );
1163         assert( Formatter( "{0:e4}", 0.0001-1i) == "1.0000e-04-1.0000e+00*1i");
1164 
1165         // 'f.' & 'e.' format truncates zeroes from floating decimals
1166         assert( Formatter( "{:f4.}", 1.230 ) == "1.23" );
1167         assert( Formatter( "{:f6.}", 1.230 ) == "1.23" );
1168         assert( Formatter( "{:f1.}", 1.230 ) == "1.2" );
1169         assert( Formatter( "{:f.}", 1.233 ) == "1.23" );
1170         assert( Formatter( "{:f.}", 1.237 ) == "1.24" );
1171         assert( Formatter( "{:f.}", 1.000 ) == "1" );
1172         assert( Formatter( "{:f2.}", 200.001 ) == "200");
1173 
1174         // array output
1175         int[] a = [ 51, 52, 53, 54, 55 ];
1176         assert( Formatter( "{}", a ) == "[51, 52, 53, 54, 55]" );
1177         assert( Formatter( "{:x}", a ) == "[33, 34, 35, 36, 37]" );
1178         assert( Formatter( "{,-4}", a ) == "[51  , 52  , 53  , 54  , 55  ]" );
1179         assert( Formatter( "{,4}", a ) == "[  51,   52,   53,   54,   55]" );
1180         int[][] b = [ [ 51, 52 ], [ 53, 54, 55 ] ];
1181         assert( Formatter( "{}", b ) == "[[51, 52], [53, 54, 55]]" );
1182 
1183         char[1024] static_buffer;
1184         static_buffer[0..10] = "1234567890";
1185 
1186         assert (Formatter( "{}", static_buffer[0..10]) == "1234567890");
1187 
1188         version(X86)
1189         {
1190             ushort[3] c = [ cast(ushort)51, 52, 53 ];
1191             assert( Formatter( "{}", c ) == "[51, 52, 53]" );
1192         }
1193 
1194         // integer AA
1195         ushort[long] d;
1196         d[234] = 2;
1197         d[345] = 3;
1198 
1199         auto formattedAA = Formatter("{}", d);
1200         auto aaOption1 = "{234 => 2, 345 => 3}";
1201         auto aaOption2 = "{345 => 3, 234 => 2}";
1202         assert(
1203             formattedAA == aaOption1 || formattedAA == aaOption2,
1204             formattedAA);
1205 
1206         // bool/string AA
1207         bool[char[]] e;
1208         e[ "key" ] = true;
1209         e[ "value" ] = false;
1210         assert( Formatter( "{}", e ) == "{key => true, value => false}" ||
1211                 Formatter( "{}", e ) == "{value => false, key => true}");
1212 
1213         // string/double AA
1214         char[][ double ] f;
1215         f[ 1.0 ] = "one".dup;
1216         f[ 3.14 ] = "PI".dup;
1217         assert( Formatter( "{}", f ) == "{1.00 => one, 3.14 => PI}" ||
1218                 Formatter( "{}", f ) == "{3.14 => PI, 1.00 => one}", Formatter("{}", f));
1219         }
1220 }
1221 
1222 
1223 
1224 debug (Layout)
1225 {
1226         import tango.io.Console;
1227 
1228         static if (is (typeof(Time)))
1229                    import tango.time.WallClock;
1230 
1231         void main ()
1232         {
1233                 auto layout = Layout!(char).instance;
1234 
1235                 layout.convert (Cout.stream, "hi {}", "there\n");
1236 
1237                 Cout (layout.sprint (new char[1], "hi")).newline;
1238                 Cout (layout.sprint (new char[10], "{.4}", "hello")).newline;
1239                 Cout (layout.sprint (new char[10], "{.-4}", "hello")).newline;
1240 
1241                 Cout (layout ("{:f1}", 3.0)).newline;
1242                 Cout (layout ("{:g}", 3.00)).newline;
1243                 Cout (layout ("{:f1}", -0.0)).newline;
1244                 Cout (layout ("{:g1}", -0.0)).newline;
1245                 Cout (layout ("{:d2}", 56)).newline;
1246                 Cout (layout ("{:d4}", cast(byte) -56)).newline;
1247                 Cout (layout ("{:f4}", 1.0e+12)).newline;
1248                 Cout (layout ("{:f4}", 1.23e-2)).newline;
1249                 Cout (layout ("{:f8}", 3.14159)).newline;
1250                 Cout (layout ("{:e20}", 1.23e-3)).newline;
1251                 Cout (layout ("{:e4.}", 1.23e-07)).newline;
1252                 Cout (layout ("{:.}", 1.2)).newline;
1253                 Cout (layout ("ptr:{}", &layout)).newline;
1254                 Cout (layout ("ulong.max {}", ulong.max)).newline;
1255 
1256                 struct S
1257                 {
1258                    char[] toString () {return "foo";}
1259                 }
1260 
1261                 S s;
1262                 Cout (layout ("struct: {}", s)).newline;
1263 
1264                 static if (is (typeof(Time)))
1265                            Cout (layout ("time: {}", WallClock.now)).newline;
1266         }
1267 }