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