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 }