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