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