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