1 // Written in the D programming language. 2 3 /** 4 A one-stop shop for converting values from one type to another. 5 6 $(SCRIPT inhibitQuickIndex = 1;) 7 $(DIVC quickindex, 8 $(BOOKTABLE, 9 $(TR $(TH Category) $(TH Functions)) 10 $(TR $(TD Generic) $(TD 11 $(LREF asOriginalType) 12 $(LREF castFrom) 13 $(LREF parse) 14 $(LREF to) 15 $(LREF toChars) 16 )) 17 $(TR $(TD Strings) $(TD 18 $(LREF text) 19 $(LREF wtext) 20 $(LREF dtext) 21 $(LREF hexString) 22 )) 23 $(TR $(TD Numeric) $(TD 24 $(LREF octal) 25 $(LREF roundTo) 26 $(LREF signed) 27 $(LREF unsigned) 28 )) 29 $(TR $(TD Exceptions) $(TD 30 $(LREF ConvException) 31 $(LREF ConvOverflowException) 32 )) 33 )) 34 35 Copyright: Copyright The D Language Foundation 2007-. 36 37 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). 38 39 Authors: $(HTTP digitalmars.com, Walter Bright), 40 $(HTTP erdani.org, Andrei Alexandrescu), 41 Shin Fujishiro, 42 Adam D. Ruppe, 43 Kenji Hara 44 45 Source: $(PHOBOSSRC std/conv.d) 46 47 */ 48 module std.conv; 49 50 public import std.ascii : LetterCase; 51 52 import std.meta; 53 import std.range; 54 import std.traits; 55 import std.typecons : Flag, Yes, No, tuple, isTuple; 56 57 // Same as std.string.format, but "self-importing". 58 // Helps reduce code and imports, particularly in static asserts. 59 // Also helps with missing imports errors. 60 package template convFormat() 61 { 62 import std.format : format; 63 alias convFormat = format; 64 } 65 66 /* ************* Exceptions *************** */ 67 68 /** 69 * Thrown on conversion errors. 70 */ 71 class ConvException : Exception 72 { 73 import std.exception : basicExceptionCtors; 74 /// 75 mixin basicExceptionCtors; 76 } 77 78 /// 79 @safe unittest 80 { 81 import std.exception : assertThrown; 82 assertThrown!ConvException(to!int("abc")); 83 } 84 85 private auto convError(S, T)(S source, string fn = __FILE__, size_t ln = __LINE__) 86 { 87 string msg; 88 89 if (source.empty) 90 msg = "Unexpected end of input when converting from type " ~ S.stringof ~ " to type " ~ T.stringof; 91 else 92 { 93 ElementType!S el = source.front; 94 95 if (el == '\n') 96 msg = text("Unexpected '\\n' when converting from type " ~ S.stringof ~ " to type " ~ T.stringof); 97 else 98 msg = text("Unexpected '", el, 99 "' when converting from type " ~ S.stringof ~ " to type " ~ T.stringof); 100 } 101 102 return new ConvException(msg, fn, ln); 103 } 104 105 @safe pure/* nothrow*/ // lazy parameter bug 106 private auto parseError(lazy string msg, string fn = __FILE__, size_t ln = __LINE__) 107 { 108 return new ConvException(text("Can't parse string: ", msg), fn, ln); 109 } 110 111 private void parseCheck(alias source)(dchar c, string fn = __FILE__, size_t ln = __LINE__) 112 { 113 if (source.empty) 114 throw parseError(text("unexpected end of input when expecting \"", c, "\"")); 115 if (source.front != c) 116 throw parseError(text("\"", c, "\" is missing"), fn, ln); 117 source.popFront(); 118 } 119 120 private 121 { 122 T toStr(T, S)(S src) 123 if (isSomeString!T) 124 { 125 // workaround for https://issues.dlang.org/show_bug.cgi?id=14198 126 static if (is(S == bool) && is(typeof({ T s = "string"; }))) 127 { 128 return src ? "true" : "false"; 129 } 130 else 131 { 132 import std.array : appender; 133 import std.format.spec : FormatSpec; 134 import std.format.write : formatValue; 135 136 auto w = appender!T(); 137 FormatSpec!(ElementEncodingType!T) f; 138 formatValue(w, src, f); 139 return w.data; 140 } 141 } 142 143 template isExactSomeString(T) 144 { 145 enum isExactSomeString = isSomeString!T && !is(T == enum); 146 } 147 148 template isEnumStrToStr(S, T) 149 { 150 enum isEnumStrToStr = is(S : T) && 151 is(S == enum) && isExactSomeString!T; 152 } 153 template isNullToStr(S, T) 154 { 155 enum isNullToStr = is(S : T) && 156 (is(immutable S == immutable typeof(null))) && isExactSomeString!T; 157 } 158 } 159 160 /** 161 * Thrown on conversion overflow errors. 162 */ 163 class ConvOverflowException : ConvException 164 { 165 @safe pure nothrow 166 this(string s, string fn = __FILE__, size_t ln = __LINE__) 167 { 168 super(s, fn, ln); 169 } 170 } 171 172 /// 173 @safe unittest 174 { 175 import std.exception : assertThrown; 176 assertThrown!ConvOverflowException(to!ubyte(1_000_000)); 177 } 178 179 /** 180 The `to` template converts a value from one type _to another. 181 The source type is deduced and the target type must be specified, for example the 182 expression `to!int(42.0)` converts the number 42 from 183 `double` _to `int`. The conversion is "safe", i.e., 184 it checks for overflow; `to!int(4.2e10)` would throw the 185 `ConvOverflowException` exception. Overflow checks are only 186 inserted when necessary, e.g., `to!double(42)` does not do 187 any checking because any `int` fits in a `double`. 188 189 Conversions from string _to numeric types differ from the C equivalents 190 `atoi()` and `atol()` by checking for overflow and not allowing whitespace. 191 192 For conversion of strings _to signed types, the grammar recognized is: 193 $(PRE $(I Integer): 194 $(I Sign UnsignedInteger) 195 $(I UnsignedInteger) 196 $(I Sign): 197 $(B +) 198 $(B -)) 199 200 For conversion _to unsigned types, the grammar recognized is: 201 $(PRE $(I UnsignedInteger): 202 $(I DecimalDigit) 203 $(I DecimalDigit) $(I UnsignedInteger)) 204 */ 205 template to(T) 206 { 207 T to(A...)(A args) 208 if (A.length > 0) 209 { 210 return toImpl!T(args); 211 } 212 213 // Fix https://issues.dlang.org/show_bug.cgi?id=6175 214 T to(S)(ref S arg) 215 if (isStaticArray!S) 216 { 217 return toImpl!T(arg); 218 } 219 220 // Fix https://issues.dlang.org/show_bug.cgi?id=16108 221 T to(S)(ref S arg) 222 if (isAggregateType!S && !isCopyable!S) 223 { 224 return toImpl!T(arg); 225 } 226 } 227 228 /** 229 * Converting a value _to its own type (useful mostly for generic code) 230 * simply returns its argument. 231 */ 232 @safe pure unittest 233 { 234 int a = 42; 235 int b = to!int(a); 236 double c = to!double(3.14); // c is double with value 3.14 237 } 238 239 /** 240 * Converting among numeric types is a safe way _to cast them around. 241 * 242 * Conversions from floating-point types _to integral types allow loss of 243 * precision (the fractional part of a floating-point number). The 244 * conversion is truncating towards zero, the same way a cast would 245 * truncate. (_To round a floating point value when casting _to an 246 * integral, use `roundTo`.) 247 */ 248 @safe pure unittest 249 { 250 import std.exception : assertThrown; 251 252 int a = 420; 253 assert(to!long(a) == a); 254 assertThrown!ConvOverflowException(to!byte(a)); 255 256 assert(to!int(4.2e6) == 4200000); 257 assertThrown!ConvOverflowException(to!uint(-3.14)); 258 assert(to!uint(3.14) == 3); 259 assert(to!uint(3.99) == 3); 260 assert(to!int(-3.99) == -3); 261 } 262 263 /** 264 * When converting strings _to numeric types, note that D hexadecimal and binary 265 * literals are not handled. Neither the prefixes that indicate the base, nor the 266 * horizontal bar used _to separate groups of digits are recognized. This also 267 * applies to the suffixes that indicate the type. 268 * 269 * _To work around this, you can specify a radix for conversions involving numbers. 270 */ 271 @safe pure unittest 272 { 273 auto str = to!string(42, 16); 274 assert(str == "2A"); 275 auto i = to!int(str, 16); 276 assert(i == 42); 277 } 278 279 /** 280 * Conversions from integral types _to floating-point types always 281 * succeed, but might lose accuracy. The largest integers with a 282 * predecessor representable in floating-point format are `2^24-1` for 283 * `float`, `2^53-1` for `double`, and `2^64-1` for `real` (when 284 * `real` is 80-bit, e.g. on Intel machines). 285 */ 286 @safe pure unittest 287 { 288 // 2^24 - 1, largest proper integer representable as float 289 int a = 16_777_215; 290 assert(to!int(to!float(a)) == a); 291 assert(to!int(to!float(-a)) == -a); 292 } 293 294 /** 295 Conversion from string types to char types enforces the input 296 to consist of a single code point, and said code point must 297 fit in the target type. Otherwise, $(LREF ConvException) is thrown. 298 */ 299 @safe pure unittest 300 { 301 import std.exception : assertThrown; 302 303 assert(to!char("a") == 'a'); 304 assertThrown(to!char("ñ")); // 'ñ' does not fit into a char 305 assert(to!wchar("ñ") == 'ñ'); 306 assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar 307 assert(to!dchar("😃") == '😃'); 308 309 // Using wstring or dstring as source type does not affect the result 310 assert(to!char("a"w) == 'a'); 311 assert(to!char("a"d) == 'a'); 312 313 // Two code points cannot be converted to a single one 314 assertThrown(to!char("ab")); 315 } 316 317 /** 318 * Converting an array _to another array type works by converting each 319 * element in turn. Associative arrays can be converted _to associative 320 * arrays as long as keys and values can in turn be converted. 321 */ 322 @safe pure unittest 323 { 324 import std.string : split; 325 326 int[] a = [1, 2, 3]; 327 auto b = to!(float[])(a); 328 assert(b == [1.0f, 2, 3]); 329 string str = "1 2 3 4 5 6"; 330 auto numbers = to!(double[])(split(str)); 331 assert(numbers == [1.0, 2, 3, 4, 5, 6]); 332 int[string] c; 333 c["a"] = 1; 334 c["b"] = 2; 335 auto d = to!(double[wstring])(c); 336 assert(d["a"w] == 1 && d["b"w] == 2); 337 } 338 339 /** 340 * Conversions operate transitively, meaning that they work on arrays and 341 * associative arrays of any complexity. 342 * 343 * This conversion works because `to!short` applies _to an `int`, `to!wstring` 344 * applies _to a `string`, `to!string` applies _to a `double`, and 345 * `to!(double[])` applies _to an `int[]`. The conversion might throw an 346 * exception because `to!short` might fail the range check. 347 */ 348 @safe unittest 349 { 350 int[string][double[int[]]] a; 351 auto b = to!(short[wstring][string[double[]]])(a); 352 } 353 354 /** 355 * Object-to-object conversions by dynamic casting throw exception when 356 * the source is non-null and the target is null. 357 */ 358 @safe pure unittest 359 { 360 import std.exception : assertThrown; 361 // Testing object conversions 362 class A {} 363 class B : A {} 364 class C : A {} 365 A a1 = new A, a2 = new B, a3 = new C; 366 assert(to!B(a2) is a2); 367 assert(to!C(a3) is a3); 368 assertThrown!ConvException(to!B(a3)); 369 } 370 371 /** 372 * Stringize conversion from all types is supported. 373 * $(UL 374 * $(LI String _to string conversion works for any two string types having 375 * (`char`, `wchar`, `dchar`) character widths and any 376 * combination of qualifiers (mutable, `const`, or `immutable`).) 377 * $(LI Converts array (other than strings) _to string. 378 * Each element is converted by calling `to!T`.) 379 * $(LI Associative array _to string conversion. 380 * Each element is converted by calling `to!T`.) 381 * $(LI Object _to string conversion calls `toString` against the object or 382 * returns `"null"` if the object is null.) 383 * $(LI Struct _to string conversion calls `toString` against the struct if 384 * it is defined.) 385 * $(LI For structs that do not define `toString`, the conversion _to string 386 * produces the list of fields.) 387 * $(LI Enumerated types are converted _to strings as their symbolic names.) 388 * $(LI Boolean values are converted to `"true"` or `"false"`.) 389 * $(LI `char`, `wchar`, `dchar` _to a string type.) 390 * $(LI Unsigned or signed integers _to strings. 391 * $(DL $(DT [special case]) 392 * $(DD Convert integral value _to string in $(D_PARAM radix) radix. 393 * radix must be a value from 2 to 36. 394 * value is treated as a signed value only if radix is 10. 395 * The characters A through Z are used to represent values 10 through 36 396 * and their case is determined by the $(D_PARAM letterCase) parameter.))) 397 * $(LI All floating point types _to all string types.) 398 * $(LI Pointer to string conversions convert the pointer to a `size_t` value. 399 * If pointer is `char*`, treat it as C-style strings. 400 * In that case, this function is `@system`.)) 401 * See $(REF formatValue, std,format) on how `toString` should be defined. 402 */ 403 @system pure unittest // @system due to cast and ptr 404 { 405 // Conversion representing dynamic/static array with string 406 long[] a = [ 1, 3, 5 ]; 407 assert(to!string(a) == "[1, 3, 5]"); 408 409 // Conversion representing associative array with string 410 int[string] associativeArray = ["0":1, "1":2]; 411 assert(to!string(associativeArray) == `["0":1, "1":2]` || 412 to!string(associativeArray) == `["1":2, "0":1]`); 413 414 // char* to string conversion 415 assert(to!string(cast(char*) null) == ""); 416 assert(to!string("foo\0".ptr) == "foo"); 417 418 // Conversion reinterpreting void array to string 419 auto w = "abcx"w; 420 const(void)[] b = w; 421 assert(b.length == 8); 422 423 auto c = to!(wchar[])(b); 424 assert(c == "abcx"); 425 } 426 427 /** 428 * Strings can be converted to enum types. The enum member with the same name as the 429 * input string is returned. The comparison is case-sensitive. 430 * 431 * A $(LREF ConvException) is thrown if the enum does not have the specified member. 432 */ 433 @safe pure unittest 434 { 435 import std.exception : assertThrown; 436 437 enum E { a, b, c } 438 assert(to!E("a") == E.a); 439 assert(to!E("b") == E.b); 440 assertThrown!ConvException(to!E("A")); 441 } 442 443 // Tests for https://issues.dlang.org/show_bug.cgi?id=6175 444 @safe pure nothrow unittest 445 { 446 char[9] sarr = "blablabla"; 447 auto darr = to!(char[])(sarr); 448 assert(sarr.ptr == darr.ptr); 449 assert(sarr.length == darr.length); 450 } 451 452 // Tests for https://issues.dlang.org/show_bug.cgi?id=7348 453 @safe pure /+nothrow+/ unittest 454 { 455 assert(to!string(null) == "null"); 456 assert(text(null) == "null"); 457 } 458 459 // Test `scope` inference of parameters of `text` 460 @safe unittest 461 { 462 static struct S 463 { 464 int* x; // make S a type with pointers 465 string toString() const scope 466 { 467 return "S"; 468 } 469 } 470 scope S s; 471 assert(text("a", s) == "aS"); 472 } 473 474 // Tests for https://issues.dlang.org/show_bug.cgi?id=11390 475 @safe pure /+nothrow+/ unittest 476 { 477 const(typeof(null)) ctn; 478 immutable(typeof(null)) itn; 479 assert(to!string(ctn) == "null"); 480 assert(to!string(itn) == "null"); 481 } 482 483 // Tests for https://issues.dlang.org/show_bug.cgi?id=8729: do NOT skip leading WS 484 @safe pure unittest 485 { 486 import std.exception; 487 static foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) 488 { 489 assertThrown!ConvException(to!T(" 0")); 490 assertThrown!ConvException(to!T(" 0", 8)); 491 } 492 static foreach (T; AliasSeq!(float, double, real)) 493 { 494 assertThrown!ConvException(to!T(" 0")); 495 } 496 497 assertThrown!ConvException(to!bool(" true")); 498 499 alias NullType = typeof(null); 500 assertThrown!ConvException(to!NullType(" null")); 501 502 alias ARR = int[]; 503 assertThrown!ConvException(to!ARR(" [1]")); 504 505 alias AA = int[int]; 506 assertThrown!ConvException(to!AA(" [1:1]")); 507 } 508 509 // https://issues.dlang.org/show_bug.cgi?id=20623 510 @safe pure nothrow unittest 511 { 512 // static class C 513 // { 514 // override string toString() const 515 // { 516 // return "C()"; 517 // } 518 // } 519 520 static struct S 521 { 522 bool b; 523 int i; 524 float f; 525 int[] a; 526 int[int] aa; 527 S* p; 528 // C c; // TODO: Fails because of hasToString 529 530 void fun() inout 531 { 532 static foreach (const idx; 0 .. this.tupleof.length) 533 { 534 { 535 const _ = this.tupleof[idx].to!string(); 536 } 537 } 538 } 539 } 540 } 541 542 /** 543 If the source type is implicitly convertible to the target type, $(D 544 to) simply performs the implicit conversion. 545 */ 546 private T toImpl(T, S)(S value) 547 if (is(S : T) && 548 !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) 549 { 550 template isSignedInt(T) 551 { 552 enum isSignedInt = isIntegral!T && isSigned!T; 553 } 554 alias isUnsignedInt = isUnsigned; 555 556 // Conversion from integer to integer, and changing its sign 557 static if (isUnsignedInt!S && isSignedInt!T && S.sizeof == T.sizeof) 558 { // unsigned to signed & same size 559 import std.exception : enforce; 560 enforce(value <= cast(S) T.max, 561 new ConvOverflowException("Conversion positive overflow")); 562 } 563 else static if (isSignedInt!S && isUnsignedInt!T) 564 { // signed to unsigned 565 import std.exception : enforce; 566 enforce(0 <= value, 567 new ConvOverflowException("Conversion negative overflow")); 568 } 569 570 return value; 571 } 572 573 // https://issues.dlang.org/show_bug.cgi?id=9523: Allow identity enum conversion 574 @safe pure nothrow unittest 575 { 576 enum E { a } 577 auto e = to!E(E.a); 578 assert(e == E.a); 579 } 580 581 @safe pure nothrow unittest 582 { 583 int a = 42; 584 auto b = to!long(a); 585 assert(a == b); 586 } 587 588 // https://issues.dlang.org/show_bug.cgi?id=6377 589 @safe pure unittest 590 { 591 import std.exception; 592 // Conversion between same size 593 static foreach (S; AliasSeq!(byte, short, int, long)) 594 {{ 595 alias U = Unsigned!S; 596 597 static foreach (Sint; AliasSeq!(S, const S, immutable S)) 598 static foreach (Uint; AliasSeq!(U, const U, immutable U)) 599 {{ 600 // positive overflow 601 Uint un = Uint.max; 602 assertThrown!ConvOverflowException(to!Sint(un), 603 text(Sint.stringof, ' ', Uint.stringof, ' ', un)); 604 605 // negative overflow 606 Sint sn = -1; 607 assertThrown!ConvOverflowException(to!Uint(sn), 608 text(Sint.stringof, ' ', Uint.stringof, ' ', un)); 609 }} 610 }} 611 612 // Conversion between different size 613 static foreach (i, S1; AliasSeq!(byte, short, int, long)) 614 static foreach ( S2; AliasSeq!(byte, short, int, long)[i+1..$]) 615 {{ 616 alias U1 = Unsigned!S1; 617 alias U2 = Unsigned!S2; 618 619 static assert(U1.sizeof < S2.sizeof); 620 621 // small unsigned to big signed 622 static foreach (Uint; AliasSeq!(U1, const U1, immutable U1)) 623 static foreach (Sint; AliasSeq!(S2, const S2, immutable S2)) 624 {{ 625 Uint un = Uint.max; 626 assertNotThrown(to!Sint(un)); 627 assert(to!Sint(un) == un); 628 }} 629 630 // big unsigned to small signed 631 static foreach (Uint; AliasSeq!(U2, const U2, immutable U2)) 632 static foreach (Sint; AliasSeq!(S1, const S1, immutable S1)) 633 {{ 634 Uint un = Uint.max; 635 assertThrown(to!Sint(un)); 636 }} 637 638 static assert(S1.sizeof < U2.sizeof); 639 640 // small signed to big unsigned 641 static foreach (Sint; AliasSeq!(S1, const S1, immutable S1)) 642 static foreach (Uint; AliasSeq!(U2, const U2, immutable U2)) 643 {{ 644 Sint sn = -1; 645 assertThrown!ConvOverflowException(to!Uint(sn)); 646 }} 647 648 // big signed to small unsigned 649 static foreach (Sint; AliasSeq!(S2, const S2, immutable S2)) 650 static foreach (Uint; AliasSeq!(U1, const U1, immutable U1)) 651 {{ 652 Sint sn = -1; 653 assertThrown!ConvOverflowException(to!Uint(sn)); 654 }} 655 }} 656 } 657 658 // https://issues.dlang.org/show_bug.cgi?id=13551 659 private T toImpl(T, S)(S value) 660 if (isTuple!T) 661 { 662 T t; 663 static foreach (i; 0 .. T.length) 664 { 665 t[i] = value[i].to!(typeof(T[i])); 666 } 667 return t; 668 } 669 670 @safe unittest 671 { 672 import std.typecons : Tuple; 673 674 auto test = ["10", "20", "30"]; 675 assert(test.to!(Tuple!(int, int, int)) == Tuple!(int, int, int)(10, 20, 30)); 676 677 auto test1 = [1, 2]; 678 assert(test1.to!(Tuple!(int, int)) == Tuple!(int, int)(1, 2)); 679 680 auto test2 = [1.0, 2.0, 3.0]; 681 assert(test2.to!(Tuple!(int, int, int)) == Tuple!(int, int, int)(1, 2, 3)); 682 } 683 684 /* 685 Converting static arrays forwards to their dynamic counterparts. 686 */ 687 private T toImpl(T, S)(ref S s) 688 if (isStaticArray!S) 689 { 690 return toImpl!(T, typeof(s[0])[])(s); 691 } 692 693 @safe pure nothrow unittest 694 { 695 char[4] test = ['a', 'b', 'c', 'd']; 696 static assert(!isInputRange!(Unqual!(char[4]))); 697 assert(to!string(test) == test); 698 } 699 700 /** 701 When source type supports member template function opCast, it is used. 702 */ 703 private T toImpl(T, S)(S value) 704 if (!is(S : T) && 705 is(typeof(S.init.opCast!T()) : T) && 706 !isExactSomeString!T && 707 !is(typeof(T(value)))) 708 { 709 return value.opCast!T(); 710 } 711 712 @safe pure unittest 713 { 714 static struct Test 715 { 716 struct T 717 { 718 this(S s) @safe pure { } 719 } 720 struct S 721 { 722 T opCast(U)() @safe pure { assert(false); } 723 } 724 } 725 cast(void) to!(Test.T)(Test.S()); 726 727 // make sure std.conv.to is doing the same thing as initialization 728 Test.S s; 729 Test.T t = s; 730 } 731 732 @safe pure unittest 733 { 734 class B 735 { 736 T opCast(T)() { return 43; } 737 } 738 auto b = new B; 739 assert(to!int(b) == 43); 740 741 struct S 742 { 743 T opCast(T)() { return 43; } 744 } 745 auto s = S(); 746 assert(to!int(s) == 43); 747 } 748 749 /** 750 When target type supports 'converting construction', it is used. 751 $(UL $(LI If target type is struct, `T(value)` is used.) 752 $(LI If target type is class, $(D new T(value)) is used.)) 753 */ 754 private T toImpl(T, S)(S value) 755 if (!is(S : T) && 756 is(T == struct) && is(typeof(T(value)))) 757 { 758 return T(value); 759 } 760 761 // https://issues.dlang.org/show_bug.cgi?id=3961 762 @safe pure unittest 763 { 764 struct Int 765 { 766 int x; 767 } 768 Int i = to!Int(1); 769 770 static struct Int2 771 { 772 int x; 773 this(int x) @safe pure { this.x = x; } 774 } 775 Int2 i2 = to!Int2(1); 776 777 static struct Int3 778 { 779 int x; 780 static Int3 opCall(int x) @safe pure 781 { 782 Int3 i; 783 i.x = x; 784 return i; 785 } 786 } 787 Int3 i3 = to!Int3(1); 788 } 789 790 // https://issues.dlang.org/show_bug.cgi?id=6808 791 @safe pure unittest 792 { 793 static struct FakeBigInt 794 { 795 this(string s) @safe pure {} 796 } 797 798 string s = "101"; 799 auto i3 = to!FakeBigInt(s); 800 } 801 802 /// ditto 803 private T toImpl(T, S)(S value) 804 if (!is(S : T) && 805 is(T == class) && is(typeof(new T(value)))) 806 { 807 return new T(value); 808 } 809 810 @safe pure unittest 811 { 812 static struct S 813 { 814 int x; 815 } 816 static class C 817 { 818 int x; 819 this(int x) @safe pure { this.x = x; } 820 } 821 822 static class B 823 { 824 int value; 825 this(S src) @safe pure { value = src.x; } 826 this(C src) @safe pure { value = src.x; } 827 } 828 829 S s = S(1); 830 auto b1 = to!B(s); // == new B(s) 831 assert(b1.value == 1); 832 833 C c = new C(2); 834 auto b2 = to!B(c); // == new B(c) 835 assert(b2.value == 2); 836 837 auto c2 = to!C(3); // == new C(3) 838 assert(c2.x == 3); 839 } 840 841 @safe pure unittest 842 { 843 struct S 844 { 845 class A 846 { 847 this(B b) @safe pure {} 848 } 849 class B : A 850 { 851 this() @safe pure { super(this); } 852 } 853 } 854 855 S.B b = new S.B(); 856 S.A a = to!(S.A)(b); // == cast(S.A) b 857 // (do not run construction conversion like new S.A(b)) 858 assert(b is a); 859 860 static class C : Object 861 { 862 this() @safe pure {} 863 this(Object o) @safe pure {} 864 } 865 866 Object oc = new C(); 867 C a2 = to!C(oc); // == new C(a) 868 // Construction conversion overrides down-casting conversion 869 assert(a2 !is a); // 870 } 871 872 /** 873 Object-to-object conversions by dynamic casting throw exception when the source is 874 non-null and the target is null. 875 */ 876 private T toImpl(T, S)(S value) 877 if (!is(S : T) && 878 (is(S == class) || is(S == interface)) && !is(typeof(value.opCast!T()) : T) && 879 (is(T == class) || is(T == interface)) && !is(typeof(new T(value)))) 880 { 881 static if (is(T == immutable)) 882 { 883 // immutable <- immutable 884 enum isModConvertible = is(S == immutable); 885 } 886 else static if (is(T == const)) 887 { 888 static if (is(T == shared)) 889 { 890 // shared const <- shared 891 // shared const <- shared const 892 // shared const <- immutable 893 enum isModConvertible = is(S == shared) || is(S == immutable); 894 } 895 else 896 { 897 // const <- mutable 898 // const <- immutable 899 enum isModConvertible = !is(S == shared); 900 } 901 } 902 else 903 { 904 static if (is(T == shared)) 905 { 906 // shared <- shared mutable 907 enum isModConvertible = is(S == shared) && !is(S == const); 908 } 909 else 910 { 911 // (mutable) <- (mutable) 912 enum isModConvertible = is(Unqual!S == S); 913 } 914 } 915 static assert(isModConvertible, "Bad modifier conversion: "~S.stringof~" to "~T.stringof); 916 917 auto result = ()@trusted{ return cast(T) value; }(); 918 if (!result && value) 919 { 920 string name(TypeInfo ti) @trusted 921 { 922 while (auto tc = (cast(TypeInfo_Const) ti)) 923 { 924 ti = tc.base; 925 } 926 if (auto tinf = cast(TypeInfo_Interface) ti) 927 { 928 ti = tinf.info; 929 } 930 TypeInfo_Class tc = cast(TypeInfo_Class) ti; 931 assert(tc); 932 return tc.name; 933 } 934 throw new ConvException("Cannot convert object of static type " ~ 935 name(typeid(S)) ~ " and dynamic type " ~ name(typeid(value)) ~ " to type " ~ name(typeid(T))); 936 } 937 return result; 938 } 939 940 // Unittest for 6288 941 @safe pure unittest 942 { 943 import std.exception; 944 945 alias Identity(T) = T; 946 alias toConst(T) = const T; 947 alias toShared(T) = shared T; 948 alias toSharedConst(T) = shared const T; 949 alias toImmutable(T) = immutable T; 950 template AddModifier(int n) 951 if (0 <= n && n < 5) 952 { 953 static if (n == 0) alias AddModifier = Identity; 954 else static if (n == 1) alias AddModifier = toConst; 955 else static if (n == 2) alias AddModifier = toShared; 956 else static if (n == 3) alias AddModifier = toSharedConst; 957 else static if (n == 4) alias AddModifier = toImmutable; 958 } 959 960 interface I {} 961 interface J {} 962 963 class A {} 964 class B : A {} 965 class C : B, I, J {} 966 class D : I {} 967 968 static foreach (m1; 0 .. 5) // enumerate modifiers 969 static foreach (m2; 0 .. 5) // ditto 970 {{ 971 alias srcmod = AddModifier!m1; 972 alias tgtmod = AddModifier!m2; 973 974 // Compile time convertible equals to modifier convertible. 975 static if (is(srcmod!Object : tgtmod!Object)) 976 { 977 // Test runtime conversions: class to class, class to interface, 978 // interface to class, and interface to interface 979 980 // Check that the runtime conversion to succeed 981 srcmod!A ac = new srcmod!C(); 982 srcmod!I ic = new srcmod!C(); 983 assert(to!(tgtmod!C)(ac) !is null); // A(c) to C 984 assert(to!(tgtmod!I)(ac) !is null); // A(c) to I 985 assert(to!(tgtmod!C)(ic) !is null); // I(c) to C 986 assert(to!(tgtmod!J)(ic) !is null); // I(c) to J 987 988 // Check that the runtime conversion fails 989 srcmod!A ab = new srcmod!B(); 990 srcmod!I id = new srcmod!D(); 991 assertThrown(to!(tgtmod!C)(ab)); // A(b) to C 992 assertThrown(to!(tgtmod!I)(ab)); // A(b) to I 993 assertThrown(to!(tgtmod!C)(id)); // I(d) to C 994 assertThrown(to!(tgtmod!J)(id)); // I(d) to J 995 } 996 else 997 { 998 // Check that the conversion is rejected statically 999 static assert(!is(typeof(to!(tgtmod!C)(srcmod!A.init)))); // A to C 1000 static assert(!is(typeof(to!(tgtmod!I)(srcmod!A.init)))); // A to I 1001 static assert(!is(typeof(to!(tgtmod!C)(srcmod!I.init)))); // I to C 1002 static assert(!is(typeof(to!(tgtmod!J)(srcmod!I.init)))); // I to J 1003 } 1004 }} 1005 } 1006 1007 /** 1008 Handles type _to string conversions 1009 */ 1010 private T toImpl(T, S)(S value) 1011 if (!(is(S : T) && 1012 !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) && 1013 !isInfinite!S && isExactSomeString!T) 1014 { 1015 static if (isExactSomeString!S && value[0].sizeof == ElementEncodingType!T.sizeof) 1016 { 1017 // string-to-string with incompatible qualifier conversion 1018 static if (is(ElementEncodingType!T == immutable)) 1019 { 1020 // conversion (mutable|const) -> immutable 1021 return value.idup; 1022 } 1023 else 1024 { 1025 // conversion (immutable|const) -> mutable 1026 return value.dup; 1027 } 1028 } 1029 else static if (isExactSomeString!S) 1030 { 1031 import std.array : appender; 1032 // other string-to-string 1033 //Use Appender directly instead of toStr, which also uses a formatedWrite 1034 auto w = appender!T(); 1035 w.put(value); 1036 return w.data; 1037 } 1038 else static if (isIntegral!S && !is(S == enum)) 1039 { 1040 // other integral-to-string conversions with default radix 1041 1042 import core.internal.string : signedToTempString, unsignedToTempString; 1043 1044 alias EEType = Unqual!(ElementEncodingType!T); 1045 EEType[long.sizeof * 3 + 1] buf = void; 1046 EEType[] t = isSigned!S 1047 ? signedToTempString!(10, false, EEType)(value, buf) 1048 : unsignedToTempString!(10, false, EEType)(value, buf); 1049 return t.dup; 1050 } 1051 else static if (is(S == void[]) || is(S == const(void)[]) || is(S == immutable(void)[])) 1052 { 1053 import core.stdc.string : memcpy; 1054 import std.exception : enforce; 1055 // Converting void array to string 1056 alias Char = Unqual!(ElementEncodingType!T); 1057 auto raw = cast(const(ubyte)[]) value; 1058 enforce(raw.length % Char.sizeof == 0, 1059 new ConvException("Alignment mismatch in converting a " 1060 ~ S.stringof ~ " to a " 1061 ~ T.stringof)); 1062 auto result = new Char[raw.length / Char.sizeof]; 1063 ()@trusted{ memcpy(result.ptr, value.ptr, value.length); }(); 1064 return cast(T) result; 1065 } 1066 else static if (isPointer!S && isSomeChar!(PointerTarget!S)) 1067 { 1068 // This is unsafe because we cannot guarantee that the pointer is null terminated. 1069 return () @system { 1070 static if (is(S : const(char)*)) 1071 import core.stdc.string : strlen; 1072 else 1073 size_t strlen(S s) nothrow 1074 { 1075 S p = s; 1076 while (*p++) {} 1077 return p-s-1; 1078 } 1079 return toImpl!T(value ? value[0 .. strlen(value)].dup : null); 1080 }(); 1081 } 1082 else static if (isSomeString!T && is(S == enum)) 1083 { 1084 static if (isSwitchable!(OriginalType!S) && EnumMembers!S.length <= 50) 1085 { 1086 switch (value) 1087 { 1088 foreach (member; NoDuplicates!(EnumMembers!S)) 1089 { 1090 case member: 1091 return to!T(enumRep!(immutable(T), S, member)); 1092 } 1093 default: 1094 } 1095 } 1096 else 1097 { 1098 foreach (member; EnumMembers!S) 1099 { 1100 if (value == member) 1101 return to!T(enumRep!(immutable(T), S, member)); 1102 } 1103 } 1104 1105 import std.array : appender; 1106 import std.format.spec : FormatSpec; 1107 import std.format.write : formatValue; 1108 1109 //Default case, delegate to format 1110 //Note: we don't call toStr directly, to avoid duplicate work. 1111 auto app = appender!T(); 1112 app.put("cast(" ~ S.stringof ~ ")"); 1113 FormatSpec!char f; 1114 formatValue(app, cast(OriginalType!S) value, f); 1115 return app.data; 1116 } 1117 else 1118 { 1119 // other non-string values runs formatting 1120 return toStr!T(value); 1121 } 1122 } 1123 1124 // https://issues.dlang.org/show_bug.cgi?id=14042 1125 @system unittest 1126 { 1127 immutable(char)* ptr = "hello".ptr; 1128 auto result = ptr.to!(char[]); 1129 } 1130 // https://issues.dlang.org/show_bug.cgi?id=8384 1131 @system unittest 1132 { 1133 void test1(T)(T lp, string cmp) 1134 { 1135 static foreach (e; AliasSeq!(char, wchar, dchar)) 1136 { 1137 test2!(e[])(lp, cmp); 1138 test2!(const(e)[])(lp, cmp); 1139 test2!(immutable(e)[])(lp, cmp); 1140 } 1141 } 1142 1143 void test2(D, S)(S lp, string cmp) 1144 { 1145 assert(to!string(to!D(lp)) == cmp); 1146 } 1147 1148 static foreach (e; AliasSeq!("Hello, world!", "Hello, world!"w, "Hello, world!"d)) 1149 { 1150 test1(e, "Hello, world!"); 1151 test1(e.ptr, "Hello, world!"); 1152 } 1153 static foreach (e; AliasSeq!("", ""w, ""d)) 1154 { 1155 test1(e, ""); 1156 test1(e.ptr, ""); 1157 } 1158 } 1159 1160 /* 1161 To string conversion for non copy-able structs 1162 */ 1163 private T toImpl(T, S)(ref S value) 1164 if (!(is(S : T) && 1165 !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) && 1166 !isInfinite!S && isExactSomeString!T && !isCopyable!S && !isStaticArray!S) 1167 { 1168 import std.array : appender; 1169 import std.format.spec : FormatSpec; 1170 import std.format.write : formatValue; 1171 1172 auto w = appender!T(); 1173 FormatSpec!(ElementEncodingType!T) f; 1174 formatValue(w, value, f); 1175 return w.data; 1176 } 1177 1178 // https://issues.dlang.org/show_bug.cgi?id=16108 1179 @safe unittest 1180 { 1181 static struct A 1182 { 1183 int val; 1184 bool flag; 1185 1186 string toString() { return text(val, ":", flag); } 1187 1188 @disable this(this); 1189 } 1190 1191 auto a = A(); 1192 assert(to!string(a) == "0:false"); 1193 1194 static struct B 1195 { 1196 int val; 1197 bool flag; 1198 1199 @disable this(this); 1200 } 1201 1202 auto b = B(); 1203 assert(to!string(b) == "B(0, false)"); 1204 } 1205 1206 // https://issues.dlang.org/show_bug.cgi?id=20070 1207 @safe unittest 1208 { 1209 void writeThem(T)(ref inout(T) them) 1210 { 1211 assert(them.to!string == "[1, 2, 3, 4]"); 1212 } 1213 1214 const(uint)[4] vals = [ 1, 2, 3, 4 ]; 1215 writeThem(vals); 1216 } 1217 1218 /* 1219 Check whether type `T` can be used in a switch statement. 1220 This is useful for compile-time generation of switch case statements. 1221 */ 1222 private template isSwitchable(E) 1223 { 1224 enum bool isSwitchable = is(typeof({ 1225 switch (E.init) { default: } 1226 })); 1227 } 1228 1229 // 1230 @safe unittest 1231 { 1232 static assert(isSwitchable!int); 1233 static assert(!isSwitchable!double); 1234 static assert(!isSwitchable!real); 1235 } 1236 1237 //Static representation of the index I of the enum S, 1238 //In representation T. 1239 //T must be an immutable string (avoids un-necessary initializations). 1240 private template enumRep(T, S, S value) 1241 if (is (T == immutable) && isExactSomeString!T && is(S == enum)) 1242 { 1243 static T enumRep = toStr!T(value); 1244 } 1245 1246 @safe pure unittest 1247 { 1248 import std.exception; 1249 void dg() 1250 { 1251 // string to string conversion 1252 alias Chars = AliasSeq!(char, wchar, dchar); 1253 foreach (LhsC; Chars) 1254 { 1255 alias LhStrings = AliasSeq!(LhsC[], const(LhsC)[], immutable(LhsC)[]); 1256 foreach (Lhs; LhStrings) 1257 { 1258 foreach (RhsC; Chars) 1259 { 1260 alias RhStrings = AliasSeq!(RhsC[], const(RhsC)[], immutable(RhsC)[]); 1261 foreach (Rhs; RhStrings) 1262 { 1263 Lhs s1 = to!Lhs("wyda"); 1264 Rhs s2 = to!Rhs(s1); 1265 //writeln(Lhs.stringof, " -> ", Rhs.stringof); 1266 assert(s1 == to!Lhs(s2)); 1267 } 1268 } 1269 } 1270 } 1271 1272 foreach (T; Chars) 1273 { 1274 foreach (U; Chars) 1275 { 1276 T[] s1 = to!(T[])("Hello, world!"); 1277 auto s2 = to!(U[])(s1); 1278 assert(s1 == to!(T[])(s2)); 1279 auto s3 = to!(const(U)[])(s1); 1280 assert(s1 == to!(T[])(s3)); 1281 auto s4 = to!(immutable(U)[])(s1); 1282 assert(s1 == to!(T[])(s4)); 1283 } 1284 } 1285 } 1286 dg(); 1287 assertCTFEable!dg; 1288 } 1289 1290 @safe pure unittest 1291 { 1292 // Conversion representing bool value with string 1293 bool b; 1294 assert(to!string(b) == "false"); 1295 b = true; 1296 assert(to!string(b) == "true"); 1297 } 1298 1299 @safe pure unittest 1300 { 1301 // Conversion representing character value with string 1302 alias AllChars = 1303 AliasSeq!( char, const( char), immutable( char), 1304 wchar, const(wchar), immutable(wchar), 1305 dchar, const(dchar), immutable(dchar)); 1306 foreach (Char1; AllChars) 1307 { 1308 foreach (Char2; AllChars) 1309 { 1310 Char1 c = 'a'; 1311 assert(to!(Char2[])(c)[0] == c); 1312 } 1313 uint x = 4; 1314 assert(to!(Char1[])(x) == "4"); 1315 } 1316 1317 string s = "foo"; 1318 string s2; 1319 foreach (char c; s) 1320 { 1321 s2 ~= to!string(c); 1322 } 1323 assert(s2 == "foo"); 1324 } 1325 1326 @safe pure nothrow unittest 1327 { 1328 import std.exception; 1329 // Conversion representing integer values with string 1330 1331 static foreach (Int; AliasSeq!(ubyte, ushort, uint, ulong)) 1332 { 1333 assert(to!string(Int(0)) == "0"); 1334 assert(to!string(Int(9)) == "9"); 1335 assert(to!string(Int(123)) == "123"); 1336 } 1337 1338 static foreach (Int; AliasSeq!(byte, short, int, long)) 1339 { 1340 assert(to!string(Int(0)) == "0"); 1341 assert(to!string(Int(9)) == "9"); 1342 assert(to!string(Int(123)) == "123"); 1343 assert(to!string(Int(-0)) == "0"); 1344 assert(to!string(Int(-9)) == "-9"); 1345 assert(to!string(Int(-123)) == "-123"); 1346 assert(to!string(const(Int)(6)) == "6"); 1347 } 1348 1349 assert(wtext(int.max) == "2147483647"w); 1350 assert(wtext(int.min) == "-2147483648"w); 1351 assert(to!string(0L) == "0"); 1352 1353 assertCTFEable!( 1354 { 1355 assert(to!string(1uL << 62) == "4611686018427387904"); 1356 assert(to!string(0x100000000) == "4294967296"); 1357 assert(to!string(-138L) == "-138"); 1358 }); 1359 } 1360 1361 @safe unittest // sprintf issue 1362 { 1363 double[2] a = [ 1.5, 2.5 ]; 1364 assert(to!string(a) == "[1.5, 2.5]"); 1365 } 1366 1367 @safe unittest 1368 { 1369 // Conversion representing class object with string 1370 class A 1371 { 1372 override string toString() @safe const { return "an A"; } 1373 } 1374 A a; 1375 assert(to!string(a) == "null"); 1376 a = new A; 1377 assert(to!string(a) == "an A"); 1378 1379 // https://issues.dlang.org/show_bug.cgi?id=7660 1380 class C { override string toString() @safe const { return "C"; } } 1381 struct S { C c; alias c this; } 1382 S s; s.c = new C(); 1383 assert(to!string(s) == "C"); 1384 } 1385 1386 @safe unittest 1387 { 1388 // Conversion representing struct object with string 1389 struct S1 1390 { 1391 string toString() { return "wyda"; } 1392 } 1393 assert(to!string(S1()) == "wyda"); 1394 1395 struct S2 1396 { 1397 int a = 42; 1398 float b = 43.5; 1399 } 1400 S2 s2; 1401 assert(to!string(s2) == "S2(42, 43.5)"); 1402 1403 // Test for https://issues.dlang.org/show_bug.cgi?id=8080 1404 struct S8080 1405 { 1406 short[4] data; 1407 alias data this; 1408 string toString() { return "<S>"; } 1409 } 1410 S8080 s8080; 1411 assert(to!string(s8080) == "<S>"); 1412 } 1413 1414 @safe unittest 1415 { 1416 // Conversion representing enum value with string 1417 enum EB : bool { a = true } 1418 enum EU : uint { a = 0, b = 1, c = 2 } // base type is unsigned 1419 // base type is signed (https://issues.dlang.org/show_bug.cgi?id=7909) 1420 enum EI : int { a = -1, b = 0, c = 1 } 1421 enum EF : real { a = 1.414, b = 1.732, c = 2.236 } 1422 enum EC : char { a = 'x', b = 'y' } 1423 enum ES : string { a = "aaa", b = "bbb" } 1424 1425 static foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES)) 1426 { 1427 assert(to! string(E.a) == "a"c); 1428 assert(to!wstring(E.a) == "a"w); 1429 assert(to!dstring(E.a) == "a"d); 1430 } 1431 1432 // Test an value not corresponding to an enum member. 1433 auto o = cast(EU) 5; 1434 assert(to! string(o) == "cast(EU)5"c); 1435 assert(to!wstring(o) == "cast(EU)5"w); 1436 assert(to!dstring(o) == "cast(EU)5"d); 1437 } 1438 1439 @safe unittest 1440 { 1441 enum E 1442 { 1443 foo, 1444 doo = foo, // check duplicate switch statements 1445 bar, 1446 } 1447 1448 //Test regression 12494 1449 assert(to!string(E.foo) == "foo"); 1450 assert(to!string(E.doo) == "foo"); 1451 assert(to!string(E.bar) == "bar"); 1452 1453 static foreach (S; AliasSeq!(string, wstring, dstring, const(char[]), const(wchar[]), const(dchar[]))) 1454 {{ 1455 auto s1 = to!S(E.foo); 1456 auto s2 = to!S(E.foo); 1457 assert(s1 == s2); 1458 // ensure we don't allocate when it's unnecessary 1459 assert(s1 is s2); 1460 }} 1461 1462 static foreach (S; AliasSeq!(char[], wchar[], dchar[])) 1463 {{ 1464 auto s1 = to!S(E.foo); 1465 auto s2 = to!S(E.foo); 1466 assert(s1 == s2); 1467 // ensure each mutable array is unique 1468 assert(s1 !is s2); 1469 }} 1470 } 1471 1472 // ditto 1473 @trusted pure private T toImpl(T, S)(S value, uint radix, LetterCase letterCase = LetterCase.upper) 1474 if (isIntegral!S && 1475 isExactSomeString!T) 1476 in 1477 { 1478 assert(radix >= 2 && radix <= 36, "radix must be in range [2,36]"); 1479 } 1480 do 1481 { 1482 alias EEType = Unqual!(ElementEncodingType!T); 1483 1484 T toStringRadixConvert(size_t bufLen)(uint runtimeRadix = 0) 1485 { 1486 Unsigned!(Unqual!S) div = void, mValue = unsigned(value); 1487 1488 size_t index = bufLen; 1489 EEType[bufLen] buffer = void; 1490 char baseChar = letterCase == LetterCase.lower ? 'a' : 'A'; 1491 char mod = void; 1492 1493 do 1494 { 1495 div = cast(S)(mValue / runtimeRadix ); 1496 mod = cast(ubyte)(mValue % runtimeRadix); 1497 mod += mod < 10 ? '0' : baseChar - 10; 1498 buffer[--index] = cast(char) mod; 1499 mValue = div; 1500 } while (mValue); 1501 1502 return cast(T) buffer[index .. $].dup; 1503 } 1504 1505 import std.array : array; 1506 switch (radix) 1507 { 1508 case 10: 1509 // The (value+0) is so integral promotions happen to the type 1510 return toChars!(10, EEType)(value + 0).array; 1511 case 16: 1512 // The unsigned(unsigned(value)+0) is so unsigned integral promotions happen to the type 1513 if (letterCase == letterCase.upper) 1514 return toChars!(16, EEType, LetterCase.upper)(unsigned(unsigned(value) + 0)).array; 1515 else 1516 return toChars!(16, EEType, LetterCase.lower)(unsigned(unsigned(value) + 0)).array; 1517 case 2: 1518 return toChars!(2, EEType)(unsigned(unsigned(value) + 0)).array; 1519 case 8: 1520 return toChars!(8, EEType)(unsigned(unsigned(value) + 0)).array; 1521 1522 default: 1523 return toStringRadixConvert!(S.sizeof * 6)(radix); 1524 } 1525 } 1526 1527 @safe pure nothrow unittest 1528 { 1529 static foreach (Int; AliasSeq!(uint, ulong)) 1530 { 1531 assert(to!string(Int(16), 16) == "10"); 1532 assert(to!string(Int(15), 2u) == "1111"); 1533 assert(to!string(Int(1), 2u) == "1"); 1534 assert(to!string(Int(0x1234AF), 16u) == "1234AF"); 1535 assert(to!string(Int(0x1234BCD), 16u, LetterCase.upper) == "1234BCD"); 1536 assert(to!string(Int(0x1234AF), 16u, LetterCase.lower) == "1234af"); 1537 } 1538 1539 static foreach (Int; AliasSeq!(int, long)) 1540 { 1541 assert(to!string(Int(-10), 10u) == "-10"); 1542 } 1543 1544 assert(to!string(byte(-10), 16) == "F6"); 1545 assert(to!string(long.min) == "-9223372036854775808"); 1546 assert(to!string(long.max) == "9223372036854775807"); 1547 } 1548 1549 /** 1550 Narrowing numeric-numeric conversions throw when the value does not 1551 fit in the narrower type. 1552 */ 1553 private T toImpl(T, S)(S value) 1554 if (!is(S : T) && 1555 (isNumeric!S || isSomeChar!S || isBoolean!S) && 1556 (isNumeric!T || isSomeChar!T || isBoolean!T) && !is(T == enum)) 1557 { 1558 static if (isFloatingPoint!S && isIntegral!T) 1559 { 1560 import std.math.traits : isNaN; 1561 if (value.isNaN) throw new ConvException("Input was NaN"); 1562 } 1563 1564 enum sSmallest = mostNegative!S; 1565 enum tSmallest = mostNegative!T; 1566 static if (sSmallest < 0) 1567 { 1568 // possible underflow converting from a signed 1569 static if (tSmallest == 0) 1570 { 1571 immutable good = value >= 0; 1572 } 1573 else 1574 { 1575 static assert(tSmallest < 0, 1576 "minimum value of T must be smaller than 0"); 1577 immutable good = value >= tSmallest; 1578 } 1579 if (!good) 1580 throw new ConvOverflowException("Conversion negative overflow"); 1581 } 1582 static if (S.max > T.max) 1583 { 1584 // possible overflow 1585 if (value > T.max) 1586 throw new ConvOverflowException("Conversion positive overflow"); 1587 } 1588 return (ref value)@trusted{ return cast(T) value; }(value); 1589 } 1590 1591 @safe pure unittest 1592 { 1593 import std.exception; 1594 1595 dchar a = ' '; 1596 assert(to!char(a) == ' '); 1597 a = 300; 1598 assert(collectException(to!char(a))); 1599 1600 dchar from0 = 'A'; 1601 char to0 = to!char(from0); 1602 1603 wchar from1 = 'A'; 1604 char to1 = to!char(from1); 1605 1606 char from2 = 'A'; 1607 char to2 = to!char(from2); 1608 1609 char from3 = 'A'; 1610 wchar to3 = to!wchar(from3); 1611 1612 char from4 = 'A'; 1613 dchar to4 = to!dchar(from4); 1614 } 1615 1616 @safe unittest 1617 { 1618 import std.exception; 1619 1620 // Narrowing conversions from enum -> integral should be allowed, but they 1621 // should throw at runtime if the enum value doesn't fit in the target 1622 // type. 1623 enum E1 : ulong { A = 1, B = 1UL << 48, C = 0 } 1624 assert(to!int(E1.A) == 1); 1625 assert(to!bool(E1.A) == true); 1626 assertThrown!ConvOverflowException(to!int(E1.B)); // E1.B overflows int 1627 assertThrown!ConvOverflowException(to!bool(E1.B)); // E1.B overflows bool 1628 assert(to!bool(E1.C) == false); 1629 1630 enum E2 : long { A = -1L << 48, B = -1 << 31, C = 1 << 31 } 1631 assertThrown!ConvOverflowException(to!int(E2.A)); // E2.A overflows int 1632 assertThrown!ConvOverflowException(to!uint(E2.B)); // E2.B overflows uint 1633 assert(to!int(E2.B) == -1 << 31); // but does not overflow int 1634 assert(to!int(E2.C) == 1 << 31); // E2.C does not overflow int 1635 1636 enum E3 : int { A = -1, B = 1, C = 255, D = 0 } 1637 assertThrown!ConvOverflowException(to!ubyte(E3.A)); 1638 assertThrown!ConvOverflowException(to!bool(E3.A)); 1639 assert(to!byte(E3.A) == -1); 1640 assert(to!byte(E3.B) == 1); 1641 assert(to!ubyte(E3.C) == 255); 1642 assert(to!bool(E3.B) == true); 1643 assertThrown!ConvOverflowException(to!byte(E3.C)); 1644 assertThrown!ConvOverflowException(to!bool(E3.C)); 1645 assert(to!bool(E3.D) == false); 1646 1647 } 1648 1649 @safe unittest 1650 { 1651 import std.exception; 1652 import std.math.traits : isNaN; 1653 1654 double d = double.nan; 1655 float f = to!float(d); 1656 assert(f.isNaN); 1657 assert(to!double(f).isNaN); 1658 assertThrown!ConvException(to!int(d)); 1659 assertThrown!ConvException(to!int(f)); 1660 auto ex = collectException(d.to!int); 1661 assert(ex.msg == "Input was NaN"); 1662 } 1663 1664 /** 1665 Array-to-array conversion (except when target is a string type) 1666 converts each element in turn by using `to`. 1667 */ 1668 private T toImpl(T, S)(scope S value) 1669 if (!is(S : T) && 1670 !isSomeString!S && isDynamicArray!S && 1671 !isExactSomeString!T && isArray!T) 1672 { 1673 alias E = typeof(T.init[0]); 1674 1675 static if (isStaticArray!T) 1676 { 1677 import std.exception : enforce; 1678 auto res = to!(E[])(value); 1679 enforce!ConvException(T.length == res.length, 1680 convFormat("Length mismatch when converting to static array: %s vs %s", T.length, res.length)); 1681 return res[0 .. T.length]; 1682 } 1683 else 1684 { 1685 import std.array : appender; 1686 auto w = appender!(E[])(); 1687 w.reserve(value.length); 1688 foreach (ref e; value) 1689 { 1690 w.put(to!E(e)); 1691 } 1692 return w.data; 1693 } 1694 } 1695 1696 @safe pure unittest 1697 { 1698 import std.exception; 1699 1700 // array to array conversions 1701 uint[] a = [ 1u, 2, 3 ]; 1702 auto b = to!(float[])(a); 1703 assert(b == [ 1.0f, 2, 3 ]); 1704 1705 immutable(int)[3] d = [ 1, 2, 3 ]; 1706 b = to!(float[])(d); 1707 assert(b == [ 1.0f, 2, 3 ]); 1708 1709 uint[][] e = [ a, a ]; 1710 auto f = to!(float[][])(e); 1711 assert(f[0] == b && f[1] == b); 1712 1713 // Test for https://issues.dlang.org/show_bug.cgi?id=8264 1714 struct Wrap 1715 { 1716 string wrap; 1717 alias wrap this; 1718 } 1719 Wrap[] warr = to!(Wrap[])(["foo", "bar"]); // should work 1720 1721 // https://issues.dlang.org/show_bug.cgi?id=12633 1722 import std.conv : to; 1723 const s2 = ["10", "20"]; 1724 1725 immutable int[2] a3 = s2.to!(int[2]); 1726 assert(a3 == [10, 20]); 1727 1728 // verify length mismatches are caught 1729 immutable s4 = [1, 2, 3, 4]; 1730 foreach (i; [1, 4]) 1731 { 1732 auto ex = collectException(s4[0 .. i].to!(int[2])); 1733 assert(ex && ex.msg == "Length mismatch when converting to static array: 2 vs " ~ [cast(char)(i + '0')], 1734 ex ? ex.msg : "Exception was not thrown!"); 1735 } 1736 } 1737 1738 @safe unittest 1739 { 1740 auto b = [ 1.0f, 2, 3 ]; 1741 1742 auto c = to!(string[])(b); 1743 assert(c[0] == "1" && c[1] == "2" && c[2] == "3"); 1744 } 1745 1746 /** 1747 Associative array to associative array conversion converts each key 1748 and each value in turn. 1749 */ 1750 private T toImpl(T, S)(S value) 1751 if (!is(S : T) && isAssociativeArray!S && 1752 isAssociativeArray!T && !is(T == enum)) 1753 { 1754 /* This code is potentially unsafe. 1755 */ 1756 alias K2 = KeyType!T; 1757 alias V2 = ValueType!T; 1758 1759 // While we are "building" the AA, we need to unqualify its values, and only re-qualify at the end 1760 Unqual!V2[K2] result; 1761 1762 foreach (k1, v1; value) 1763 { 1764 // Cast values temporarily to Unqual!V2 to store them to result variable 1765 result[to!K2(k1)] = to!(Unqual!V2)(v1); 1766 } 1767 // Cast back to original type 1768 return () @trusted { return cast(T) result; }(); 1769 } 1770 1771 @safe unittest 1772 { 1773 // hash to hash conversions 1774 int[string] a; 1775 a["0"] = 1; 1776 a["1"] = 2; 1777 auto b = to!(double[dstring])(a); 1778 assert(b["0"d] == 1 && b["1"d] == 2); 1779 } 1780 1781 // https://issues.dlang.org/show_bug.cgi?id=8705, from doc 1782 @safe unittest 1783 { 1784 import std.exception; 1785 int[string][double[int[]]] a; 1786 auto b = to!(short[wstring][string[double[]]])(a); 1787 a = [null:["hello":int.max]]; 1788 assertThrown!ConvOverflowException(to!(short[wstring][string[double[]]])(a)); 1789 } 1790 @system unittest // Extra cases for AA with qualifiers conversion 1791 { 1792 int[][int[]] a;// = [[], []]; 1793 auto b = to!(immutable(short[])[immutable short[]])(a); 1794 1795 double[dstring][int[long[]]] c; 1796 auto d = to!(immutable(short[immutable wstring])[immutable string[double[]]])(c); 1797 } 1798 1799 @safe unittest 1800 { 1801 import std.algorithm.comparison : equal; 1802 import std.array : byPair; 1803 1804 int[int] a; 1805 assert(a.to!(int[int]) == a); 1806 assert(a.to!(const(int)[int]).byPair.equal(a.byPair)); 1807 } 1808 1809 @safe pure unittest 1810 { 1811 static void testIntegralToFloating(Integral, Floating)() 1812 { 1813 Integral a = 42; 1814 auto b = to!Floating(a); 1815 assert(a == b); 1816 assert(a == to!Integral(b)); 1817 } 1818 static void testFloatingToIntegral(Floating, Integral)() 1819 { 1820 import std.math.traits : floatTraits, RealFormat; 1821 1822 bool convFails(Source, Target, E)(Source src) 1823 { 1824 try 1825 cast(void) to!Target(src); 1826 catch (E) 1827 return true; 1828 return false; 1829 } 1830 1831 // convert some value 1832 Floating a = 4.2e1; 1833 auto b = to!Integral(a); 1834 assert(is(typeof(b) == Integral) && b == 42); 1835 // convert some negative value (if applicable) 1836 a = -4.2e1; 1837 static if (Integral.min < 0) 1838 { 1839 b = to!Integral(a); 1840 assert(is(typeof(b) == Integral) && b == -42); 1841 } 1842 else 1843 { 1844 // no go for unsigned types 1845 assert(convFails!(Floating, Integral, ConvOverflowException)(a)); 1846 } 1847 // convert to the smallest integral value 1848 a = 0.0 + Integral.min; 1849 static if (Integral.min < 0) 1850 { 1851 a = -a; // -Integral.min not representable as an Integral 1852 assert(convFails!(Floating, Integral, ConvOverflowException)(a) 1853 || Floating.sizeof <= Integral.sizeof 1854 || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53); 1855 } 1856 a = 0.0 + Integral.min; 1857 assert(to!Integral(a) == Integral.min); 1858 --a; // no more representable as an Integral 1859 assert(convFails!(Floating, Integral, ConvOverflowException)(a) 1860 || Floating.sizeof <= Integral.sizeof 1861 || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53); 1862 a = 0.0 + Integral.max; 1863 assert(to!Integral(a) == Integral.max 1864 || Floating.sizeof <= Integral.sizeof 1865 || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53); 1866 ++a; // no more representable as an Integral 1867 assert(convFails!(Floating, Integral, ConvOverflowException)(a) 1868 || Floating.sizeof <= Integral.sizeof 1869 || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53); 1870 // convert a value with a fractional part 1871 a = 3.14; 1872 assert(to!Integral(a) == 3); 1873 a = 3.99; 1874 assert(to!Integral(a) == 3); 1875 static if (Integral.min < 0) 1876 { 1877 a = -3.14; 1878 assert(to!Integral(a) == -3); 1879 a = -3.99; 1880 assert(to!Integral(a) == -3); 1881 } 1882 } 1883 1884 alias AllInts = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong); 1885 alias AllFloats = AliasSeq!(float, double, real); 1886 alias AllNumerics = AliasSeq!(AllInts, AllFloats); 1887 // test with same type 1888 { 1889 foreach (T; AllNumerics) 1890 { 1891 T a = 42; 1892 auto b = to!T(a); 1893 assert(is(typeof(a) == typeof(b)) && a == b); 1894 } 1895 } 1896 // test that floating-point numbers convert properly to largest ints 1897 // see http://oregonstate.edu/~peterseb/mth351/docs/351s2001_fp80x87.html 1898 // look for "largest fp integer with a predecessor" 1899 { 1900 // float 1901 int a = 16_777_215; // 2^24 - 1 1902 assert(to!int(to!float(a)) == a); 1903 assert(to!int(to!float(-a)) == -a); 1904 // double 1905 long b = 9_007_199_254_740_991; // 2^53 - 1 1906 assert(to!long(to!double(b)) == b); 1907 assert(to!long(to!double(-b)) == -b); 1908 // real 1909 static if (real.mant_dig >= 64) 1910 { 1911 ulong c = 18_446_744_073_709_551_615UL; // 2^64 - 1 1912 assert(to!ulong(to!real(c)) == c); 1913 } 1914 } 1915 // test conversions floating => integral 1916 { 1917 foreach (Integral; AllInts) 1918 { 1919 foreach (Floating; AllFloats) 1920 { 1921 testFloatingToIntegral!(Floating, Integral)(); 1922 } 1923 } 1924 } 1925 // test conversion integral => floating 1926 { 1927 foreach (Integral; AllInts) 1928 { 1929 foreach (Floating; AllFloats) 1930 { 1931 testIntegralToFloating!(Integral, Floating)(); 1932 } 1933 } 1934 } 1935 // test parsing 1936 { 1937 foreach (T; AllNumerics) 1938 { 1939 // from type immutable(char)[2] 1940 auto a = to!T("42"); 1941 assert(a == 42); 1942 // from type char[] 1943 char[] s1 = "42".dup; 1944 a = to!T(s1); 1945 assert(a == 42); 1946 // from type char[2] 1947 char[2] s2; 1948 s2[] = "42"; 1949 a = to!T(s2); 1950 assert(a == 42); 1951 // from type immutable(wchar)[2] 1952 a = to!T("42"w); 1953 assert(a == 42); 1954 } 1955 } 1956 } 1957 1958 @safe unittest 1959 { 1960 alias AllInts = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong); 1961 alias AllFloats = AliasSeq!(float, double, real); 1962 alias AllNumerics = AliasSeq!(AllInts, AllFloats); 1963 // test conversions to string 1964 { 1965 foreach (T; AllNumerics) 1966 { 1967 T a = 42; 1968 string s = to!string(a); 1969 assert(s == "42", s); 1970 wstring ws = to!wstring(a); 1971 assert(ws == "42"w, to!string(ws)); 1972 dstring ds = to!dstring(a); 1973 assert(ds == "42"d, to!string(ds)); 1974 // array test 1975 T[] b = new T[2]; 1976 b[0] = 42; 1977 b[1] = 33; 1978 assert(to!string(b) == "[42, 33]"); 1979 } 1980 } 1981 // test array to string conversion 1982 foreach (T ; AllNumerics) 1983 { 1984 auto a = [to!T(1), 2, 3]; 1985 assert(to!string(a) == "[1, 2, 3]"); 1986 } 1987 // test enum to int conversion 1988 enum Testing { Test1, Test2 } 1989 Testing t; 1990 auto a = to!string(t); 1991 assert(a == "Test1"); 1992 } 1993 1994 1995 /** 1996 String, or string-like input range, to non-string conversion runs parsing. 1997 $(UL 1998 $(LI When the source is a wide string, it is first converted to a narrow 1999 string and then parsed.) 2000 $(LI When the source is a narrow string, normal text parsing occurs.)) 2001 */ 2002 private T toImpl(T, S)(S value) 2003 if (isInputRange!S && isSomeChar!(ElementEncodingType!S) && 2004 !isExactSomeString!T && is(typeof(parse!T(value))) && 2005 // https://issues.dlang.org/show_bug.cgi?id=20539 2006 !(is(T == enum) && is(typeof(value == OriginalType!T.init)) && !isSomeString!(OriginalType!T))) 2007 { 2008 scope(success) 2009 { 2010 if (!value.empty) 2011 { 2012 throw convError!(S, T)(value); 2013 } 2014 } 2015 return parse!T(value); 2016 } 2017 2018 /// ditto 2019 private T toImpl(T, S)(S value, uint radix) 2020 if (isSomeFiniteCharInputRange!S && 2021 isIntegral!T && is(typeof(parse!T(value, radix)))) 2022 { 2023 scope(success) 2024 { 2025 if (!value.empty) 2026 { 2027 throw convError!(S, T)(value); 2028 } 2029 } 2030 return parse!T(value, radix); 2031 } 2032 2033 @safe pure unittest 2034 { 2035 // https://issues.dlang.org/show_bug.cgi?id=6668 2036 // ensure no collaterals thrown 2037 try { to!uint("-1"); } 2038 catch (ConvException e) { assert(e.next is null); } 2039 } 2040 2041 @safe pure unittest 2042 { 2043 static foreach (Str; AliasSeq!(string, wstring, dstring)) 2044 {{ 2045 Str a = "123"; 2046 assert(to!int(a) == 123); 2047 assert(to!double(a) == 123); 2048 }} 2049 2050 // https://issues.dlang.org/show_bug.cgi?id=6255 2051 auto n = to!int("FF", 16); 2052 assert(n == 255); 2053 } 2054 2055 // https://issues.dlang.org/show_bug.cgi?id=15800 2056 @safe unittest 2057 { 2058 import std.utf : byCodeUnit, byChar, byWchar, byDchar; 2059 2060 assert(to!int(byCodeUnit("10")) == 10); 2061 assert(to!int(byCodeUnit("10"), 10) == 10); 2062 assert(to!int(byCodeUnit("10"w)) == 10); 2063 assert(to!int(byCodeUnit("10"w), 10) == 10); 2064 2065 assert(to!int(byChar("10")) == 10); 2066 assert(to!int(byChar("10"), 10) == 10); 2067 assert(to!int(byWchar("10")) == 10); 2068 assert(to!int(byWchar("10"), 10) == 10); 2069 assert(to!int(byDchar("10")) == 10); 2070 assert(to!int(byDchar("10"), 10) == 10); 2071 } 2072 2073 /** 2074 String, or string-like input range, to char type not directly 2075 supported by parse parses the first dchar of the source. 2076 2077 Returns: the first code point of the input range, converted 2078 to type T. 2079 2080 Throws: ConvException if the input range contains more than 2081 a single code point, or if the code point does not 2082 fit into a code unit of type T. 2083 */ 2084 private T toImpl(T, S)(S value) 2085 if (isSomeChar!T && !is(typeof(parse!T(value))) && 2086 is(typeof(parse!dchar(value)))) 2087 { 2088 import std.utf : encode; 2089 2090 immutable dchar codepoint = parse!dchar(value); 2091 if (!value.empty) 2092 throw new ConvException(convFormat("Cannot convert \"%s\" to %s because it " ~ 2093 "contains more than a single code point.", 2094 value, T.stringof)); 2095 T[dchar.sizeof / T.sizeof] decodedCodepoint; 2096 if (encode(decodedCodepoint, codepoint) != 1) 2097 throw new ConvException(convFormat("First code point '%s' of \"%s\" does not fit into a " ~ 2098 "single %s code unit", codepoint, value, T.stringof)); 2099 return decodedCodepoint[0]; 2100 } 2101 2102 @safe pure unittest 2103 { 2104 import std.exception : assertThrown; 2105 2106 assert(toImpl!wchar("a") == 'a'); 2107 2108 assert(toImpl!char("a"d) == 'a'); 2109 assert(toImpl!char("a"w) == 'a'); 2110 assert(toImpl!wchar("a"d) == 'a'); 2111 2112 assertThrown!ConvException(toImpl!wchar("ab")); 2113 assertThrown!ConvException(toImpl!char("😃"d)); 2114 } 2115 2116 /** 2117 Convert a value that is implicitly convertible to the enum base type 2118 into an Enum value. If the value does not match any enum member values 2119 a ConvException is thrown. 2120 Enums with floating-point or string base types are not supported. 2121 */ 2122 private T toImpl(T, S)(S value) 2123 if (is(T == enum) && !is(S == enum) 2124 && is(typeof(value == OriginalType!T.init)) 2125 && !isFloatingPoint!(OriginalType!T) && !isSomeString!(OriginalType!T)) 2126 { 2127 foreach (Member; EnumMembers!T) 2128 { 2129 if (Member == value) 2130 return Member; 2131 } 2132 throw new ConvException(convFormat("Value (%s) does not match any member value of enum '%s'", value, T.stringof)); 2133 } 2134 2135 @safe pure unittest 2136 { 2137 import std.exception; 2138 enum En8143 : int { A = 10, B = 20, C = 30, D = 20 } 2139 enum En8143[][] m3 = to!(En8143[][])([[10, 30], [30, 10]]); 2140 static assert(m3 == [[En8143.A, En8143.C], [En8143.C, En8143.A]]); 2141 2142 En8143 en1 = to!En8143(10); 2143 assert(en1 == En8143.A); 2144 assertThrown!ConvException(to!En8143(5)); // matches none 2145 En8143[][] m1 = to!(En8143[][])([[10, 30], [30, 10]]); 2146 assert(m1 == [[En8143.A, En8143.C], [En8143.C, En8143.A]]); 2147 } 2148 2149 // https://issues.dlang.org/show_bug.cgi?id=20539 2150 @safe pure unittest 2151 { 2152 import std.exception : assertNotThrown; 2153 2154 // To test that the bug is fixed it is required that the struct is static, 2155 // otherwise, the frame pointer makes the test pass even if the bug is not 2156 // fixed. 2157 2158 static struct A 2159 { 2160 auto opEquals(U)(U) 2161 { 2162 return true; 2163 } 2164 } 2165 2166 enum ColorA 2167 { 2168 red = A() 2169 } 2170 2171 assertNotThrown("xxx".to!ColorA); 2172 2173 // This is a guard for the future. 2174 2175 struct B 2176 { 2177 auto opEquals(U)(U) 2178 { 2179 return true; 2180 } 2181 } 2182 2183 enum ColorB 2184 { 2185 red = B() 2186 } 2187 2188 assertNotThrown("xxx".to!ColorB); 2189 } 2190 2191 /*************************************************************** 2192 Rounded conversion from floating point to integral. 2193 2194 Rounded conversions do not work with non-integral target types. 2195 */ 2196 2197 template roundTo(Target) 2198 { 2199 Target roundTo(Source)(Source value) 2200 { 2201 import core.math : abs = fabs; 2202 import std.math.exponential : log2; 2203 import std.math.rounding : trunc; 2204 2205 static assert(isFloatingPoint!Source); 2206 static assert(isIntegral!Target); 2207 2208 // If value >= 2 ^^ (real.mant_dig - 1), the number is an integer 2209 // and adding 0.5 won't work, but we allready know, that we do 2210 // not have to round anything. 2211 if (log2(abs(value)) >= real.mant_dig - 1) 2212 return to!Target(value); 2213 2214 return to!Target(trunc(value + (value < 0 ? -0.5L : 0.5L))); 2215 } 2216 } 2217 2218 /// 2219 @safe unittest 2220 { 2221 assert(roundTo!int(3.14) == 3); 2222 assert(roundTo!int(3.49) == 3); 2223 assert(roundTo!int(3.5) == 4); 2224 assert(roundTo!int(3.999) == 4); 2225 assert(roundTo!int(-3.14) == -3); 2226 assert(roundTo!int(-3.49) == -3); 2227 assert(roundTo!int(-3.5) == -4); 2228 assert(roundTo!int(-3.999) == -4); 2229 assert(roundTo!(const int)(to!(const double)(-3.999)) == -4); 2230 } 2231 2232 @safe unittest 2233 { 2234 import std.exception; 2235 // boundary values 2236 static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint)) 2237 { 2238 assert(roundTo!Int(Int.min - 0.4L) == Int.min); 2239 assert(roundTo!Int(Int.max + 0.4L) == Int.max); 2240 assertThrown!ConvOverflowException(roundTo!Int(Int.min - 0.5L)); 2241 assertThrown!ConvOverflowException(roundTo!Int(Int.max + 0.5L)); 2242 } 2243 } 2244 2245 @safe unittest 2246 { 2247 import std.exception; 2248 assertThrown!ConvException(roundTo!int(float.init)); 2249 auto ex = collectException(roundTo!int(float.init)); 2250 assert(ex.msg == "Input was NaN"); 2251 } 2252 2253 // https://issues.dlang.org/show_bug.cgi?id=5232 2254 @safe pure unittest 2255 { 2256 static if (real.mant_dig >= 64) 2257 ulong maxOdd = ulong.max; 2258 else 2259 ulong maxOdd = (1UL << real.mant_dig) - 1; 2260 2261 real r1 = maxOdd; 2262 assert(roundTo!ulong(r1) == maxOdd); 2263 2264 real r2 = maxOdd - 1; 2265 assert(roundTo!ulong(r2) == maxOdd - 1); 2266 2267 real r3 = maxOdd / 2; 2268 assert(roundTo!ulong(r3) == maxOdd / 2); 2269 2270 real r4 = maxOdd / 2 + 1; 2271 assert(roundTo!ulong(r4) == maxOdd / 2 + 1); 2272 2273 // this is only an issue on computers where real == double 2274 long l = -((1L << double.mant_dig) - 1); 2275 double r5 = l; 2276 assert(roundTo!long(r5) == l); 2277 } 2278 2279 /** 2280 $(PANEL 2281 The `parse` family of functions works quite like the $(LREF to) 2282 family, except that: 2283 $(OL 2284 $(LI It only works with character ranges as input.) 2285 $(LI It takes the input by reference. This means that rvalues (such 2286 as string literals) are not accepted: use `to` instead.) 2287 $(LI It advances the input to the position following the conversion.) 2288 $(LI It does not throw if it could not convert the entire input.)) 2289 ) 2290 2291 This overload parses a `bool` from a character input range. 2292 2293 Params: 2294 Target = the boolean type to convert to 2295 source = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 2296 doCount = the flag for deciding to report the number of consumed characters 2297 2298 Returns: 2299 $(UL 2300 $(LI A `bool` if `doCount` is set to `No.doCount`) 2301 $(LI A `tuple` containing a `bool` and a `size_t` if `doCount` is set to `Yes.doCount`)) 2302 2303 Throws: 2304 A $(LREF ConvException) if the range does not represent a `bool`. 2305 2306 Note: 2307 All character input range conversions using $(LREF to) are forwarded 2308 to `parse` and do not require lvalues. 2309 */ 2310 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source) 2311 if (is(immutable Target == immutable bool) && 2312 isInputRange!Source && 2313 isSomeChar!(ElementType!Source)) 2314 { 2315 import std.ascii : toLower; 2316 2317 static if (isNarrowString!Source) 2318 { 2319 import std.string : representation; 2320 auto s = source.representation; 2321 } 2322 else 2323 { 2324 alias s = source; 2325 } 2326 2327 if (!s.empty) 2328 { 2329 auto c1 = toLower(s.front); 2330 bool result = c1 == 't'; 2331 if (result || c1 == 'f') 2332 { 2333 s.popFront(); 2334 foreach (c; result ? "rue" : "alse") 2335 { 2336 if (s.empty || toLower(s.front) != c) 2337 goto Lerr; 2338 s.popFront(); 2339 } 2340 2341 static if (isNarrowString!Source) 2342 source = cast(Source) s; 2343 2344 static if (doCount) 2345 { 2346 if (result) 2347 return tuple!("data", "count")(result, 4); 2348 return tuple!("data", "count")(result, 5); 2349 } 2350 else 2351 { 2352 return result; 2353 } 2354 } 2355 } 2356 Lerr: 2357 throw parseError("bool should be case-insensitive 'true' or 'false'"); 2358 } 2359 2360 /// 2361 @safe unittest 2362 { 2363 import std.typecons : Flag, Yes, No; 2364 auto s = "true"; 2365 bool b = parse!bool(s); 2366 assert(b); 2367 auto s2 = "true"; 2368 bool b2 = parse!(bool, string, No.doCount)(s2); 2369 assert(b2); 2370 auto s3 = "true"; 2371 auto b3 = parse!(bool, string, Yes.doCount)(s3); 2372 assert(b3.data && b3.count == 4); 2373 auto s4 = "falSE"; 2374 auto b4 = parse!(bool, string, Yes.doCount)(s4); 2375 assert(!b4.data && b4.count == 5); 2376 } 2377 2378 @safe unittest 2379 { 2380 import std.algorithm.comparison : equal; 2381 import std.exception; 2382 struct InputString 2383 { 2384 string _s; 2385 @property auto front() { return _s.front; } 2386 @property bool empty() { return _s.empty; } 2387 void popFront() { _s.popFront(); } 2388 } 2389 2390 auto s = InputString("trueFALSETrueFalsetRUEfALSE"); 2391 assert(parse!bool(s) == true); 2392 assert(s.equal("FALSETrueFalsetRUEfALSE")); 2393 assert(parse!bool(s) == false); 2394 assert(s.equal("TrueFalsetRUEfALSE")); 2395 assert(parse!bool(s) == true); 2396 assert(s.equal("FalsetRUEfALSE")); 2397 assert(parse!bool(s) == false); 2398 assert(s.equal("tRUEfALSE")); 2399 assert(parse!bool(s) == true); 2400 assert(s.equal("fALSE")); 2401 assert(parse!bool(s) == false); 2402 assert(s.empty); 2403 2404 foreach (ss; ["tfalse", "ftrue", "t", "f", "tru", "fals", ""]) 2405 { 2406 s = InputString(ss); 2407 assertThrown!ConvException(parse!bool(s)); 2408 } 2409 } 2410 2411 /** 2412 Parses an integer from a character $(REF_ALTTEXT input range, isInputRange, std,range,primitives). 2413 2414 Params: 2415 Target = the integral type to convert to 2416 s = the lvalue of an input range 2417 doCount = the flag for deciding to report the number of consumed characters 2418 2419 Returns: 2420 $(UL 2421 $(LI A number of type `Target` if `doCount` is set to `No.doCount`) 2422 $(LI A `tuple` containing a number of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`)) 2423 2424 Throws: 2425 A $(LREF ConvException) If an overflow occurred during conversion or 2426 if no character of the input was meaningfully converted. 2427 */ 2428 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref scope Source s) 2429 if (isIntegral!Target && !is(Target == enum) && 2430 isSomeChar!(ElementType!Source)) 2431 { 2432 static if (Target.sizeof < int.sizeof) 2433 { 2434 // smaller types are handled like integers 2435 auto v = .parse!(Select!(Target.min < 0, int, uint), Source, Yes.doCount)(s); 2436 auto result = (() @trusted => cast (Target) v.data)(); 2437 if (result == v.data) 2438 { 2439 static if (doCount) 2440 { 2441 return tuple!("data", "count")(result, v.count); 2442 } 2443 else 2444 { 2445 return result; 2446 } 2447 } 2448 throw new ConvOverflowException("Overflow in integral conversion"); 2449 } 2450 else 2451 { 2452 // int or larger types 2453 2454 static if (Target.min < 0) 2455 bool sign = false; 2456 else 2457 enum bool sign = false; 2458 2459 enum char maxLastDigit = Target.min < 0 ? 7 : 5; 2460 uint c; 2461 2462 static if (isNarrowString!Source) 2463 { 2464 import std.string : representation; 2465 auto source = s.representation; 2466 } 2467 else 2468 { 2469 alias source = s; 2470 } 2471 2472 size_t count = 0; 2473 2474 if (source.empty) 2475 goto Lerr; 2476 2477 c = source.front; 2478 2479 static if (Target.min < 0) 2480 { 2481 switch (c) 2482 { 2483 case '-': 2484 sign = true; 2485 goto case '+'; 2486 case '+': 2487 ++count; 2488 source.popFront(); 2489 2490 if (source.empty) 2491 goto Lerr; 2492 2493 c = source.front; 2494 2495 break; 2496 2497 default: 2498 break; 2499 } 2500 } 2501 c -= '0'; 2502 if (c <= 9) 2503 { 2504 Target v = cast(Target) c; 2505 2506 ++count; 2507 source.popFront(); 2508 2509 while (!source.empty) 2510 { 2511 c = cast(typeof(c)) (source.front - '0'); 2512 2513 if (c > 9) 2514 break; 2515 2516 if (v >= 0 && (v < Target.max/10 || 2517 (v == Target.max/10 && c <= maxLastDigit + sign))) 2518 { 2519 // Note: `v` can become negative here in case of parsing 2520 // the most negative value: 2521 v = cast(Target) (v * 10 + c); 2522 ++count; 2523 source.popFront(); 2524 } 2525 else 2526 throw new ConvOverflowException("Overflow in integral conversion"); 2527 } 2528 2529 if (sign) 2530 v = -v; 2531 2532 static if (isNarrowString!Source) 2533 s = s[$-source.length..$]; 2534 2535 static if (doCount) 2536 { 2537 return tuple!("data", "count")(v, count); 2538 } 2539 else 2540 { 2541 return v; 2542 } 2543 } 2544 Lerr: 2545 static if (isNarrowString!Source) 2546 throw convError!(Source, Target)(cast(Source) source); 2547 else 2548 throw convError!(Source, Target)(source); 2549 } 2550 } 2551 2552 /// 2553 @safe pure unittest 2554 { 2555 import std.typecons : Flag, Yes, No; 2556 string s = "123"; 2557 auto a = parse!int(s); 2558 assert(a == 123); 2559 2560 string s1 = "123"; 2561 auto a1 = parse!(int, string, Yes.doCount)(s1); 2562 assert(a1.data == 123 && a1.count == 3); 2563 } 2564 2565 /// 2566 @safe pure unittest 2567 { 2568 import std.string : tr; 2569 import std.typecons : Flag, Yes, No; 2570 string test = "123 \t 76.14"; 2571 auto a = parse!uint(test); 2572 assert(a == 123); 2573 assert(test == " \t 76.14"); // parse bumps string 2574 test = tr(test, " \t\n\r", "", "d"); // skip ws 2575 assert(test == "76.14"); 2576 auto b = parse!double(test); 2577 assert(b == 76.14); 2578 assert(test == ""); 2579 2580 string test2 = "123 \t 76.14"; 2581 auto a2 = parse!(uint, string, Yes.doCount)(test2); 2582 assert(a2.data == 123 && a2.count == 3); 2583 assert(test2 == " \t 76.14");// parse bumps string 2584 test2 = tr(test2, " \t\n\r", "", "d"); // skip ws 2585 assert(test2 == "76.14"); 2586 auto b2 = parse!(double, string, Yes.doCount)(test2); 2587 assert(b2.data == 76.14 && b2.count == 5); 2588 assert(test2 == ""); 2589 2590 } 2591 2592 @safe pure unittest 2593 { 2594 static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) 2595 { 2596 { 2597 assert(to!Int("0") == 0); 2598 2599 static if (isSigned!Int) 2600 { 2601 assert(to!Int("+0") == 0); 2602 assert(to!Int("-0") == 0); 2603 } 2604 } 2605 2606 static if (Int.sizeof >= byte.sizeof) 2607 { 2608 assert(to!Int("6") == 6); 2609 assert(to!Int("23") == 23); 2610 assert(to!Int("68") == 68); 2611 assert(to!Int("127") == 0x7F); 2612 2613 static if (isUnsigned!Int) 2614 { 2615 assert(to!Int("255") == 0xFF); 2616 } 2617 static if (isSigned!Int) 2618 { 2619 assert(to!Int("+6") == 6); 2620 assert(to!Int("+23") == 23); 2621 assert(to!Int("+68") == 68); 2622 assert(to!Int("+127") == 0x7F); 2623 2624 assert(to!Int("-6") == -6); 2625 assert(to!Int("-23") == -23); 2626 assert(to!Int("-68") == -68); 2627 assert(to!Int("-128") == -128); 2628 } 2629 } 2630 2631 static if (Int.sizeof >= short.sizeof) 2632 { 2633 assert(to!Int("468") == 468); 2634 assert(to!Int("32767") == 0x7FFF); 2635 2636 static if (isUnsigned!Int) 2637 { 2638 assert(to!Int("65535") == 0xFFFF); 2639 } 2640 static if (isSigned!Int) 2641 { 2642 assert(to!Int("+468") == 468); 2643 assert(to!Int("+32767") == 0x7FFF); 2644 2645 assert(to!Int("-468") == -468); 2646 assert(to!Int("-32768") == -32768); 2647 } 2648 } 2649 2650 static if (Int.sizeof >= int.sizeof) 2651 { 2652 assert(to!Int("2147483647") == 0x7FFFFFFF); 2653 2654 static if (isUnsigned!Int) 2655 { 2656 assert(to!Int("4294967295") == 0xFFFFFFFF); 2657 } 2658 2659 static if (isSigned!Int) 2660 { 2661 assert(to!Int("+2147483647") == 0x7FFFFFFF); 2662 2663 assert(to!Int("-2147483648") == -2147483648); 2664 } 2665 } 2666 2667 static if (Int.sizeof >= long.sizeof) 2668 { 2669 assert(to!Int("9223372036854775807") == 0x7FFFFFFFFFFFFFFF); 2670 2671 static if (isUnsigned!Int) 2672 { 2673 assert(to!Int("18446744073709551615") == 0xFFFFFFFFFFFFFFFF); 2674 } 2675 2676 static if (isSigned!Int) 2677 { 2678 assert(to!Int("+9223372036854775807") == 0x7FFFFFFFFFFFFFFF); 2679 2680 assert(to!Int("-9223372036854775808") == 0x8000000000000000); 2681 } 2682 } 2683 } 2684 } 2685 2686 @safe pure unittest 2687 { 2688 import std.exception; 2689 2690 immutable string[] errors = 2691 [ 2692 "", 2693 "-", 2694 "+", 2695 "-+", 2696 " ", 2697 " 0", 2698 "0 ", 2699 "- 0", 2700 "1-", 2701 "xx", 2702 "123h", 2703 "-+1", 2704 "--1", 2705 "+-1", 2706 "++1", 2707 ]; 2708 2709 immutable string[] unsignedErrors = 2710 [ 2711 "+5", 2712 "-78", 2713 ]; 2714 2715 // parsing error check 2716 static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) 2717 { 2718 foreach (j, s; errors) 2719 assertThrown!ConvException(to!Int(s)); 2720 2721 // parse!SomeUnsigned cannot parse head sign. 2722 static if (isUnsigned!Int) 2723 { 2724 foreach (j, s; unsignedErrors) 2725 assertThrown!ConvException(to!Int(s)); 2726 } 2727 } 2728 2729 immutable string[] positiveOverflowErrors = 2730 [ 2731 "128", // > byte.max 2732 "256", // > ubyte.max 2733 "32768", // > short.max 2734 "65536", // > ushort.max 2735 "2147483648", // > int.max 2736 "4294967296", // > uint.max 2737 "9223372036854775808", // > long.max 2738 "18446744073709551616", // > ulong.max 2739 ]; 2740 // positive overflow check 2741 static foreach (i, Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) 2742 { 2743 foreach (j, s; positiveOverflowErrors[i..$]) 2744 assertThrown!ConvOverflowException(to!Int(s)); 2745 } 2746 2747 immutable string[] negativeOverflowErrors = 2748 [ 2749 "-129", // < byte.min 2750 "-32769", // < short.min 2751 "-2147483649", // < int.min 2752 "-9223372036854775809", // < long.min 2753 ]; 2754 // negative overflow check 2755 static foreach (i, Int; AliasSeq!(byte, short, int, long)) 2756 { 2757 foreach (j, s; negativeOverflowErrors[i..$]) 2758 assertThrown!ConvOverflowException(to!Int(s)); 2759 } 2760 } 2761 2762 @safe pure unittest 2763 { 2764 void checkErrMsg(string input, dchar charInMsg, dchar charNotInMsg) 2765 { 2766 try 2767 { 2768 int x = input.to!int(); 2769 assert(false, "Invalid conversion did not throw"); 2770 } 2771 catch (ConvException e) 2772 { 2773 // Ensure error message contains failing character, not the character 2774 // beyond. 2775 import std.algorithm.searching : canFind; 2776 assert( e.msg.canFind(charInMsg) && 2777 !e.msg.canFind(charNotInMsg)); 2778 } 2779 catch (Exception e) 2780 { 2781 assert(false, "Did not throw ConvException"); 2782 } 2783 } 2784 checkErrMsg("@$", '@', '$'); 2785 checkErrMsg("@$123", '@', '$'); 2786 checkErrMsg("1@$23", '@', '$'); 2787 checkErrMsg("1@$", '@', '$'); 2788 checkErrMsg("1@$2", '@', '$'); 2789 checkErrMsg("12@$", '@', '$'); 2790 } 2791 2792 @safe pure unittest 2793 { 2794 import std.exception; 2795 assertCTFEable!({ string s = "1234abc"; assert(parse! int(s) == 1234 && s == "abc"); }); 2796 assertCTFEable!({ string s = "-1234abc"; assert(parse! int(s) == -1234 && s == "abc"); }); 2797 assertCTFEable!({ string s = "1234abc"; assert(parse!uint(s) == 1234 && s == "abc"); }); 2798 2799 assertCTFEable!({ string s = "1234abc"; assert(parse!( int, string, Yes.doCount)(s) == 2800 tuple( 1234, 4) && s == "abc"); }); 2801 assertCTFEable!({ string s = "-1234abc"; assert(parse!( int, string, Yes.doCount)(s) == 2802 tuple(-1234, 5) && s == "abc"); }); 2803 assertCTFEable!({ string s = "1234abc"; assert(parse!(uint, string, Yes.doCount)(s) == 2804 tuple( 1234 ,4) && s == "abc"); }); 2805 } 2806 2807 // https://issues.dlang.org/show_bug.cgi?id=13931 2808 @safe pure unittest 2809 { 2810 import std.exception; 2811 2812 assertThrown!ConvOverflowException("-21474836480".to!int()); 2813 assertThrown!ConvOverflowException("-92233720368547758080".to!long()); 2814 } 2815 2816 // https://issues.dlang.org/show_bug.cgi?id=14396 2817 @safe pure unittest 2818 { 2819 struct StrInputRange 2820 { 2821 this (string s) { str = s; } 2822 char front() const @property { return str[front_index]; } 2823 char popFront() { return str[front_index++]; } 2824 bool empty() const @property { return str.length <= front_index; } 2825 string str; 2826 size_t front_index = 0; 2827 } 2828 auto input = StrInputRange("777"); 2829 assert(parse!int(input) == 777); 2830 2831 auto input2 = StrInputRange("777"); 2832 assert(parse!(int, StrInputRange, Yes.doCount)(input2) == tuple(777, 3)); 2833 } 2834 2835 // https://issues.dlang.org/show_bug.cgi?id=9621 2836 @safe pure unittest 2837 { 2838 string s1 = "[ \"\\141\", \"\\0\", \"\\41\", \"\\418\" ]"; 2839 assert(parse!(string[])(s1) == ["a", "\0", "!", "!8"]); 2840 2841 s1 = "[ \"\\141\", \"\\0\", \"\\41\", \"\\418\" ]"; 2842 auto len = s1.length; 2843 assert(parse!(string[], string, Yes.doCount)(s1) == tuple(["a", "\0", "!", "!8"], len)); 2844 } 2845 2846 /// ditto 2847 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source, uint radix) 2848 if (isIntegral!Target && !is(Target == enum) && 2849 isSomeChar!(ElementType!Source)) 2850 in 2851 { 2852 assert(radix >= 2 && radix <= 36, "radix must be in range [2,36]"); 2853 } 2854 do 2855 { 2856 import core.checkedint : mulu, addu; 2857 import std.exception : enforce; 2858 2859 if (radix == 10) 2860 { 2861 return parse!(Target, Source, doCount)(source); 2862 } 2863 2864 enforce!ConvException(!source.empty, "s must not be empty in integral parse"); 2865 2866 immutable uint beyond = (radix < 10 ? '0' : 'a'-10) + radix; 2867 Target v = 0; 2868 2869 static if (isNarrowString!Source) 2870 { 2871 import std.string : representation; 2872 scope s = source.representation; 2873 } 2874 else 2875 { 2876 alias s = source; 2877 } 2878 2879 size_t count = 0; 2880 auto found = false; 2881 do 2882 { 2883 uint c = s.front; 2884 if (c < '0') 2885 break; 2886 if (radix < 10) 2887 { 2888 if (c >= beyond) 2889 break; 2890 } 2891 else 2892 { 2893 if (c > '9') 2894 { 2895 c |= 0x20;//poorman's tolower 2896 if (c < 'a' || c >= beyond) 2897 break; 2898 c -= 'a'-10-'0'; 2899 } 2900 } 2901 2902 bool overflow = false; 2903 auto nextv = v.mulu(radix, overflow).addu(c - '0', overflow); 2904 enforce!ConvOverflowException(!overflow && nextv <= Target.max, "Overflow in integral conversion"); 2905 v = cast(Target) nextv; 2906 ++count; 2907 s.popFront(); 2908 found = true; 2909 } while (!s.empty); 2910 2911 if (!found) 2912 { 2913 static if (isNarrowString!Source) 2914 throw convError!(Source, Target)(cast(Source) source); 2915 else 2916 throw convError!(Source, Target)(source); 2917 } 2918 2919 static if (isNarrowString!Source) 2920 source = source[$ - s.length .. $]; 2921 2922 static if (doCount) 2923 { 2924 return tuple!("data", "count")(v, count); 2925 } 2926 else 2927 { 2928 return v; 2929 } 2930 } 2931 2932 @safe pure unittest 2933 { 2934 string s; // parse doesn't accept rvalues 2935 foreach (i; 2 .. 37) 2936 { 2937 assert(parse!int(s = "0", i) == 0); 2938 assert(parse!int(s = "1", i) == 1); 2939 assert(parse!byte(s = "10", i) == i); 2940 assert(parse!(int, string, Yes.doCount)(s = "0", i) == tuple(0, 1)); 2941 assert(parse!(int, string, Yes.doCount)(s = "1", i) == tuple(1, 1)); 2942 assert(parse!(byte, string, Yes.doCount)(s = "10", i) == tuple(i, 2)); 2943 } 2944 2945 assert(parse!int(s = "0011001101101", 2) == 0b0011001101101); 2946 assert(parse!int(s = "765", 8) == octal!765); 2947 assert(parse!int(s = "000135", 8) == octal!"135"); 2948 assert(parse!int(s = "fCDe", 16) == 0xfcde); 2949 2950 // https://issues.dlang.org/show_bug.cgi?id=6609 2951 assert(parse!int(s = "-42", 10) == -42); 2952 2953 assert(parse!ubyte(s = "ff", 16) == 0xFF); 2954 } 2955 2956 // https://issues.dlang.org/show_bug.cgi?id=7302 2957 @safe pure unittest 2958 { 2959 import std.range : cycle; 2960 auto r = cycle("2A!"); 2961 auto u = parse!uint(r, 16); 2962 assert(u == 42); 2963 assert(r.front == '!'); 2964 2965 auto r2 = cycle("2A!"); 2966 auto u2 = parse!(uint, typeof(r2), Yes.doCount)(r2, 16); 2967 assert(u2.data == 42 && u2.count == 2); 2968 assert(r2.front == '!'); 2969 } 2970 2971 // https://issues.dlang.org/show_bug.cgi?id=13163 2972 @safe pure unittest 2973 { 2974 import std.exception; 2975 foreach (s; ["fff", "123"]) 2976 assertThrown!ConvOverflowException(s.parse!ubyte(16)); 2977 } 2978 2979 // https://issues.dlang.org/show_bug.cgi?id=17282 2980 @safe pure unittest 2981 { 2982 auto str = "0=\x00\x02\x55\x40&\xff\xf0\n\x00\x04\x55\x40\xff\xf0~4+10\n"; 2983 assert(parse!uint(str) == 0); 2984 2985 str = "0=\x00\x02\x55\x40&\xff\xf0\n\x00\x04\x55\x40\xff\xf0~4+10\n"; 2986 assert(parse!(uint, string, Yes.doCount)(str) == tuple(0, 1)); 2987 } 2988 2989 // https://issues.dlang.org/show_bug.cgi?id=18248 2990 @safe pure unittest 2991 { 2992 import std.exception : assertThrown; 2993 2994 auto str = ";"; 2995 assertThrown(str.parse!uint(16)); 2996 assertThrown(str.parse!(uint, string, Yes.doCount)(16)); 2997 } 2998 2999 /** 3000 * Parses an `enum` type from a string representing an enum member name. 3001 * 3002 * Params: 3003 * Target = the `enum` type to convert to 3004 * s = the lvalue of the range to _parse 3005 * doCount = the flag for deciding to report the number of consumed characters 3006 * 3007 * Returns: 3008 $(UL 3009 * $(LI An `enum` of type `Target` if `doCount` is set to `No.doCount`) 3010 * $(LI A `tuple` containing an `enum` of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`)) 3011 * 3012 * Throws: 3013 * A $(LREF ConvException) if type `Target` does not have a member 3014 * represented by `s`. 3015 */ 3016 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 3017 if (is(Target == enum) && isSomeString!Source && !is(Source == enum)) 3018 { 3019 import std.algorithm.searching : startsWith; 3020 import std.traits : Unqual, EnumMembers; 3021 3022 Unqual!Target result; 3023 size_t longest_match = 0; 3024 3025 foreach (i, e; EnumMembers!Target) 3026 { 3027 auto ident = __traits(allMembers, Target)[i]; 3028 if (longest_match < ident.length && s.startsWith(ident)) 3029 { 3030 result = e; 3031 longest_match = ident.length ; 3032 } 3033 } 3034 3035 if (longest_match > 0) 3036 { 3037 s = s[longest_match .. $]; 3038 static if (doCount) 3039 { 3040 return tuple!("data", "count")(result, longest_match); 3041 } 3042 else 3043 { 3044 return result; 3045 } 3046 } 3047 3048 throw new ConvException( 3049 Target.stringof ~ " does not have a member named '" 3050 ~ to!string(s) ~ "'"); 3051 } 3052 3053 /// 3054 @safe unittest 3055 { 3056 import std.typecons : Flag, Yes, No, tuple; 3057 enum EnumType : bool { a = true, b = false, c = a } 3058 3059 auto str = "a"; 3060 assert(parse!EnumType(str) == EnumType.a); 3061 auto str2 = "a"; 3062 assert(parse!(EnumType, string, No.doCount)(str2) == EnumType.a); 3063 auto str3 = "a"; 3064 assert(parse!(EnumType, string, Yes.doCount)(str3) == tuple(EnumType.a, 1)); 3065 3066 } 3067 3068 @safe unittest 3069 { 3070 import std.exception; 3071 3072 enum EB : bool { a = true, b = false, c = a } 3073 enum EU { a, b, c } 3074 enum EI { a = -1, b = 0, c = 1 } 3075 enum EF : real { a = 1.414, b = 1.732, c = 2.236 } 3076 enum EC : char { a = 'a', b = 'b', c = 'c' } 3077 enum ES : string { a = "aaa", b = "bbb", c = "ccc" } 3078 3079 static foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES)) 3080 { 3081 assert(to!E("a"c) == E.a); 3082 assert(to!E("b"w) == E.b); 3083 assert(to!E("c"d) == E.c); 3084 3085 assert(to!(const E)("a") == E.a); 3086 assert(to!(immutable E)("a") == E.a); 3087 assert(to!(shared E)("a") == E.a); 3088 3089 assertThrown!ConvException(to!E("d")); 3090 } 3091 } 3092 3093 // https://issues.dlang.org/show_bug.cgi?id=4744 3094 @safe pure unittest 3095 { 3096 enum A { member1, member11, member111 } 3097 assert(to!A("member1" ) == A.member1 ); 3098 assert(to!A("member11" ) == A.member11 ); 3099 assert(to!A("member111") == A.member111); 3100 auto s = "member1111"; 3101 assert(parse!A(s) == A.member111 && s == "1"); 3102 auto s2 = "member1111"; 3103 assert(parse!(A, string, No.doCount)(s2) == A.member111 && s2 == "1"); 3104 auto s3 = "member1111"; 3105 assert(parse!(A, string, Yes.doCount)(s3) == tuple(A.member111, 9) && s3 == "1"); 3106 } 3107 3108 /** 3109 * Parses a floating point number from a character range. 3110 * 3111 * Params: 3112 * Target = a floating point type 3113 * source = the lvalue of the range to _parse 3114 * doCount = the flag for deciding to report the number of consumed characters 3115 * 3116 * Returns: 3117 $(UL 3118 * $(LI A floating point number of type `Target` if `doCount` is set to `No.doCount`) 3119 * $(LI A `tuple` containing a floating point number of·type `Target` and a `size_t` 3120 * if `doCount` is set to `Yes.doCount`)) 3121 * 3122 * Throws: 3123 * A $(LREF ConvException) if `source` is empty, if no number could be 3124 * parsed, or if an overflow occurred. 3125 */ 3126 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source) 3127 if (isFloatingPoint!Target && !is(Target == enum) && 3128 isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum)) 3129 { 3130 import std.ascii : isDigit, isAlpha, toLower, toUpper, isHexDigit; 3131 import std.exception : enforce; 3132 3133 static if (isNarrowString!Source) 3134 { 3135 import std.string : representation; 3136 scope p = source.representation; 3137 } 3138 else 3139 { 3140 alias p = source; 3141 } 3142 3143 void advanceSource() 3144 { 3145 static if (isNarrowString!Source) 3146 source = source[$ - p.length .. $]; 3147 } 3148 3149 static immutable real[14] negtab = 3150 [ 1e-4096L,1e-2048L,1e-1024L,1e-512L,1e-256L,1e-128L,1e-64L,1e-32L, 3151 1e-16L,1e-8L,1e-4L,1e-2L,1e-1L,1.0L ]; 3152 static immutable real[13] postab = 3153 [ 1e+4096L,1e+2048L,1e+1024L,1e+512L,1e+256L,1e+128L,1e+64L,1e+32L, 3154 1e+16L,1e+8L,1e+4L,1e+2L,1e+1L ]; 3155 3156 ConvException bailOut()(string msg = null, string fn = __FILE__, size_t ln = __LINE__) 3157 { 3158 if (msg == null) 3159 msg = "Floating point conversion error"; 3160 return new ConvException(text(msg, " for input \"", source, "\"."), fn, ln); 3161 } 3162 3163 enforce(!p.empty, bailOut()); 3164 3165 3166 size_t count = 0; 3167 bool sign = false; 3168 switch (p.front) 3169 { 3170 case '-': 3171 sign = true; 3172 ++count; 3173 p.popFront(); 3174 enforce(!p.empty, bailOut()); 3175 if (toLower(p.front) == 'i') 3176 goto case 'i'; 3177 break; 3178 case '+': 3179 ++count; 3180 p.popFront(); 3181 enforce(!p.empty, bailOut()); 3182 break; 3183 case 'i': case 'I': 3184 // inf 3185 ++count; 3186 p.popFront(); 3187 enforce(!p.empty && toUpper(p.front) == 'N', 3188 bailOut("error converting input to floating point")); 3189 ++count; 3190 p.popFront(); 3191 enforce(!p.empty && toUpper(p.front) == 'F', 3192 bailOut("error converting input to floating point")); 3193 // skip past the last 'f' 3194 ++count; 3195 p.popFront(); 3196 advanceSource(); 3197 static if (doCount) 3198 { 3199 return tuple!("data", "count")(sign ? -Target.infinity : Target.infinity, count); 3200 } 3201 else 3202 { 3203 return sign ? -Target.infinity : Target.infinity; 3204 } 3205 default: {} 3206 } 3207 3208 bool isHex = false; 3209 bool startsWithZero = p.front == '0'; 3210 if (startsWithZero) 3211 { 3212 ++count; 3213 p.popFront(); 3214 if (p.empty) 3215 { 3216 advanceSource(); 3217 static if (doCount) 3218 { 3219 return tuple!("data", "count")(cast (Target) (sign ? -0.0 : 0.0), count); 3220 } 3221 else 3222 { 3223 return sign ? -0.0 : 0.0; 3224 } 3225 } 3226 3227 isHex = p.front == 'x' || p.front == 'X'; 3228 if (isHex) 3229 { 3230 ++count; 3231 p.popFront(); 3232 } 3233 } 3234 else if (toLower(p.front) == 'n') 3235 { 3236 // nan 3237 ++count; 3238 p.popFront(); 3239 enforce(!p.empty && toUpper(p.front) == 'A', 3240 bailOut("error converting input to floating point")); 3241 ++count; 3242 p.popFront(); 3243 enforce(!p.empty && toUpper(p.front) == 'N', 3244 bailOut("error converting input to floating point")); 3245 // skip past the last 'n' 3246 ++count; 3247 p.popFront(); 3248 advanceSource(); 3249 static if (doCount) 3250 { 3251 return tuple!("data", "count")(Target.nan, count); 3252 } 3253 else 3254 { 3255 return typeof(return).nan; 3256 } 3257 } 3258 3259 /* 3260 * The following algorithm consists of 2 steps: 3261 * 1) parseDigits processes the textual input into msdec and possibly 3262 * lsdec/msscale variables, followed by the exponent parser which sets 3263 * exp below. 3264 * Hex: input is 0xaaaaa...p+000... where aaaa is the mantissa in hex 3265 * and 000 is the exponent in decimal format with base 2. 3266 * Decimal: input is 0.00333...p+000... where 0.0033 is the mantissa 3267 * in decimal and 000 is the exponent in decimal format with base 10. 3268 * 2) Convert msdec/lsdec and exp into native real format 3269 */ 3270 3271 real ldval = 0.0; 3272 char dot = 0; /* if decimal point has been seen */ 3273 int exp = 0; 3274 ulong msdec = 0, lsdec = 0; 3275 ulong msscale = 1; 3276 bool sawDigits; 3277 3278 enum { hex, decimal } 3279 3280 // sets msdec, lsdec/msscale, and sawDigits by parsing the mantissa digits 3281 void parseDigits(alias FloatFormat)() 3282 { 3283 static if (FloatFormat == hex) 3284 { 3285 enum uint base = 16; 3286 enum ulong msscaleMax = 0x1000_0000_0000_0000UL; // largest power of 16 a ulong holds 3287 enum ubyte expIter = 4; // iterate the base-2 exponent by 4 for every hex digit 3288 alias checkDigit = isHexDigit; 3289 /* 3290 * convert letter to binary representation: First clear bit 3291 * to convert lower space chars to upperspace, then -('A'-10) 3292 * converts letter A to 10, letter B to 11, ... 3293 */ 3294 alias convertDigit = (int x) => isAlpha(x) ? ((x & ~0x20) - ('A' - 10)) : x - '0'; 3295 sawDigits = false; 3296 } 3297 else static if (FloatFormat == decimal) 3298 { 3299 enum uint base = 10; 3300 enum ulong msscaleMax = 10_000_000_000_000_000_000UL; // largest power of 10 a ulong holds 3301 enum ubyte expIter = 1; // iterate the base-10 exponent once for every decimal digit 3302 alias checkDigit = isDigit; 3303 alias convertDigit = (int x) => x - '0'; 3304 // Used to enforce that any mantissa digits are present 3305 sawDigits = startsWithZero; 3306 } 3307 else 3308 static assert(false, "Unrecognized floating-point format used."); 3309 3310 while (!p.empty) 3311 { 3312 int i = p.front; 3313 while (checkDigit(i)) 3314 { 3315 sawDigits = true; /* must have at least 1 digit */ 3316 3317 i = convertDigit(i); 3318 3319 if (msdec < (ulong.max - base)/base) 3320 { 3321 // For base 16: Y = ... + y3*16^3 + y2*16^2 + y1*16^1 + y0*16^0 3322 msdec = msdec * base + i; 3323 } 3324 else if (msscale < msscaleMax) 3325 { 3326 lsdec = lsdec * base + i; 3327 msscale *= base; 3328 } 3329 else 3330 { 3331 exp += expIter; 3332 } 3333 exp -= dot; 3334 ++count; 3335 p.popFront(); 3336 if (p.empty) 3337 break; 3338 i = p.front; 3339 if (i == '_') 3340 { 3341 ++count; 3342 p.popFront(); 3343 if (p.empty) 3344 break; 3345 i = p.front; 3346 } 3347 } 3348 if (i == '.' && !dot) 3349 { 3350 ++count; 3351 p.popFront(); 3352 dot += expIter; 3353 } 3354 else 3355 break; 3356 } 3357 3358 // Have we seen any mantissa digits so far? 3359 enforce(sawDigits, bailOut("no digits seen")); 3360 static if (FloatFormat == hex) 3361 enforce(!p.empty && (p.front == 'p' || p.front == 'P'), 3362 bailOut("Floating point parsing: exponent is required")); 3363 } 3364 3365 if (isHex) 3366 parseDigits!hex; 3367 else 3368 parseDigits!decimal; 3369 3370 if (isHex || (!p.empty && (p.front == 'e' || p.front == 'E'))) 3371 { 3372 char sexp = 0; 3373 int e = 0; 3374 3375 ++count; 3376 p.popFront(); 3377 enforce(!p.empty, new ConvException("Unexpected end of input")); 3378 switch (p.front) 3379 { 3380 case '-': sexp++; 3381 goto case; 3382 case '+': ++count; 3383 p.popFront(); 3384 break; 3385 default: {} 3386 } 3387 sawDigits = false; 3388 while (!p.empty && isDigit(p.front)) 3389 { 3390 if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow 3391 { 3392 e = e * 10 + p.front - '0'; 3393 } 3394 ++count; 3395 p.popFront(); 3396 sawDigits = true; 3397 } 3398 exp += (sexp) ? -e : e; 3399 enforce(sawDigits, new ConvException("No digits seen.")); 3400 } 3401 3402 ldval = msdec; 3403 if (msscale != 1) /* if stuff was accumulated in lsdec */ 3404 ldval = ldval * msscale + lsdec; 3405 if (isHex) 3406 { 3407 import core.math : ldexp; 3408 3409 // Exponent is power of 2, not power of 10 3410 ldval = ldexp(ldval,exp); 3411 } 3412 else if (ldval) 3413 { 3414 uint u = 0; 3415 int pow = 4096; 3416 3417 while (exp > 0) 3418 { 3419 while (exp >= pow) 3420 { 3421 ldval *= postab[u]; 3422 exp -= pow; 3423 } 3424 pow >>= 1; 3425 u++; 3426 } 3427 while (exp < 0) 3428 { 3429 while (exp <= -pow) 3430 { 3431 ldval *= negtab[u]; 3432 enforce(ldval != 0, new ConvException("Range error")); 3433 exp += pow; 3434 } 3435 pow >>= 1; 3436 u++; 3437 } 3438 } 3439 3440 Target result = cast(Target) (sign ? -ldval : ldval); 3441 3442 // if overflow occurred 3443 import std.math.traits : isFinite; 3444 enforce(isFinite(result), new ConvException("Range error")); 3445 3446 advanceSource(); 3447 static if (doCount) 3448 { 3449 return tuple!("data", "count")(result, count); 3450 } 3451 else 3452 { 3453 return result; 3454 } 3455 } 3456 3457 3458 /// 3459 @safe unittest 3460 { 3461 import std.math.operations : isClose; 3462 import std.math.traits : isNaN, isInfinity; 3463 import std.typecons : Flag, Yes, No; 3464 auto str = "123.456"; 3465 assert(parse!double(str).isClose(123.456)); 3466 auto str2 = "123.456"; 3467 assert(parse!(double, string, No.doCount)(str2).isClose(123.456)); 3468 auto str3 = "123.456"; 3469 auto r = parse!(double, string, Yes.doCount)(str3); 3470 assert(r.data.isClose(123.456)); 3471 assert(r.count == 7); 3472 auto str4 = "-123.456"; 3473 r = parse!(double, string, Yes.doCount)(str4); 3474 assert(r.data.isClose(-123.456)); 3475 assert(r.count == 8); 3476 auto str5 = "+123.456"; 3477 r = parse!(double, string, Yes.doCount)(str5); 3478 assert(r.data.isClose(123.456)); 3479 assert(r.count == 8); 3480 auto str6 = "inf0"; 3481 r = parse!(double, string, Yes.doCount)(str6); 3482 assert(isInfinity(r.data) && r.count == 3 && str6 == "0"); 3483 auto str7 = "-0"; 3484 auto r2 = parse!(float, string, Yes.doCount)(str7); 3485 assert(r2.data.isClose(0.0) && r2.count == 2); 3486 auto str8 = "nan"; 3487 auto r3 = parse!(real, string, Yes.doCount)(str8); 3488 assert(isNaN(r3.data) && r3.count == 3); 3489 } 3490 3491 @safe unittest 3492 { 3493 import std.exception; 3494 import std.math.traits : isNaN, isInfinity; 3495 import std.math.algebraic : fabs; 3496 3497 // Compare reals with given precision 3498 bool feq(in real rx, in real ry, in real precision = 0.000001L) 3499 { 3500 if (rx == ry) 3501 return 1; 3502 3503 if (isNaN(rx)) 3504 return cast(bool) isNaN(ry); 3505 3506 if (isNaN(ry)) 3507 return 0; 3508 3509 return cast(bool)(fabs(rx - ry) <= precision); 3510 } 3511 3512 // Make given typed literal 3513 F Literal(F)(F f) 3514 { 3515 return f; 3516 } 3517 3518 static foreach (Float; AliasSeq!(float, double, real)) 3519 { 3520 assert(to!Float("123") == Literal!Float(123)); 3521 assert(to!Float("+123") == Literal!Float(+123)); 3522 assert(to!Float("-123") == Literal!Float(-123)); 3523 assert(to!Float("123e2") == Literal!Float(123e2)); 3524 assert(to!Float("123e+2") == Literal!Float(123e+2)); 3525 assert(to!Float("123e-2") == Literal!Float(123e-2L)); 3526 assert(to!Float("123.") == Literal!Float(123.0)); 3527 assert(to!Float(".375") == Literal!Float(.375)); 3528 3529 assert(to!Float("1.23375E+2") == Literal!Float(1.23375E+2)); 3530 3531 assert(to!Float("0") is 0.0); 3532 assert(to!Float("-0") is -0.0); 3533 3534 assert(isNaN(to!Float("nan"))); 3535 3536 assertThrown!ConvException(to!Float("\x00")); 3537 } 3538 3539 // min and max 3540 float f = to!float("1.17549e-38"); 3541 assert(feq(cast(real) f, cast(real) 1.17549e-38)); 3542 assert(feq(cast(real) f, cast(real) float.min_normal)); 3543 f = to!float("3.40282e+38"); 3544 assert(to!string(f) == to!string(3.40282e+38)); 3545 3546 // min and max 3547 double d = to!double("2.22508e-308"); 3548 assert(feq(cast(real) d, cast(real) 2.22508e-308)); 3549 assert(feq(cast(real) d, cast(real) double.min_normal)); 3550 d = to!double("1.79769e+308"); 3551 assert(to!string(d) == to!string(1.79769e+308)); 3552 assert(to!string(d) == to!string(double.max)); 3553 3554 auto z = real.max / 2L; 3555 static assert(is(typeof(z) == real)); 3556 assert(!isNaN(z)); 3557 assert(!isInfinity(z)); 3558 string a = to!string(z); 3559 real b = to!real(a); 3560 string c = to!string(b); 3561 3562 assert(c == a, "\n" ~ c ~ "\n" ~ a); 3563 3564 assert(to!string(to!real(to!string(real.max / 2L))) == to!string(real.max / 2L)); 3565 3566 // min and max 3567 real r = to!real(to!string(real.min_normal)); 3568 version (NetBSD) 3569 { 3570 // NetBSD notice 3571 // to!string returns 3.3621e-4932L. It is less than real.min_normal and it is subnormal value 3572 // Simple C code 3573 // long double rd = 3.3621e-4932L; 3574 // printf("%Le\n", rd); 3575 // has unexpected result: 1.681050e-4932 3576 // 3577 // Bug report: http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50937 3578 } 3579 else 3580 { 3581 assert(to!string(r) == to!string(real.min_normal)); 3582 } 3583 r = to!real(to!string(real.max)); 3584 assert(to!string(r) == to!string(real.max)); 3585 3586 real pi = 3.1415926535897932384626433832795028841971693993751L; 3587 string fullPrecision = "3.1415926535897932384626433832795028841971693993751"; 3588 assert(feq(parse!real(fullPrecision), pi, 2*real.epsilon)); 3589 string fullPrecision2 = "3.1415926535897932384626433832795028841971693993751"; 3590 assert(feq(parse!(real, string, No.doCount)(fullPrecision2), pi, 2*real.epsilon)); 3591 string fullPrecision3= "3.1415926535897932384626433832795028841971693993751"; 3592 auto len = fullPrecision3.length; 3593 auto res = parse!(real, string, Yes.doCount)(fullPrecision3); 3594 assert(feq(res.data, pi, 2*real.epsilon)); 3595 assert(res.count == len); 3596 3597 real x = 0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252L; 3598 string full = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252"; 3599 assert(parse!real(full) == x); 3600 string full2 = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252"; 3601 assert(parse!(real, string, No.doCount)(full2) == x); 3602 string full3 = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252"; 3603 auto len2 = full3.length; 3604 assert(parse!(real, string, Yes.doCount)(full3) == tuple(x, len2)); 3605 } 3606 3607 // Tests for the double implementation 3608 @system unittest 3609 { 3610 // @system because strtod is not @safe. 3611 import std.math.traits : floatTraits, RealFormat; 3612 3613 static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) 3614 { 3615 import core.stdc.stdlib, std.exception, std.math; 3616 3617 //Should be parsed exactly: 53 bit mantissa 3618 string s = "0x1A_BCDE_F012_3456p10"; 3619 auto x = parse!real(s); 3620 assert(x == 0x1A_BCDE_F012_3456p10L); 3621 //1 bit is implicit 3622 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0xA_BCDE_F012_3456); 3623 assert(strtod("0x1ABCDEF0123456p10", null) == x); 3624 3625 s = "0x1A_BCDE_F012_3456p10"; 3626 auto len = s.length; 3627 assert(parse!(real, string, Yes.doCount)(s) == tuple(x, len)); 3628 3629 //Should be parsed exactly: 10 bit mantissa 3630 s = "0x3FFp10"; 3631 x = parse!real(s); 3632 assert(x == 0x03FFp10); 3633 //1 bit is implicit 3634 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_F800_0000_0000); 3635 assert(strtod("0x3FFp10", null) == x); 3636 3637 //60 bit mantissa, round up 3638 s = "0xFFF_FFFF_FFFF_FFFFp10"; 3639 x = parse!real(s); 3640 assert(isClose(x, 0xFFF_FFFF_FFFF_FFFFp10)); 3641 //1 bit is implicit 3642 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x0000_0000_0000_0000); 3643 assert(strtod("0xFFFFFFFFFFFFFFFp10", null) == x); 3644 3645 //60 bit mantissa, round down 3646 s = "0xFFF_FFFF_FFFF_FF90p10"; 3647 x = parse!real(s); 3648 assert(isClose(x, 0xFFF_FFFF_FFFF_FF90p10)); 3649 //1 bit is implicit 3650 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_FFFF_FFFF_FFFF); 3651 assert(strtod("0xFFFFFFFFFFFFF90p10", null) == x); 3652 3653 //61 bit mantissa, round up 2 3654 s = "0x1F0F_FFFF_FFFF_FFFFp10"; 3655 x = parse!real(s); 3656 assert(isClose(x, 0x1F0F_FFFF_FFFF_FFFFp10)); 3657 //1 bit is implicit 3658 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_1000_0000_0000); 3659 assert(strtod("0x1F0FFFFFFFFFFFFFp10", null) == x); 3660 3661 //61 bit mantissa, round down 2 3662 s = "0x1F0F_FFFF_FFFF_FF10p10"; 3663 x = parse!real(s); 3664 assert(isClose(x, 0x1F0F_FFFF_FFFF_FF10p10)); 3665 //1 bit is implicit 3666 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_0FFF_FFFF_FFFF); 3667 assert(strtod("0x1F0FFFFFFFFFFF10p10", null) == x); 3668 3669 //Huge exponent 3670 s = "0x1F_FFFF_FFFF_FFFFp900"; 3671 x = parse!real(s); 3672 assert(strtod("0x1FFFFFFFFFFFFFp900", null) == x); 3673 3674 //exponent too big -> converror 3675 s = ""; 3676 assertThrown!ConvException(x = parse!real(s)); 3677 assert(strtod("0x1FFFFFFFFFFFFFp1024", null) == real.infinity); 3678 3679 //-exponent too big -> 0 3680 s = "0x1FFFFFFFFFFFFFp-2000"; 3681 x = parse!real(s); 3682 assert(x == 0); 3683 assert(strtod("0x1FFFFFFFFFFFFFp-2000", null) == x); 3684 3685 s = "0x1FFFFFFFFFFFFFp-2000"; 3686 len = s.length; 3687 assert(parse!(real, string, Yes.doCount)(s) == tuple(x, len)); 3688 } 3689 } 3690 3691 @system unittest 3692 { 3693 import core.stdc.errno; 3694 import core.stdc.stdlib; 3695 import std.math.traits : floatTraits, RealFormat; 3696 3697 errno = 0; // In case it was set by another unittest in a different module. 3698 struct longdouble 3699 { 3700 static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) 3701 { 3702 ushort[8] value; 3703 } 3704 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended || 3705 floatTraits!real.realFormat == RealFormat.ieeeExtended53) 3706 { 3707 ushort[5] value; 3708 } 3709 else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) 3710 { 3711 ushort[4] value; 3712 } 3713 else 3714 static assert(false, "Not implemented"); 3715 } 3716 3717 real ld; 3718 longdouble x; 3719 real ld1; 3720 longdouble x1; 3721 int i; 3722 3723 static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) 3724 enum s = "0x1.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp-16382"; 3725 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended) 3726 enum s = "0x1.FFFFFFFFFFFFFFFEp-16382"; 3727 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended53) 3728 enum s = "0x1.FFFFFFFFFFFFFFFEp-16382"; 3729 else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) 3730 enum s = "0x1.FFFFFFFFFFFFFFFEp-1000"; 3731 else 3732 static assert(false, "Floating point format for real not supported"); 3733 3734 auto s2 = s.idup; 3735 ld = parse!real(s2); 3736 assert(s2.empty); 3737 x = *cast(longdouble *)&ld; 3738 3739 static if (floatTraits!real.realFormat == RealFormat.ieeeExtended) 3740 { 3741 version (CRuntime_Microsoft) 3742 ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod 3743 else 3744 ld1 = strtold(s.ptr, null); 3745 } 3746 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended53) 3747 ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold rounds to 53 bits. 3748 else 3749 ld1 = strtold(s.ptr, null); 3750 3751 x1 = *cast(longdouble *)&ld1; 3752 assert(x1 == x && ld1 == ld); 3753 3754 assert(!errno); 3755 3756 s2 = "1.0e5"; 3757 ld = parse!real(s2); 3758 assert(s2.empty); 3759 x = *cast(longdouble *)&ld; 3760 ld1 = strtold("1.0e5", null); 3761 x1 = *cast(longdouble *)&ld1; 3762 } 3763 3764 @safe pure unittest 3765 { 3766 import std.exception; 3767 3768 // https://issues.dlang.org/show_bug.cgi?id=4959 3769 { 3770 auto s = "0 "; 3771 auto x = parse!double(s); 3772 assert(s == " "); 3773 assert(x == 0.0); 3774 } 3775 { 3776 auto s = "0 "; 3777 auto x = parse!(double, string, Yes.doCount)(s); 3778 assert(s == " "); 3779 assert(x == tuple(0.0, 1)); 3780 } 3781 3782 // https://issues.dlang.org/show_bug.cgi?id=3369 3783 assert(to!float("inf") == float.infinity); 3784 assert(to!float("-inf") == -float.infinity); 3785 3786 // https://issues.dlang.org/show_bug.cgi?id=6160 3787 assert(6_5.536e3L == to!real("6_5.536e3")); // 2^16 3788 assert(0x1000_000_000_p10 == to!real("0x1000_000_000_p10")); // 7.03687e+13 3789 3790 // https://issues.dlang.org/show_bug.cgi?id=6258 3791 assertThrown!ConvException(to!real("-")); 3792 assertThrown!ConvException(to!real("in")); 3793 3794 // https://issues.dlang.org/show_bug.cgi?id=7055 3795 assertThrown!ConvException(to!float("INF2")); 3796 3797 //extra stress testing 3798 auto ssOK = ["1.", "1.1.1", "1.e5", "2e1e", "2a", "2e1_1", "3.4_", 3799 "inf", "-inf", "infa", "-infa", "inf2e2", "-inf2e2", 3800 "nan", "-NAN", "+NaN", "-nAna", "NAn2e2", "-naN2e2"]; 3801 auto ssKO = ["", " ", "2e", "2e+", "2e-", "2ee", "2e++1", "2e--1", "2e_1", 3802 "+inf", "-in", "I", "+N", "-NaD", "0x3.F"]; 3803 foreach (s; ssOK) 3804 parse!double(s); 3805 foreach (s; ssKO) 3806 assertThrown!ConvException(parse!double(s)); 3807 } 3808 3809 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=22637 3810 { 3811 import std.exception : assertThrown, assertNotThrown; 3812 auto src = "9991232549867999698999493543521458974414359998784641646846435132132543645435456345634541999999999999999" 3813 ~ "9999999943321231321311999231345312413646846354354354399999934153465464654646464654134135354199999999996515734999" 3814 ~ "9999999320135273486741354354731567431324134999999999999999999999999999999999999999999999135411.9"; 3815 assertThrown!ConvException(parse!double(src)); 3816 static if (real.max_10_exp > 310) assertNotThrown!ConvException(parse!real(src)); 3817 } 3818 3819 /** 3820 Parses one character from a character range. 3821 3822 Params: 3823 Target = the type to convert to 3824 s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 3825 doCount = the flag for deciding to report the number of consumed characters 3826 3827 Returns: 3828 $(UL 3829 $(LI A character of type `Target` if `doCount` is set to `No.doCount`) 3830 $(LI A `tuple` containing a character of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`)) 3831 3832 Throws: 3833 A $(LREF ConvException) if the range is empty. 3834 */ 3835 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 3836 if (staticIndexOf!(immutable Target, immutable dchar, immutable ElementEncodingType!Source) >= 0 && 3837 isSomeString!Source && !is(Source == enum)) 3838 { 3839 if (s.empty) 3840 throw convError!(Source, Target)(s); 3841 static if (is(immutable Target == immutable dchar)) 3842 { 3843 Target result = s.front; 3844 s.popFront(); 3845 static if (doCount) 3846 { 3847 return tuple!("data", "count")(result, 1); 3848 } 3849 else 3850 { 3851 return result; 3852 } 3853 3854 } 3855 else 3856 { 3857 // Special case: okay so parse a Char off a Char[] 3858 Target result = s[0]; 3859 s = s[1 .. $]; 3860 static if (doCount) 3861 { 3862 return tuple!("data", "count")(result, 1); 3863 } 3864 else 3865 { 3866 return result; 3867 } 3868 } 3869 } 3870 3871 @safe pure unittest 3872 { 3873 static foreach (Str; AliasSeq!(string, wstring, dstring)) 3874 { 3875 static foreach (Char; AliasSeq!(char, wchar, dchar)) 3876 {{ 3877 static if (is(immutable Char == immutable dchar) || 3878 Char.sizeof == ElementEncodingType!Str.sizeof) 3879 { 3880 Str s = "aaa"; 3881 assert(parse!Char(s) == 'a'); 3882 assert(s == "aa"); 3883 assert(parse!(Char, typeof(s), No.doCount)(s) == 'a'); 3884 assert(s == "a"); 3885 assert(parse!(Char, typeof(s), Yes.doCount)(s) == tuple('a', 1) && s == ""); 3886 } 3887 }} 3888 } 3889 } 3890 3891 /// ditto 3892 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 3893 if (isSomeChar!Target && Target.sizeof >= ElementType!Source.sizeof && !is(Target == enum) && 3894 !isSomeString!Source && isInputRange!Source && isSomeChar!(ElementType!Source)) 3895 { 3896 if (s.empty) 3897 throw convError!(Source, Target)(s); 3898 Target result = s.front; 3899 s.popFront(); 3900 static if (doCount) 3901 { 3902 return tuple!("data", "count")(result, 1); 3903 } 3904 else 3905 { 3906 return result; 3907 } 3908 } 3909 3910 /// 3911 @safe pure unittest 3912 { 3913 import std.typecons : Flag, Yes, No; 3914 auto s = "Hello, World!"; 3915 char first = parse!char(s); 3916 assert(first == 'H'); 3917 assert(s == "ello, World!"); 3918 char second = parse!(char, string, No.doCount)(s); 3919 assert(second == 'e'); 3920 assert(s == "llo, World!"); 3921 auto third = parse!(char, string, Yes.doCount)(s); 3922 assert(third.data == 'l' && third.count == 1); 3923 assert(s == "lo, World!"); 3924 } 3925 3926 3927 /* 3928 Tests for to!bool and parse!bool 3929 */ 3930 @safe pure unittest 3931 { 3932 import std.exception; 3933 3934 assert(to!bool("TruE") == true); 3935 assert(to!bool("faLse"d) == false); 3936 assertThrown!ConvException(to!bool("maybe")); 3937 3938 auto t = "TrueType"; 3939 assert(parse!bool(t) == true); 3940 assert(t == "Type"); 3941 3942 auto f = "False killer whale"d; 3943 assert(parse!bool(f) == false); 3944 assert(f == " killer whale"d); 3945 3946 f = "False killer whale"d; 3947 assert(parse!(bool, dstring, Yes.doCount)(f) == tuple(false, 5)); 3948 assert(f == " killer whale"d); 3949 3950 auto m = "maybe"; 3951 assertThrown!ConvException(parse!bool(m)); 3952 assertThrown!ConvException(parse!(bool, string, Yes.doCount)(m)); 3953 assert(m == "maybe"); // m shouldn't change on failure 3954 3955 auto s = "true"; 3956 auto b = parse!(const(bool))(s); 3957 assert(b == true); 3958 } 3959 3960 /** 3961 Parses `typeof(null)` from a character range if the range 3962 spells `"null"`. This function is case insensitive. 3963 3964 Params: 3965 Target = the type to convert to 3966 s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 3967 doCount = the flag for deciding to report the number of consumed characters 3968 3969 Returns: 3970 $(UL 3971 $(LI `null` if `doCount` is set to `No.doCount`) 3972 $(LI A `tuple` containing `null` and a `size_t` if `doCount` is set to `Yes.doCount`)) 3973 3974 Throws: 3975 A $(LREF ConvException) if the range doesn't represent `null`. 3976 */ 3977 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 3978 if (is(immutable Target == immutable typeof(null)) && 3979 isInputRange!Source && 3980 isSomeChar!(ElementType!Source)) 3981 { 3982 import std.ascii : toLower; 3983 foreach (c; "null") 3984 { 3985 if (s.empty || toLower(s.front) != c) 3986 throw parseError("null should be case-insensitive 'null'"); 3987 s.popFront(); 3988 } 3989 static if (doCount) 3990 { 3991 return tuple!("data", "count")(null, 4); 3992 } 3993 else 3994 { 3995 return null; 3996 } 3997 } 3998 3999 /// 4000 @safe pure unittest 4001 { 4002 import std.exception : assertThrown; 4003 import std.typecons : Flag, Yes, No; 4004 4005 alias NullType = typeof(null); 4006 auto s1 = "null"; 4007 assert(parse!NullType(s1) is null); 4008 assert(s1 == ""); 4009 4010 auto s2 = "NUll"d; 4011 assert(parse!NullType(s2) is null); 4012 assert(s2 == ""); 4013 4014 auto s3 = "nuLlNULl"; 4015 assert(parse!(NullType, string, No.doCount)(s3) is null); 4016 auto r = parse!(NullType, string, Yes.doCount)(s3); 4017 assert(r.data is null && r.count == 4); 4018 4019 auto m = "maybe"; 4020 assertThrown!ConvException(parse!NullType(m)); 4021 assertThrown!ConvException(parse!(NullType, string, Yes.doCount)(m)); 4022 assert(m == "maybe"); // m shouldn't change on failure 4023 4024 auto s = "NULL"; 4025 assert(parse!(const NullType)(s) is null); 4026 } 4027 4028 //Used internally by parse Array/AA, to remove ascii whites 4029 package auto skipWS(R, Flag!"doCount" doCount = No.doCount)(ref R r) 4030 { 4031 import std.ascii : isWhite; 4032 static if (isSomeString!R) 4033 { 4034 //Implementation inspired from stripLeft. 4035 foreach (i, c; r) 4036 { 4037 if (!isWhite(c)) 4038 { 4039 r = r[i .. $]; 4040 static if (doCount) 4041 { 4042 return i; 4043 } 4044 else 4045 { 4046 return; 4047 } 4048 } 4049 } 4050 auto len = r.length; 4051 r = r[0 .. 0]; //Empty string with correct type. 4052 static if (doCount) 4053 { 4054 return len; 4055 } 4056 else 4057 { 4058 return; 4059 } 4060 } 4061 else 4062 { 4063 size_t i = 0; 4064 for (; !r.empty && isWhite(r.front); r.popFront(), ++i) 4065 { } 4066 static if (doCount) 4067 { 4068 return i; 4069 } 4070 } 4071 } 4072 4073 /** 4074 * Parses an array from a string given the left bracket (default $(D 4075 * '[')), right bracket (default `']'`), and element separator (by 4076 * default `','`). A trailing separator is allowed. 4077 * 4078 * Params: 4079 * s = The string to parse 4080 * lbracket = the character that starts the array 4081 * rbracket = the character that ends the array 4082 * comma = the character that separates the elements of the array 4083 * doCount = the flag for deciding to report the number of consumed characters 4084 * 4085 * Returns: 4086 $(UL 4087 * $(LI An array of type `Target` if `doCount` is set to `No.doCount`) 4088 * $(LI A `tuple` containing an array of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`)) 4089 */ 4090 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[', 4091 dchar rbracket = ']', dchar comma = ',') 4092 if (isDynamicArray!Target && !is(Target == enum) && 4093 isSomeString!Source && !is(Source == enum)) 4094 { 4095 import std.array : appender; 4096 4097 auto result = appender!Target(); 4098 4099 parseCheck!s(lbracket); 4100 size_t count = 1 + skipWS!(Source, Yes.doCount)(s); 4101 if (s.empty) 4102 throw convError!(Source, Target)(s); 4103 if (s.front == rbracket) 4104 { 4105 s.popFront(); 4106 static if (doCount) 4107 { 4108 return tuple!("data", "count")(result.data, ++count); 4109 } 4110 else 4111 { 4112 return result.data; 4113 } 4114 } 4115 for (;; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s)) 4116 { 4117 if (!s.empty && s.front == rbracket) 4118 break; 4119 auto r = parseElement!(WideElementType!Target, Source, Yes.doCount)(s); 4120 result ~= r.data; 4121 count += r.count + skipWS!(Source, Yes.doCount)(s); 4122 if (s.empty) 4123 throw convError!(Source, Target)(s); 4124 if (s.front != comma) 4125 break; 4126 } 4127 parseCheck!s(rbracket); 4128 static if (doCount) 4129 { 4130 return tuple!("data", "count")(result.data, ++count); 4131 } 4132 else 4133 { 4134 return result.data; 4135 } 4136 } 4137 4138 /// 4139 @safe pure unittest 4140 { 4141 import std.typecons : Flag, Yes, No; 4142 auto s1 = `[['h', 'e', 'l', 'l', 'o'], "world"]`; 4143 auto a1 = parse!(string[])(s1); 4144 assert(a1 == ["hello", "world"]); 4145 4146 auto s2 = `["aaa", "bbb", "ccc"]`; 4147 auto a2 = parse!(string[])(s2); 4148 assert(a2 == ["aaa", "bbb", "ccc"]); 4149 4150 auto s3 = `[['h', 'e', 'l', 'l', 'o'], "world"]`; 4151 auto len3 = s3.length; 4152 auto a3 = parse!(string[], string, Yes.doCount)(s3); 4153 assert(a3.data == ["hello", "world"]); 4154 assert(a3.count == len3); 4155 } 4156 4157 // https://issues.dlang.org/show_bug.cgi?id=9615 4158 @safe unittest 4159 { 4160 import std.typecons : Flag, Yes, No, tuple; 4161 string s0 = "[1,2, ]"; 4162 string s1 = "[1,2, \t\v\r\n]"; 4163 string s2 = "[1,2]"; 4164 assert(s0.parse!(int[]) == [1,2]); 4165 assert(s1.parse!(int[]) == [1,2]); 4166 assert(s2.parse!(int[]) == [1,2]); 4167 4168 s0 = "[1,2, ]"; 4169 auto len0 = s0.length; 4170 s1 = "[1,2, \t\v\r\n]"; 4171 auto len1 = s1.length; 4172 s2 = "[1,2]"; 4173 auto len2 = s2.length; 4174 assert(s0.parse!(int[], string, Yes.doCount) == tuple([1,2], len0)); 4175 assert(s1.parse!(int[], string, Yes.doCount) == tuple([1,2], len1)); 4176 assert(s2.parse!(int[], string, Yes.doCount) == tuple([1,2], len2)); 4177 4178 string s3 = `["a","b",]`; 4179 string s4 = `["a","b"]`; 4180 assert(s3.parse!(string[]) == ["a","b"]); 4181 assert(s4.parse!(string[]) == ["a","b"]); 4182 4183 s3 = `["a","b",]`; 4184 auto len3 = s3.length; 4185 assert(s3.parse!(string[], string, Yes.doCount) == tuple(["a","b"], len3)); 4186 4187 s3 = `[ ]`; 4188 assert(tuple([], s3.length) == s3.parse!(string[], string, Yes.doCount)); 4189 4190 import std.exception : assertThrown; 4191 string s5 = "[,]"; 4192 string s6 = "[, \t,]"; 4193 assertThrown!ConvException(parse!(string[])(s5)); 4194 assertThrown!ConvException(parse!(int[])(s6)); 4195 4196 s5 = "[,]"; 4197 s6 = "[,·\t,]"; 4198 assertThrown!ConvException(parse!(string[], string, Yes.doCount)(s5)); 4199 assertThrown!ConvException(parse!(string[], string, Yes.doCount)(s6)); 4200 } 4201 4202 @safe unittest 4203 { 4204 int[] a = [1, 2, 3, 4, 5]; 4205 auto s = to!string(a); 4206 assert(to!(int[])(s) == a); 4207 } 4208 4209 @safe unittest 4210 { 4211 int[][] a = [ [1, 2] , [3], [4, 5] ]; 4212 auto s = to!string(a); 4213 assert(to!(int[][])(s) == a); 4214 } 4215 4216 @safe unittest 4217 { 4218 int[][][] ia = [ [[1,2],[3,4],[5]] , [[6],[],[7,8,9]] , [[]] ]; 4219 4220 char[] s = to!(char[])(ia); 4221 int[][][] ia2; 4222 4223 ia2 = to!(typeof(ia2))(s); 4224 assert( ia == ia2); 4225 } 4226 4227 @safe pure unittest 4228 { 4229 import std.exception; 4230 import std.typecons : Flag, Yes, No; 4231 4232 //Check proper failure 4233 auto s = "[ 1 , 2 , 3 ]"; 4234 auto s2 = s.save; 4235 foreach (i ; 0 .. s.length-1) 4236 { 4237 auto ss = s[0 .. i]; 4238 assertThrown!ConvException(parse!(int[])(ss)); 4239 assertThrown!ConvException(parse!(int[], string, Yes.doCount)(ss)); 4240 } 4241 int[] arr = parse!(int[])(s); 4242 auto arr2 = parse!(int[], string, Yes.doCount)(s2); 4243 arr = arr2.data; 4244 } 4245 4246 @safe pure unittest 4247 { 4248 //Checks parsing of strings with escaped characters 4249 string s1 = `[ 4250 "Contains a\0null!", 4251 "tab\there", 4252 "line\nbreak", 4253 "backslash \\ slash / question \?", 4254 "number \x35 five", 4255 "unicode \u65E5 sun", 4256 "very long \U000065E5 sun" 4257 ]`; 4258 4259 //Note: escaped characters purposefully replaced and isolated to guarantee 4260 //there are no typos in the escape syntax 4261 string[] s2 = [ 4262 "Contains a" ~ '\0' ~ "null!", 4263 "tab" ~ '\t' ~ "here", 4264 "line" ~ '\n' ~ "break", 4265 "backslash " ~ '\\' ~ " slash / question ?", 4266 "number 5 five", 4267 "unicode 日 sun", 4268 "very long 日 sun" 4269 ]; 4270 string s3 = s1.save; 4271 assert(s2 == parse!(string[])(s1)); 4272 assert(s1.empty); 4273 assert(tuple(s2, s3.length) == parse!(string[], string, Yes.doCount)(s3)); 4274 } 4275 4276 /// ditto 4277 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[', 4278 dchar rbracket = ']', dchar comma = ',') 4279 if (isStaticArray!Target && !is(Target == enum) && 4280 isExactSomeString!Source) 4281 { 4282 static if (hasIndirections!Target) 4283 Target result = Target.init[0].init; 4284 else 4285 Target result = void; 4286 4287 parseCheck!s(lbracket); 4288 size_t count = 1 + skipWS!(Source, Yes.doCount)(s); 4289 if (s.empty) 4290 throw convError!(Source, Target)(s); 4291 if (s.front == rbracket) 4292 { 4293 static if (result.length != 0) 4294 goto Lmanyerr; 4295 else 4296 { 4297 s.popFront(); 4298 static if (doCount) 4299 { 4300 return tuple!("data", "count")(result, ++count); 4301 } 4302 else 4303 { 4304 return result; 4305 } 4306 } 4307 } 4308 for (size_t i = 0; ; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s)) 4309 { 4310 if (i == result.length) 4311 goto Lmanyerr; 4312 auto r = parseElement!(ElementType!Target, Source, Yes.doCount)(s); 4313 result[i++] = r.data; 4314 count += r.count + skipWS!(Source, Yes.doCount)(s); 4315 if (s.empty) 4316 throw convError!(Source, Target)(s); 4317 if (s.front != comma) 4318 { 4319 if (i != result.length) 4320 goto Lfewerr; 4321 break; 4322 } 4323 } 4324 parseCheck!s(rbracket); 4325 static if (doCount) 4326 { 4327 return tuple!("data", "count")(result, ++count); 4328 } 4329 else 4330 { 4331 return result; 4332 } 4333 4334 4335 Lmanyerr: 4336 throw parseError(text("Too many elements in input, ", result.length, " elements expected.")); 4337 4338 Lfewerr: 4339 throw parseError(text("Too few elements in input, ", result.length, " elements expected.")); 4340 } 4341 4342 @safe pure unittest 4343 { 4344 import std.exception; 4345 4346 auto s1 = "[1,2,3,4]"; 4347 auto sa1 = parse!(int[4])(s1); 4348 assert(sa1 == [1,2,3,4]); 4349 s1 = "[1,2,3,4]"; 4350 assert(tuple([1,2,3,4], s1.length) == parse!(int[4], string, Yes.doCount)(s1)); 4351 4352 auto s2 = "[[1],[2,3],[4]]"; 4353 auto sa2 = parse!(int[][3])(s2); 4354 assert(sa2 == [[1],[2,3],[4]]); 4355 s2 = "[[1],[2,3],[4]]"; 4356 assert(tuple([[1],[2,3],[4]], s2.length) == parse!(int[][3], string, Yes.doCount)(s2)); 4357 4358 auto s3 = "[1,2,3]"; 4359 assertThrown!ConvException(parse!(int[4])(s3)); 4360 assertThrown!ConvException(parse!(int[4], string, Yes.doCount)(s3)); 4361 4362 auto s4 = "[1,2,3,4,5]"; 4363 assertThrown!ConvException(parse!(int[4])(s4)); 4364 assertThrown!ConvException(parse!(int[4], string, Yes.doCount)(s4)); 4365 } 4366 4367 /** 4368 * Parses an associative array from a string given the left bracket (default $(D 4369 * '[')), right bracket (default `']'`), key-value separator (default $(D 4370 * ':')), and element seprator (by default `','`). 4371 * 4372 * Params: 4373 * s = the string to parse 4374 * lbracket = the character that starts the associative array 4375 * rbracket = the character that ends the associative array 4376 * keyval = the character that associates the key with the value 4377 * comma = the character that separates the elements of the associative array 4378 * doCount = the flag for deciding to report the number of consumed characters 4379 * 4380 * Returns: 4381 $(UL 4382 * $(LI An associative array of type `Target` if `doCount` is set to `No.doCount`) 4383 * $(LI A `tuple` containing an associative array of type `Target` and a `size_t` 4384 * if `doCount` is set to `Yes.doCount`)) 4385 */ 4386 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[', 4387 dchar rbracket = ']', dchar keyval = ':', dchar comma = ',') 4388 if (isAssociativeArray!Target && !is(Target == enum) && 4389 isSomeString!Source && !is(Source == enum)) 4390 { 4391 alias KeyType = typeof(Target.init.keys[0]); 4392 alias ValType = typeof(Target.init.values[0]); 4393 4394 Target result; 4395 4396 parseCheck!s(lbracket); 4397 size_t count = 1 + skipWS!(Source, Yes.doCount)(s); 4398 if (s.empty) 4399 throw convError!(Source, Target)(s); 4400 if (s.front == rbracket) 4401 { 4402 s.popFront(); 4403 static if (doCount) 4404 { 4405 return tuple!("data", "count")(result, ++count); 4406 } 4407 else 4408 { 4409 return result; 4410 } 4411 } 4412 for (;; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s)) 4413 { 4414 auto key = parseElement!(KeyType, Source, Yes.doCount)(s); 4415 count += key.count + skipWS!(Source, Yes.doCount)(s); 4416 parseCheck!s(keyval); 4417 count += 1 + skipWS!(Source, Yes.doCount)(s); 4418 auto val = parseElement!(ValType, Source, Yes.doCount)(s); 4419 count += val.count + skipWS!(Source, Yes.doCount)(s); 4420 result[key.data] = val.data; 4421 if (s.empty) 4422 throw convError!(Source, Target)(s); 4423 if (s.front != comma) 4424 break; 4425 } 4426 parseCheck!s(rbracket); 4427 static if (doCount) 4428 { 4429 return tuple!("data", "count")(result, ++count); 4430 } 4431 else 4432 { 4433 return result; 4434 } 4435 } 4436 4437 /// 4438 @safe pure unittest 4439 { 4440 import std.typecons : Flag, Yes, No, tuple; 4441 import std.range.primitives : save; 4442 import std.array : assocArray; 4443 auto s1 = "[1:10, 2:20, 3:30]"; 4444 auto copyS1 = s1.save; 4445 auto aa1 = parse!(int[int])(s1); 4446 assert(aa1 == [1:10, 2:20, 3:30]); 4447 assert(tuple([1:10, 2:20, 3:30], copyS1.length) == parse!(int[int], string, Yes.doCount)(copyS1)); 4448 4449 auto s2 = `["aaa":10, "bbb":20, "ccc":30]`; 4450 auto copyS2 = s2.save; 4451 auto aa2 = parse!(int[string])(s2); 4452 assert(aa2 == ["aaa":10, "bbb":20, "ccc":30]); 4453 assert(tuple(["aaa":10, "bbb":20, "ccc":30], copyS2.length) == 4454 parse!(int[string], string, Yes.doCount)(copyS2)); 4455 4456 auto s3 = `["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]`; 4457 auto copyS3 = s3.save; 4458 auto aa3 = parse!(int[][string])(s3); 4459 assert(aa3 == ["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]); 4460 assert(tuple(["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]], copyS3.length) == 4461 parse!(int[][string], string, Yes.doCount)(copyS3)); 4462 4463 auto s4 = `[]`; 4464 int[int] emptyAA; 4465 assert(tuple(emptyAA, s4.length) == parse!(int[int], string, Yes.doCount)(s4)); 4466 } 4467 4468 @safe pure unittest 4469 { 4470 import std.exception; 4471 4472 //Check proper failure 4473 auto s = "[1:10, 2:20, 3:30]"; 4474 auto s2 = s.save; 4475 foreach (i ; 0 .. s.length-1) 4476 { 4477 auto ss = s[0 .. i]; 4478 assertThrown!ConvException(parse!(int[int])(ss)); 4479 assertThrown!ConvException(parse!(int[int], string, Yes.doCount)(ss)); 4480 } 4481 int[int] aa = parse!(int[int])(s); 4482 auto aa2 = parse!(int[int], string, Yes.doCount)(s2); 4483 aa = aa2[0]; 4484 4485 } 4486 4487 private auto parseEscape(Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 4488 if (isInputRange!Source && isSomeChar!(ElementType!Source)) 4489 { 4490 parseCheck!s('\\'); 4491 size_t count = 1; 4492 if (s.empty) 4493 throw parseError("Unterminated escape sequence"); 4494 4495 // consumes 1 element from Source 4496 dchar getHexDigit()(ref Source s_ = s) // workaround 4497 { 4498 import std.ascii : isAlpha, isHexDigit; 4499 if (s_.empty) 4500 throw parseError("Unterminated escape sequence"); 4501 s_.popFront(); 4502 if (s_.empty) 4503 throw parseError("Unterminated escape sequence"); 4504 dchar c = s_.front; 4505 if (!isHexDigit(c)) 4506 throw parseError("Hex digit is missing"); 4507 return isAlpha(c) ? ((c & ~0x20) - ('A' - 10)) : c - '0'; 4508 } 4509 4510 // We need to do octals separate, because they need a lookahead to find out, 4511 // where the escape sequence ends. 4512 auto first = s.front; 4513 if (first >= '0' && first <= '7') 4514 { 4515 dchar c1 = s.front; 4516 ++count; 4517 s.popFront(); 4518 if (s.empty) 4519 { 4520 static if (doCount) 4521 { 4522 return tuple!("data", "count")(cast (dchar) (c1 - '0'), count); 4523 } 4524 else 4525 { 4526 return cast (dchar) (c1 - '0'); 4527 } 4528 } 4529 dchar c2 = s.front; 4530 if (c2 < '0' || c2 > '7') 4531 { 4532 static if (doCount) 4533 { 4534 return tuple!("data", "count")(cast (dchar)(c1 - '0'), count); 4535 } 4536 else 4537 { 4538 return cast (dchar)(c1 - '0'); 4539 } 4540 } 4541 ++count; 4542 s.popFront(); 4543 dchar c3 = s.front; 4544 if (c3 < '0' || c3 > '7') 4545 { 4546 static if (doCount) 4547 { 4548 return tuple!("data", "count")(cast (dchar) (8 * (c1 - '0') + (c2 - '0')), count); 4549 } 4550 else 4551 { 4552 return cast (dchar) (8 * (c1 - '0') + (c2 - '0')); 4553 } 4554 } 4555 ++count; 4556 s.popFront(); 4557 if (c1 > '3') 4558 throw parseError("Octal sequence is larger than \\377"); 4559 static if (doCount) 4560 { 4561 return tuple!("data", "count")(cast (dchar) (64 * (c1 - '0') + 8 * (c2 - '0') + (c3 - '0')), count); 4562 } 4563 else 4564 { 4565 return cast (dchar) (64 * (c1 - '0') + 8 * (c2 - '0') + (c3 - '0')); 4566 } 4567 } 4568 4569 dchar result; 4570 4571 switch (first) 4572 { 4573 case '"': result = '\"'; break; 4574 case '\'': result = '\''; break; 4575 case '?': result = '\?'; break; 4576 case '\\': result = '\\'; break; 4577 case 'a': result = '\a'; break; 4578 case 'b': result = '\b'; break; 4579 case 'f': result = '\f'; break; 4580 case 'n': result = '\n'; break; 4581 case 'r': result = '\r'; break; 4582 case 't': result = '\t'; break; 4583 case 'v': result = '\v'; break; 4584 case 'x': 4585 result = getHexDigit() << 4; 4586 result |= getHexDigit(); 4587 count += 2; 4588 break; 4589 case 'u': 4590 result = getHexDigit() << 12; 4591 result |= getHexDigit() << 8; 4592 result |= getHexDigit() << 4; 4593 result |= getHexDigit(); 4594 count += 4; 4595 break; 4596 case 'U': 4597 result = getHexDigit() << 28; 4598 result |= getHexDigit() << 24; 4599 result |= getHexDigit() << 20; 4600 result |= getHexDigit() << 16; 4601 result |= getHexDigit() << 12; 4602 result |= getHexDigit() << 8; 4603 result |= getHexDigit() << 4; 4604 result |= getHexDigit(); 4605 count += 8; 4606 break; 4607 default: 4608 throw parseError("Unknown escape character " ~ to!string(s.front)); 4609 } 4610 if (s.empty) 4611 throw parseError("Unterminated escape sequence"); 4612 4613 s.popFront(); 4614 4615 static if (doCount) 4616 { 4617 return tuple!("data", "count")(cast (dchar) result, ++count); 4618 } 4619 else 4620 { 4621 return cast (dchar) result; 4622 } 4623 } 4624 4625 @safe pure unittest 4626 { 4627 string[] s1 = [ 4628 `\"`, `\'`, `\?`, `\\`, `\a`, `\b`, `\f`, `\n`, `\r`, `\t`, `\v`, //Normal escapes 4629 `\141`, 4630 `\x61`, 4631 `\u65E5`, `\U00012456`, 4632 // https://issues.dlang.org/show_bug.cgi?id=9621 (Named Character Entities) 4633 //`\&`, `\"`, 4634 ]; 4635 string[] copyS1 = s1 ~ s1[0 .. 0]; 4636 4637 const(dchar)[] s2 = [ 4638 '\"', '\'', '\?', '\\', '\a', '\b', '\f', '\n', '\r', '\t', '\v', //Normal escapes 4639 '\141', 4640 '\x61', 4641 '\u65E5', '\U00012456', 4642 // https://issues.dlang.org/show_bug.cgi?id=9621 (Named Character Entities) 4643 //'\&', '\"', 4644 ]; 4645 4646 foreach (i ; 0 .. s1.length) 4647 { 4648 assert(s2[i] == parseEscape(s1[i])); 4649 assert(s1[i].empty); 4650 4651 assert(tuple(s2[i], copyS1[i].length) == parseEscape!(string, Yes.doCount)(copyS1[i])); 4652 assert(copyS1[i].empty); 4653 } 4654 } 4655 4656 @safe pure unittest 4657 { 4658 import std.exception; 4659 4660 string[] ss = [ 4661 `hello!`, //Not an escape 4662 `\`, //Premature termination 4663 `\/`, //Not an escape 4664 `\gggg`, //Not an escape 4665 `\xzz`, //Not an hex 4666 `\x0`, //Premature hex end 4667 `\XB9`, //Not legal hex syntax 4668 `\u!!`, //Not a unicode hex 4669 `\777`, //Octal is larger than a byte 4670 `\80`, //Wrong digit at beginning of octal 4671 `\u123`, //Premature hex end 4672 `\U123123` //Premature hex end 4673 ]; 4674 foreach (s ; ss) 4675 { 4676 assertThrown!ConvException(parseEscape(s)); 4677 assertThrown!ConvException(parseEscape!(string, Yes.doCount)(s)); 4678 } 4679 } 4680 4681 // Undocumented 4682 auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 4683 if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && 4684 isExactSomeString!Target) 4685 { 4686 import std.array : appender; 4687 auto result = appender!Target(); 4688 4689 // parse array of chars 4690 if (s.empty) 4691 throw convError!(Source, Target)(s); 4692 if (s.front == '[') 4693 { 4694 return parse!(Target, Source, doCount)(s); 4695 } 4696 4697 parseCheck!s('\"'); 4698 size_t count = 1; 4699 if (s.empty) 4700 throw convError!(Source, Target)(s); 4701 if (s.front == '\"') 4702 { 4703 s.popFront(); 4704 static if (doCount) 4705 { 4706 return tuple!("data", "count")(result.data, ++count); 4707 } 4708 else 4709 { 4710 return result.data; 4711 } 4712 4713 } 4714 while (true) 4715 { 4716 if (s.empty) 4717 throw parseError("Unterminated quoted string"); 4718 switch (s.front) 4719 { 4720 case '\"': 4721 s.popFront(); 4722 static if (doCount) 4723 { 4724 return tuple!("data", "count")(result.data, ++count); 4725 } 4726 else 4727 { 4728 return result.data; 4729 } 4730 case '\\': 4731 auto r = parseEscape!(typeof(s), Yes.doCount)(s); 4732 result.put(r[0]); 4733 count += r[1]; 4734 break; 4735 default: 4736 result.put(s.front); 4737 ++count; 4738 s.popFront(); 4739 break; 4740 } 4741 } 4742 assert(false, "Unexpected fallthrough"); 4743 } 4744 4745 // ditto 4746 auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 4747 if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && 4748 is(CharTypeOf!Target == dchar) && !is(Target == enum)) 4749 { 4750 Unqual!Target c; 4751 4752 parseCheck!s('\''); 4753 size_t count = 1; 4754 if (s.empty) 4755 throw convError!(Source, Target)(s); 4756 ++count; // for the following if-else sequence 4757 if (s.front != '\\') 4758 { 4759 c = s.front; 4760 s.popFront(); 4761 } 4762 else 4763 c = parseEscape(s); 4764 parseCheck!s('\''); 4765 static if (doCount) 4766 { 4767 return tuple!("data", "count")(c, ++count); 4768 } 4769 else 4770 { 4771 return c; 4772 } 4773 } 4774 4775 // ditto 4776 auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 4777 if (isInputRange!Source && isSomeChar!(ElementType!Source) && 4778 !isSomeString!Target && !isSomeChar!Target) 4779 { 4780 return parse!(Target, Source, doCount)(s); 4781 } 4782 4783 // Use this when parsing a type that will ultimately be appended to a 4784 // string. 4785 package template WideElementType(T) 4786 { 4787 alias E = ElementType!T; 4788 static if (isSomeChar!E) 4789 alias WideElementType = dchar; 4790 else 4791 alias WideElementType = E; 4792 } 4793 4794 4795 /*************************************************************** 4796 * Convenience functions for converting one or more arguments 4797 * of any type into _text (the three character widths). 4798 */ 4799 string text(T...)(T args) 4800 if (T.length > 0) { return textImpl!string(args); } 4801 4802 ///ditto 4803 wstring wtext(T...)(T args) 4804 if (T.length > 0) { return textImpl!wstring(args); } 4805 4806 ///ditto 4807 dstring dtext(T...)(T args) 4808 if (T.length > 0) { return textImpl!dstring(args); } 4809 4810 /// 4811 @safe unittest 4812 { 4813 assert( text(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"c); 4814 assert(wtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"w); 4815 assert(dtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"d); 4816 } 4817 4818 @safe unittest 4819 { 4820 char c = 'h'; 4821 wchar w = '你'; 4822 dchar d = 'እ'; 4823 4824 assert( text(c, "ello", ' ', w, "好 ", d, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"c); 4825 assert(wtext(c, "ello", ' ', w, "好 ", d, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"w); 4826 assert(dtext(c, "ello", ' ', w, "好 ", d, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"d); 4827 4828 string cs = "今日は"; 4829 wstring ws = "여보세요"; 4830 dstring ds = "Здравствуйте"; 4831 4832 assert( text(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"c); 4833 assert(wtext(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"w); 4834 assert(dtext(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"d); 4835 } 4836 4837 private S textImpl(S, U...)(U args) 4838 { 4839 static if (U.length == 0) 4840 { 4841 return null; 4842 } 4843 else static if (U.length == 1) 4844 { 4845 return to!S(args[0]); 4846 } 4847 else 4848 { 4849 import std.array : appender; 4850 import std.traits : isSomeChar, isSomeString; 4851 4852 auto app = appender!S(); 4853 4854 // assume that on average, parameters will have less 4855 // than 20 elements 4856 app.reserve(U.length * 20); 4857 // Must be static foreach because of https://issues.dlang.org/show_bug.cgi?id=21209 4858 static foreach (arg; args) 4859 { 4860 static if ( 4861 isSomeChar!(typeof(arg)) 4862 || isSomeString!(typeof(arg)) 4863 || ( isInputRange!(typeof(arg)) && isSomeChar!(ElementType!(typeof(arg))) ) 4864 ) 4865 app.put(arg); 4866 else static if ( 4867 4868 is(immutable typeof(arg) == immutable uint) || is(immutable typeof(arg) == immutable ulong) || 4869 is(immutable typeof(arg) == immutable int) || is(immutable typeof(arg) == immutable long) 4870 ) 4871 // https://issues.dlang.org/show_bug.cgi?id=17712#c15 4872 app.put(textImpl!(S)(arg)); 4873 else 4874 app.put(to!S(arg)); 4875 } 4876 4877 return app.data; 4878 } 4879 } 4880 4881 4882 /*************************************************************** 4883 The `octal` facility provides a means to declare a number in base 8. 4884 Using `octal!177` or `octal!"177"` for 127 represented in octal 4885 (same as 0177 in C). 4886 4887 The rules for strings are the usual for literals: If it can fit in an 4888 `int`, it is an `int`. Otherwise, it is a `long`. But, if the 4889 user specifically asks for a `long` with the `L` suffix, always 4890 give the `long`. Give an unsigned iff it is asked for with the $(D 4891 U) or `u` suffix. _Octals created from integers preserve the type 4892 of the passed-in integral. 4893 4894 See_Also: 4895 $(LREF parse) for parsing octal strings at runtime. 4896 */ 4897 template octal(string num) 4898 if (isOctalLiteral(num)) 4899 { 4900 static if ((octalFitsInInt!num && !literalIsLong!num) && !literalIsUnsigned!num) 4901 enum octal = octal!int(num); 4902 else static if ((!octalFitsInInt!num || literalIsLong!num) && !literalIsUnsigned!num) 4903 enum octal = octal!long(num); 4904 else static if ((octalFitsInInt!num && !literalIsLong!num) && literalIsUnsigned!num) 4905 enum octal = octal!uint(num); 4906 else static if ((!octalFitsInInt!(num) || literalIsLong!(num)) && literalIsUnsigned!(num)) 4907 enum octal = octal!ulong(num); 4908 else 4909 static assert(false, "Unusable input " ~ num); 4910 } 4911 4912 /// Ditto 4913 template octal(alias decimalInteger) 4914 if (is(typeof(decimalInteger)) && isIntegral!(typeof(decimalInteger))) 4915 { 4916 enum octal = convertToOctal(decimalInteger); 4917 } 4918 4919 /// 4920 @safe unittest 4921 { 4922 // Same as 0177 4923 auto a = octal!177; 4924 // octal is a compile-time device 4925 enum b = octal!160; 4926 // Create an unsigned octal 4927 auto c = octal!"1_000_000u"; 4928 // Leading zeros are allowed when converting from a string 4929 auto d = octal!"0001_200_000"; 4930 } 4931 4932 /************************************* 4933 * Convert a decimal integer to an octal integer with the same digits. 4934 * Params: 4935 * i = integer to convert 4936 * Returns: 4937 * octal integer with the same type and same digits 4938 */ 4939 private T convertToOctal(T)(T i) 4940 { 4941 assert((i % 10) < 8); 4942 return i ? convertToOctal(i / 10) * 8 + i % 10 : 0; 4943 } 4944 4945 /* 4946 Takes a string, num, which is an octal literal, and returns its 4947 value, in the type T specified. 4948 */ 4949 private T octal(T)(const string num) 4950 { 4951 assert(isOctalLiteral(num), num ~ " is not an octal literal"); 4952 4953 T value = 0; 4954 4955 foreach (const char s; num) 4956 { 4957 if (s < '0' || s > '7') // we only care about digits; skip the rest 4958 // safe to skip - this is checked out in the assert so these 4959 // are just suffixes 4960 continue; 4961 4962 value *= 8; 4963 value += s - '0'; 4964 } 4965 4966 return value; 4967 } 4968 4969 @safe unittest 4970 { 4971 int a = octal!int("10"); 4972 assert(a == 8); 4973 4974 int b = octal!int("000137"); 4975 assert(b == 95); 4976 } 4977 4978 /* 4979 Take a look at int.max and int.max+1 in octal and the logic for this 4980 function follows directly. 4981 */ 4982 private template octalFitsInInt(string octalNum) 4983 { 4984 // note it is important to strip the literal of all 4985 // non-numbers. kill the suffix and underscores lest they mess up 4986 // the number of digits here that we depend on. 4987 enum bool octalFitsInInt = strippedOctalLiteral(octalNum).length < 11 || 4988 strippedOctalLiteral(octalNum).length == 11 && 4989 strippedOctalLiteral(octalNum)[0] == '1'; 4990 } 4991 4992 private string strippedOctalLiteral(string original) 4993 { 4994 string stripped = ""; 4995 bool leading_zeros = true; 4996 foreach (c; original) 4997 { 4998 if (!('0' <= c && c <= '7')) 4999 continue; 5000 if (c == '0') 5001 { 5002 if (leading_zeros) 5003 continue; 5004 } 5005 else 5006 { 5007 leading_zeros = false; 5008 } 5009 stripped ~= c; 5010 } 5011 if (stripped.length == 0) 5012 { 5013 assert(leading_zeros); 5014 return "0"; 5015 } 5016 return stripped; 5017 } 5018 5019 @safe unittest 5020 { 5021 static assert(strippedOctalLiteral("7") == "7"); 5022 static assert(strippedOctalLiteral("123") == "123"); 5023 static assert(strippedOctalLiteral("00123") == "123"); 5024 static assert(strippedOctalLiteral("01230") == "1230"); 5025 static assert(strippedOctalLiteral("0") == "0"); 5026 static assert(strippedOctalLiteral("00_000") == "0"); 5027 static assert(strippedOctalLiteral("000_000_12_300") == "12300"); 5028 } 5029 5030 private template literalIsLong(string num) 5031 { 5032 static if (num.length > 1) 5033 // can be xxL or xxLu according to spec 5034 enum literalIsLong = (num[$-1] == 'L' || num[$-2] == 'L'); 5035 else 5036 enum literalIsLong = false; 5037 } 5038 5039 private template literalIsUnsigned(string num) 5040 { 5041 static if (num.length > 1) 5042 // can be xxU or xxUL according to spec 5043 enum literalIsUnsigned = (num[$-1] == 'u' || num[$-2] == 'u') 5044 // both cases are allowed too 5045 || (num[$-1] == 'U' || num[$-2] == 'U'); 5046 else 5047 enum literalIsUnsigned = false; 5048 } 5049 5050 /* 5051 Returns if the given string is a correctly formatted octal literal. 5052 5053 The format is specified in spec/lex.html. The leading zeros are allowed, 5054 but not required. 5055 */ 5056 @safe pure nothrow @nogc 5057 private bool isOctalLiteral(const string num) 5058 { 5059 if (num.length == 0) 5060 return false; 5061 5062 // Must start with a digit. 5063 if (num[0] < '0' || num[0] > '7') 5064 return false; 5065 5066 foreach (i, c; num) 5067 { 5068 if (('0' <= c && c <= '7') || c == '_') // a legal character 5069 continue; 5070 5071 if (i < num.length - 2) 5072 return false; 5073 5074 // gotta check for those suffixes 5075 if (c != 'U' && c != 'u' && c != 'L') 5076 return false; 5077 if (i != num.length - 1) 5078 { 5079 // if we're not the last one, the next one must 5080 // also be a suffix to be valid 5081 char c2 = num[$-1]; 5082 if (c2 != 'U' && c2 != 'u' && c2 != 'L') 5083 return false; // spam at the end of the string 5084 if (c2 == c) 5085 return false; // repeats are disallowed 5086 } 5087 } 5088 5089 return true; 5090 } 5091 5092 @safe unittest 5093 { 5094 // ensure that you get the right types, even with embedded underscores 5095 auto w = octal!"100_000_000_000"; 5096 static assert(!is(typeof(w) == int)); 5097 auto w2 = octal!"1_000_000_000"; 5098 static assert(is(typeof(w2) == int)); 5099 5100 static assert(octal!"45" == 37); 5101 static assert(octal!"0" == 0); 5102 static assert(octal!"7" == 7); 5103 static assert(octal!"10" == 8); 5104 static assert(octal!"666" == 438); 5105 static assert(octal!"0004001" == 2049); 5106 static assert(octal!"00" == 0); 5107 static assert(octal!"0_0" == 0); 5108 5109 static assert(octal!45 == 37); 5110 static assert(octal!0 == 0); 5111 static assert(octal!7 == 7); 5112 static assert(octal!10 == 8); 5113 static assert(octal!666 == 438); 5114 5115 static assert(octal!"66_6" == 438); 5116 static assert(octal!"0_0_66_6" == 438); 5117 5118 static assert(octal!2520046213 == 356535435); 5119 static assert(octal!"2520046213" == 356535435); 5120 5121 static assert(octal!17777777777 == int.max); 5122 5123 static assert(!__traits(compiles, octal!823)); 5124 5125 static assert(!__traits(compiles, octal!"823")); 5126 5127 static assert(!__traits(compiles, octal!"_823")); 5128 static assert(!__traits(compiles, octal!"spam")); 5129 static assert(!__traits(compiles, octal!"77%")); 5130 5131 static assert(is(typeof(octal!"17777777777") == int)); 5132 static assert(octal!"17777777777" == int.max); 5133 5134 static assert(is(typeof(octal!"20000000000U") == ulong)); // Shouldn't this be uint? 5135 static assert(octal!"20000000000" == uint(int.max) + 1); 5136 5137 static assert(is(typeof(octal!"777777777777777777777") == long)); 5138 static assert(octal!"777777777777777777777" == long.max); 5139 5140 static assert(is(typeof(octal!"1000000000000000000000U") == ulong)); 5141 static assert(octal!"1000000000000000000000" == ulong(long.max) + 1); 5142 5143 int a; 5144 long b; 5145 5146 // biggest value that should fit in an it 5147 a = octal!"17777777777"; 5148 assert(a == int.max); 5149 // should not fit in the int 5150 static assert(!__traits(compiles, a = octal!"20000000000")); 5151 // ... but should fit in a long 5152 b = octal!"20000000000"; 5153 assert(b == 1L + int.max); 5154 5155 b = octal!"1L"; 5156 assert(b == 1); 5157 b = octal!1L; 5158 assert(b == 1); 5159 } 5160 5161 // emplace() used to be here but was moved to druntime 5162 public import core.lifetime : emplace; 5163 5164 // https://issues.dlang.org/show_bug.cgi?id=9559 5165 @safe unittest 5166 { 5167 import std.algorithm.iteration : map; 5168 import std.array : array; 5169 import std.typecons : Nullable; 5170 alias I = Nullable!int; 5171 auto ints = [0, 1, 2].map!(i => i & 1 ? I.init : I(i))(); 5172 auto asArray = array(ints); 5173 } 5174 5175 @system unittest //http://forum.dlang.org/post/nxbdgtdlmwscocbiypjs@forum.dlang.org 5176 { 5177 import std.array : array; 5178 import std.datetime : SysTime, UTC; 5179 import std.math.traits : isNaN; 5180 5181 static struct A 5182 { 5183 double i; 5184 } 5185 5186 static struct B 5187 { 5188 invariant() 5189 { 5190 if (j == 0) 5191 assert(a.i.isNaN(), "why is 'j' zero?? and i is not NaN?"); 5192 else 5193 assert(!a.i.isNaN()); 5194 } 5195 SysTime when; // comment this line avoid the breakage 5196 int j; 5197 A a; 5198 } 5199 5200 B b1 = B.init; 5201 assert(&b1); // verify that default eyes invariants are ok; 5202 5203 auto b2 = B(SysTime(0, UTC()), 1, A(1)); 5204 assert(&b2); 5205 auto b3 = B(SysTime(0, UTC()), 1, A(1)); 5206 assert(&b3); 5207 5208 auto arr = [b2, b3]; 5209 5210 assert(arr[0].j == 1); 5211 assert(arr[1].j == 1); 5212 auto a2 = arr.array(); // << bang, invariant is raised, also if b2 and b3 are good 5213 } 5214 5215 @safe unittest 5216 { 5217 import std.algorithm.comparison : equal; 5218 import std.algorithm.iteration : map; 5219 // Check fix for https://issues.dlang.org/show_bug.cgi?id=2971 5220 assert(equal(map!(to!int)(["42", "34", "345"]), [42, 34, 345])); 5221 } 5222 5223 // Undocumented for the time being 5224 void toTextRange(T, W)(T value, W writer) 5225 if (isIntegral!T && isOutputRange!(W, char)) 5226 { 5227 import core.internal.string : SignedStringBuf, signedToTempString, 5228 UnsignedStringBuf, unsignedToTempString; 5229 5230 if (value < 0) 5231 { 5232 SignedStringBuf buf = void; 5233 put(writer, signedToTempString(value, buf)); 5234 } 5235 else 5236 { 5237 UnsignedStringBuf buf = void; 5238 put(writer, unsignedToTempString(value, buf)); 5239 } 5240 } 5241 5242 @safe unittest 5243 { 5244 import std.array : appender; 5245 auto result = appender!(char[])(); 5246 toTextRange(-1, result); 5247 assert(result.data == "-1"); 5248 } 5249 5250 5251 /** 5252 Returns the corresponding _unsigned value for `x` (e.g. if `x` has type 5253 `int`, it returns $(D cast(uint) x)). The advantage compared to the cast 5254 is that you do not need to rewrite the cast if `x` later changes type 5255 (e.g from `int` to `long`). 5256 5257 Note that the result is always mutable even if the original type was const 5258 or immutable. In order to retain the constness, use $(REF Unsigned, std,traits). 5259 */ 5260 auto unsigned(T)(T x) 5261 if (isIntegral!T) 5262 { 5263 return cast() cast(Unsigned!T) x; 5264 } 5265 5266 /// 5267 @safe unittest 5268 { 5269 import std.traits : Unsigned; 5270 immutable int s = 42; 5271 auto u1 = unsigned(s); //not qualified 5272 static assert(is(typeof(u1) == uint)); 5273 Unsigned!(typeof(s)) u2 = unsigned(s); //same qualification 5274 static assert(is(typeof(u2) == immutable uint)); 5275 immutable u3 = unsigned(s); //explicitly qualified 5276 } 5277 5278 /// Ditto 5279 auto unsigned(T)(T x) 5280 if (isSomeChar!T) 5281 { 5282 // All characters are unsigned 5283 static assert(T.min == 0, T.stringof ~ ".min must be zero"); 5284 return cast() x; 5285 } 5286 5287 @safe unittest 5288 { 5289 static foreach (T; AliasSeq!(byte, ubyte)) 5290 { 5291 static assert(is(typeof(unsigned(cast(T) 1)) == ubyte)); 5292 static assert(is(typeof(unsigned(cast(const T) 1)) == ubyte)); 5293 static assert(is(typeof(unsigned(cast(immutable T) 1)) == ubyte)); 5294 } 5295 5296 static foreach (T; AliasSeq!(short, ushort)) 5297 { 5298 static assert(is(typeof(unsigned(cast(T) 1)) == ushort)); 5299 static assert(is(typeof(unsigned(cast(const T) 1)) == ushort)); 5300 static assert(is(typeof(unsigned(cast(immutable T) 1)) == ushort)); 5301 } 5302 5303 static foreach (T; AliasSeq!(int, uint)) 5304 { 5305 static assert(is(typeof(unsigned(cast(T) 1)) == uint)); 5306 static assert(is(typeof(unsigned(cast(const T) 1)) == uint)); 5307 static assert(is(typeof(unsigned(cast(immutable T) 1)) == uint)); 5308 } 5309 5310 static foreach (T; AliasSeq!(long, ulong)) 5311 { 5312 static assert(is(typeof(unsigned(cast(T) 1)) == ulong)); 5313 static assert(is(typeof(unsigned(cast(const T) 1)) == ulong)); 5314 static assert(is(typeof(unsigned(cast(immutable T) 1)) == ulong)); 5315 } 5316 } 5317 5318 @safe unittest 5319 { 5320 static foreach (T; AliasSeq!(char, wchar, dchar)) 5321 { 5322 static assert(is(typeof(unsigned(cast(T)'A')) == T)); 5323 static assert(is(typeof(unsigned(cast(const T)'A')) == T)); 5324 static assert(is(typeof(unsigned(cast(immutable T)'A')) == T)); 5325 } 5326 } 5327 5328 5329 /** 5330 Returns the corresponding _signed value for `x` (e.g. if `x` has type 5331 `uint`, it returns $(D cast(int) x)). The advantage compared to the cast 5332 is that you do not need to rewrite the cast if `x` later changes type 5333 (e.g from `uint` to `ulong`). 5334 5335 Note that the result is always mutable even if the original type was const 5336 or immutable. In order to retain the constness, use $(REF Signed, std,traits). 5337 */ 5338 auto signed(T)(T x) 5339 if (isIntegral!T) 5340 { 5341 return cast() cast(Signed!T) x; 5342 } 5343 5344 /// 5345 @safe unittest 5346 { 5347 import std.traits : Signed; 5348 5349 immutable uint u = 42; 5350 auto s1 = signed(u); //not qualified 5351 static assert(is(typeof(s1) == int)); 5352 Signed!(typeof(u)) s2 = signed(u); //same qualification 5353 static assert(is(typeof(s2) == immutable int)); 5354 immutable s3 = signed(u); //explicitly qualified 5355 } 5356 5357 @system unittest 5358 { 5359 static foreach (T; AliasSeq!(byte, ubyte)) 5360 { 5361 static assert(is(typeof(signed(cast(T) 1)) == byte)); 5362 static assert(is(typeof(signed(cast(const T) 1)) == byte)); 5363 static assert(is(typeof(signed(cast(immutable T) 1)) == byte)); 5364 } 5365 5366 static foreach (T; AliasSeq!(short, ushort)) 5367 { 5368 static assert(is(typeof(signed(cast(T) 1)) == short)); 5369 static assert(is(typeof(signed(cast(const T) 1)) == short)); 5370 static assert(is(typeof(signed(cast(immutable T) 1)) == short)); 5371 } 5372 5373 static foreach (T; AliasSeq!(int, uint)) 5374 { 5375 static assert(is(typeof(signed(cast(T) 1)) == int)); 5376 static assert(is(typeof(signed(cast(const T) 1)) == int)); 5377 static assert(is(typeof(signed(cast(immutable T) 1)) == int)); 5378 } 5379 5380 static foreach (T; AliasSeq!(long, ulong)) 5381 { 5382 static assert(is(typeof(signed(cast(T) 1)) == long)); 5383 static assert(is(typeof(signed(cast(const T) 1)) == long)); 5384 static assert(is(typeof(signed(cast(immutable T) 1)) == long)); 5385 } 5386 } 5387 5388 // https://issues.dlang.org/show_bug.cgi?id=10874 5389 @safe unittest 5390 { 5391 enum Test { a = 0 } 5392 ulong l = 0; 5393 auto t = l.to!Test; 5394 } 5395 5396 // asOriginalType 5397 /** 5398 Returns the representation of an enumerated value, i.e. the value converted to 5399 the base type of the enumeration. 5400 */ 5401 OriginalType!E asOriginalType(E)(E value) 5402 if (is(E == enum)) 5403 { 5404 return value; 5405 } 5406 5407 /// 5408 @safe unittest 5409 { 5410 enum A { a = 42 } 5411 static assert(is(typeof(A.a.asOriginalType) == int)); 5412 assert(A.a.asOriginalType == 42); 5413 enum B : double { a = 43 } 5414 static assert(is(typeof(B.a.asOriginalType) == double)); 5415 assert(B.a.asOriginalType == 43); 5416 } 5417 5418 /** 5419 A wrapper on top of the built-in cast operator that allows one to restrict 5420 casting of the original type of the value. 5421 5422 A common issue with using a raw cast is that it may silently continue to 5423 compile even if the value's type has changed during refactoring, 5424 which breaks the initial assumption about the cast. 5425 5426 Params: 5427 From = The type to cast from. The programmer must ensure it is legal 5428 to make this cast. 5429 */ 5430 template castFrom(From) 5431 { 5432 /** 5433 Params: 5434 To = The type _to cast _to. 5435 value = The value _to cast. It must be of type `From`, 5436 otherwise a compile-time error is emitted. 5437 5438 Returns: 5439 the value after the cast, returned by reference if possible. 5440 */ 5441 auto ref to(To, T)(auto ref T value) @system 5442 { 5443 static assert( 5444 is(From == T), 5445 "the value to cast is not of specified type '" ~ From.stringof ~ 5446 "', it is of type '" ~ T.stringof ~ "'" 5447 ); 5448 5449 static assert( 5450 is(typeof(cast(To) value)), 5451 "can't cast from '" ~ From.stringof ~ "' to '" ~ To.stringof ~ "'" 5452 ); 5453 5454 return cast(To) value; 5455 } 5456 } 5457 5458 /// 5459 @system unittest 5460 { 5461 // Regular cast, which has been verified to be legal by the programmer: 5462 { 5463 long x; 5464 auto y = cast(int) x; 5465 } 5466 5467 // However this will still compile if 'x' is changed to be a pointer: 5468 { 5469 long* x; 5470 auto y = cast(int) x; 5471 } 5472 5473 // castFrom provides a more reliable alternative to casting: 5474 { 5475 long x; 5476 auto y = castFrom!long.to!int(x); 5477 } 5478 5479 // Changing the type of 'x' will now issue a compiler error, 5480 // allowing bad casts to be caught before it's too late: 5481 { 5482 long* x; 5483 static assert( 5484 !__traits(compiles, castFrom!long.to!int(x)) 5485 ); 5486 5487 // if cast is still needed, must be changed to: 5488 auto y = castFrom!(long*).to!int(x); 5489 } 5490 } 5491 5492 // https://issues.dlang.org/show_bug.cgi?id=16667 5493 @system unittest 5494 { 5495 ubyte[] a = ['a', 'b', 'c']; 5496 assert(castFrom!(ubyte[]).to!(string)(a) == "abc"); 5497 } 5498 5499 /** 5500 Check the correctness of a string for `hexString`. 5501 The result is true if and only if the input string is composed of whitespace 5502 characters (\f\n\r\t\v lineSep paraSep nelSep) and 5503 an even number of hexadecimal digits (regardless of the case). 5504 */ 5505 @safe pure @nogc 5506 private bool isHexLiteral(String)(scope const String hexData) 5507 { 5508 import std.ascii : isHexDigit; 5509 import std.uni : lineSep, paraSep, nelSep; 5510 size_t i; 5511 foreach (const dchar c; hexData) 5512 { 5513 switch (c) 5514 { 5515 case ' ': 5516 case '\t': 5517 case '\v': 5518 case '\f': 5519 case '\r': 5520 case '\n': 5521 case lineSep: 5522 case paraSep: 5523 case nelSep: 5524 continue; 5525 5526 default: 5527 break; 5528 } 5529 if (c.isHexDigit) 5530 ++i; 5531 else 5532 return false; 5533 } 5534 return !(i & 1); 5535 } 5536 5537 @safe unittest 5538 { 5539 // test all the hex digits 5540 static assert( ("0123456789abcdefABCDEF").isHexLiteral); 5541 // empty or white strings are not valid 5542 static assert( "\r\n\t".isHexLiteral); 5543 // but are accepted if the count of hex digits is even 5544 static assert( "A\r\n\tB".isHexLiteral); 5545 } 5546 5547 @safe unittest 5548 { 5549 import std.ascii; 5550 // empty/whites 5551 static assert( "".isHexLiteral); 5552 static assert( " \r".isHexLiteral); 5553 static assert( whitespace.isHexLiteral); 5554 static assert( ""w.isHexLiteral); 5555 static assert( " \r"w.isHexLiteral); 5556 static assert( ""d.isHexLiteral); 5557 static assert( " \r"d.isHexLiteral); 5558 static assert( "\u2028\u2029\u0085"d.isHexLiteral); 5559 // odd x strings 5560 static assert( !("5" ~ whitespace).isHexLiteral); 5561 static assert( !"123".isHexLiteral); 5562 static assert( !"1A3".isHexLiteral); 5563 static assert( !"1 23".isHexLiteral); 5564 static assert( !"\r\n\tC".isHexLiteral); 5565 static assert( !"123"w.isHexLiteral); 5566 static assert( !"1A3"w.isHexLiteral); 5567 static assert( !"1 23"w.isHexLiteral); 5568 static assert( !"\r\n\tC"w.isHexLiteral); 5569 static assert( !"123"d.isHexLiteral); 5570 static assert( !"1A3"d.isHexLiteral); 5571 static assert( !"1 23"d.isHexLiteral); 5572 static assert( !"\r\n\tC"d.isHexLiteral); 5573 // even x strings with invalid charset 5574 static assert( !"12gG".isHexLiteral); 5575 static assert( !"2A 3q".isHexLiteral); 5576 static assert( !"12gG"w.isHexLiteral); 5577 static assert( !"2A 3q"w.isHexLiteral); 5578 static assert( !"12gG"d.isHexLiteral); 5579 static assert( !"2A 3q"d.isHexLiteral); 5580 // valid x strings 5581 static assert( ("5A" ~ whitespace).isHexLiteral); 5582 static assert( ("5A 01A C FF de 1b").isHexLiteral); 5583 static assert( ("0123456789abcdefABCDEF").isHexLiteral); 5584 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF").isHexLiteral); 5585 static assert( ("5A 01A C FF de 1b"w).isHexLiteral); 5586 static assert( ("0123456789abcdefABCDEF"w).isHexLiteral); 5587 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"w).isHexLiteral); 5588 static assert( ("5A 01A C FF de 1b"d).isHexLiteral); 5589 static assert( ("0123456789abcdefABCDEF"d).isHexLiteral); 5590 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"d).isHexLiteral); 5591 // library version allows what's pointed by https://issues.dlang.org/show_bug.cgi?id=10454 5592 static assert( ("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").isHexLiteral); 5593 } 5594 5595 /** 5596 Converts a hex literal to a string at compile time. 5597 5598 Takes a string made of hexadecimal digits and returns 5599 the matching string by converting each pair of digits to a character. 5600 The input string can also include white characters, which can be used 5601 to keep the literal string readable in the source code. 5602 5603 The function is intended to replace the hexadecimal literal strings 5604 starting with `'x'`, which could be removed to simplify the core language. 5605 5606 Params: 5607 hexData = string to be converted. 5608 5609 Returns: 5610 a `string`, a `wstring` or a `dstring`, according to the type of hexData. 5611 5612 See_Also: 5613 Use $(REF fromHexString, std, digest) for run time conversions. 5614 Note, these functions are not drop-in replacements and have different 5615 input requirements. 5616 This template inherits its data syntax from builtin 5617 $(LINK2 $(ROOT_DIR)spec/lex.html#hex_string, hex strings). 5618 See $(REF fromHexString, std, digest) for its own respective requirements. 5619 */ 5620 template hexString(string hexData) 5621 if (hexData.isHexLiteral) 5622 { 5623 enum hexString = mixin(hexToString(hexData)); 5624 } 5625 5626 /// ditto 5627 template hexString(wstring hexData) 5628 if (hexData.isHexLiteral) 5629 { 5630 enum wstring hexString = mixin(hexToString(hexData)); 5631 } 5632 5633 /// ditto 5634 template hexString(dstring hexData) 5635 if (hexData.isHexLiteral) 5636 { 5637 enum dstring hexString = mixin(hexToString(hexData)); 5638 } 5639 5640 /// 5641 @safe unittest 5642 { 5643 // conversion at compile time 5644 auto string1 = hexString!"304A314B"; 5645 assert(string1 == "0J1K"); 5646 auto string2 = hexString!"304A314B"w; 5647 assert(string2 == "0J1K"w); 5648 auto string3 = hexString!"304A314B"d; 5649 assert(string3 == "0J1K"d); 5650 } 5651 5652 @safe nothrow pure private 5653 { 5654 /* These are meant to be used with CTFE. 5655 * They cause the instantiations of hexStrLiteral() 5656 * to be in Phobos, not user code. 5657 */ 5658 string hexToString(string s) 5659 { 5660 return hexStrLiteral(s); 5661 } 5662 5663 wstring hexToString(wstring s) 5664 { 5665 return hexStrLiteral(s); 5666 } 5667 5668 dstring hexToString(dstring s) 5669 { 5670 return hexStrLiteral(s); 5671 } 5672 } 5673 5674 /* 5675 Turn a hexadecimal string into a regular string literal. 5676 I.e. "dead beef" is transformed into "\xde\xad\xbe\xef" 5677 suitable for use in a mixin. 5678 Params: 5679 hexData is string, wstring, or dstring and validated by isHexLiteral() 5680 */ 5681 @trusted nothrow pure 5682 private auto hexStrLiteral(String)(scope String hexData) 5683 { 5684 import std.ascii : isHexDigit; 5685 alias C = Unqual!(ElementEncodingType!String); // char, wchar or dchar 5686 C[] result; 5687 result.length = 1 + hexData.length * 2 + 1; // don't forget the " " 5688 /* Use a pointer because we know it won't overrun, 5689 * and this will reduce the size of the function substantially 5690 * by not doing the array bounds checks. 5691 * This is why this function is @trusted. 5692 */ 5693 auto r = result.ptr; 5694 r[0] = '"'; 5695 size_t cnt = 0; 5696 foreach (c; hexData) 5697 { 5698 if (c.isHexDigit) 5699 { 5700 if ((cnt & 1) == 0) 5701 { 5702 r[1 + cnt] = '\\'; 5703 r[1 + cnt + 1] = 'x'; 5704 cnt += 2; 5705 } 5706 r[1 + cnt] = c; 5707 ++cnt; 5708 } 5709 } 5710 r[1 + cnt] = '"'; 5711 result.length = 1 + cnt + 1; // trim off any excess length 5712 return result; 5713 } 5714 5715 5716 @safe unittest 5717 { 5718 // compile time 5719 assert(hexString!"46 47 48 49 4A 4B" == "FGHIJK"); 5720 assert(hexString!"30\r\n\t\f\v31 32 33 32 31 30" == "0123210"); 5721 assert(hexString!"ab cd" == hexString!"ABCD"); 5722 } 5723 5724 5725 /** 5726 * Convert integer to a range of characters. 5727 * Intended to be lightweight and fast. 5728 * 5729 * Params: 5730 * radix = 2, 8, 10, 16 5731 * Char = character type for output 5732 * letterCase = lower for deadbeef, upper for DEADBEEF 5733 * value = integer to convert. Can be ubyte, ushort, uint or ulong. If radix 5734 * is 10, can also be byte, short, int or long. 5735 * Returns: 5736 * Random access range with slicing and everything 5737 */ 5738 5739 auto toChars(ubyte radix = 10, Char = char, LetterCase letterCase = LetterCase.lower, T)(T value) 5740 pure nothrow @nogc @safe 5741 if ((radix == 2 || radix == 8 || radix == 10 || radix == 16) && 5742 isIntegral!T && (radix == 10 || isUnsigned!T)) 5743 { 5744 alias UT = Unqual!T; 5745 5746 static if (radix == 10) 5747 { 5748 /* uint.max is 42_9496_7295 5749 * int.max is 21_4748_3647 5750 * ulong.max is 1844_6744_0737_0955_1615 5751 * long.max is 922_3372_0368_5477_5807 5752 */ 5753 static struct Result 5754 { 5755 void initialize(UT value) 5756 { 5757 import core.internal.string : signedToTempString, unsignedToTempString; 5758 5759 char[] t = value < 0 5760 ? signedToTempString!(10, false, char)(value, buf) 5761 : unsignedToTempString!(10, false, char)(value, buf); 5762 5763 lwr = cast(uint) (buf.length - t.length); 5764 upr = cast(uint) buf.length; 5765 } 5766 5767 @property size_t length() { return upr - lwr; } 5768 5769 alias opDollar = length; 5770 5771 @property bool empty() { return upr == lwr; } 5772 5773 @property Char front() { return buf[lwr]; } 5774 5775 void popFront() { ++lwr; } 5776 5777 @property Char back() { return buf[upr - 1]; } 5778 5779 void popBack() { --upr; } 5780 5781 @property Result save() { return this; } 5782 5783 Char opIndex(size_t i) { return buf[lwr + i]; } 5784 5785 Result opSlice(size_t lwr, size_t upr) 5786 { 5787 Result result = void; 5788 result.buf = buf; 5789 result.lwr = cast(uint)(this.lwr + lwr); 5790 result.upr = cast(uint)(this.lwr + upr); 5791 return result; 5792 } 5793 5794 private: 5795 uint lwr = void, upr = void; 5796 char[(UT.sizeof == 4) ? 10 + isSigned!T : 20] buf = void; 5797 } 5798 5799 Result result; 5800 result.initialize(value); 5801 return result; 5802 } 5803 else 5804 { 5805 static if (radix == 2) 5806 enum SHIFT = 1; 5807 else static if (radix == 8) 5808 enum SHIFT = 3; 5809 else static if (radix == 16) 5810 enum SHIFT = 4; 5811 else 5812 static assert(false, "radix must be 2, 8, 10, or 16"); 5813 static struct Result 5814 { 5815 this(UT value) 5816 { 5817 this.value = value; 5818 5819 ubyte len = 1; 5820 while (value >>>= SHIFT) 5821 ++len; 5822 this.len = len; 5823 } 5824 5825 @property size_t length() { return len; } 5826 5827 @property bool empty() { return len == 0; } 5828 5829 @property Char front() { return opIndex(0); } 5830 5831 void popFront() { --len; } 5832 5833 @property Char back() { return opIndex(len - 1); } 5834 5835 void popBack() 5836 { 5837 value >>>= SHIFT; 5838 --len; 5839 } 5840 5841 @property Result save() { return this; } 5842 5843 Char opIndex(size_t i) 5844 { 5845 Char c = (value >>> ((len - i - 1) * SHIFT)) & ((1 << SHIFT) - 1); 5846 return cast(Char)((radix < 10 || c < 10) ? c + '0' 5847 : (letterCase == LetterCase.upper ? c + 'A' - 10 5848 : c + 'a' - 10)); 5849 } 5850 5851 Result opSlice(size_t lwr, size_t upr) 5852 { 5853 Result result = void; 5854 result.value = value >>> ((len - upr) * SHIFT); 5855 result.len = cast(ubyte)(upr - lwr); 5856 return result; 5857 } 5858 5859 private: 5860 UT value; 5861 ubyte len; 5862 } 5863 5864 return Result(value); 5865 } 5866 } 5867 5868 /// 5869 @safe unittest 5870 { 5871 import std.algorithm.comparison : equal; 5872 5873 assert(toChars(1).equal("1")); 5874 assert(toChars(1_000_000).equal("1000000")); 5875 5876 assert(toChars!(2)(2U).equal("10")); 5877 assert(toChars!(16)(255U).equal("ff")); 5878 assert(toChars!(16, char, LetterCase.upper)(255U).equal("FF")); 5879 } 5880 5881 5882 @safe unittest 5883 { 5884 import std.array; 5885 import std.range; 5886 5887 assert(toChars(123) == toChars(123)); 5888 5889 { 5890 assert(toChars!2(ubyte(0)).array == "0"); 5891 assert(toChars!2(ushort(0)).array == "0"); 5892 assert(toChars!2(0u).array == "0"); 5893 assert(toChars!2(0Lu).array == "0"); 5894 assert(toChars!2(ubyte(1)).array == "1"); 5895 assert(toChars!2(ushort(1)).array == "1"); 5896 assert(toChars!2(1u).array == "1"); 5897 assert(toChars!2(1Lu).array == "1"); 5898 5899 auto r = toChars!2(2u); 5900 assert(r.length == 2); 5901 assert(r[0] == '1'); 5902 assert(r[1 .. 2].array == "0"); 5903 auto s = r.save; 5904 assert(r.array == "10"); 5905 assert(s.retro.array == "01"); 5906 } 5907 { 5908 assert(toChars!8(ubyte(0)).array == "0"); 5909 assert(toChars!8(ushort(0)).array == "0"); 5910 assert(toChars!8(0u).array == "0"); 5911 assert(toChars!8(0Lu).array == "0"); 5912 assert(toChars!8(1u).array == "1"); 5913 assert(toChars!8(1234567Lu).array == "4553207"); 5914 assert(toChars!8(ubyte.max).array == "377"); 5915 assert(toChars!8(ushort.max).array == "177777"); 5916 5917 auto r = toChars!8(8u); 5918 assert(r.length == 2); 5919 assert(r[0] == '1'); 5920 assert(r[1 .. 2].array == "0"); 5921 auto s = r.save; 5922 assert(r.array == "10"); 5923 assert(s.retro.array == "01"); 5924 } 5925 { 5926 assert(toChars!10(ubyte(0)).array == "0"); 5927 assert(toChars!10(ushort(0)).array == "0"); 5928 assert(toChars!10(0u).array == "0"); 5929 assert(toChars!10(0Lu).array == "0"); 5930 assert(toChars!10(1u).array == "1"); 5931 assert(toChars!10(1234567Lu).array == "1234567"); 5932 assert(toChars!10(ubyte.max).array == "255"); 5933 assert(toChars!10(ushort.max).array == "65535"); 5934 assert(toChars!10(uint.max).array == "4294967295"); 5935 assert(toChars!10(ulong.max).array == "18446744073709551615"); 5936 5937 auto r = toChars(10u); 5938 assert(r.length == 2); 5939 assert(r[0] == '1'); 5940 assert(r[1 .. 2].array == "0"); 5941 auto s = r.save; 5942 assert(r.array == "10"); 5943 assert(s.retro.array == "01"); 5944 } 5945 { 5946 assert(toChars!10(0).array == "0"); 5947 assert(toChars!10(0L).array == "0"); 5948 assert(toChars!10(1).array == "1"); 5949 assert(toChars!10(1234567L).array == "1234567"); 5950 assert(toChars!10(byte.max).array == "127"); 5951 assert(toChars!10(short.max).array == "32767"); 5952 assert(toChars!10(int.max).array == "2147483647"); 5953 assert(toChars!10(long.max).array == "9223372036854775807"); 5954 assert(toChars!10(-byte.max).array == "-127"); 5955 assert(toChars!10(-short.max).array == "-32767"); 5956 assert(toChars!10(-int.max).array == "-2147483647"); 5957 assert(toChars!10(-long.max).array == "-9223372036854775807"); 5958 assert(toChars!10(byte.min).array == "-128"); 5959 assert(toChars!10(short.min).array == "-32768"); 5960 assert(toChars!10(int.min).array == "-2147483648"); 5961 assert(toChars!10(long.min).array == "-9223372036854775808"); 5962 5963 auto r = toChars!10(10); 5964 assert(r.length == 2); 5965 assert(r[0] == '1'); 5966 assert(r[1 .. 2].array == "0"); 5967 auto s = r.save; 5968 assert(r.array == "10"); 5969 assert(s.retro.array == "01"); 5970 } 5971 { 5972 assert(toChars!(16)(0u).array == "0"); 5973 assert(toChars!(16)(0Lu).array == "0"); 5974 assert(toChars!(16)(10u).array == "a"); 5975 assert(toChars!(16, char, LetterCase.upper)(0x12AF34567Lu).array == "12AF34567"); 5976 assert(toChars!(16)(ubyte(0)).array == "0"); 5977 assert(toChars!(16)(ushort(0)).array == "0"); 5978 assert(toChars!(16)(ubyte.max).array == "ff"); 5979 assert(toChars!(16)(ushort.max).array == "ffff"); 5980 5981 auto r = toChars!(16)(16u); 5982 assert(r.length == 2); 5983 assert(r[0] == '1'); 5984 assert(r[1 .. 2].array == "0"); 5985 auto s = r.save; 5986 assert(r.array == "10"); 5987 assert(s.retro.array == "01"); 5988 } 5989 } 5990 5991 @safe unittest // opSlice (https://issues.dlang.org/show_bug.cgi?id=16192) 5992 { 5993 import std.meta : AliasSeq; 5994 5995 static struct Test { ubyte radix; uint number; } 5996 5997 alias tests = AliasSeq!( 5998 Test(2, 0b1_0110_0111u), 5999 Test(2, 0b10_1100_1110u), 6000 Test(8, octal!123456701u), 6001 Test(8, octal!1234567012u), 6002 Test(10, 123456789u), 6003 Test(10, 1234567890u), 6004 Test(16, 0x789ABCDu), 6005 Test(16, 0x789ABCDEu), 6006 ); 6007 6008 foreach (test; tests) 6009 { 6010 enum ubyte radix = test.radix; 6011 auto original = toChars!radix(test.number); 6012 6013 // opSlice vs popFront 6014 auto r = original.save; 6015 size_t i = 0; 6016 for (; !r.empty; r.popFront(), ++i) 6017 { 6018 assert(original[i .. original.length].tupleof == r.tupleof); 6019 // tupleof is used to work around https://issues.dlang.org/show_bug.cgi?id=16216. 6020 } 6021 6022 // opSlice vs popBack 6023 r = original.save; 6024 i = 0; 6025 for (; !r.empty; r.popBack(), ++i) 6026 { 6027 assert(original[0 .. original.length - i].tupleof == r.tupleof); 6028 } 6029 6030 // opSlice vs both popFront and popBack 6031 r = original.save; 6032 i = 0; 6033 for (; r.length >= 2; r.popFront(), r.popBack(), ++i) 6034 { 6035 assert(original[i .. original.length - i].tupleof == r.tupleof); 6036 } 6037 } 6038 } 6039 6040 // Converts an unsigned integer to a compile-time string constant. 6041 package enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length]; 6042 6043 // Check that .stringof does what we expect, since it's not guaranteed by the 6044 // language spec. 6045 @safe /*@betterC*/ unittest 6046 { 6047 assert(toCtString!0 == "0"); 6048 assert(toCtString!123456 == "123456"); 6049 }