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 }