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