1 // Written in the D programming language. 2 3 /** 4 Copyright: Copyright Digital Mars 2000-2013. 5 6 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). 7 8 Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com, 9 Andrei Alexandrescu), and Kenji Hara 10 11 Source: $(PHOBOSSRC std/_format.d) 12 */ 13 module undead.doformat; 14 15 //debug=format; // uncomment to turn on debugging printf's 16 17 import core.vararg; 18 import std.exception; 19 import std.meta; 20 import std.range.primitives; 21 import std.traits; 22 import std.format; 23 24 version(CRuntime_DigitalMars) 25 { 26 version = DigitalMarsC; 27 } 28 29 version (DigitalMarsC) 30 { 31 // This is DMC's internal floating point formatting function 32 extern (C) 33 { 34 extern shared char* function(int c, int flags, int precision, 35 in real* pdval, 36 char* buf, size_t* psl, int width) __pfloatfmt; 37 } 38 } 39 40 /********************************************************************** 41 * Signals a mismatch between a format and its corresponding argument. 42 */ 43 class FormatException : Exception 44 { 45 @safe pure nothrow 46 this() 47 { 48 super("format error"); 49 } 50 51 @safe pure nothrow 52 this(string msg, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) 53 { 54 super(msg, fn, ln, next); 55 } 56 } 57 58 59 // Legacy implementation 60 61 enum Mangle : char 62 { 63 Tvoid = 'v', 64 Tbool = 'b', 65 Tbyte = 'g', 66 Tubyte = 'h', 67 Tshort = 's', 68 Tushort = 't', 69 Tint = 'i', 70 Tuint = 'k', 71 Tlong = 'l', 72 Tulong = 'm', 73 Tfloat = 'f', 74 Tdouble = 'd', 75 Treal = 'e', 76 77 Tifloat = 'o', 78 Tidouble = 'p', 79 Tireal = 'j', 80 Tcfloat = 'q', 81 Tcdouble = 'r', 82 Tcreal = 'c', 83 84 Tchar = 'a', 85 Twchar = 'u', 86 Tdchar = 'w', 87 88 Tarray = 'A', 89 Tsarray = 'G', 90 Taarray = 'H', 91 Tpointer = 'P', 92 Tfunction = 'F', 93 Tident = 'I', 94 Tclass = 'C', 95 Tstruct = 'S', 96 Tenum = 'E', 97 Ttypedef = 'T', 98 Tdelegate = 'D', 99 100 Tconst = 'x', 101 Timmutable = 'y', 102 } 103 104 // return the TypeInfo for a primitive type and null otherwise. This 105 // is required since for arrays of ints we only have the mangled char 106 // to work from. If arrays always subclassed TypeInfo_Array this 107 // routine could go away. 108 private TypeInfo primitiveTypeInfo(Mangle m) 109 { 110 // BUG: should fix this in static this() to avoid double checked locking bug 111 __gshared TypeInfo[Mangle] dic; 112 if (!dic.length) 113 { 114 dic = [ 115 Mangle.Tvoid : typeid(void), 116 Mangle.Tbool : typeid(bool), 117 Mangle.Tbyte : typeid(byte), 118 Mangle.Tubyte : typeid(ubyte), 119 Mangle.Tshort : typeid(short), 120 Mangle.Tushort : typeid(ushort), 121 Mangle.Tint : typeid(int), 122 Mangle.Tuint : typeid(uint), 123 Mangle.Tlong : typeid(long), 124 Mangle.Tulong : typeid(ulong), 125 Mangle.Tfloat : typeid(float), 126 Mangle.Tdouble : typeid(double), 127 Mangle.Treal : typeid(real), 128 Mangle.Tifloat : typeid(ifloat), 129 Mangle.Tidouble : typeid(idouble), 130 Mangle.Tireal : typeid(ireal), 131 Mangle.Tcfloat : typeid(cfloat), 132 Mangle.Tcdouble : typeid(cdouble), 133 Mangle.Tcreal : typeid(creal), 134 Mangle.Tchar : typeid(char), 135 Mangle.Twchar : typeid(wchar), 136 Mangle.Tdchar : typeid(dchar) 137 ]; 138 } 139 auto p = m in dic; 140 return p ? *p : null; 141 } 142 143 // This stuff has been removed from the docs and is planned for deprecation. 144 /* 145 * Interprets variadic argument list pointed to by argptr whose types 146 * are given by arguments[], formats them according to embedded format 147 * strings in the variadic argument list, and sends the resulting 148 * characters to putc. 149 * 150 * The variadic arguments are consumed in order. Each is formatted 151 * into a sequence of chars, using the default format specification 152 * for its type, and the characters are sequentially passed to putc. 153 * If a $(D char[]), $(D wchar[]), or $(D dchar[]) argument is 154 * encountered, it is interpreted as a format string. As many 155 * arguments as specified in the format string are consumed and 156 * formatted according to the format specifications in that string and 157 * passed to putc. If there are too few remaining arguments, a 158 * $(D FormatException) is thrown. If there are more remaining arguments than 159 * needed by the format specification, the default processing of 160 * arguments resumes until they are all consumed. 161 * 162 * Params: 163 * putc = Output is sent do this delegate, character by character. 164 * arguments = Array of $(D TypeInfo)s, one for each argument to be formatted. 165 * argptr = Points to variadic argument list. 166 * 167 * Throws: 168 * Mismatched arguments and formats result in a $(D FormatException) being thrown. 169 * 170 * Format_String: 171 * <a name="format-string">$(I Format strings)</a> 172 * consist of characters interspersed with 173 * $(I format specifications). Characters are simply copied 174 * to the output (such as putc) after any necessary conversion 175 * to the corresponding UTF-8 sequence. 176 * 177 * A $(I format specification) starts with a '%' character, 178 * and has the following grammar: 179 180 $(CONSOLE 181 $(I FormatSpecification): 182 $(B '%%') 183 $(B '%') $(I Flags) $(I Width) $(I Precision) $(I FormatChar) 184 185 $(I Flags): 186 $(I empty) 187 $(B '-') $(I Flags) 188 $(B '+') $(I Flags) 189 $(B '#') $(I Flags) 190 $(B '0') $(I Flags) 191 $(B ' ') $(I Flags) 192 193 $(I Width): 194 $(I empty) 195 $(I Integer) 196 $(B '*') 197 198 $(I Precision): 199 $(I empty) 200 $(B '.') 201 $(B '.') $(I Integer) 202 $(B '.*') 203 204 $(I Integer): 205 $(I Digit) 206 $(I Digit) $(I Integer) 207 208 $(I Digit): 209 $(B '0') 210 $(B '1') 211 $(B '2') 212 $(B '3') 213 $(B '4') 214 $(B '5') 215 $(B '6') 216 $(B '7') 217 $(B '8') 218 $(B '9') 219 220 $(I FormatChar): 221 $(B 's') 222 $(B 'b') 223 $(B 'd') 224 $(B 'o') 225 $(B 'x') 226 $(B 'X') 227 $(B 'e') 228 $(B 'E') 229 $(B 'f') 230 $(B 'F') 231 $(B 'g') 232 $(B 'G') 233 $(B 'a') 234 $(B 'A') 235 ) 236 $(DL 237 $(DT $(I Flags)) 238 $(DL 239 $(DT $(B '-')) 240 $(DD 241 Left justify the result in the field. 242 It overrides any $(B 0) flag.) 243 244 $(DT $(B '+')) 245 $(DD Prefix positive numbers in a signed conversion with a $(B +). 246 It overrides any $(I space) flag.) 247 248 $(DT $(B '#')) 249 $(DD Use alternative formatting: 250 $(DL 251 $(DT For $(B 'o'):) 252 $(DD Add to precision as necessary so that the first digit 253 of the octal formatting is a '0', even if both the argument 254 and the $(I Precision) are zero.) 255 $(DT For $(B 'x') ($(B 'X')):) 256 $(DD If non-zero, prefix result with $(B 0x) ($(B 0X)).) 257 $(DT For floating point formatting:) 258 $(DD Always insert the decimal point.) 259 $(DT For $(B 'g') ($(B 'G')):) 260 $(DD Do not elide trailing zeros.) 261 )) 262 263 $(DT $(B '0')) 264 $(DD For integer and floating point formatting when not nan or 265 infinity, use leading zeros 266 to pad rather than spaces. 267 Ignore if there's a $(I Precision).) 268 269 $(DT $(B ' ')) 270 $(DD Prefix positive numbers in a signed conversion with a space.) 271 ) 272 273 $(DT $(I Width)) 274 $(DD 275 Specifies the minimum field width. 276 If the width is a $(B *), the next argument, which must be 277 of type $(B int), is taken as the width. 278 If the width is negative, it is as if the $(B -) was given 279 as a $(I Flags) character.) 280 281 $(DT $(I Precision)) 282 $(DD Gives the precision for numeric conversions. 283 If the precision is a $(B *), the next argument, which must be 284 of type $(B int), is taken as the precision. If it is negative, 285 it is as if there was no $(I Precision).) 286 287 $(DT $(I FormatChar)) 288 $(DD 289 $(DL 290 $(DT $(B 's')) 291 $(DD The corresponding argument is formatted in a manner consistent 292 with its type: 293 $(DL 294 $(DT $(B bool)) 295 $(DD The result is <tt>'true'</tt> or <tt>'false'</tt>.) 296 $(DT integral types) 297 $(DD The $(B %d) format is used.) 298 $(DT floating point types) 299 $(DD The $(B %g) format is used.) 300 $(DT string types) 301 $(DD The result is the string converted to UTF-8.) 302 A $(I Precision) specifies the maximum number of characters 303 to use in the result. 304 $(DT classes derived from $(B Object)) 305 $(DD The result is the string returned from the class instance's 306 $(B .toString()) method. 307 A $(I Precision) specifies the maximum number of characters 308 to use in the result.) 309 $(DT non-string static and dynamic arrays) 310 $(DD The result is [s<sub>0</sub>, s<sub>1</sub>, ...] 311 where s<sub>k</sub> is the kth element 312 formatted with the default format.) 313 )) 314 315 $(DT $(B 'b','d','o','x','X')) 316 $(DD The corresponding argument must be an integral type 317 and is formatted as an integer. If the argument is a signed type 318 and the $(I FormatChar) is $(B d) it is converted to 319 a signed string of characters, otherwise it is treated as 320 unsigned. An argument of type $(B bool) is formatted as '1' 321 or '0'. The base used is binary for $(B b), octal for $(B o), 322 decimal 323 for $(B d), and hexadecimal for $(B x) or $(B X). 324 $(B x) formats using lower case letters, $(B X) uppercase. 325 If there are fewer resulting digits than the $(I Precision), 326 leading zeros are used as necessary. 327 If the $(I Precision) is 0 and the number is 0, no digits 328 result.) 329 330 $(DT $(B 'e','E')) 331 $(DD A floating point number is formatted as one digit before 332 the decimal point, $(I Precision) digits after, the $(I FormatChar), 333 ±, followed by at least a two digit exponent: $(I d.dddddd)e$(I ±dd). 334 If there is no $(I Precision), six 335 digits are generated after the decimal point. 336 If the $(I Precision) is 0, no decimal point is generated.) 337 338 $(DT $(B 'f','F')) 339 $(DD A floating point number is formatted in decimal notation. 340 The $(I Precision) specifies the number of digits generated 341 after the decimal point. It defaults to six. At least one digit 342 is generated before the decimal point. If the $(I Precision) 343 is zero, no decimal point is generated.) 344 345 $(DT $(B 'g','G')) 346 $(DD A floating point number is formatted in either $(B e) or 347 $(B f) format for $(B g); $(B E) or $(B F) format for 348 $(B G). 349 The $(B f) format is used if the exponent for an $(B e) format 350 is greater than -5 and less than the $(I Precision). 351 The $(I Precision) specifies the number of significant 352 digits, and defaults to six. 353 Trailing zeros are elided after the decimal point, if the fractional 354 part is zero then no decimal point is generated.) 355 356 $(DT $(B 'a','A')) 357 $(DD A floating point number is formatted in hexadecimal 358 exponential notation 0x$(I h.hhhhhh)p$(I ±d). 359 There is one hexadecimal digit before the decimal point, and as 360 many after as specified by the $(I Precision). 361 If the $(I Precision) is zero, no decimal point is generated. 362 If there is no $(I Precision), as many hexadecimal digits as 363 necessary to exactly represent the mantissa are generated. 364 The exponent is written in as few digits as possible, 365 but at least one, is in decimal, and represents a power of 2 as in 366 $(I h.hhhhhh)*2<sup>$(I ±d)</sup>. 367 The exponent for zero is zero. 368 The hexadecimal digits, x and p are in upper case if the 369 $(I FormatChar) is upper case.) 370 ) 371 372 Floating point NaN's are formatted as $(B nan) if the 373 $(I FormatChar) is lower case, or $(B NAN) if upper. 374 Floating point infinities are formatted as $(B inf) or 375 $(B infinity) if the 376 $(I FormatChar) is lower case, or $(B INF) or $(B INFINITY) if upper. 377 )) 378 379 Example: 380 381 ------------------------- 382 import core.stdc.stdio; 383 import std.format; 384 385 void myPrint(...) 386 { 387 void putc(dchar c) 388 { 389 fputc(c, stdout); 390 } 391 392 std.format.doFormat(&putc, _arguments, _argptr); 393 } 394 395 void main() 396 { 397 int x = 27; 398 399 // prints 'The answer is 27:6' 400 myPrint("The answer is %s:", x, 6); 401 } 402 ------------------------ 403 */ 404 void doFormat()(scope void delegate(dchar) putc, TypeInfo[] arguments, va_list ap) 405 { 406 import std.utf : encode, toUCSindex, isValidDchar, UTFException, toUTF8; 407 import core.stdc.string : strlen; 408 import core.stdc.stdlib : alloca, malloc, realloc, free; 409 import core.stdc.stdio : snprintf; 410 411 size_t bufLength = 1024; 412 void* argBuffer = malloc(bufLength); 413 scope(exit) free(argBuffer); 414 415 size_t bufUsed = 0; 416 foreach (ti; arguments) 417 { 418 // Ensure the required alignment 419 bufUsed += ti.talign - 1; 420 bufUsed -= (cast(size_t)argBuffer + bufUsed) & (ti.talign - 1); 421 auto pos = bufUsed; 422 // Align to next word boundary 423 bufUsed += ti.tsize + size_t.sizeof - 1; 424 bufUsed -= (cast(size_t)argBuffer + bufUsed) & (size_t.sizeof - 1); 425 // Resize buffer if necessary 426 while (bufUsed > bufLength) 427 { 428 bufLength *= 2; 429 argBuffer = realloc(argBuffer, bufLength); 430 } 431 // Copy argument into buffer 432 va_arg(ap, ti, argBuffer + pos); 433 } 434 435 auto argptr = argBuffer; 436 void* skipArg(TypeInfo ti) 437 { 438 // Ensure the required alignment 439 argptr += ti.talign - 1; 440 argptr -= cast(size_t)argptr & (ti.talign - 1); 441 auto p = argptr; 442 // Align to next word boundary 443 argptr += ti.tsize + size_t.sizeof - 1; 444 argptr -= cast(size_t)argptr & (size_t.sizeof - 1); 445 return p; 446 } 447 auto getArg(T)() 448 { 449 return *cast(T*)skipArg(typeid(T)); 450 } 451 452 TypeInfo ti; 453 Mangle m; 454 uint flags; 455 int field_width; 456 int precision; 457 458 enum : uint 459 { 460 FLdash = 1, 461 FLplus = 2, 462 FLspace = 4, 463 FLhash = 8, 464 FLlngdbl = 0x20, 465 FL0pad = 0x40, 466 FLprecision = 0x80, 467 } 468 469 static TypeInfo skipCI(TypeInfo valti) 470 { 471 for (;;) 472 { 473 if (typeid(valti).name.length == 18 && 474 typeid(valti).name[9..18] == "Invariant") 475 valti = (cast(TypeInfo_Invariant)valti).base; 476 else if (typeid(valti).name.length == 14 && 477 typeid(valti).name[9..14] == "Const") 478 valti = (cast(TypeInfo_Const)valti).base; 479 else 480 break; 481 } 482 483 return valti; 484 } 485 486 void formatArg(char fc) 487 { 488 bool vbit; 489 ulong vnumber; 490 char vchar; 491 dchar vdchar; 492 Object vobject; 493 real vreal; 494 creal vcreal; 495 Mangle m2; 496 int signed = 0; 497 uint base = 10; 498 int uc; 499 char[ulong.sizeof * 8] tmpbuf; // long enough to print long in binary 500 const(char)* prefix = ""; 501 string s; 502 503 void putstr(const char[] s) 504 { 505 //printf("putstr: s = %.*s, flags = x%x\n", s.length, s.ptr, flags); 506 ptrdiff_t padding = field_width - 507 (strlen(prefix) + toUCSindex(s, s.length)); 508 ptrdiff_t prepad = 0; 509 ptrdiff_t postpad = 0; 510 if (padding > 0) 511 { 512 if (flags & FLdash) 513 postpad = padding; 514 else 515 prepad = padding; 516 } 517 518 if (flags & FL0pad) 519 { 520 while (*prefix) 521 putc(*prefix++); 522 while (prepad--) 523 putc('0'); 524 } 525 else 526 { 527 while (prepad--) 528 putc(' '); 529 while (*prefix) 530 putc(*prefix++); 531 } 532 533 foreach (dchar c; s) 534 putc(c); 535 536 while (postpad--) 537 putc(' '); 538 } 539 540 void putreal(real v) 541 { 542 //printf("putreal %Lg\n", vreal); 543 544 switch (fc) 545 { 546 case 's': 547 fc = 'g'; 548 break; 549 550 case 'f', 'F', 'e', 'E', 'g', 'G', 'a', 'A': 551 break; 552 553 default: 554 //printf("fc = '%c'\n", fc); 555 Lerror: 556 throw new FormatException("incompatible format character for floating point type"); 557 } 558 version (DigitalMarsC) 559 { 560 uint sl; 561 char[] fbuf = tmpbuf; 562 if (!(flags & FLprecision)) 563 precision = 6; 564 while (1) 565 { 566 sl = fbuf.length; 567 prefix = (*__pfloatfmt)(fc, flags | FLlngdbl, 568 precision, &v, cast(char*)fbuf, &sl, field_width); 569 if (sl != -1) 570 break; 571 sl = fbuf.length * 2; 572 fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; 573 } 574 putstr(fbuf[0 .. sl]); 575 } 576 else 577 { 578 ptrdiff_t sl; 579 char[] fbuf = tmpbuf; 580 char[12] format; 581 format[0] = '%'; 582 int i = 1; 583 if (flags & FLdash) 584 format[i++] = '-'; 585 if (flags & FLplus) 586 format[i++] = '+'; 587 if (flags & FLspace) 588 format[i++] = ' '; 589 if (flags & FLhash) 590 format[i++] = '#'; 591 if (flags & FL0pad) 592 format[i++] = '0'; 593 format[i + 0] = '*'; 594 format[i + 1] = '.'; 595 format[i + 2] = '*'; 596 format[i + 3] = 'L'; 597 format[i + 4] = fc; 598 format[i + 5] = 0; 599 if (!(flags & FLprecision)) 600 precision = -1; 601 while (1) 602 { 603 sl = fbuf.length; 604 int n; 605 version (CRuntime_Microsoft) 606 { 607 import std.math : isNaN, isInfinity; 608 if (isNaN(v)) // snprintf writes 1.#QNAN 609 n = snprintf(fbuf.ptr, sl, "nan"); 610 else if (isInfinity(v)) // snprintf writes 1.#INF 611 n = snprintf(fbuf.ptr, sl, v < 0 ? "-inf" : "inf"); 612 else 613 n = snprintf(fbuf.ptr, sl, format.ptr, field_width, 614 precision, cast(double)v); 615 } 616 else 617 n = snprintf(fbuf.ptr, sl, format.ptr, field_width, 618 precision, v); 619 //printf("format = '%s', n = %d\n", cast(char*)format, n); 620 if (n >= 0 && n < sl) 621 { sl = n; 622 break; 623 } 624 if (n < 0) 625 sl = sl * 2; 626 else 627 sl = n + 1; 628 fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; 629 } 630 putstr(fbuf[0 .. sl]); 631 } 632 return; 633 } 634 635 static Mangle getMan(TypeInfo ti) 636 { 637 auto m = cast(Mangle)typeid(ti).name[9]; 638 if (typeid(ti).name.length == 20 && 639 typeid(ti).name[9..20] == "StaticArray") 640 m = cast(Mangle)'G'; 641 return m; 642 } 643 644 /* p = pointer to the first element in the array 645 * len = number of elements in the array 646 * valti = type of the elements 647 */ 648 void putArray(void* p, size_t len, TypeInfo valti) 649 { 650 //printf("\nputArray(len = %u), tsize = %u\n", len, valti.tsize); 651 putc('['); 652 valti = skipCI(valti); 653 size_t tsize = valti.tsize; 654 auto argptrSave = argptr; 655 auto tiSave = ti; 656 auto mSave = m; 657 ti = valti; 658 //printf("\n%.*s\n", typeid(valti).name.length, typeid(valti).name.ptr); 659 m = getMan(valti); 660 while (len--) 661 { 662 //doFormat(putc, (&valti)[0 .. 1], p); 663 argptr = p; 664 formatArg('s'); 665 p += tsize; 666 if (len > 0) putc(','); 667 } 668 m = mSave; 669 ti = tiSave; 670 argptr = argptrSave; 671 putc(']'); 672 } 673 674 void putAArray(ubyte[long] vaa, TypeInfo valti, TypeInfo keyti) 675 { 676 putc('['); 677 bool comma=false; 678 auto argptrSave = argptr; 679 auto tiSave = ti; 680 auto mSave = m; 681 valti = skipCI(valti); 682 keyti = skipCI(keyti); 683 foreach (ref fakevalue; vaa) 684 { 685 if (comma) putc(','); 686 comma = true; 687 void *pkey = &fakevalue; 688 version (D_LP64) 689 pkey -= (long.sizeof + 15) & ~(15); 690 else 691 pkey -= (long.sizeof + size_t.sizeof - 1) & ~(size_t.sizeof - 1); 692 693 // the key comes before the value 694 auto keysize = keyti.tsize; 695 version (D_LP64) 696 auto keysizet = (keysize + 15) & ~(15); 697 else 698 auto keysizet = (keysize + size_t.sizeof - 1) & ~(size_t.sizeof - 1); 699 700 void* pvalue = pkey + keysizet; 701 702 //doFormat(putc, (&keyti)[0..1], pkey); 703 m = getMan(keyti); 704 argptr = pkey; 705 706 ti = keyti; 707 formatArg('s'); 708 709 putc(':'); 710 //doFormat(putc, (&valti)[0..1], pvalue); 711 m = getMan(valti); 712 argptr = pvalue; 713 714 ti = valti; 715 formatArg('s'); 716 } 717 m = mSave; 718 ti = tiSave; 719 argptr = argptrSave; 720 putc(']'); 721 } 722 723 //printf("formatArg(fc = '%c', m = '%c')\n", fc, m); 724 int mi; 725 switch (m) 726 { 727 case Mangle.Tbool: 728 vbit = getArg!(bool)(); 729 if (fc != 's') 730 { vnumber = vbit; 731 goto Lnumber; 732 } 733 putstr(vbit ? "true" : "false"); 734 return; 735 736 case Mangle.Tchar: 737 vchar = getArg!(char)(); 738 if (fc != 's') 739 { vnumber = vchar; 740 goto Lnumber; 741 } 742 L2: 743 putstr((&vchar)[0 .. 1]); 744 return; 745 746 case Mangle.Twchar: 747 vdchar = getArg!(wchar)(); 748 goto L1; 749 750 case Mangle.Tdchar: 751 vdchar = getArg!(dchar)(); 752 L1: 753 if (fc != 's') 754 { vnumber = vdchar; 755 goto Lnumber; 756 } 757 if (vdchar <= 0x7F) 758 { vchar = cast(char)vdchar; 759 goto L2; 760 } 761 else 762 { if (!isValidDchar(vdchar)) 763 throw new UTFException("invalid dchar in format"); 764 char[4] vbuf; 765 putstr(vbuf[0 .. encode(vbuf, vdchar)]); 766 } 767 return; 768 769 case Mangle.Tbyte: 770 signed = 1; 771 vnumber = getArg!(byte)(); 772 goto Lnumber; 773 774 case Mangle.Tubyte: 775 vnumber = getArg!(ubyte)(); 776 goto Lnumber; 777 778 case Mangle.Tshort: 779 signed = 1; 780 vnumber = getArg!(short)(); 781 goto Lnumber; 782 783 case Mangle.Tushort: 784 vnumber = getArg!(ushort)(); 785 goto Lnumber; 786 787 case Mangle.Tint: 788 signed = 1; 789 vnumber = getArg!(int)(); 790 goto Lnumber; 791 792 case Mangle.Tuint: 793 Luint: 794 vnumber = getArg!(uint)(); 795 goto Lnumber; 796 797 case Mangle.Tlong: 798 signed = 1; 799 vnumber = cast(ulong)getArg!(long)(); 800 goto Lnumber; 801 802 case Mangle.Tulong: 803 Lulong: 804 vnumber = getArg!(ulong)(); 805 goto Lnumber; 806 807 case Mangle.Tclass: 808 vobject = getArg!(Object)(); 809 if (vobject is null) 810 s = "null"; 811 else 812 s = vobject.toString(); 813 goto Lputstr; 814 815 case Mangle.Tpointer: 816 vnumber = cast(ulong)getArg!(void*)(); 817 if (fc != 'x') uc = 1; 818 flags |= FL0pad; 819 if (!(flags & FLprecision)) 820 { flags |= FLprecision; 821 precision = (void*).sizeof; 822 } 823 base = 16; 824 goto Lnumber; 825 826 case Mangle.Tfloat: 827 case Mangle.Tifloat: 828 if (fc == 'x' || fc == 'X') 829 goto Luint; 830 vreal = getArg!(float)(); 831 goto Lreal; 832 833 case Mangle.Tdouble: 834 case Mangle.Tidouble: 835 if (fc == 'x' || fc == 'X') 836 goto Lulong; 837 vreal = getArg!(double)(); 838 goto Lreal; 839 840 case Mangle.Treal: 841 case Mangle.Tireal: 842 vreal = getArg!(real)(); 843 goto Lreal; 844 845 case Mangle.Tcfloat: 846 vcreal = getArg!(cfloat)(); 847 goto Lcomplex; 848 849 case Mangle.Tcdouble: 850 vcreal = getArg!(cdouble)(); 851 goto Lcomplex; 852 853 case Mangle.Tcreal: 854 vcreal = getArg!(creal)(); 855 goto Lcomplex; 856 857 case Mangle.Tsarray: 858 putArray(argptr, (cast(TypeInfo_StaticArray)ti).len, (cast(TypeInfo_StaticArray)ti).next); 859 return; 860 861 case Mangle.Tarray: 862 mi = 10; 863 if (typeid(ti).name.length == 14 && 864 typeid(ti).name[9..14] == "Array") 865 { // array of non-primitive types 866 TypeInfo tn = (cast(TypeInfo_Array)ti).next; 867 tn = skipCI(tn); 868 switch (cast(Mangle)typeid(tn).name[9]) 869 { 870 case Mangle.Tchar: goto LarrayChar; 871 case Mangle.Twchar: goto LarrayWchar; 872 case Mangle.Tdchar: goto LarrayDchar; 873 default: 874 break; 875 } 876 void[] va = getArg!(void[])(); 877 putArray(va.ptr, va.length, tn); 878 return; 879 } 880 if (typeid(ti).name.length == 25 && 881 typeid(ti).name[9..25] == "AssociativeArray") 882 { // associative array 883 ubyte[long] vaa = getArg!(ubyte[long])(); 884 putAArray(vaa, 885 (cast(TypeInfo_AssociativeArray)ti).next, 886 (cast(TypeInfo_AssociativeArray)ti).key); 887 return; 888 } 889 890 while (1) 891 { 892 m2 = cast(Mangle)typeid(ti).name[mi]; 893 switch (m2) 894 { 895 case Mangle.Tchar: 896 LarrayChar: 897 s = getArg!(string)(); 898 goto Lputstr; 899 900 case Mangle.Twchar: 901 LarrayWchar: 902 wchar[] sw = getArg!(wchar[])(); 903 s = toUTF8(sw); 904 goto Lputstr; 905 906 case Mangle.Tdchar: 907 LarrayDchar: 908 s = toUTF8(getArg!(dstring)()); 909 Lputstr: 910 if (fc != 's') 911 throw new FormatException("string"); 912 if (flags & FLprecision && precision < s.length) 913 s = s[0 .. precision]; 914 putstr(s); 915 break; 916 917 case Mangle.Tconst: 918 case Mangle.Timmutable: 919 mi++; 920 continue; 921 922 default: 923 TypeInfo ti2 = primitiveTypeInfo(m2); 924 if (!ti2) 925 goto Lerror; 926 void[] va = getArg!(void[])(); 927 putArray(va.ptr, va.length, ti2); 928 } 929 return; 930 } 931 assert(0); 932 933 case Mangle.Tenum: 934 ti = (cast(TypeInfo_Enum)ti).base; 935 m = cast(Mangle)typeid(ti).name[9]; 936 formatArg(fc); 937 return; 938 939 case Mangle.Tstruct: 940 { TypeInfo_Struct tis = cast(TypeInfo_Struct)ti; 941 if (tis.xtoString is null) 942 throw new FormatException("Can't convert " ~ tis.toString() 943 ~ " to string: \"string toString()\" not defined"); 944 s = tis.xtoString(skipArg(tis)); 945 goto Lputstr; 946 } 947 948 default: 949 goto Lerror; 950 } 951 952 Lnumber: 953 switch (fc) 954 { 955 case 's': 956 case 'd': 957 if (signed) 958 { if (cast(long)vnumber < 0) 959 { prefix = "-"; 960 vnumber = -vnumber; 961 } 962 else if (flags & FLplus) 963 prefix = "+"; 964 else if (flags & FLspace) 965 prefix = " "; 966 } 967 break; 968 969 case 'b': 970 signed = 0; 971 base = 2; 972 break; 973 974 case 'o': 975 signed = 0; 976 base = 8; 977 break; 978 979 case 'X': 980 uc = 1; 981 if (flags & FLhash && vnumber) 982 prefix = "0X"; 983 signed = 0; 984 base = 16; 985 break; 986 987 case 'x': 988 if (flags & FLhash && vnumber) 989 prefix = "0x"; 990 signed = 0; 991 base = 16; 992 break; 993 994 default: 995 goto Lerror; 996 } 997 998 if (!signed) 999 { 1000 switch (m) 1001 { 1002 case Mangle.Tbyte: 1003 vnumber &= 0xFF; 1004 break; 1005 1006 case Mangle.Tshort: 1007 vnumber &= 0xFFFF; 1008 break; 1009 1010 case Mangle.Tint: 1011 vnumber &= 0xFFFFFFFF; 1012 break; 1013 1014 default: 1015 break; 1016 } 1017 } 1018 1019 if (flags & FLprecision && fc != 'p') 1020 flags &= ~FL0pad; 1021 1022 if (vnumber < base) 1023 { 1024 if (vnumber == 0 && precision == 0 && flags & FLprecision && 1025 !(fc == 'o' && flags & FLhash)) 1026 { 1027 putstr(null); 1028 return; 1029 } 1030 if (precision == 0 || !(flags & FLprecision)) 1031 { vchar = cast(char)('0' + vnumber); 1032 if (vnumber < 10) 1033 vchar = cast(char)('0' + vnumber); 1034 else 1035 vchar = cast(char)((uc ? 'A' - 10 : 'a' - 10) + vnumber); 1036 goto L2; 1037 } 1038 } 1039 1040 { 1041 ptrdiff_t n = tmpbuf.length; 1042 char c; 1043 int hexoffset = uc ? ('A' - ('9' + 1)) : ('a' - ('9' + 1)); 1044 1045 while (vnumber) 1046 { 1047 c = cast(char)((vnumber % base) + '0'); 1048 if (c > '9') 1049 c += hexoffset; 1050 vnumber /= base; 1051 tmpbuf[--n] = c; 1052 } 1053 if (tmpbuf.length - n < precision && precision < tmpbuf.length) 1054 { 1055 ptrdiff_t m = tmpbuf.length - precision; 1056 tmpbuf[m .. n] = '0'; 1057 n = m; 1058 } 1059 else if (flags & FLhash && fc == 'o') 1060 prefix = "0"; 1061 putstr(tmpbuf[n .. tmpbuf.length]); 1062 return; 1063 } 1064 1065 Lreal: 1066 putreal(vreal); 1067 return; 1068 1069 Lcomplex: 1070 putreal(vcreal.re); 1071 if (vcreal.im >= 0) 1072 { 1073 putc('+'); 1074 } 1075 putreal(vcreal.im); 1076 putc('i'); 1077 return; 1078 1079 Lerror: 1080 throw new FormatException("formatArg"); 1081 } 1082 1083 for (int j = 0; j < arguments.length; ) 1084 { 1085 ti = arguments[j++]; 1086 //printf("arg[%d]: '%.*s' %d\n", j, typeid(ti).name.length, typeid(ti).name.ptr, typeid(ti).name.length); 1087 //ti.print(); 1088 1089 flags = 0; 1090 precision = 0; 1091 field_width = 0; 1092 1093 ti = skipCI(ti); 1094 int mi = 9; 1095 do 1096 { 1097 if (typeid(ti).name.length <= mi) 1098 goto Lerror; 1099 m = cast(Mangle)typeid(ti).name[mi++]; 1100 } while (m == Mangle.Tconst || m == Mangle.Timmutable); 1101 1102 if (m == Mangle.Tarray) 1103 { 1104 if (typeid(ti).name.length == 14 && 1105 typeid(ti).name[9..14] == "Array") 1106 { 1107 TypeInfo tn = (cast(TypeInfo_Array)ti).next; 1108 tn = skipCI(tn); 1109 switch (cast(Mangle)typeid(tn).name[9]) 1110 { 1111 case Mangle.Tchar: 1112 case Mangle.Twchar: 1113 case Mangle.Tdchar: 1114 ti = tn; 1115 mi = 9; 1116 break; 1117 default: 1118 break; 1119 } 1120 } 1121 L1: 1122 Mangle m2 = cast(Mangle)typeid(ti).name[mi]; 1123 string fmt; // format string 1124 wstring wfmt; 1125 dstring dfmt; 1126 1127 /* For performance reasons, this code takes advantage of the 1128 * fact that most format strings will be ASCII, and that the 1129 * format specifiers are always ASCII. This means we only need 1130 * to deal with UTF in a couple of isolated spots. 1131 */ 1132 1133 switch (m2) 1134 { 1135 case Mangle.Tchar: 1136 fmt = getArg!(string)(); 1137 break; 1138 1139 case Mangle.Twchar: 1140 wfmt = getArg!(wstring)(); 1141 fmt = toUTF8(wfmt); 1142 break; 1143 1144 case Mangle.Tdchar: 1145 dfmt = getArg!(dstring)(); 1146 fmt = toUTF8(dfmt); 1147 break; 1148 1149 case Mangle.Tconst: 1150 case Mangle.Timmutable: 1151 mi++; 1152 goto L1; 1153 1154 default: 1155 formatArg('s'); 1156 continue; 1157 } 1158 1159 for (size_t i = 0; i < fmt.length; ) 1160 { dchar c = fmt[i++]; 1161 1162 dchar getFmtChar() 1163 { // Valid format specifier characters will never be UTF 1164 if (i == fmt.length) 1165 throw new FormatException("invalid specifier"); 1166 return fmt[i++]; 1167 } 1168 1169 int getFmtInt() 1170 { int n; 1171 1172 while (1) 1173 { 1174 n = n * 10 + (c - '0'); 1175 if (n < 0) // overflow 1176 throw new FormatException("int overflow"); 1177 c = getFmtChar(); 1178 if (c < '0' || c > '9') 1179 break; 1180 } 1181 return n; 1182 } 1183 1184 int getFmtStar() 1185 { Mangle m; 1186 TypeInfo ti; 1187 1188 if (j == arguments.length) 1189 throw new FormatException("too few arguments"); 1190 ti = arguments[j++]; 1191 m = cast(Mangle)typeid(ti).name[9]; 1192 if (m != Mangle.Tint) 1193 throw new FormatException("int argument expected"); 1194 return getArg!(int)(); 1195 } 1196 1197 if (c != '%') 1198 { 1199 if (c > 0x7F) // if UTF sequence 1200 { 1201 i--; // back up and decode UTF sequence 1202 import std.utf : decode; 1203 c = decode(fmt, i); 1204 } 1205 Lputc: 1206 putc(c); 1207 continue; 1208 } 1209 1210 // Get flags {-+ #} 1211 flags = 0; 1212 while (1) 1213 { 1214 c = getFmtChar(); 1215 switch (c) 1216 { 1217 case '-': flags |= FLdash; continue; 1218 case '+': flags |= FLplus; continue; 1219 case ' ': flags |= FLspace; continue; 1220 case '#': flags |= FLhash; continue; 1221 case '0': flags |= FL0pad; continue; 1222 1223 case '%': if (flags == 0) 1224 goto Lputc; 1225 break; 1226 1227 default: break; 1228 } 1229 break; 1230 } 1231 1232 // Get field width 1233 field_width = 0; 1234 if (c == '*') 1235 { 1236 field_width = getFmtStar(); 1237 if (field_width < 0) 1238 { flags |= FLdash; 1239 field_width = -field_width; 1240 } 1241 1242 c = getFmtChar(); 1243 } 1244 else if (c >= '0' && c <= '9') 1245 field_width = getFmtInt(); 1246 1247 if (flags & FLplus) 1248 flags &= ~FLspace; 1249 if (flags & FLdash) 1250 flags &= ~FL0pad; 1251 1252 // Get precision 1253 precision = 0; 1254 if (c == '.') 1255 { flags |= FLprecision; 1256 //flags &= ~FL0pad; 1257 1258 c = getFmtChar(); 1259 if (c == '*') 1260 { 1261 precision = getFmtStar(); 1262 if (precision < 0) 1263 { precision = 0; 1264 flags &= ~FLprecision; 1265 } 1266 1267 c = getFmtChar(); 1268 } 1269 else if (c >= '0' && c <= '9') 1270 precision = getFmtInt(); 1271 } 1272 1273 if (j == arguments.length) 1274 goto Lerror; 1275 ti = arguments[j++]; 1276 ti = skipCI(ti); 1277 mi = 9; 1278 do 1279 { 1280 m = cast(Mangle)typeid(ti).name[mi++]; 1281 } while (m == Mangle.Tconst || m == Mangle.Timmutable); 1282 1283 if (c > 0x7F) // if UTF sequence 1284 goto Lerror; // format specifiers can't be UTF 1285 formatArg(cast(char)c); 1286 } 1287 } 1288 else 1289 { 1290 formatArg('s'); 1291 } 1292 } 1293 return; 1294 1295 Lerror: 1296 throw new FormatException(); 1297 } 1298 1299 1300 private bool needToSwapEndianess(Char)(ref FormatSpec!Char f) 1301 { 1302 import std.system : endian, Endian; 1303 1304 return endian == Endian.littleEndian && f.flPlus 1305 || endian == Endian.bigEndian && f.flDash; 1306 } 1307 1308 unittest 1309 { 1310 string res; 1311 void putc(dchar c) 1312 { 1313 res ~= c; 1314 } 1315 1316 void myPrint(...) 1317 { 1318 undead.doformat.doFormat(&putc, _arguments, _argptr); 1319 } 1320 1321 myPrint("The answer is %s:", 27, 6); 1322 assert(res == "The answer is 27:6"); 1323 }