]> gcc.gnu.org Git - gcc.git/blame - libphobos/src/std/conv.d
d: Merge upstream dmd d579c467c1, phobos 88aa69b14.
[gcc.git] / libphobos / src / std / conv.d
CommitLineData
b4c522fa
IB
1// Written in the D programming language.
2
3/**
4A one-stop shop for converting values from one type to another.
5
6$(SCRIPT inhibitQuickIndex = 1;)
5fee5ec3 7$(DIVC quickindex,
b4c522fa
IB
8$(BOOKTABLE,
9$(TR $(TH Category) $(TH Functions))
10$(TR $(TD Generic) $(TD
11 $(LREF asOriginalType)
12 $(LREF castFrom)
b4c522fa
IB
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))
5fee5ec3 33))
b4c522fa 34
5fee5ec3 35Copyright: Copyright The D Language Foundation 2007-.
b4c522fa
IB
36
37License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
38
39Authors: $(HTTP digitalmars.com, Walter Bright),
40 $(HTTP erdani.org, Andrei Alexandrescu),
41 Shin Fujishiro,
42 Adam D. Ruppe,
43 Kenji Hara
44
5fee5ec3 45Source: $(PHOBOSSRC std/conv.d)
b4c522fa
IB
46
47*/
48module std.conv;
49
50public import std.ascii : LetterCase;
51
52import std.meta;
d7569187 53import std.range;
b4c522fa 54import std.traits;
d7569187 55import std.typecons : Flag, Yes, No, tuple, isTuple;
b4c522fa
IB
56
57// Same as std.string.format, but "self-importing".
58// Helps reduce code and imports, particularly in static asserts.
59// Also helps with missing imports errors.
60package template convFormat()
61{
62 import std.format : format;
63 alias convFormat = format;
64}
65
66/* ************* Exceptions *************** */
67
68/**
69 * Thrown on conversion errors.
70 */
71class ConvException : Exception
72{
73 import std.exception : basicExceptionCtors;
74 ///
75 mixin basicExceptionCtors;
76}
77
5fee5ec3
IB
78///
79@safe unittest
80{
81 import std.exception : assertThrown;
82 assertThrown!ConvException(to!int("abc"));
83}
84
b4c522fa
IB
85private auto convError(S, T)(S source, string fn = __FILE__, size_t ln = __LINE__)
86{
87 string msg;
88
89 if (source.empty)
90 msg = "Unexpected end of input when converting from type " ~ S.stringof ~ " to type " ~ T.stringof;
91 else
92 {
93 ElementType!S el = source.front;
94
95 if (el == '\n')
96 msg = text("Unexpected '\\n' when converting from type " ~ S.stringof ~ " to type " ~ T.stringof);
97 else
98 msg = text("Unexpected '", el,
99 "' when converting from type " ~ S.stringof ~ " to type " ~ T.stringof);
100 }
101
102 return new ConvException(msg, fn, ln);
103}
104
105private auto convError(S, T)(S source, int radix, string fn = __FILE__, size_t ln = __LINE__)
106{
107 string msg;
108
109 if (source.empty)
110 msg = text("Unexpected end of input when converting from type " ~ S.stringof ~ " base ", radix,
111 " to type " ~ T.stringof);
112 else
113 msg = text("Unexpected '", source.front,
114 "' when converting from type " ~ S.stringof ~ " base ", radix,
115 " to type " ~ T.stringof);
116
117 return new ConvException(msg, fn, ln);
118}
119
120@safe pure/* nothrow*/ // lazy parameter bug
121private auto parseError(lazy string msg, string fn = __FILE__, size_t ln = __LINE__)
122{
123 return new ConvException(text("Can't parse string: ", msg), fn, ln);
124}
125
126private void parseCheck(alias source)(dchar c, string fn = __FILE__, size_t ln = __LINE__)
127{
128 if (source.empty)
5fee5ec3 129 throw parseError(text("unexpected end of input when expecting \"", c, "\""));
b4c522fa
IB
130 if (source.front != c)
131 throw parseError(text("\"", c, "\" is missing"), fn, ln);
132 source.popFront();
133}
134
135private
136{
137 T toStr(T, S)(S src)
138 if (isSomeString!T)
139 {
5fee5ec3 140 // workaround for https://issues.dlang.org/show_bug.cgi?id=14198
b4c522fa
IB
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;
5fee5ec3
IB
148 import std.format.spec : FormatSpec;
149 import std.format.write : formatValue;
b4c522fa
IB
150
151 auto w = appender!T();
152 FormatSpec!(ElementEncodingType!T) f;
153 formatValue(w, src, f);
154 return w.data;
155 }
156 }
157
158 template isExactSomeString(T)
159 {
160 enum isExactSomeString = isSomeString!T && !is(T == enum);
161 }
162
163 template isEnumStrToStr(S, T)
164 {
165 enum isEnumStrToStr = isImplicitlyConvertible!(S, T) &&
166 is(S == enum) && isExactSomeString!T;
167 }
168 template isNullToStr(S, T)
169 {
170 enum isNullToStr = isImplicitlyConvertible!(S, T) &&
5fee5ec3 171 (is(immutable S == immutable typeof(null))) && isExactSomeString!T;
b4c522fa
IB
172 }
173}
174
175/**
176 * Thrown on conversion overflow errors.
177 */
178class ConvOverflowException : ConvException
179{
180 @safe pure nothrow
181 this(string s, string fn = __FILE__, size_t ln = __LINE__)
182 {
183 super(s, fn, ln);
184 }
185}
186
5fee5ec3
IB
187///
188@safe unittest
189{
190 import std.exception : assertThrown;
191 assertThrown!ConvOverflowException(to!ubyte(1_000_000));
192}
193
b4c522fa
IB
194/**
195The `to` template converts a value from one type _to another.
196The source type is deduced and the target type must be specified, for example the
197expression `to!int(42.0)` converts the number 42 from
198`double` _to `int`. The conversion is "safe", i.e.,
199it checks for overflow; `to!int(4.2e10)` would throw the
200`ConvOverflowException` exception. Overflow checks are only
201inserted when necessary, e.g., `to!double(42)` does not do
202any checking because any `int` fits in a `double`.
203
204Conversions from string _to numeric types differ from the C equivalents
205`atoi()` and `atol()` by checking for overflow and not allowing whitespace.
206
207For conversion of strings _to signed types, the grammar recognized is:
208$(PRE $(I Integer): $(I Sign UnsignedInteger)
209$(I UnsignedInteger)
210$(I Sign):
211 $(B +)
212 $(B -))
213
214For conversion _to unsigned types, the grammar recognized is:
215$(PRE $(I UnsignedInteger):
216 $(I DecimalDigit)
217 $(I DecimalDigit) $(I UnsignedInteger))
218 */
219template to(T)
220{
221 T to(A...)(A args)
222 if (A.length > 0)
223 {
224 return toImpl!T(args);
225 }
226
227 // Fix issue 6175
228 T to(S)(ref S arg)
229 if (isStaticArray!S)
230 {
231 return toImpl!T(arg);
232 }
233
234 // Fix issue 16108
235 T to(S)(ref S arg)
236 if (isAggregateType!S && !isCopyable!S)
237 {
238 return toImpl!T(arg);
239 }
240}
241
242/**
243 * Converting a value _to its own type (useful mostly for generic code)
244 * simply returns its argument.
245 */
246@safe pure unittest
247{
248 int a = 42;
249 int b = to!int(a);
250 double c = to!double(3.14); // c is double with value 3.14
251}
252
253/**
254 * Converting among numeric types is a safe way _to cast them around.
255 *
256 * Conversions from floating-point types _to integral types allow loss of
257 * precision (the fractional part of a floating-point number). The
258 * conversion is truncating towards zero, the same way a cast would
259 * truncate. (_To round a floating point value when casting _to an
260 * integral, use `roundTo`.)
261 */
262@safe pure unittest
263{
264 import std.exception : assertThrown;
265
266 int a = 420;
267 assert(to!long(a) == a);
268 assertThrown!ConvOverflowException(to!byte(a));
269
270 assert(to!int(4.2e6) == 4200000);
271 assertThrown!ConvOverflowException(to!uint(-3.14));
272 assert(to!uint(3.14) == 3);
273 assert(to!uint(3.99) == 3);
274 assert(to!int(-3.99) == -3);
275}
276
277/**
278 * When converting strings _to numeric types, note that the D hexadecimal and binary
279 * literals are not handled. Neither the prefixes that indicate the base, nor the
280 * horizontal bar used _to separate groups of digits are recognized. This also
281 * applies to the suffixes that indicate the type.
282 *
283 * _To work around this, you can specify a radix for conversions involving numbers.
284 */
285@safe pure unittest
286{
287 auto str = to!string(42, 16);
288 assert(str == "2A");
289 auto i = to!int(str, 16);
290 assert(i == 42);
291}
292
293/**
294 * Conversions from integral types _to floating-point types always
295 * succeed, but might lose accuracy. The largest integers with a
296 * predecessor representable in floating-point format are `2^24-1` for
297 * `float`, `2^53-1` for `double`, and `2^64-1` for `real` (when
298 * `real` is 80-bit, e.g. on Intel machines).
299 */
300@safe pure unittest
301{
302 // 2^24 - 1, largest proper integer representable as float
303 int a = 16_777_215;
304 assert(to!int(to!float(a)) == a);
305 assert(to!int(to!float(-a)) == -a);
306}
307
5fee5ec3
IB
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
b4c522fa
IB
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
5fee5ec3
IB
389 * (`char`, `wchar`, `dchar`) character widths and any
390 * combination of qualifiers (mutable, `const`, or `immutable`).)
b4c522fa 391 * $(LI Converts array (other than strings) _to string.
5fee5ec3 392 * Each element is converted by calling `to!T`.)
b4c522fa 393 * $(LI Associative array _to string conversion.
5fee5ec3
IB
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
b4c522fa 398 * it is defined.)
5fee5ec3 399 * $(LI For structs that do not define `toString`, the conversion _to string
b4c522fa
IB
400 * produces the list of fields.)
401 * $(LI Enumerated types are converted _to strings as their symbolic names.)
5fee5ec3
IB
402 * $(LI Boolean values are converted to `"true"` or `"false"`.)
403 * $(LI `char`, `wchar`, `dchar` _to a string type.)
b4c522fa
IB
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.)
5fee5ec3
IB
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.
b4c522fa
IB
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
5fee5ec3
IB
457// Test `scope` inference of parameters of `text`
458@safe unittest
459{
460 static struct S
461 {
462 int* x; // make S a type with pointers
463 string toString() const scope
464 {
465 return "S";
466 }
467 }
468 scope S s;
469 assert(text("a", s) == "aS");
470}
471
b4c522fa
IB
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;
5fee5ec3 485 static foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
b4c522fa
IB
486 {
487 assertThrown!ConvException(to!T(" 0"));
488 assertThrown!ConvException(to!T(" 0", 8));
489 }
5fee5ec3 490 static foreach (T; AliasSeq!(float, double, real))
b4c522fa
IB
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
5fee5ec3
IB
507// https://issues.dlang.org/show_bug.cgi?id=20623
508@safe pure nothrow unittest
509{
510 // static class C
511 // {
512 // override string toString() const
513 // {
514 // return "C()";
515 // }
516 // }
517
518 static struct S
519 {
520 bool b;
521 int i;
522 float f;
523 int[] a;
524 int[int] aa;
525 S* p;
526 // C c; // TODO: Fails because of hasToString
527
528 void fun() inout
529 {
530 static foreach (const idx; 0 .. this.tupleof.length)
531 {
532 {
533 const _ = this.tupleof[idx].to!string();
534 }
535 }
536 }
537 }
538}
539
b4c522fa
IB
540/**
541If the source type is implicitly convertible to the target type, $(D
542to) simply performs the implicit conversion.
543 */
544private T toImpl(T, S)(S value)
545if (isImplicitlyConvertible!(S, T) &&
546 !isEnumStrToStr!(S, T) && !isNullToStr!(S, T))
547{
548 template isSignedInt(T)
549 {
550 enum isSignedInt = isIntegral!T && isSigned!T;
551 }
552 alias isUnsignedInt = isUnsigned;
553
554 // Conversion from integer to integer, and changing its sign
555 static if (isUnsignedInt!S && isSignedInt!T && S.sizeof == T.sizeof)
556 { // unsigned to signed & same size
557 import std.exception : enforce;
558 enforce(value <= cast(S) T.max,
559 new ConvOverflowException("Conversion positive overflow"));
560 }
561 else static if (isSignedInt!S && isUnsignedInt!T)
562 { // signed to unsigned
563 import std.exception : enforce;
564 enforce(0 <= value,
565 new ConvOverflowException("Conversion negative overflow"));
566 }
567
568 return value;
569}
570
5fee5ec3 571// https://issues.dlang.org/show_bug.cgi?id=9523: Allow identity enum conversion
b4c522fa
IB
572@safe pure nothrow unittest
573{
5fee5ec3 574 enum E { a }
b4c522fa
IB
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
5fee5ec3 586// https://issues.dlang.org/show_bug.cgi?id=6377
b4c522fa
IB
587@safe pure unittest
588{
589 import std.exception;
590 // Conversion between same size
5fee5ec3
IB
591 static foreach (S; AliasSeq!(byte, short, int, long))
592 {{
b4c522fa
IB
593 alias U = Unsigned!S;
594
5fee5ec3
IB
595 static foreach (Sint; AliasSeq!(S, const S, immutable S))
596 static foreach (Uint; AliasSeq!(U, const U, immutable U))
597 {{
b4c522fa
IB
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));
5fee5ec3
IB
607 }}
608 }}
b4c522fa
IB
609
610 // Conversion between different size
5fee5ec3
IB
611 static foreach (i, S1; AliasSeq!(byte, short, int, long))
612 static foreach ( S2; AliasSeq!(byte, short, int, long)[i+1..$])
613 {{
b4c522fa
IB
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
5fee5ec3
IB
620 static foreach (Uint; AliasSeq!(U1, const U1, immutable U1))
621 static foreach (Sint; AliasSeq!(S2, const S2, immutable S2))
622 {{
b4c522fa
IB
623 Uint un = Uint.max;
624 assertNotThrown(to!Sint(un));
625 assert(to!Sint(un) == un);
5fee5ec3 626 }}
b4c522fa
IB
627
628 // big unsigned to small signed
5fee5ec3
IB
629 static foreach (Uint; AliasSeq!(U2, const U2, immutable U2))
630 static foreach (Sint; AliasSeq!(S1, const S1, immutable S1))
631 {{
b4c522fa
IB
632 Uint un = Uint.max;
633 assertThrown(to!Sint(un));
5fee5ec3 634 }}
b4c522fa
IB
635
636 static assert(S1.sizeof < U2.sizeof);
637
638 // small signed to big unsigned
5fee5ec3
IB
639 static foreach (Sint; AliasSeq!(S1, const S1, immutable S1))
640 static foreach (Uint; AliasSeq!(U2, const U2, immutable U2))
641 {{
b4c522fa
IB
642 Sint sn = -1;
643 assertThrown!ConvOverflowException(to!Uint(sn));
5fee5ec3 644 }}
b4c522fa
IB
645
646 // big signed to small unsigned
5fee5ec3
IB
647 static foreach (Sint; AliasSeq!(S2, const S2, immutable S2))
648 static foreach (Uint; AliasSeq!(U1, const U1, immutable U1))
649 {{
b4c522fa
IB
650 Sint sn = -1;
651 assertThrown!ConvOverflowException(to!Uint(sn));
5fee5ec3
IB
652 }}
653 }}
b4c522fa
IB
654}
655
d7569187
IB
656// https://issues.dlang.org/show_bug.cgi?id=13551
657private T toImpl(T, S)(S value)
658if (isTuple!T)
659{
660 T t;
661 static foreach (i; 0 .. T.length)
662 {
663 t[i] = value[i].to!(typeof(T[i]));
664 }
665 return t;
666}
667
668@safe unittest
669{
670 import std.typecons : Tuple;
671
672 auto test = ["10", "20", "30"];
673 assert(test.to!(Tuple!(int, int, int)) == Tuple!(int, int, int)(10, 20, 30));
674
675 auto test1 = [1, 2];
676 assert(test1.to!(Tuple!(int, int)) == Tuple!(int, int)(1, 2));
677
678 auto test2 = [1.0, 2.0, 3.0];
679 assert(test2.to!(Tuple!(int, int, int)) == Tuple!(int, int, int)(1, 2, 3));
680}
681
b4c522fa
IB
682/*
683 Converting static arrays forwards to their dynamic counterparts.
684 */
685private T toImpl(T, S)(ref S s)
686if (isStaticArray!S)
687{
688 return toImpl!(T, typeof(s[0])[])(s);
689}
690
691@safe pure nothrow unittest
692{
693 char[4] test = ['a', 'b', 'c', 'd'];
694 static assert(!isInputRange!(Unqual!(char[4])));
695 assert(to!string(test) == test);
696}
697
698/**
699When source type supports member template function opCast, it is used.
700*/
701private T toImpl(T, S)(S value)
702if (!isImplicitlyConvertible!(S, T) &&
703 is(typeof(S.init.opCast!T()) : T) &&
704 !isExactSomeString!T &&
705 !is(typeof(T(value))))
706{
707 return value.opCast!T();
708}
709
710@safe pure unittest
711{
712 static struct Test
713 {
714 struct T
715 {
716 this(S s) @safe pure { }
717 }
718 struct S
719 {
720 T opCast(U)() @safe pure { assert(false); }
721 }
722 }
723 cast(void) to!(Test.T)(Test.S());
724
725 // make sure std.conv.to is doing the same thing as initialization
726 Test.S s;
727 Test.T t = s;
728}
729
730@safe pure unittest
731{
732 class B
733 {
734 T opCast(T)() { return 43; }
735 }
736 auto b = new B;
737 assert(to!int(b) == 43);
738
739 struct S
740 {
741 T opCast(T)() { return 43; }
742 }
743 auto s = S();
744 assert(to!int(s) == 43);
745}
746
747/**
748When target type supports 'converting construction', it is used.
5fee5ec3 749$(UL $(LI If target type is struct, `T(value)` is used.)
b4c522fa
IB
750 $(LI If target type is class, $(D new T(value)) is used.))
751*/
752private T toImpl(T, S)(S value)
753if (!isImplicitlyConvertible!(S, T) &&
754 is(T == struct) && is(typeof(T(value))))
755{
756 return T(value);
757}
758
5fee5ec3 759// https://issues.dlang.org/show_bug.cgi?id=3961
b4c522fa
IB
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
5fee5ec3 788// https://issues.dlang.org/show_bug.cgi?id=6808
b4c522fa
IB
789@safe pure unittest
790{
791 static struct FakeBigInt
792 {
793 this(string s) @safe pure {}
794 }
795
796 string s = "101";
797 auto i3 = to!FakeBigInt(s);
798}
799
800/// ditto
801private T toImpl(T, S)(S value)
802if (!isImplicitlyConvertible!(S, T) &&
803 is(T == class) && is(typeof(new T(value))))
804{
805 return new T(value);
806}
807
808@safe pure unittest
809{
810 static struct S
811 {
812 int x;
813 }
814 static class C
815 {
816 int x;
817 this(int x) @safe pure { this.x = x; }
818 }
819
820 static class B
821 {
822 int value;
823 this(S src) @safe pure { value = src.x; }
824 this(C src) @safe pure { value = src.x; }
825 }
826
827 S s = S(1);
828 auto b1 = to!B(s); // == new B(s)
829 assert(b1.value == 1);
830
831 C c = new C(2);
832 auto b2 = to!B(c); // == new B(c)
833 assert(b2.value == 2);
834
835 auto c2 = to!C(3); // == new C(3)
836 assert(c2.x == 3);
837}
838
839@safe pure unittest
840{
841 struct S
842 {
843 class A
844 {
845 this(B b) @safe pure {}
846 }
847 class B : A
848 {
849 this() @safe pure { super(this); }
850 }
851 }
852
853 S.B b = new S.B();
854 S.A a = to!(S.A)(b); // == cast(S.A) b
855 // (do not run construction conversion like new S.A(b))
856 assert(b is a);
857
858 static class C : Object
859 {
860 this() @safe pure {}
861 this(Object o) @safe pure {}
862 }
863
864 Object oc = new C();
865 C a2 = to!C(oc); // == new C(a)
866 // Construction conversion overrides down-casting conversion
867 assert(a2 !is a); //
868}
869
870/**
871Object-to-object conversions by dynamic casting throw exception when the source is
872non-null and the target is null.
873 */
874private T toImpl(T, S)(S value)
875if (!isImplicitlyConvertible!(S, T) &&
876 (is(S == class) || is(S == interface)) && !is(typeof(value.opCast!T()) : T) &&
877 (is(T == class) || is(T == interface)) && !is(typeof(new T(value))))
878{
879 static if (is(T == immutable))
880 {
881 // immutable <- immutable
882 enum isModConvertible = is(S == immutable);
883 }
884 else static if (is(T == const))
885 {
886 static if (is(T == shared))
887 {
888 // shared const <- shared
889 // shared const <- shared const
890 // shared const <- immutable
891 enum isModConvertible = is(S == shared) || is(S == immutable);
892 }
893 else
894 {
895 // const <- mutable
896 // const <- immutable
897 enum isModConvertible = !is(S == shared);
898 }
899 }
900 else
901 {
902 static if (is(T == shared))
903 {
904 // shared <- shared mutable
905 enum isModConvertible = is(S == shared) && !is(S == const);
906 }
907 else
908 {
909 // (mutable) <- (mutable)
910 enum isModConvertible = is(Unqual!S == S);
911 }
912 }
913 static assert(isModConvertible, "Bad modifier conversion: "~S.stringof~" to "~T.stringof);
914
915 auto result = ()@trusted{ return cast(T) value; }();
916 if (!result && value)
917 {
918 throw new ConvException("Cannot convert object of static type "
919 ~S.classinfo.name~" and dynamic type "~value.classinfo.name
920 ~" to type "~T.classinfo.name);
921 }
922 return result;
923}
924
925// Unittest for 6288
926@safe pure unittest
927{
928 import std.exception;
929
930 alias Identity(T) = T;
931 alias toConst(T) = const T;
932 alias toShared(T) = shared T;
933 alias toSharedConst(T) = shared const T;
934 alias toImmutable(T) = immutable T;
935 template AddModifier(int n)
936 if (0 <= n && n < 5)
937 {
938 static if (n == 0) alias AddModifier = Identity;
939 else static if (n == 1) alias AddModifier = toConst;
940 else static if (n == 2) alias AddModifier = toShared;
941 else static if (n == 3) alias AddModifier = toSharedConst;
942 else static if (n == 4) alias AddModifier = toImmutable;
943 }
944
945 interface I {}
946 interface J {}
947
948 class A {}
949 class B : A {}
950 class C : B, I, J {}
951 class D : I {}
952
5fee5ec3
IB
953 static foreach (m1; 0 .. 5) // enumerate modifiers
954 static foreach (m2; 0 .. 5) // ditto
955 {{
b4c522fa
IB
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 }
5fee5ec3 989 }}
b4c522fa
IB
990}
991
992/**
993Handles type _to string conversions
994*/
995private T toImpl(T, S)(S value)
996if (!(isImplicitlyConvertible!(S, T) &&
997 !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) &&
998 !isInfinite!S && isExactSomeString!T)
999{
1000 static if (isExactSomeString!S && value[0].sizeof == ElementEncodingType!T.sizeof)
1001 {
1002 // string-to-string with incompatible qualifier conversion
1003 static if (is(ElementEncodingType!T == immutable))
1004 {
1005 // conversion (mutable|const) -> immutable
1006 return value.idup;
1007 }
1008 else
1009 {
1010 // conversion (immutable|const) -> mutable
1011 return value.dup;
1012 }
1013 }
1014 else static if (isExactSomeString!S)
1015 {
1016 import std.array : appender;
1017 // other string-to-string
1018 //Use Appender directly instead of toStr, which also uses a formatedWrite
1019 auto w = appender!T();
1020 w.put(value);
1021 return w.data;
1022 }
1023 else static if (isIntegral!S && !is(S == enum))
1024 {
1025 // other integral-to-string conversions with default radix
1026 return toImpl!(T, S)(value, 10);
1027 }
1028 else static if (is(S == void[]) || is(S == const(void)[]) || is(S == immutable(void)[]))
1029 {
1030 import core.stdc.string : memcpy;
1031 import std.exception : enforce;
1032 // Converting void array to string
1033 alias Char = Unqual!(ElementEncodingType!T);
1034 auto raw = cast(const(ubyte)[]) value;
1035 enforce(raw.length % Char.sizeof == 0,
1036 new ConvException("Alignment mismatch in converting a "
1037 ~ S.stringof ~ " to a "
1038 ~ T.stringof));
1039 auto result = new Char[raw.length / Char.sizeof];
1040 ()@trusted{ memcpy(result.ptr, value.ptr, value.length); }();
1041 return cast(T) result;
1042 }
1043 else static if (isPointer!S && isSomeChar!(PointerTarget!S))
1044 {
1045 // This is unsafe because we cannot guarantee that the pointer is null terminated.
1046 return () @system {
1047 static if (is(S : const(char)*))
1048 import core.stdc.string : strlen;
1049 else
1050 size_t strlen(S s) nothrow
1051 {
1052 S p = s;
1053 while (*p++) {}
1054 return p-s-1;
1055 }
1056 return toImpl!T(value ? value[0 .. strlen(value)].dup : null);
1057 }();
1058 }
1059 else static if (isSomeString!T && is(S == enum))
1060 {
1061 static if (isSwitchable!(OriginalType!S) && EnumMembers!S.length <= 50)
1062 {
1063 switch (value)
1064 {
1065 foreach (member; NoDuplicates!(EnumMembers!S))
1066 {
1067 case member:
1068 return to!T(enumRep!(immutable(T), S, member));
1069 }
1070 default:
1071 }
1072 }
1073 else
1074 {
1075 foreach (member; EnumMembers!S)
1076 {
1077 if (value == member)
1078 return to!T(enumRep!(immutable(T), S, member));
1079 }
1080 }
1081
1082 import std.array : appender;
5fee5ec3
IB
1083 import std.format.spec : FormatSpec;
1084 import std.format.write : formatValue;
b4c522fa
IB
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
5fee5ec3 1101// https://issues.dlang.org/show_bug.cgi?id=14042
b4c522fa
IB
1102@system unittest
1103{
1104 immutable(char)* ptr = "hello".ptr;
1105 auto result = ptr.to!(char[]);
1106}
5fee5ec3 1107// https://issues.dlang.org/show_bug.cgi?id=8384
b4c522fa
IB
1108@system unittest
1109{
1110 void test1(T)(T lp, string cmp)
1111 {
5fee5ec3 1112 static foreach (e; AliasSeq!(char, wchar, dchar))
b4c522fa
IB
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
5fee5ec3 1125 static foreach (e; AliasSeq!("Hello, world!", "Hello, world!"w, "Hello, world!"d))
b4c522fa
IB
1126 {
1127 test1(e, "Hello, world!");
1128 test1(e.ptr, "Hello, world!");
1129 }
5fee5ec3 1130 static foreach (e; AliasSeq!("", ""w, ""d))
b4c522fa
IB
1131 {
1132 test1(e, "");
1133 test1(e.ptr, "");
1134 }
1135}
1136
1137/*
1138 To string conversion for non copy-able structs
1139 */
1140private T toImpl(T, S)(ref S value)
1141if (!(isImplicitlyConvertible!(S, T) &&
1142 !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) &&
5fee5ec3 1143 !isInfinite!S && isExactSomeString!T && !isCopyable!S && !isStaticArray!S)
b4c522fa
IB
1144{
1145 import std.array : appender;
5fee5ec3
IB
1146 import std.format.spec : FormatSpec;
1147 import std.format.write : formatValue;
b4c522fa
IB
1148
1149 auto w = appender!T();
1150 FormatSpec!(ElementEncodingType!T) f;
1151 formatValue(w, value, f);
1152 return w.data;
1153}
1154
5fee5ec3 1155// https://issues.dlang.org/show_bug.cgi?id=16108
fbdaa581 1156@safe unittest
b4c522fa
IB
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
5fee5ec3
IB
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
b4c522fa 1195/*
5fee5ec3 1196 Check whether type `T` can be used in a switch statement.
b4c522fa
IB
1197 This is useful for compile-time generation of switch case statements.
1198*/
1199private template isSwitchable(E)
1200{
1201 enum bool isSwitchable = is(typeof({
1202 switch (E.init) { default: }
1203 }));
1204}
1205
1206//
1207@safe unittest
1208{
1209 static assert(isSwitchable!int);
1210 static assert(!isSwitchable!double);
1211 static assert(!isSwitchable!real);
1212}
1213
1214//Static representation of the index I of the enum S,
1215//In representation T.
1216//T must be an immutable string (avoids un-necessary initializations).
1217private template enumRep(T, S, S value)
1218if (is (T == immutable) && isExactSomeString!T && is(S == enum))
1219{
1220 static T enumRep = toStr!T(value);
1221}
1222
1223@safe pure unittest
1224{
1225 import std.exception;
1226 void dg()
1227 {
1228 // string to string conversion
1229 alias Chars = AliasSeq!(char, wchar, dchar);
1230 foreach (LhsC; Chars)
1231 {
1232 alias LhStrings = AliasSeq!(LhsC[], const(LhsC)[], immutable(LhsC)[]);
1233 foreach (Lhs; LhStrings)
1234 {
1235 foreach (RhsC; Chars)
1236 {
1237 alias RhStrings = AliasSeq!(RhsC[], const(RhsC)[], immutable(RhsC)[]);
1238 foreach (Rhs; RhStrings)
1239 {
1240 Lhs s1 = to!Lhs("wyda");
1241 Rhs s2 = to!Rhs(s1);
1242 //writeln(Lhs.stringof, " -> ", Rhs.stringof);
1243 assert(s1 == to!Lhs(s2));
1244 }
1245 }
1246 }
1247 }
1248
1249 foreach (T; Chars)
1250 {
1251 foreach (U; Chars)
1252 {
1253 T[] s1 = to!(T[])("Hello, world!");
1254 auto s2 = to!(U[])(s1);
1255 assert(s1 == to!(T[])(s2));
1256 auto s3 = to!(const(U)[])(s1);
1257 assert(s1 == to!(T[])(s3));
1258 auto s4 = to!(immutable(U)[])(s1);
1259 assert(s1 == to!(T[])(s4));
1260 }
1261 }
1262 }
1263 dg();
1264 assertCTFEable!dg;
1265}
1266
1267@safe pure unittest
1268{
1269 // Conversion representing bool value with string
1270 bool b;
1271 assert(to!string(b) == "false");
1272 b = true;
1273 assert(to!string(b) == "true");
1274}
1275
1276@safe pure unittest
1277{
1278 // Conversion representing character value with string
1279 alias AllChars =
1280 AliasSeq!( char, const( char), immutable( char),
1281 wchar, const(wchar), immutable(wchar),
1282 dchar, const(dchar), immutable(dchar));
1283 foreach (Char1; AllChars)
1284 {
1285 foreach (Char2; AllChars)
1286 {
1287 Char1 c = 'a';
1288 assert(to!(Char2[])(c)[0] == c);
1289 }
1290 uint x = 4;
1291 assert(to!(Char1[])(x) == "4");
1292 }
1293
1294 string s = "foo";
1295 string s2;
1296 foreach (char c; s)
1297 {
1298 s2 ~= to!string(c);
1299 }
1300 assert(s2 == "foo");
1301}
1302
1303@safe pure nothrow unittest
1304{
1305 import std.exception;
1306 // Conversion representing integer values with string
1307
5fee5ec3 1308 static foreach (Int; AliasSeq!(ubyte, ushort, uint, ulong))
b4c522fa
IB
1309 {
1310 assert(to!string(Int(0)) == "0");
1311 assert(to!string(Int(9)) == "9");
1312 assert(to!string(Int(123)) == "123");
1313 }
1314
5fee5ec3 1315 static foreach (Int; AliasSeq!(byte, short, int, long))
b4c522fa
IB
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
fbdaa581 1344@safe unittest
b4c522fa
IB
1345{
1346 // Conversion representing class object with string
1347 class A
1348 {
fbdaa581 1349 override string toString() @safe const { return "an A"; }
b4c522fa
IB
1350 }
1351 A a;
1352 assert(to!string(a) == "null");
1353 a = new A;
1354 assert(to!string(a) == "an A");
1355
5fee5ec3 1356 // https://issues.dlang.org/show_bug.cgi?id=7660
fbdaa581 1357 class C { override string toString() @safe const { return "C"; } }
b4c522fa
IB
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
5fee5ec3
IB
1396 // base type is signed (https://issues.dlang.org/show_bug.cgi?id=7909)
1397 enum EI : int { a = -1, b = 0, c = 1 }
b4c522fa
IB
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
5fee5ec3 1402 static foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES))
b4c522fa
IB
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
5fee5ec3
IB
1430 static foreach (S; AliasSeq!(string, wstring, dstring, const(char[]), const(wchar[]), const(dchar[])))
1431 {{
b4c522fa
IB
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);
5fee5ec3 1437 }}
b4c522fa 1438
5fee5ec3
IB
1439 static foreach (S; AliasSeq!(char[], wchar[], dchar[]))
1440 {{
b4c522fa
IB
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);
5fee5ec3 1446 }}
b4c522fa
IB
1447}
1448
1449// ditto
1450@trusted pure private T toImpl(T, S)(S value, uint radix, LetterCase letterCase = LetterCase.upper)
1451if (isIntegral!S &&
1452 isExactSomeString!T)
1453in
1454{
5fee5ec3 1455 assert(radix >= 2 && radix <= 36, "radix must be in range [2,36]");
b4c522fa 1456}
5fee5ec3 1457do
b4c522fa
IB
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{
5fee5ec3 1506 static foreach (Int; AliasSeq!(uint, ulong))
b4c522fa
IB
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
5fee5ec3 1516 static foreach (Int; AliasSeq!(int, long))
b4c522fa
IB
1517 {
1518 assert(to!string(Int(-10), 10u) == "-10");
1519 }
1520
1521 assert(to!string(byte(-10), 16) == "F6");
1522 assert(to!string(long.min) == "-9223372036854775808");
1523 assert(to!string(long.max) == "9223372036854775807");
1524}
1525
1526/**
1527Narrowing numeric-numeric conversions throw when the value does not
1528fit in the narrower type.
1529 */
1530private T toImpl(T, S)(S value)
1531if (!isImplicitlyConvertible!(S, T) &&
1532 (isNumeric!S || isSomeChar!S || isBoolean!S) &&
1533 (isNumeric!T || isSomeChar!T || isBoolean!T) && !is(T == enum))
1534{
5fee5ec3
IB
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
b4c522fa
IB
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 {
5fee5ec3
IB
1552 static assert(tSmallest < 0,
1553 "minimum value of T must be smaller than 0");
b4c522fa
IB
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
5fee5ec3
IB
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
b4c522fa
IB
1641/**
1642Array-to-array conversion (except when target is a string type)
5fee5ec3 1643converts each element in turn by using `to`.
b4c522fa 1644 */
6384eff5 1645private T toImpl(T, S)(scope S value)
b4c522fa
IB
1646if (!isImplicitlyConvertible!(S, T) &&
1647 !isSomeString!S && isDynamicArray!S &&
1648 !isExactSomeString!T && isArray!T)
1649{
1650 alias E = typeof(T.init[0]);
1651
1652 static if (isStaticArray!T)
1653 {
1654 import std.exception : enforce;
1655 auto res = to!(E[])(value);
1656 enforce!ConvException(T.length == res.length,
1657 convFormat("Length mismatch when converting to static array: %s vs %s", T.length, res.length));
1658 return res[0 .. T.length];
1659 }
1660 else
1661 {
1662 import std.array : appender;
1663 auto w = appender!(E[])();
1664 w.reserve(value.length);
5fee5ec3 1665 foreach (ref e; value)
b4c522fa
IB
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
5fee5ec3 1690 // Test for https://issues.dlang.org/show_bug.cgi?id=8264
b4c522fa
IB
1691 struct Wrap
1692 {
1693 string wrap;
1694 alias wrap this;
1695 }
1696 Wrap[] warr = to!(Wrap[])(["foo", "bar"]); // should work
1697
5fee5ec3 1698 // https://issues.dlang.org/show_bug.cgi?id=12633
b4c522fa
IB
1699 import std.conv : to;
1700 const s2 = ["10", "20"];
1701
1702 immutable int[2] a3 = s2.to!(int[2]);
1703 assert(a3 == [10, 20]);
1704
1705 // verify length mismatches are caught
1706 immutable s4 = [1, 2, 3, 4];
1707 foreach (i; [1, 4])
1708 {
1709 auto ex = collectException(s4[0 .. i].to!(int[2]));
1710 assert(ex && ex.msg == "Length mismatch when converting to static array: 2 vs " ~ [cast(char)(i + '0')],
1711 ex ? ex.msg : "Exception was not thrown!");
1712 }
1713}
1714
1715@safe unittest
1716{
1717 auto b = [ 1.0f, 2, 3 ];
1718
1719 auto c = to!(string[])(b);
1720 assert(c[0] == "1" && c[1] == "2" && c[2] == "3");
1721}
1722
1723/**
1724Associative array to associative array conversion converts each key
1725and each value in turn.
1726 */
1727private T toImpl(T, S)(S value)
5fee5ec3 1728if (!isImplicitlyConvertible!(S, T) && isAssociativeArray!S &&
b4c522fa
IB
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
fbdaa581 1742 result[to!K2(k1)] = to!(Unqual!V2)(v1);
b4c522fa
IB
1743 }
1744 // Cast back to original type
fbdaa581 1745 return () @trusted { return cast(T) result; }();
b4c522fa
IB
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}
5fee5ec3
IB
1757
1758// https://issues.dlang.org/show_bug.cgi?id=8705, from doc
1759@safe unittest
b4c522fa
IB
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
5fee5ec3 1776@safe unittest
b4c522fa 1777{
5fee5ec3
IB
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));
b4c522fa
IB
1784}
1785
5fee5ec3 1786@safe pure unittest
b4c522fa 1787{
5fee5ec3 1788 static void testIntegralToFloating(Integral, Floating)()
b4c522fa 1789 {
5fee5ec3
IB
1790 Integral a = 42;
1791 auto b = to!Floating(a);
1792 assert(a == b);
1793 assert(a == to!Integral(b));
b4c522fa 1794 }
5fee5ec3 1795 static void testFloatingToIntegral(Floating, Integral)()
b4c522fa 1796 {
5fee5ec3
IB
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
b4c522fa 1836 assert(convFails!(Floating, Integral, ConvOverflowException)(a)
6ac67ddd
IB
1837 || Floating.sizeof <= Integral.sizeof
1838 || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
5fee5ec3
IB
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 }
b4c522fa 1859 }
b4c522fa 1860
b4c522fa
IB
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;
5fee5ec3
IB
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));
b4c522fa
IB
1953 // array test
1954 T[] b = new T[2];
1955 b[0] = 42;
1956 b[1] = 33;
1957 assert(to!string(b) == "[42, 33]");
1958 }
1959 }
1960 // test array to string conversion
1961 foreach (T ; AllNumerics)
1962 {
1963 auto a = [to!T(1), 2, 3];
1964 assert(to!string(a) == "[1, 2, 3]");
1965 }
1966 // test enum to int conversion
1967 enum Testing { Test1, Test2 }
1968 Testing t;
1969 auto a = to!string(t);
1970 assert(a == "Test1");
1971}
1972
1973
1974/**
1975String, or string-like input range, to non-string conversion runs parsing.
1976$(UL
1977 $(LI When the source is a wide string, it is first converted to a narrow
1978 string and then parsed.)
1979 $(LI When the source is a narrow string, normal text parsing occurs.))
1980*/
1981private T toImpl(T, S)(S value)
1982if (isInputRange!S && isSomeChar!(ElementEncodingType!S) &&
5fee5ec3
IB
1983 !isExactSomeString!T && is(typeof(parse!T(value))) &&
1984 // issue 20539
1985 !(is(T == enum) && is(typeof(value == OriginalType!T.init)) && !isSomeString!(OriginalType!T)))
b4c522fa
IB
1986{
1987 scope(success)
1988 {
1989 if (!value.empty)
1990 {
1991 throw convError!(S, T)(value);
1992 }
1993 }
1994 return parse!T(value);
1995}
1996
1997/// ditto
1998private T toImpl(T, S)(S value, uint radix)
d7569187 1999if (isSomeFiniteCharInputRange!S &&
b4c522fa
IB
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{
5fee5ec3
IB
2014 // https://issues.dlang.org/show_bug.cgi?id=6668
2015 // ensure no collaterals thrown
b4c522fa
IB
2016 try { to!uint("-1"); }
2017 catch (ConvException e) { assert(e.next is null); }
2018}
2019
2020@safe pure unittest
2021{
5fee5ec3
IB
2022 static foreach (Str; AliasSeq!(string, wstring, dstring))
2023 {{
b4c522fa
IB
2024 Str a = "123";
2025 assert(to!int(a) == 123);
2026 assert(to!double(a) == 123);
5fee5ec3 2027 }}
b4c522fa 2028
5fee5ec3 2029 // https://issues.dlang.org/show_bug.cgi?id=6255
b4c522fa
IB
2030 auto n = to!int("FF", 16);
2031 assert(n == 255);
2032}
2033
5fee5ec3 2034// https://issues.dlang.org/show_bug.cgi?id=15800
b4c522fa
IB
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
5fee5ec3
IB
2052/**
2053String, or string-like input range, to char type not directly
2054supported by parse parses the first dchar of the source.
2055
2056Returns: the first code point of the input range, converted
2057 to type T.
2058
2059Throws: ConvException if the input range contains more than
2060 a single code point, or if the code point does not
2061 fit into a code unit of type T.
2062*/
2063private T toImpl(T, S)(S value)
2064if (isSomeChar!T && !is(typeof(parse!T(value))) &&
2065 is(typeof(parse!dchar(value))))
2066{
2067 import std.utf : encode;
2068
2069 immutable dchar codepoint = parse!dchar(value);
2070 if (!value.empty)
2071 throw new ConvException(convFormat("Cannot convert \"%s\" to %s because it " ~
2072 "contains more than a single code point.",
2073 value, T.stringof));
2074 T[dchar.sizeof / T.sizeof] decodedCodepoint;
2075 if (encode(decodedCodepoint, codepoint) != 1)
2076 throw new ConvException(convFormat("First code point '%s' of \"%s\" does not fit into a " ~
2077 "single %s code unit", codepoint, value, T.stringof));
2078 return decodedCodepoint[0];
2079}
2080
2081@safe pure unittest
2082{
2083 import std.exception : assertThrown;
2084
2085 assert(toImpl!wchar("a") == 'a');
2086
2087 assert(toImpl!char("a"d) == 'a');
2088 assert(toImpl!char("a"w) == 'a');
2089 assert(toImpl!wchar("a"d) == 'a');
2090
2091 assertThrown!ConvException(toImpl!wchar("ab"));
2092 assertThrown!ConvException(toImpl!char("😃"d));
2093}
2094
b4c522fa
IB
2095/**
2096Convert a value that is implicitly convertible to the enum base type
2097into an Enum value. If the value does not match any enum member values
2098a ConvException is thrown.
2099Enums with floating-point or string base types are not supported.
2100*/
2101private T toImpl(T, S)(S value)
2102if (is(T == enum) && !is(S == enum)
2103 && is(typeof(value == OriginalType!T.init))
2104 && !isFloatingPoint!(OriginalType!T) && !isSomeString!(OriginalType!T))
2105{
2106 foreach (Member; EnumMembers!T)
2107 {
2108 if (Member == value)
2109 return Member;
2110 }
2111 throw new ConvException(convFormat("Value (%s) does not match any member value of enum '%s'", value, T.stringof));
2112}
2113
2114@safe pure unittest
2115{
2116 import std.exception;
2117 enum En8143 : int { A = 10, B = 20, C = 30, D = 20 }
2118 enum En8143[][] m3 = to!(En8143[][])([[10, 30], [30, 10]]);
2119 static assert(m3 == [[En8143.A, En8143.C], [En8143.C, En8143.A]]);
2120
2121 En8143 en1 = to!En8143(10);
2122 assert(en1 == En8143.A);
2123 assertThrown!ConvException(to!En8143(5)); // matches none
2124 En8143[][] m1 = to!(En8143[][])([[10, 30], [30, 10]]);
2125 assert(m1 == [[En8143.A, En8143.C], [En8143.C, En8143.A]]);
2126}
2127
5fee5ec3
IB
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
b4c522fa
IB
2170/***************************************************************
2171 Rounded conversion from floating point to integral.
2172
2173Rounded conversions do not work with non-integral target types.
2174 */
2175
2176template roundTo(Target)
2177{
2178 Target roundTo(Source)(Source value)
2179 {
5fee5ec3
IB
2180 import core.math : abs = fabs;
2181 import std.math.exponential : log2;
2182 import std.math.rounding : trunc;
b4c522fa
IB
2183
2184 static assert(isFloatingPoint!Source);
2185 static assert(isIntegral!Target);
5fee5ec3
IB
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
b4c522fa
IB
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
5fee5ec3 2215 static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint))
b4c522fa
IB
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
5fee5ec3
IB
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
b4c522fa 2258/**
5fee5ec3 2259The `parse` family of functions works quite like the `to`
b4c522fa
IB
2260family, except that:
2261$(OL
2262 $(LI It only works with character ranges as input.)
2263 $(LI It takes the input by reference. (This means that rvalues - such
5fee5ec3 2264 as string literals - are not accepted: use `to` instead.))
b4c522fa
IB
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
5fee5ec3 2268This overload converts a character input range to a `bool`.
b4c522fa
IB
2269
2270Params:
2271 Target = the type to convert to
2272 source = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
5fee5ec3 2273 doCount = the flag for deciding to report the number of consumed characters
b4c522fa
IB
2274
2275Returns:
5fee5ec3
IB
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`))
b4c522fa
IB
2279
2280Throws:
2281 A $(LREF ConvException) if the range does not represent a `bool`.
2282
2283Note:
2284 All character input range conversions using $(LREF to) are forwarded
2285 to `parse` and do not require lvalues.
2286*/
5fee5ec3 2287auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source)
b4c522fa
IB
2288if (isInputRange!Source &&
2289 isSomeChar!(ElementType!Source) &&
5fee5ec3 2290 is(immutable Target == immutable bool))
b4c522fa
IB
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
5fee5ec3
IB
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 }
b4c522fa
IB
2331 }
2332 }
2333Lerr:
2334 throw parseError("bool should be case-insensitive 'true' or 'false'");
2335}
2336
2337///
2338@safe unittest
2339{
5fee5ec3 2340 import std.typecons : Flag, Yes, No;
b4c522fa
IB
2341 auto s = "true";
2342 bool b = parse!bool(s);
2343 assert(b);
5fee5ec3
IB
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);
b4c522fa
IB
2353}
2354
2355@safe unittest
2356{
2357 import std.algorithm.comparison : equal;
2358 import std.exception;
2359 struct InputString
2360 {
2361 string _s;
2362 @property auto front() { return _s.front; }
2363 @property bool empty() { return _s.empty; }
2364 void popFront() { _s.popFront(); }
2365 }
2366
2367 auto s = InputString("trueFALSETrueFalsetRUEfALSE");
2368 assert(parse!bool(s) == true);
2369 assert(s.equal("FALSETrueFalsetRUEfALSE"));
2370 assert(parse!bool(s) == false);
2371 assert(s.equal("TrueFalsetRUEfALSE"));
2372 assert(parse!bool(s) == true);
2373 assert(s.equal("FalsetRUEfALSE"));
2374 assert(parse!bool(s) == false);
2375 assert(s.equal("tRUEfALSE"));
2376 assert(parse!bool(s) == true);
2377 assert(s.equal("fALSE"));
2378 assert(parse!bool(s) == false);
2379 assert(s.empty);
2380
2381 foreach (ss; ["tfalse", "ftrue", "t", "f", "tru", "fals", ""])
2382 {
2383 s = InputString(ss);
2384 assertThrown!ConvException(parse!bool(s));
2385 }
2386}
2387
2388/**
2389Parses a character $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2390to an integral value.
2391
2392Params:
2393 Target = the integral type to convert to
2394 s = the lvalue of an input range
5fee5ec3 2395 doCount = the flag for deciding to report the number of consumed characters
b4c522fa
IB
2396
2397Returns:
5fee5ec3
IB
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`))
b4c522fa
IB
2401
2402Throws:
2403 A $(LREF ConvException) If an overflow occurred during conversion or
2404 if no character of the input was meaningfully converted.
2405*/
c43b5909 2406auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref scope Source s)
b4c522fa
IB
2407if (isSomeChar!(ElementType!Source) &&
2408 isIntegral!Target && !is(Target == enum))
2409{
2410 static if (Target.sizeof < int.sizeof)
2411 {
2412 // smaller types are handled like integers
5fee5ec3
IB
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 }
b4c522fa
IB
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
5fee5ec3
IB
2450 size_t count = 0;
2451
b4c522fa
IB
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 '+':
5fee5ec3 2465 ++count;
b4c522fa
IB
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
5fee5ec3 2484 ++count;
b4c522fa
IB
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);
5fee5ec3 2500 ++count;
b4c522fa
IB
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)
c43b5909 2511 s = s[$-source.length..$];
b4c522fa 2512
5fee5ec3
IB
2513 static if (doCount)
2514 {
2515 return tuple!("data", "count")(v, count);
2516 }
2517 else
2518 {
2519 return v;
2520 }
b4c522fa
IB
2521 }
2522Lerr:
2523 static if (isNarrowString!Source)
2524 throw convError!(Source, Target)(cast(Source) source);
2525 else
2526 throw convError!(Source, Target)(source);
2527 }
2528}
2529
2530///
2531@safe pure unittest
2532{
5fee5ec3 2533 import std.typecons : Flag, Yes, No;
b4c522fa
IB
2534 string s = "123";
2535 auto a = parse!int(s);
2536 assert(a == 123);
2537
5fee5ec3
IB
2538 string s1 = "123";
2539 auto a1 = parse!(int, string, Yes.doCount)(s1);
2540 assert(a1.data == 123 && a1.count == 3);
2541
b4c522fa
IB
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;
5fee5ec3 2550 import std.typecons : Flag, Yes, No;
b4c522fa
IB
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 == "");
5fee5ec3
IB
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
b4c522fa
IB
2571}
2572
2573@safe pure unittest
2574{
5fee5ec3 2575 static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
b4c522fa
IB
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;
5fee5ec3
IB
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
b4c522fa 2696 // parsing error check
5fee5ec3 2697 static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
b4c522fa 2698 {
5fee5ec3
IB
2699 foreach (j, s; errors)
2700 assertThrown!ConvException(to!Int(s));
b4c522fa
IB
2701
2702 // parse!SomeUnsigned cannot parse head sign.
2703 static if (isUnsigned!Int)
2704 {
5fee5ec3 2705 foreach (j, s; unsignedErrors)
b4c522fa
IB
2706 assertThrown!ConvException(to!Int(s));
2707 }
2708 }
2709
5fee5ec3
IB
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 ];
b4c522fa 2721 // positive overflow check
5fee5ec3
IB
2722 static foreach (i, Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
2723 {
2724 foreach (j, s; positiveOverflowErrors[i..$])
b4c522fa
IB
2725 assertThrown!ConvOverflowException(to!Int(s));
2726 }
2727
5fee5ec3
IB
2728 immutable string[] negativeOverflowErrors =
2729 [
2730 "-129", // < byte.min
2731 "-32769", // < short.min
2732 "-2147483649", // < int.min
2733 "-9223372036854775809", // < long.min
2734 ];
b4c522fa 2735 // negative overflow check
5fee5ec3
IB
2736 static foreach (i, Int; AliasSeq!(byte, short, int, long))
2737 {
2738 foreach (j, s; negativeOverflowErrors[i..$])
b4c522fa
IB
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"); });
5fee5ec3
IB
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"); });
b4c522fa
IB
2786}
2787
5fee5ec3 2788// https://issues.dlang.org/show_bug.cgi?id=13931
b4c522fa
IB
2789@safe pure unittest
2790{
2791 import std.exception;
2792
2793 assertThrown!ConvOverflowException("-21474836480".to!int());
2794 assertThrown!ConvOverflowException("-92233720368547758080".to!long());
2795}
2796
5fee5ec3 2797// https://issues.dlang.org/show_bug.cgi?id=14396
b4c522fa
IB
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);
5fee5ec3
IB
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));
b4c522fa
IB
2825}
2826
2827/// ditto
5fee5ec3 2828auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source, uint radix)
b4c522fa
IB
2829if (isSomeChar!(ElementType!Source) &&
2830 isIntegral!Target && !is(Target == enum))
2831in
2832{
5fee5ec3 2833 assert(radix >= 2 && radix <= 36, "radix must be in range [2,36]");
b4c522fa 2834}
5fee5ec3 2835do
b4c522fa
IB
2836{
2837 import core.checkedint : mulu, addu;
2838 import std.exception : enforce;
2839
2840 if (radix == 10)
5fee5ec3
IB
2841 {
2842 return parse!(Target, Source, doCount)(source);
2843 }
b4c522fa
IB
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;
235d5a96 2853 scope s = source.representation;
b4c522fa
IB
2854 }
2855 else
2856 {
2857 alias s = source;
2858 }
2859
5fee5ec3
IB
2860 size_t count = 0;
2861 auto found = false;
b4c522fa
IB
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;
5fee5ec3 2887 ++count;
b4c522fa 2888 s.popFront();
5fee5ec3 2889 found = true;
b4c522fa
IB
2890 } while (!s.empty);
2891
5fee5ec3
IB
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
b4c522fa 2900 static if (isNarrowString!Source)
235d5a96 2901 source = source[$ - s.length .. $];
b4c522fa 2902
5fee5ec3
IB
2903 static if (doCount)
2904 {
2905 return tuple!("data", "count")(v, count);
2906 }
2907 else
2908 {
2909 return v;
2910 }
b4c522fa
IB
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);
5fee5ec3
IB
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));
b4c522fa
IB
2924 }
2925
2926 assert(parse!int(s = "0011001101101", 2) == 0b0011001101101);
2927 assert(parse!int(s = "765", 8) == octal!765);
5fee5ec3 2928 assert(parse!int(s = "000135", 8) == octal!"135");
b4c522fa
IB
2929 assert(parse!int(s = "fCDe", 16) == 0xfcde);
2930
5fee5ec3 2931 // https://issues.dlang.org/show_bug.cgi?id=6609
b4c522fa
IB
2932 assert(parse!int(s = "-42", 10) == -42);
2933
2934 assert(parse!ubyte(s = "ff", 16) == 0xFF);
2935}
2936
5fee5ec3
IB
2937// https://issues.dlang.org/show_bug.cgi?id=7302
2938@safe pure unittest
b4c522fa
IB
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 == '!');
5fee5ec3
IB
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 == '!');
b4c522fa
IB
2950}
2951
5fee5ec3
IB
2952// https://issues.dlang.org/show_bug.cgi?id=13163
2953@safe pure unittest
b4c522fa
IB
2954{
2955 import std.exception;
2956 foreach (s; ["fff", "123"])
2957 assertThrown!ConvOverflowException(s.parse!ubyte(16));
2958}
2959
5fee5ec3
IB
2960// https://issues.dlang.org/show_bug.cgi?id=17282
2961@safe pure unittest
b4c522fa
IB
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);
5fee5ec3
IB
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));
b4c522fa
IB
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
5fee5ec3 2986 * doCount = the flag for deciding to report the number of consumed characters
b4c522fa
IB
2987 *
2988 * Returns:
5fee5ec3
IB
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`))
b4c522fa
IB
2992 *
2993 * Throws:
2994 * A $(LREF ConvException) if type `Target` does not have a member
2995 * represented by `s`.
2996 */
5fee5ec3 2997auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
b4c522fa
IB
2998if (isSomeString!Source && !is(Source == enum) &&
2999 is(Target == enum))
3000{
3001 import std.algorithm.searching : startsWith;
5fee5ec3
IB
3002 import std.traits : Unqual, EnumMembers;
3003
3004 Unqual!Target result;
b4c522fa
IB
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 .. $];
5fee5ec3
IB
3020 static if (doCount)
3021 {
3022 return tuple!("data", "count")(result, longest_match);
3023 }
3024 else
3025 {
3026 return result;
3027 }
b4c522fa
IB
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{
5fee5ec3 3038 import std.typecons : Flag, Yes, No, tuple;
b4c522fa
IB
3039 enum EnumType : bool { a = true, b = false, c = a }
3040
3041 auto str = "a";
3042 assert(parse!EnumType(str) == EnumType.a);
5fee5ec3
IB
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
b4c522fa
IB
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
5fee5ec3 3061 static foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES))
b4c522fa
IB
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
5fee5ec3
IB
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
b4c522fa
IB
3071 assertThrown!ConvException(to!E("d"));
3072 }
3073}
3074
5fee5ec3
IB
3075// https://issues.dlang.org/show_bug.cgi?id=4744
3076@safe pure unittest
b4c522fa
IB
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");
5fee5ec3
IB
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");
b4c522fa
IB
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
5fee5ec3 3096 * doCount = the flag for deciding to report the number of consumed characters
b4c522fa
IB
3097 *
3098 * Returns:
5fee5ec3
IB
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`))
b4c522fa
IB
3103 *
3104 * Throws:
5fee5ec3 3105 * A $(LREF ConvException) if `source` is empty, if no number could be
b4c522fa
IB
3106 * parsed, or if an overflow occurred.
3107 */
235d5a96 3108auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source)
b4c522fa
IB
3109if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) &&
3110 isFloatingPoint!Target && !is(Target == enum))
3111{
b4c522fa
IB
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;
235d5a96 3118 scope p = source.representation;
b4c522fa
IB
3119 }
3120 else
3121 {
3122 alias p = source;
3123 }
3124
235d5a96 3125 void advanceSource()
fbdaa581 3126 {
fbdaa581 3127 static if (isNarrowString!Source)
235d5a96 3128 source = source[$ - p.length .. $];
fbdaa581
IB
3129 }
3130
b4c522fa
IB
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";
5d11bfef 3142 return new ConvException(text(msg, " for input \"", source, "\"."), fn, ln);
b4c522fa
IB
3143 }
3144
b4c522fa
IB
3145 enforce(!p.empty, bailOut());
3146
fbdaa581 3147
5fee5ec3 3148 size_t count = 0;
b4c522fa
IB
3149 bool sign = false;
3150 switch (p.front)
3151 {
3152 case '-':
3153 sign = true;
5fee5ec3 3154 ++count;
b4c522fa
IB
3155 p.popFront();
3156 enforce(!p.empty, bailOut());
3157 if (toLower(p.front) == 'i')
3158 goto case 'i';
b4c522fa
IB
3159 break;
3160 case '+':
5fee5ec3 3161 ++count;
b4c522fa
IB
3162 p.popFront();
3163 enforce(!p.empty, bailOut());
3164 break;
3165 case 'i': case 'I':
5d11bfef 3166 // inf
5fee5ec3 3167 ++count;
b4c522fa 3168 p.popFront();
5d11bfef
IB
3169 enforce(!p.empty && toUpper(p.front) == 'N',
3170 bailOut("error converting input to floating point"));
5fee5ec3 3171 ++count;
5d11bfef
IB
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'
5fee5ec3 3176 ++count;
5d11bfef 3177 p.popFront();
fbdaa581 3178 advanceSource();
5fee5ec3
IB
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 }
b4c522fa
IB
3187 default: {}
3188 }
3189
3190 bool isHex = false;
3191 bool startsWithZero = p.front == '0';
3192 if (startsWithZero)
3193 {
5fee5ec3 3194 ++count;
b4c522fa
IB
3195 p.popFront();
3196 if (p.empty)
3197 {
fbdaa581 3198 advanceSource();
5fee5ec3
IB
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 }
b4c522fa
IB
3207 }
3208
3209 isHex = p.front == 'x' || p.front == 'X';
5fee5ec3
IB
3210 if (isHex)
3211 {
3212 ++count;
3213 p.popFront();
3214 }
b4c522fa 3215 }
5d11bfef
IB
3216 else if (toLower(p.front) == 'n')
3217 {
3218 // nan
5fee5ec3 3219 ++count;
5d11bfef
IB
3220 p.popFront();
3221 enforce(!p.empty && toUpper(p.front) == 'A',
3222 bailOut("error converting input to floating point"));
5fee5ec3 3223 ++count;
5d11bfef
IB
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'
5fee5ec3 3228 ++count;
5d11bfef 3229 p.popFront();
fbdaa581 3230 advanceSource();
5fee5ec3
IB
3231 static if (doCount)
3232 {
3233 return tuple!("data", "count")(Target.nan, count);
3234 }
3235 else
3236 {
3237 return typeof(return).nan;
3238 }
5d11bfef
IB
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 */
b4c522fa
IB
3252
3253 real ldval = 0.0;
3254 char dot = 0; /* if decimal point has been seen */
3255 int exp = 0;
5d11bfef 3256 ulong msdec = 0, lsdec = 0;
b4c522fa 3257 ulong msscale = 1;
5d11bfef 3258 bool sawDigits;
b4c522fa 3259
5d11bfef 3260 enum { hex, decimal }
b4c522fa 3261
5d11bfef
IB
3262 // sets msdec, lsdec/msscale, and sawDigits by parsing the mantissa digits
3263 void parseDigits(alias FloatFormat)()
3264 {
3265 static if (FloatFormat == hex)
b4c522fa 3266 {
5d11bfef
IB
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;
b4c522fa 3278 }
5d11bfef 3279 else static if (FloatFormat == decimal)
b4c522fa 3280 {
5d11bfef
IB
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;
b4c522fa
IB
3288 }
3289 else
5d11bfef 3290 static assert(false, "Unrecognized floating-point format used.");
b4c522fa
IB
3291
3292 while (!p.empty)
3293 {
3294 int i = p.front;
5d11bfef 3295 while (checkDigit(i))
b4c522fa
IB
3296 {
3297 sawDigits = true; /* must have at least 1 digit */
5d11bfef
IB
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)
b4c522fa 3307 {
5d11bfef
IB
3308 lsdec = lsdec * base + i;
3309 msscale *= base;
b4c522fa
IB
3310 }
3311 else
3312 {
5d11bfef 3313 exp += expIter;
b4c522fa
IB
3314 }
3315 exp -= dot;
5fee5ec3 3316 ++count;
b4c522fa
IB
3317 p.popFront();
3318 if (p.empty)
3319 break;
3320 i = p.front;
3321 if (i == '_')
3322 {
5fee5ec3 3323 ++count;
b4c522fa
IB
3324 p.popFront();
3325 if (p.empty)
3326 break;
3327 i = p.front;
3328 }
3329 }
3330 if (i == '.' && !dot)
3331 {
5fee5ec3 3332 ++count;
b4c522fa 3333 p.popFront();
5d11bfef 3334 dot += expIter;
b4c522fa
IB
3335 }
3336 else
b4c522fa 3337 break;
b4c522fa 3338 }
5d11bfef
IB
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"));
b4c522fa 3345 }
5d11bfef
IB
3346
3347 if (isHex)
3348 parseDigits!hex;
3349 else
3350 parseDigits!decimal;
3351
3352 if (isHex || (!p.empty && (p.front == 'e' || p.front == 'E')))
b4c522fa 3353 {
5d11bfef
IB
3354 char sexp = 0;
3355 int e = 0;
b4c522fa 3356
5fee5ec3 3357 ++count;
b4c522fa
IB
3358 p.popFront();
3359 enforce(!p.empty, new ConvException("Unexpected end of input"));
3360 switch (p.front)
3361 {
3362 case '-': sexp++;
3363 goto case;
5fee5ec3
IB
3364 case '+': ++count;
3365 p.popFront();
b4c522fa
IB
3366 break;
3367 default: {}
3368 }
5d11bfef 3369 sawDigits = false;
b4c522fa
IB
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 }
5fee5ec3 3376 ++count;
b4c522fa 3377 p.popFront();
5d11bfef 3378 sawDigits = true;
b4c522fa
IB
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;
5d11bfef
IB
3387 if (isHex)
3388 {
5fee5ec3 3389 import core.math : ldexp;
5d11bfef
IB
3390
3391 // Exponent is power of 2, not power of 10
3392 ldval = ldexp(ldval,exp);
3393 }
3394 else if (ldval)
b4c522fa
IB
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 }
b4c522fa 3421
b6df1132
IB
3422 Target result = cast(Target) (sign ? -ldval : ldval);
3423
5d11bfef 3424 // if overflow occurred
b6df1132
IB
3425 import std.math : isFinite;
3426 enforce(isFinite(result), new ConvException("Range error"));
5d11bfef 3427
fbdaa581 3428 advanceSource();
5fee5ec3
IB
3429 static if (doCount)
3430 {
b6df1132 3431 return tuple!("data", "count")(result, count);
5fee5ec3
IB
3432 }
3433 else
3434 {
b6df1132 3435 return result;
5fee5ec3 3436 }
b4c522fa
IB
3437}
3438
fbdaa581 3439
b4c522fa
IB
3440///
3441@safe unittest
3442{
5fee5ec3
IB
3443 import std.math.operations : isClose;
3444 import std.math.traits : isNaN, isInfinity;
3445 import std.typecons : Flag, Yes, No;
b4c522fa 3446 auto str = "123.456";
5fee5ec3
IB
3447 assert(parse!double(str).isClose(123.456));
3448 auto str2 = "123.456";
3449 assert(parse!(double, string, No.doCount)(str2).isClose(123.456));
3450 auto str3 = "123.456";
3451 auto r = parse!(double, string, Yes.doCount)(str3);
3452 assert(r.data.isClose(123.456));
3453 assert(r.count == 7);
3454 auto str4 = "-123.456";
3455 r = parse!(double, string, Yes.doCount)(str4);
3456 assert(r.data.isClose(-123.456));
3457 assert(r.count == 8);
3458 auto str5 = "+123.456";
3459 r = parse!(double, string, Yes.doCount)(str5);
3460 assert(r.data.isClose(123.456));
3461 assert(r.count == 8);
3462 auto str6 = "inf0";
3463 r = parse!(double, string, Yes.doCount)(str6);
3464 assert(isInfinity(r.data) && r.count == 3 && str6 == "0");
3465 auto str7 = "-0";
3466 auto r2 = parse!(float, string, Yes.doCount)(str7);
3467 assert(r2.data.isClose(0.0) && r2.count == 2);
3468 auto str8 = "nan";
3469 auto r3 = parse!(real, string, Yes.doCount)(str8);
3470 assert(isNaN(r3.data) && r3.count == 3);
b4c522fa
IB
3471}
3472
3473@safe unittest
3474{
3475 import std.exception;
5fee5ec3
IB
3476 import std.math.traits : isNaN, isInfinity;
3477 import std.math.algebraic : fabs;
b4c522fa
IB
3478
3479 // Compare reals with given precision
3480 bool feq(in real rx, in real ry, in real precision = 0.000001L)
3481 {
3482 if (rx == ry)
3483 return 1;
3484
3485 if (isNaN(rx))
3486 return cast(bool) isNaN(ry);
3487
3488 if (isNaN(ry))
3489 return 0;
3490
3491 return cast(bool)(fabs(rx - ry) <= precision);
3492 }
3493
3494 // Make given typed literal
3495 F Literal(F)(F f)
3496 {
3497 return f;
3498 }
3499
5fee5ec3 3500 static foreach (Float; AliasSeq!(float, double, real))
b4c522fa
IB
3501 {
3502 assert(to!Float("123") == Literal!Float(123));
3503 assert(to!Float("+123") == Literal!Float(+123));
3504 assert(to!Float("-123") == Literal!Float(-123));
3505 assert(to!Float("123e2") == Literal!Float(123e2));
3506 assert(to!Float("123e+2") == Literal!Float(123e+2));
5fee5ec3 3507 assert(to!Float("123e-2") == Literal!Float(123e-2L));
b4c522fa
IB
3508 assert(to!Float("123.") == Literal!Float(123.0));
3509 assert(to!Float(".375") == Literal!Float(.375));
3510
3511 assert(to!Float("1.23375E+2") == Literal!Float(1.23375E+2));
3512
3513 assert(to!Float("0") is 0.0);
3514 assert(to!Float("-0") is -0.0);
3515
3516 assert(isNaN(to!Float("nan")));
3517
3518 assertThrown!ConvException(to!Float("\x00"));
3519 }
3520
3521 // min and max
3522 float f = to!float("1.17549e-38");
3523 assert(feq(cast(real) f, cast(real) 1.17549e-38));
3524 assert(feq(cast(real) f, cast(real) float.min_normal));
3525 f = to!float("3.40282e+38");
3526 assert(to!string(f) == to!string(3.40282e+38));
3527
3528 // min and max
3529 double d = to!double("2.22508e-308");
3530 assert(feq(cast(real) d, cast(real) 2.22508e-308));
3531 assert(feq(cast(real) d, cast(real) double.min_normal));
3532 d = to!double("1.79769e+308");
3533 assert(to!string(d) == to!string(1.79769e+308));
3534 assert(to!string(d) == to!string(double.max));
3535
5fee5ec3
IB
3536 auto z = real.max / 2L;
3537 static assert(is(typeof(z) == real));
3538 assert(!isNaN(z));
3539 assert(!isInfinity(z));
3540 string a = to!string(z);
3541 real b = to!real(a);
3542 string c = to!string(b);
3543
3544 assert(c == a, "\n" ~ c ~ "\n" ~ a);
3545
b4c522fa
IB
3546 assert(to!string(to!real(to!string(real.max / 2L))) == to!string(real.max / 2L));
3547
3548 // min and max
3549 real r = to!real(to!string(real.min_normal));
3550 version (NetBSD)
3551 {
3552 // NetBSD notice
3553 // to!string returns 3.3621e-4932L. It is less than real.min_normal and it is subnormal value
3554 // Simple C code
3555 // long double rd = 3.3621e-4932L;
3556 // printf("%Le\n", rd);
3557 // has unexpected result: 1.681050e-4932
3558 //
3559 // Bug report: http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50937
3560 }
3561 else
3562 {
3563 assert(to!string(r) == to!string(real.min_normal));
3564 }
3565 r = to!real(to!string(real.max));
3566 assert(to!string(r) == to!string(real.max));
5fee5ec3
IB
3567
3568 real pi = 3.1415926535897932384626433832795028841971693993751L;
3569 string fullPrecision = "3.1415926535897932384626433832795028841971693993751";
3570 assert(feq(parse!real(fullPrecision), pi, 2*real.epsilon));
3571 string fullPrecision2 = "3.1415926535897932384626433832795028841971693993751";
3572 assert(feq(parse!(real, string, No.doCount)(fullPrecision2), pi, 2*real.epsilon));
3573 string fullPrecision3= "3.1415926535897932384626433832795028841971693993751";
3574 auto len = fullPrecision3.length;
3575 auto res = parse!(real, string, Yes.doCount)(fullPrecision3);
3576 assert(feq(res.data, pi, 2*real.epsilon));
3577 assert(res.count == len);
3578
3579 real x = 0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252L;
3580 string full = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252";
3581 assert(parse!real(full) == x);
3582 string full2 = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252";
3583 assert(parse!(real, string, No.doCount)(full2) == x);
3584 string full3 = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252";
3585 auto len2 = full3.length;
3586 assert(parse!(real, string, Yes.doCount)(full3) == tuple(x, len2));
b4c522fa
IB
3587}
3588
3589// Tests for the double implementation
3590@system unittest
3591{
3592 // @system because strtod is not @safe.
6ac67ddd
IB
3593 import std.math : floatTraits, RealFormat;
3594
3595 static if (floatTraits!real.realFormat == RealFormat.ieeeDouble)
b4c522fa
IB
3596 {
3597 import core.stdc.stdlib, std.exception, std.math;
3598
3599 //Should be parsed exactly: 53 bit mantissa
3600 string s = "0x1A_BCDE_F012_3456p10";
3601 auto x = parse!real(s);
3602 assert(x == 0x1A_BCDE_F012_3456p10L);
3603 //1 bit is implicit
3604 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0xA_BCDE_F012_3456);
3605 assert(strtod("0x1ABCDEF0123456p10", null) == x);
3606
5fee5ec3
IB
3607 s = "0x1A_BCDE_F012_3456p10";
3608 auto len = s.length;
3609 assert(parse!(real, string, Yes.doCount)(s) == tuple(x, len));
3610
b4c522fa
IB
3611 //Should be parsed exactly: 10 bit mantissa
3612 s = "0x3FFp10";
3613 x = parse!real(s);
3614 assert(x == 0x03FFp10);
3615 //1 bit is implicit
3616 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_F800_0000_0000);
3617 assert(strtod("0x3FFp10", null) == x);
3618
3619 //60 bit mantissa, round up
3620 s = "0xFFF_FFFF_FFFF_FFFFp10";
3621 x = parse!real(s);
5fee5ec3 3622 assert(isClose(x, 0xFFF_FFFF_FFFF_FFFFp10));
b4c522fa
IB
3623 //1 bit is implicit
3624 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x0000_0000_0000_0000);
3625 assert(strtod("0xFFFFFFFFFFFFFFFp10", null) == x);
3626
3627 //60 bit mantissa, round down
3628 s = "0xFFF_FFFF_FFFF_FF90p10";
3629 x = parse!real(s);
5fee5ec3 3630 assert(isClose(x, 0xFFF_FFFF_FFFF_FF90p10));
b4c522fa
IB
3631 //1 bit is implicit
3632 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_FFFF_FFFF_FFFF);
3633 assert(strtod("0xFFFFFFFFFFFFF90p10", null) == x);
3634
3635 //61 bit mantissa, round up 2
3636 s = "0x1F0F_FFFF_FFFF_FFFFp10";
3637 x = parse!real(s);
5fee5ec3 3638 assert(isClose(x, 0x1F0F_FFFF_FFFF_FFFFp10));
b4c522fa
IB
3639 //1 bit is implicit
3640 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_1000_0000_0000);
3641 assert(strtod("0x1F0FFFFFFFFFFFFFp10", null) == x);
3642
3643 //61 bit mantissa, round down 2
3644 s = "0x1F0F_FFFF_FFFF_FF10p10";
3645 x = parse!real(s);
5fee5ec3 3646 assert(isClose(x, 0x1F0F_FFFF_FFFF_FF10p10));
b4c522fa
IB
3647 //1 bit is implicit
3648 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_0FFF_FFFF_FFFF);
3649 assert(strtod("0x1F0FFFFFFFFFFF10p10", null) == x);
3650
3651 //Huge exponent
3652 s = "0x1F_FFFF_FFFF_FFFFp900";
3653 x = parse!real(s);
3654 assert(strtod("0x1FFFFFFFFFFFFFp900", null) == x);
3655
3656 //exponent too big -> converror
3657 s = "";
3658 assertThrown!ConvException(x = parse!real(s));
3659 assert(strtod("0x1FFFFFFFFFFFFFp1024", null) == real.infinity);
3660
3661 //-exponent too big -> 0
3662 s = "0x1FFFFFFFFFFFFFp-2000";
3663 x = parse!real(s);
3664 assert(x == 0);
3665 assert(strtod("0x1FFFFFFFFFFFFFp-2000", null) == x);
5fee5ec3
IB
3666
3667 s = "0x1FFFFFFFFFFFFFp-2000";
3668 len = s.length;
3669 assert(parse!(real, string, Yes.doCount)(s) == tuple(x, len));
b4c522fa
IB
3670 }
3671}
3672
3673@system unittest
3674{
3675 import core.stdc.errno;
3676 import core.stdc.stdlib;
5d11bfef 3677 import std.math : floatTraits, RealFormat;
b4c522fa
IB
3678
3679 errno = 0; // In case it was set by another unittest in a different module.
3680 struct longdouble
3681 {
5d11bfef
IB
3682 static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
3683 {
3684 ushort[8] value;
3685 }
6ac67ddd
IB
3686 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended ||
3687 floatTraits!real.realFormat == RealFormat.ieeeExtended53)
b4c522fa
IB
3688 {
3689 ushort[5] value;
3690 }
5d11bfef 3691 else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble)
b4c522fa
IB
3692 {
3693 ushort[4] value;
3694 }
3695 else
3696 static assert(false, "Not implemented");
3697 }
3698
3699 real ld;
3700 longdouble x;
3701 real ld1;
3702 longdouble x1;
3703 int i;
3704
5d11bfef 3705 static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
5fee5ec3 3706 enum s = "0x1.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp-16382";
5d11bfef 3707 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended)
b4c522fa 3708 enum s = "0x1.FFFFFFFFFFFFFFFEp-16382";
6ac67ddd
IB
3709 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended53)
3710 enum s = "0x1.FFFFFFFFFFFFFFFEp-16382";
5d11bfef 3711 else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble)
b4c522fa
IB
3712 enum s = "0x1.FFFFFFFFFFFFFFFEp-1000";
3713 else
3714 static assert(false, "Floating point format for real not supported");
3715
3716 auto s2 = s.idup;
3717 ld = parse!real(s2);
3718 assert(s2.empty);
3719 x = *cast(longdouble *)&ld;
3720
5d11bfef 3721 static if (floatTraits!real.realFormat == RealFormat.ieeeExtended)
b4c522fa
IB
3722 {
3723 version (CRuntime_Microsoft)
3724 ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod
b4c522fa
IB
3725 else
3726 ld1 = strtold(s.ptr, null);
3727 }
6ac67ddd
IB
3728 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended53)
3729 ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold rounds to 53 bits.
b4c522fa
IB
3730 else
3731 ld1 = strtold(s.ptr, null);
3732
3733 x1 = *cast(longdouble *)&ld1;
3734 assert(x1 == x && ld1 == ld);
3735
3736 assert(!errno);
3737
3738 s2 = "1.0e5";
3739 ld = parse!real(s2);
3740 assert(s2.empty);
3741 x = *cast(longdouble *)&ld;
3742 ld1 = strtold("1.0e5", null);
3743 x1 = *cast(longdouble *)&ld1;
3744}
3745
3746@safe pure unittest
3747{
3748 import std.exception;
3749
5fee5ec3 3750 // https://issues.dlang.org/show_bug.cgi?id=4959
b4c522fa
IB
3751 {
3752 auto s = "0 ";
3753 auto x = parse!double(s);
3754 assert(s == " ");
3755 assert(x == 0.0);
3756 }
5fee5ec3
IB
3757 {
3758 auto s = "0 ";
3759 auto x = parse!(double, string, Yes.doCount)(s);
3760 assert(s == " ");
3761 assert(x == tuple(0.0, 1));
3762 }
b4c522fa 3763
5fee5ec3 3764 // https://issues.dlang.org/show_bug.cgi?id=3369
b4c522fa
IB
3765 assert(to!float("inf") == float.infinity);
3766 assert(to!float("-inf") == -float.infinity);
3767
5fee5ec3 3768 // https://issues.dlang.org/show_bug.cgi?id=6160
b4c522fa
IB
3769 assert(6_5.536e3L == to!real("6_5.536e3")); // 2^16
3770 assert(0x1000_000_000_p10 == to!real("0x1000_000_000_p10")); // 7.03687e+13
3771
5fee5ec3 3772 // https://issues.dlang.org/show_bug.cgi?id=6258
b4c522fa
IB
3773 assertThrown!ConvException(to!real("-"));
3774 assertThrown!ConvException(to!real("in"));
3775
5fee5ec3 3776 // https://issues.dlang.org/show_bug.cgi?id=7055
b4c522fa
IB
3777 assertThrown!ConvException(to!float("INF2"));
3778
3779 //extra stress testing
5fee5ec3
IB
3780 auto ssOK = ["1.", "1.1.1", "1.e5", "2e1e", "2a", "2e1_1", "3.4_",
3781 "inf", "-inf", "infa", "-infa", "inf2e2", "-inf2e2",
3782 "nan", "-NAN", "+NaN", "-nAna", "NAn2e2", "-naN2e2"];
3783 auto ssKO = ["", " ", "2e", "2e+", "2e-", "2ee", "2e++1", "2e--1", "2e_1",
3784 "+inf", "-in", "I", "+N", "-NaD", "0x3.F"];
b4c522fa
IB
3785 foreach (s; ssOK)
3786 parse!double(s);
3787 foreach (s; ssKO)
3788 assertThrown!ConvException(parse!double(s));
3789}
3790
b6df1132
IB
3791@safe unittest // https://issues.dlang.org/show_bug.cgi?id=22637
3792{
3793 import std.exception : assertThrown, assertNotThrown;
3794 auto src = "9991232549867999698999493543521458974414359998784641646846435132132543645435456345634541999999999999999"
3795 ~ "9999999943321231321311999231345312413646846354354354399999934153465464654646464654134135354199999999996515734999"
3796 ~ "9999999320135273486741354354731567431324134999999999999999999999999999999999999999999999135411.9";
3797 assertThrown!ConvException(parse!double(src));
3798 static if (real.max_10_exp > 310) assertNotThrown!ConvException(parse!real(src));
3799}
3800
b4c522fa
IB
3801/**
3802Parsing one character off a range returns the first element and calls `popFront`.
3803
3804Params:
3805 Target = the type to convert to
3806 s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
5fee5ec3 3807 doCount = the flag for deciding to report the number of consumed characters
b4c522fa
IB
3808
3809Returns:
5fee5ec3
IB
3810$(UL
3811 $(LI A character of type `Target` if `doCount` is set to `No.doCount`)
3812 $(LI A `tuple` containing a character of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`))
b4c522fa
IB
3813
3814Throws:
3815 A $(LREF ConvException) if the range is empty.
3816 */
5fee5ec3 3817auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
b4c522fa 3818if (isSomeString!Source && !is(Source == enum) &&
5fee5ec3 3819 staticIndexOf!(immutable Target, immutable dchar, immutable ElementEncodingType!Source) >= 0)
b4c522fa
IB
3820{
3821 if (s.empty)
3822 throw convError!(Source, Target)(s);
5fee5ec3 3823 static if (is(immutable Target == immutable dchar))
b4c522fa
IB
3824 {
3825 Target result = s.front;
3826 s.popFront();
5fee5ec3
IB
3827 static if (doCount)
3828 {
3829 return tuple!("data", "count")(result, 1);
3830 }
3831 else
3832 {
3833 return result;
3834 }
3835
b4c522fa
IB
3836 }
3837 else
3838 {
3839 // Special case: okay so parse a Char off a Char[]
3840 Target result = s[0];
3841 s = s[1 .. $];
5fee5ec3
IB
3842 static if (doCount)
3843 {
3844 return tuple!("data", "count")(result, 1);
3845 }
3846 else
3847 {
3848 return result;
3849 }
b4c522fa
IB
3850 }
3851}
3852
3853@safe pure unittest
3854{
5fee5ec3 3855 static foreach (Str; AliasSeq!(string, wstring, dstring))
b4c522fa 3856 {
5fee5ec3
IB
3857 static foreach (Char; AliasSeq!(char, wchar, dchar))
3858 {{
3859 static if (is(immutable Char == immutable dchar) ||
b4c522fa
IB
3860 Char.sizeof == ElementEncodingType!Str.sizeof)
3861 {
3862 Str s = "aaa";
3863 assert(parse!Char(s) == 'a');
3864 assert(s == "aa");
5fee5ec3
IB
3865 assert(parse!(Char, typeof(s), No.doCount)(s) == 'a');
3866 assert(s == "a");
3867 assert(parse!(Char, typeof(s), Yes.doCount)(s) == tuple('a', 1) && s == "");
b4c522fa 3868 }
5fee5ec3 3869 }}
b4c522fa
IB
3870 }
3871}
3872
3873/// ditto
5fee5ec3 3874auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
b4c522fa
IB
3875if (!isSomeString!Source && isInputRange!Source && isSomeChar!(ElementType!Source) &&
3876 isSomeChar!Target && Target.sizeof >= ElementType!Source.sizeof && !is(Target == enum))
3877{
3878 if (s.empty)
3879 throw convError!(Source, Target)(s);
3880 Target result = s.front;
3881 s.popFront();
5fee5ec3
IB
3882 static if (doCount)
3883 {
3884 return tuple!("data", "count")(result, 1);
3885 }
3886 else
3887 {
3888 return result;
3889 }
b4c522fa
IB
3890}
3891
3892///
3893@safe pure unittest
3894{
5fee5ec3 3895 import std.typecons : Flag, Yes, No;
b4c522fa
IB
3896 auto s = "Hello, World!";
3897 char first = parse!char(s);
3898 assert(first == 'H');
3899 assert(s == "ello, World!");
5fee5ec3
IB
3900 char second = parse!(char, string, No.doCount)(s);
3901 assert(second == 'e');
3902 assert(s == "llo, World!");
3903 auto third = parse!(char, string, Yes.doCount)(s);
3904 assert(third.data == 'l' && third.count == 1);
3905 assert(s == "lo, World!");
b4c522fa
IB
3906}
3907
3908
3909/*
3910 Tests for to!bool and parse!bool
3911*/
3912@safe pure unittest
3913{
3914 import std.exception;
3915
3916 assert(to!bool("TruE") == true);
3917 assert(to!bool("faLse"d) == false);
3918 assertThrown!ConvException(to!bool("maybe"));
3919
3920 auto t = "TrueType";
3921 assert(parse!bool(t) == true);
3922 assert(t == "Type");
3923
3924 auto f = "False killer whale"d;
3925 assert(parse!bool(f) == false);
3926 assert(f == " killer whale"d);
3927
5fee5ec3
IB
3928 f = "False killer whale"d;
3929 assert(parse!(bool, dstring, Yes.doCount)(f) == tuple(false, 5));
3930 assert(f == " killer whale"d);
3931
b4c522fa
IB
3932 auto m = "maybe";
3933 assertThrown!ConvException(parse!bool(m));
5fee5ec3 3934 assertThrown!ConvException(parse!(bool, string, Yes.doCount)(m));
b4c522fa
IB
3935 assert(m == "maybe"); // m shouldn't change on failure
3936
3937 auto s = "true";
3938 auto b = parse!(const(bool))(s);
3939 assert(b == true);
3940}
3941
3942/**
3943Parsing a character range to `typeof(null)` returns `null` if the range
3944spells `"null"`. This function is case insensitive.
3945
3946Params:
3947 Target = the type to convert to
3948 s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
5fee5ec3 3949 doCount = the flag for deciding to report the number of consumed characters
b4c522fa
IB
3950
3951Returns:
5fee5ec3
IB
3952$(UL
3953 $(LI `null` if `doCount` is set to `No.doCount`)
3954 $(LI A `tuple` containing `null` and a `size_t` if `doCount` is set to `Yes.doCount`))
b4c522fa
IB
3955
3956Throws:
3957 A $(LREF ConvException) if the range doesn't represent `null`.
3958 */
5fee5ec3 3959auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
b4c522fa
IB
3960if (isInputRange!Source &&
3961 isSomeChar!(ElementType!Source) &&
5fee5ec3 3962 is(immutable Target == immutable typeof(null)))
b4c522fa
IB
3963{
3964 import std.ascii : toLower;
3965 foreach (c; "null")
3966 {
3967 if (s.empty || toLower(s.front) != c)
3968 throw parseError("null should be case-insensitive 'null'");
3969 s.popFront();
3970 }
5fee5ec3
IB
3971 static if (doCount)
3972 {
3973 return tuple!("data", "count")(null, 4);
3974 }
3975 else
3976 {
3977 return null;
3978 }
b4c522fa
IB
3979}
3980
3981///
3982@safe pure unittest
3983{
3984 import std.exception : assertThrown;
5fee5ec3 3985 import std.typecons : Flag, Yes, No;
b4c522fa
IB
3986
3987 alias NullType = typeof(null);
3988 auto s1 = "null";
3989 assert(parse!NullType(s1) is null);
3990 assert(s1 == "");
3991
3992 auto s2 = "NUll"d;
3993 assert(parse!NullType(s2) is null);
3994 assert(s2 == "");
3995
5fee5ec3
IB
3996 auto s3 = "nuLlNULl";
3997 assert(parse!(NullType, string, No.doCount)(s3) is null);
3998 auto r = parse!(NullType, string, Yes.doCount)(s3);
3999 assert(r.data is null && r.count == 4);
4000
b4c522fa
IB
4001 auto m = "maybe";
4002 assertThrown!ConvException(parse!NullType(m));
5fee5ec3 4003 assertThrown!ConvException(parse!(NullType, string, Yes.doCount)(m));
b4c522fa
IB
4004 assert(m == "maybe"); // m shouldn't change on failure
4005
4006 auto s = "NULL";
4007 assert(parse!(const NullType)(s) is null);
4008}
4009
4010//Used internally by parse Array/AA, to remove ascii whites
5fee5ec3 4011package auto skipWS(R, Flag!"doCount" doCount = No.doCount)(ref R r)
b4c522fa
IB
4012{
4013 import std.ascii : isWhite;
4014 static if (isSomeString!R)
4015 {
4016 //Implementation inspired from stripLeft.
4017 foreach (i, c; r)
4018 {
4019 if (!isWhite(c))
4020 {
4021 r = r[i .. $];
5fee5ec3
IB
4022 static if (doCount)
4023 {
4024 return i;
4025 }
4026 else
4027 {
4028 return;
4029 }
b4c522fa
IB
4030 }
4031 }
5fee5ec3 4032 auto len = r.length;
b4c522fa 4033 r = r[0 .. 0]; //Empty string with correct type.
5fee5ec3
IB
4034 static if (doCount)
4035 {
4036 return len;
4037 }
4038 else
4039 {
4040 return;
4041 }
b4c522fa
IB
4042 }
4043 else
4044 {
5fee5ec3
IB
4045 size_t i = 0;
4046 for (; !r.empty && isWhite(r.front); r.popFront(), ++i)
4047 { }
4048 static if (doCount)
4049 {
4050 return i;
4051 }
b4c522fa
IB
4052 }
4053}
4054
4055/**
4056 * Parses an array from a string given the left bracket (default $(D
5fee5ec3
IB
4057 * '[')), right bracket (default `']'`), and element separator (by
4058 * default `','`). A trailing separator is allowed.
b4c522fa
IB
4059 *
4060 * Params:
4061 * s = The string to parse
4062 * lbracket = the character that starts the array
4063 * rbracket = the character that ends the array
4064 * comma = the character that separates the elements of the array
5fee5ec3 4065 * doCount = the flag for deciding to report the number of consumed characters
b4c522fa
IB
4066 *
4067 * Returns:
5fee5ec3
IB
4068 $(UL
4069 * $(LI An array of type `Target` if `doCount` is set to `No.doCount`)
4070 * $(LI A `tuple` containing an array of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`))
b4c522fa 4071 */
5fee5ec3
IB
4072auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[',
4073 dchar rbracket = ']', dchar comma = ',')
b4c522fa
IB
4074if (isSomeString!Source && !is(Source == enum) &&
4075 isDynamicArray!Target && !is(Target == enum))
4076{
4077 import std.array : appender;
4078
4079 auto result = appender!Target();
4080
4081 parseCheck!s(lbracket);
5fee5ec3 4082 size_t count = 1 + skipWS!(Source, Yes.doCount)(s);
b4c522fa
IB
4083 if (s.empty)
4084 throw convError!(Source, Target)(s);
4085 if (s.front == rbracket)
4086 {
4087 s.popFront();
5fee5ec3
IB
4088 static if (doCount)
4089 {
4090 return tuple!("data", "count")(result.data, ++count);
4091 }
4092 else
4093 {
4094 return result.data;
4095 }
b4c522fa 4096 }
5fee5ec3 4097 for (;; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s))
b4c522fa
IB
4098 {
4099 if (!s.empty && s.front == rbracket)
4100 break;
5fee5ec3
IB
4101 auto r = parseElement!(WideElementType!Target, Source, Yes.doCount)(s);
4102 result ~= r.data;
4103 count += r.count + skipWS!(Source, Yes.doCount)(s);
b4c522fa
IB
4104 if (s.empty)
4105 throw convError!(Source, Target)(s);
4106 if (s.front != comma)
4107 break;
4108 }
4109 parseCheck!s(rbracket);
5fee5ec3
IB
4110 static if (doCount)
4111 {
4112 return tuple!("data", "count")(result.data, ++count);
4113 }
4114 else
4115 {
4116 return result.data;
4117 }
b4c522fa
IB
4118}
4119
4120///
4121@safe pure unittest
4122{
5fee5ec3 4123 import std.typecons : Flag, Yes, No;
b4c522fa
IB
4124 auto s1 = `[['h', 'e', 'l', 'l', 'o'], "world"]`;
4125 auto a1 = parse!(string[])(s1);
4126 assert(a1 == ["hello", "world"]);
4127
4128 auto s2 = `["aaa", "bbb", "ccc"]`;
4129 auto a2 = parse!(string[])(s2);
4130 assert(a2 == ["aaa", "bbb", "ccc"]);
5fee5ec3
IB
4131
4132 auto s3 = `[['h', 'e', 'l', 'l', 'o'], "world"]`;
4133 auto len3 = s3.length;
4134 auto a3 = parse!(string[], string, Yes.doCount)(s3);
4135 assert(a3.data == ["hello", "world"]);
4136 assert(a3.count == len3);
b4c522fa
IB
4137}
4138
5fee5ec3
IB
4139// https://issues.dlang.org/show_bug.cgi?id=9615
4140@safe unittest
b4c522fa 4141{
5fee5ec3 4142 import std.typecons : Flag, Yes, No, tuple;
b4c522fa
IB
4143 string s0 = "[1,2, ]";
4144 string s1 = "[1,2, \t\v\r\n]";
4145 string s2 = "[1,2]";
4146 assert(s0.parse!(int[]) == [1,2]);
4147 assert(s1.parse!(int[]) == [1,2]);
4148 assert(s2.parse!(int[]) == [1,2]);
4149
5fee5ec3
IB
4150 s0 = "[1,2, ]";
4151 auto len0 = s0.length;
4152 s1 = "[1,2, \t\v\r\n]";
4153 auto len1 = s1.length;
4154 s2 = "[1,2]";
4155 auto len2 = s2.length;
4156 assert(s0.parse!(int[], string, Yes.doCount) == tuple([1,2], len0));
4157 assert(s1.parse!(int[], string, Yes.doCount) == tuple([1,2], len1));
4158 assert(s2.parse!(int[], string, Yes.doCount) == tuple([1,2], len2));
4159
b4c522fa
IB
4160 string s3 = `["a","b",]`;
4161 string s4 = `["a","b"]`;
4162 assert(s3.parse!(string[]) == ["a","b"]);
4163 assert(s4.parse!(string[]) == ["a","b"]);
4164
5fee5ec3
IB
4165 s3 = `["a","b",]`;
4166 auto len3 = s3.length;
4167 assert(s3.parse!(string[], string, Yes.doCount) == tuple(["a","b"], len3));
4168
4169 s3 = `[ ]`;
4170 assert(tuple([], s3.length) == s3.parse!(string[], string, Yes.doCount));
4171
b4c522fa
IB
4172 import std.exception : assertThrown;
4173 string s5 = "[,]";
4174 string s6 = "[, \t,]";
4175 assertThrown!ConvException(parse!(string[])(s5));
4176 assertThrown!ConvException(parse!(int[])(s6));
5fee5ec3
IB
4177
4178 s5 = "[,]";
4179 s6 = "[,·\t,]";
4180 assertThrown!ConvException(parse!(string[], string, Yes.doCount)(s5));
4181 assertThrown!ConvException(parse!(string[], string, Yes.doCount)(s6));
b4c522fa
IB
4182}
4183
4184@safe unittest
4185{
4186 int[] a = [1, 2, 3, 4, 5];
4187 auto s = to!string(a);
4188 assert(to!(int[])(s) == a);
4189}
4190
4191@safe unittest
4192{
4193 int[][] a = [ [1, 2] , [3], [4, 5] ];
4194 auto s = to!string(a);
4195 assert(to!(int[][])(s) == a);
4196}
4197
4198@safe unittest
4199{
4200 int[][][] ia = [ [[1,2],[3,4],[5]] , [[6],[],[7,8,9]] , [[]] ];
4201
4202 char[] s = to!(char[])(ia);
4203 int[][][] ia2;
4204
4205 ia2 = to!(typeof(ia2))(s);
4206 assert( ia == ia2);
4207}
4208
4209@safe pure unittest
4210{
4211 import std.exception;
5fee5ec3 4212 import std.typecons : Flag, Yes, No;
b4c522fa
IB
4213
4214 //Check proper failure
4215 auto s = "[ 1 , 2 , 3 ]";
5fee5ec3 4216 auto s2 = s.save;
b4c522fa
IB
4217 foreach (i ; 0 .. s.length-1)
4218 {
4219 auto ss = s[0 .. i];
4220 assertThrown!ConvException(parse!(int[])(ss));
5fee5ec3 4221 assertThrown!ConvException(parse!(int[], string, Yes.doCount)(ss));
b4c522fa
IB
4222 }
4223 int[] arr = parse!(int[])(s);
5fee5ec3
IB
4224 auto arr2 = parse!(int[], string, Yes.doCount)(s2);
4225 arr = arr2.data;
b4c522fa
IB
4226}
4227
4228@safe pure unittest
4229{
4230 //Checks parsing of strings with escaped characters
4231 string s1 = `[
4232 "Contains a\0null!",
4233 "tab\there",
4234 "line\nbreak",
4235 "backslash \\ slash / question \?",
4236 "number \x35 five",
4237 "unicode \u65E5 sun",
4238 "very long \U000065E5 sun"
4239 ]`;
4240
4241 //Note: escaped characters purposefully replaced and isolated to guarantee
4242 //there are no typos in the escape syntax
4243 string[] s2 = [
4244 "Contains a" ~ '\0' ~ "null!",
4245 "tab" ~ '\t' ~ "here",
4246 "line" ~ '\n' ~ "break",
4247 "backslash " ~ '\\' ~ " slash / question ?",
4248 "number 5 five",
4249 "unicode 日 sun",
4250 "very long 日 sun"
4251 ];
5fee5ec3 4252 string s3 = s1.save;
b4c522fa
IB
4253 assert(s2 == parse!(string[])(s1));
4254 assert(s1.empty);
5fee5ec3 4255 assert(tuple(s2, s3.length) == parse!(string[], string, Yes.doCount)(s3));
b4c522fa
IB
4256}
4257
4258/// ditto
5fee5ec3
IB
4259auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[',
4260 dchar rbracket = ']', dchar comma = ',')
b4c522fa
IB
4261if (isExactSomeString!Source &&
4262 isStaticArray!Target && !is(Target == enum))
4263{
4264 static if (hasIndirections!Target)
4265 Target result = Target.init[0].init;
4266 else
4267 Target result = void;
4268
4269 parseCheck!s(lbracket);
5fee5ec3 4270 size_t count = 1 + skipWS!(Source, Yes.doCount)(s);
b4c522fa
IB
4271 if (s.empty)
4272 throw convError!(Source, Target)(s);
4273 if (s.front == rbracket)
4274 {
4275 static if (result.length != 0)
4276 goto Lmanyerr;
4277 else
4278 {
4279 s.popFront();
5fee5ec3
IB
4280 static if (doCount)
4281 {
4282 return tuple!("data", "count")(result, ++count);
4283 }
4284 else
4285 {
4286 return result;
4287 }
b4c522fa
IB
4288 }
4289 }
5fee5ec3 4290 for (size_t i = 0; ; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s))
b4c522fa
IB
4291 {
4292 if (i == result.length)
4293 goto Lmanyerr;
5fee5ec3
IB
4294 auto r = parseElement!(ElementType!Target, Source, Yes.doCount)(s);
4295 result[i++] = r.data;
4296 count += r.count + skipWS!(Source, Yes.doCount)(s);
b4c522fa
IB
4297 if (s.empty)
4298 throw convError!(Source, Target)(s);
4299 if (s.front != comma)
4300 {
4301 if (i != result.length)
4302 goto Lfewerr;
4303 break;
4304 }
4305 }
4306 parseCheck!s(rbracket);
5fee5ec3
IB
4307 static if (doCount)
4308 {
4309 return tuple!("data", "count")(result, ++count);
4310 }
4311 else
4312 {
4313 return result;
4314 }
b4c522fa 4315
b4c522fa
IB
4316
4317Lmanyerr:
4318 throw parseError(text("Too many elements in input, ", result.length, " elements expected."));
4319
4320Lfewerr:
4321 throw parseError(text("Too few elements in input, ", result.length, " elements expected."));
4322}
4323
4324@safe pure unittest
4325{
4326 import std.exception;
4327
4328 auto s1 = "[1,2,3,4]";
4329 auto sa1 = parse!(int[4])(s1);
4330 assert(sa1 == [1,2,3,4]);
5fee5ec3
IB
4331 s1 = "[1,2,3,4]";
4332 assert(tuple([1,2,3,4], s1.length) == parse!(int[4], string, Yes.doCount)(s1));
b4c522fa
IB
4333
4334 auto s2 = "[[1],[2,3],[4]]";
4335 auto sa2 = parse!(int[][3])(s2);
4336 assert(sa2 == [[1],[2,3],[4]]);
5fee5ec3
IB
4337 s2 = "[[1],[2,3],[4]]";
4338 assert(tuple([[1],[2,3],[4]], s2.length) == parse!(int[][3], string, Yes.doCount)(s2));
b4c522fa
IB
4339
4340 auto s3 = "[1,2,3]";
4341 assertThrown!ConvException(parse!(int[4])(s3));
5fee5ec3 4342 assertThrown!ConvException(parse!(int[4], string, Yes.doCount)(s3));
b4c522fa
IB
4343
4344 auto s4 = "[1,2,3,4,5]";
4345 assertThrown!ConvException(parse!(int[4])(s4));
5fee5ec3 4346 assertThrown!ConvException(parse!(int[4], string, Yes.doCount)(s4));
b4c522fa
IB
4347}
4348
4349/**
4350 * Parses an associative array from a string given the left bracket (default $(D
5fee5ec3
IB
4351 * '[')), right bracket (default `']'`), key-value separator (default $(D
4352 * ':')), and element seprator (by default `','`).
b4c522fa
IB
4353 *
4354 * Params:
4355 * s = the string to parse
4356 * lbracket = the character that starts the associative array
4357 * rbracket = the character that ends the associative array
4358 * keyval = the character that associates the key with the value
4359 * comma = the character that separates the elements of the associative array
5fee5ec3 4360 * doCount = the flag for deciding to report the number of consumed characters
b4c522fa
IB
4361 *
4362 * Returns:
5fee5ec3
IB
4363 $(UL
4364 * $(LI An associative array of type `Target` if `doCount` is set to `No.doCount`)
4365 * $(LI A `tuple` containing an associative array of type `Target` and a `size_t`
4366 * if `doCount` is set to `Yes.doCount`))
b4c522fa 4367 */
5fee5ec3 4368auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[',
b4c522fa
IB
4369 dchar rbracket = ']', dchar keyval = ':', dchar comma = ',')
4370if (isSomeString!Source && !is(Source == enum) &&
4371 isAssociativeArray!Target && !is(Target == enum))
4372{
4373 alias KeyType = typeof(Target.init.keys[0]);
4374 alias ValType = typeof(Target.init.values[0]);
4375
4376 Target result;
4377
4378 parseCheck!s(lbracket);
5fee5ec3 4379 size_t count = 1 + skipWS!(Source, Yes.doCount)(s);
b4c522fa
IB
4380 if (s.empty)
4381 throw convError!(Source, Target)(s);
4382 if (s.front == rbracket)
4383 {
4384 s.popFront();
5fee5ec3
IB
4385 static if (doCount)
4386 {
4387 return tuple!("data", "count")(result, ++count);
4388 }
4389 else
4390 {
4391 return result;
4392 }
b4c522fa 4393 }
5fee5ec3 4394 for (;; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s))
b4c522fa 4395 {
5fee5ec3
IB
4396 auto key = parseElement!(KeyType, Source, Yes.doCount)(s);
4397 count += key.count + skipWS!(Source, Yes.doCount)(s);
b4c522fa 4398 parseCheck!s(keyval);
5fee5ec3
IB
4399 count += 1 + skipWS!(Source, Yes.doCount)(s);
4400 auto val = parseElement!(ValType, Source, Yes.doCount)(s);
4401 count += val.count + skipWS!(Source, Yes.doCount)(s);
4402 result[key.data] = val.data;
b4c522fa
IB
4403 if (s.empty)
4404 throw convError!(Source, Target)(s);
4405 if (s.front != comma)
4406 break;
4407 }
4408 parseCheck!s(rbracket);
5fee5ec3
IB
4409 static if (doCount)
4410 {
4411 return tuple!("data", "count")(result, ++count);
4412 }
4413 else
4414 {
4415 return result;
4416 }
b4c522fa
IB
4417}
4418
4419///
4420@safe pure unittest
4421{
5fee5ec3
IB
4422 import std.typecons : Flag, Yes, No, tuple;
4423 import std.range.primitives : save;
4424 import std.array : assocArray;
b4c522fa 4425 auto s1 = "[1:10, 2:20, 3:30]";
5fee5ec3 4426 auto copyS1 = s1.save;
b4c522fa
IB
4427 auto aa1 = parse!(int[int])(s1);
4428 assert(aa1 == [1:10, 2:20, 3:30]);
5fee5ec3 4429 assert(tuple([1:10, 2:20, 3:30], copyS1.length) == parse!(int[int], string, Yes.doCount)(copyS1));
b4c522fa
IB
4430
4431 auto s2 = `["aaa":10, "bbb":20, "ccc":30]`;
5fee5ec3 4432 auto copyS2 = s2.save;
b4c522fa
IB
4433 auto aa2 = parse!(int[string])(s2);
4434 assert(aa2 == ["aaa":10, "bbb":20, "ccc":30]);
5fee5ec3
IB
4435 assert(tuple(["aaa":10, "bbb":20, "ccc":30], copyS2.length) ==
4436 parse!(int[string], string, Yes.doCount)(copyS2));
b4c522fa
IB
4437
4438 auto s3 = `["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]`;
5fee5ec3 4439 auto copyS3 = s3.save;
b4c522fa
IB
4440 auto aa3 = parse!(int[][string])(s3);
4441 assert(aa3 == ["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]);
5fee5ec3
IB
4442 assert(tuple(["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]], copyS3.length) ==
4443 parse!(int[][string], string, Yes.doCount)(copyS3));
4444
4445 auto s4 = `[]`;
4446 int[int] emptyAA;
4447 assert(tuple(emptyAA, s4.length) == parse!(int[int], string, Yes.doCount)(s4));
b4c522fa
IB
4448}
4449
4450@safe pure unittest
4451{
4452 import std.exception;
4453
4454 //Check proper failure
4455 auto s = "[1:10, 2:20, 3:30]";
5fee5ec3 4456 auto s2 = s.save;
b4c522fa
IB
4457 foreach (i ; 0 .. s.length-1)
4458 {
4459 auto ss = s[0 .. i];
4460 assertThrown!ConvException(parse!(int[int])(ss));
5fee5ec3 4461 assertThrown!ConvException(parse!(int[int], string, Yes.doCount)(ss));
b4c522fa
IB
4462 }
4463 int[int] aa = parse!(int[int])(s);
5fee5ec3
IB
4464 auto aa2 = parse!(int[int], string, Yes.doCount)(s2);
4465 aa = aa2[0];
4466
b4c522fa
IB
4467}
4468
5fee5ec3 4469private auto parseEscape(Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
b4c522fa
IB
4470if (isInputRange!Source && isSomeChar!(ElementType!Source))
4471{
4472 parseCheck!s('\\');
5fee5ec3 4473 size_t count = 1;
b4c522fa
IB
4474 if (s.empty)
4475 throw parseError("Unterminated escape sequence");
4476
5fee5ec3 4477 // consumes 1 element from Source
b4c522fa
IB
4478 dchar getHexDigit()(ref Source s_ = s) // workaround
4479 {
4480 import std.ascii : isAlpha, isHexDigit;
4481 if (s_.empty)
4482 throw parseError("Unterminated escape sequence");
4483 s_.popFront();
4484 if (s_.empty)
4485 throw parseError("Unterminated escape sequence");
4486 dchar c = s_.front;
4487 if (!isHexDigit(c))
4488 throw parseError("Hex digit is missing");
4489 return isAlpha(c) ? ((c & ~0x20) - ('A' - 10)) : c - '0';
4490 }
4491
5fee5ec3
IB
4492 // We need to do octals separate, because they need a lookahead to find out,
4493 // where the escape sequence ends.
4494 auto first = s.front;
4495 if (first >= '0' && first <= '7')
4496 {
4497 dchar c1 = s.front;
4498 ++count;
4499 s.popFront();
4500 if (s.empty)
4501 {
4502 static if (doCount)
4503 {
4504 return tuple!("data", "count")(cast (dchar) (c1 - '0'), count);
4505 }
4506 else
4507 {
4508 return cast (dchar) (c1 - '0');
4509 }
4510 }
4511 dchar c2 = s.front;
4512 if (c2 < '0' || c2 > '7')
4513 {
4514 static if (doCount)
4515 {
4516 return tuple!("data", "count")(cast (dchar)(c1 - '0'), count);
4517 }
4518 else
4519 {
4520 return cast (dchar)(c1 - '0');
4521 }
4522 }
4523 ++count;
4524 s.popFront();
4525 dchar c3 = s.front;
4526 if (c3 < '0' || c3 > '7')
4527 {
4528 static if (doCount)
4529 {
4530 return tuple!("data", "count")(cast (dchar) (8 * (c1 - '0') + (c2 - '0')), count);
4531 }
4532 else
4533 {
4534 return cast (dchar) (8 * (c1 - '0') + (c2 - '0'));
4535 }
4536 }
4537 ++count;
4538 s.popFront();
4539 if (c1 > '3')
4540 throw parseError("Octal sequence is larger than \\377");
4541 static if (doCount)
4542 {
4543 return tuple!("data", "count")(cast (dchar) (64 * (c1 - '0') + 8 * (c2 - '0') + (c3 - '0')), count);
4544 }
4545 else
4546 {
4547 return cast (dchar) (64 * (c1 - '0') + 8 * (c2 - '0') + (c3 - '0'));
4548 }
4549 }
4550
b4c522fa
IB
4551 dchar result;
4552
5fee5ec3 4553 switch (first)
b4c522fa
IB
4554 {
4555 case '"': result = '\"'; break;
4556 case '\'': result = '\''; break;
b4c522fa
IB
4557 case '?': result = '\?'; break;
4558 case '\\': result = '\\'; break;
4559 case 'a': result = '\a'; break;
4560 case 'b': result = '\b'; break;
4561 case 'f': result = '\f'; break;
4562 case 'n': result = '\n'; break;
4563 case 'r': result = '\r'; break;
4564 case 't': result = '\t'; break;
4565 case 'v': result = '\v'; break;
4566 case 'x':
4567 result = getHexDigit() << 4;
4568 result |= getHexDigit();
5fee5ec3 4569 count += 2;
b4c522fa
IB
4570 break;
4571 case 'u':
4572 result = getHexDigit() << 12;
4573 result |= getHexDigit() << 8;
4574 result |= getHexDigit() << 4;
4575 result |= getHexDigit();
5fee5ec3 4576 count += 4;
b4c522fa
IB
4577 break;
4578 case 'U':
4579 result = getHexDigit() << 28;
4580 result |= getHexDigit() << 24;
4581 result |= getHexDigit() << 20;
4582 result |= getHexDigit() << 16;
4583 result |= getHexDigit() << 12;
4584 result |= getHexDigit() << 8;
4585 result |= getHexDigit() << 4;
4586 result |= getHexDigit();
5fee5ec3 4587 count += 8;
b4c522fa
IB
4588 break;
4589 default:
4590 throw parseError("Unknown escape character " ~ to!string(s.front));
4591 }
4592 if (s.empty)
4593 throw parseError("Unterminated escape sequence");
4594
4595 s.popFront();
4596
5fee5ec3
IB
4597 static if (doCount)
4598 {
4599 return tuple!("data", "count")(cast (dchar) result, ++count);
4600 }
4601 else
4602 {
4603 return cast (dchar) result;
4604 }
b4c522fa
IB
4605}
4606
4607@safe pure unittest
4608{
4609 string[] s1 = [
4610 `\"`, `\'`, `\?`, `\\`, `\a`, `\b`, `\f`, `\n`, `\r`, `\t`, `\v`, //Normal escapes
5fee5ec3 4611 `\141`,
b4c522fa 4612 `\x61`,
5fee5ec3
IB
4613 `\u65E5`, `\U00012456`,
4614 // https://issues.dlang.org/show_bug.cgi?id=9621 (Named Character Entities)
4615 //`\&amp;`, `\&quot;`,
b4c522fa 4616 ];
5fee5ec3 4617 string[] copyS1 = s1 ~ s1[0 .. 0];
b4c522fa
IB
4618
4619 const(dchar)[] s2 = [
4620 '\"', '\'', '\?', '\\', '\a', '\b', '\f', '\n', '\r', '\t', '\v', //Normal escapes
5fee5ec3 4621 '\141',
b4c522fa 4622 '\x61',
5fee5ec3
IB
4623 '\u65E5', '\U00012456',
4624 // https://issues.dlang.org/show_bug.cgi?id=9621 (Named Character Entities)
4625 //'\&amp;', '\&quot;',
b4c522fa
IB
4626 ];
4627
4628 foreach (i ; 0 .. s1.length)
4629 {
4630 assert(s2[i] == parseEscape(s1[i]));
4631 assert(s1[i].empty);
5fee5ec3
IB
4632
4633 assert(tuple(s2[i], copyS1[i].length) == parseEscape!(string, Yes.doCount)(copyS1[i]));
4634 assert(copyS1[i].empty);
b4c522fa
IB
4635 }
4636}
4637
4638@safe pure unittest
4639{
4640 import std.exception;
4641
4642 string[] ss = [
4643 `hello!`, //Not an escape
4644 `\`, //Premature termination
4645 `\/`, //Not an escape
4646 `\gggg`, //Not an escape
4647 `\xzz`, //Not an hex
4648 `\x0`, //Premature hex end
4649 `\XB9`, //Not legal hex syntax
4650 `\u!!`, //Not a unicode hex
5fee5ec3
IB
4651 `\777`, //Octal is larger than a byte
4652 `\80`, //Wrong digit at beginning of octal
b4c522fa
IB
4653 `\u123`, //Premature hex end
4654 `\U123123` //Premature hex end
4655 ];
4656 foreach (s ; ss)
5fee5ec3 4657 {
b4c522fa 4658 assertThrown!ConvException(parseEscape(s));
5fee5ec3
IB
4659 assertThrown!ConvException(parseEscape!(string, Yes.doCount)(s));
4660 }
b4c522fa
IB
4661}
4662
4663// Undocumented
5fee5ec3 4664auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
b4c522fa
IB
4665if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) &&
4666 isExactSomeString!Target)
4667{
4668 import std.array : appender;
4669 auto result = appender!Target();
4670
4671 // parse array of chars
4672 if (s.empty)
4673 throw convError!(Source, Target)(s);
4674 if (s.front == '[')
5fee5ec3
IB
4675 {
4676 return parse!(Target, Source, doCount)(s);
4677 }
b4c522fa
IB
4678
4679 parseCheck!s('\"');
5fee5ec3 4680 size_t count = 1;
b4c522fa
IB
4681 if (s.empty)
4682 throw convError!(Source, Target)(s);
4683 if (s.front == '\"')
4684 {
4685 s.popFront();
5fee5ec3
IB
4686 static if (doCount)
4687 {
4688 return tuple!("data", "count")(result.data, ++count);
4689 }
4690 else
4691 {
4692 return result.data;
4693 }
4694
b4c522fa
IB
4695 }
4696 while (true)
4697 {
4698 if (s.empty)
4699 throw parseError("Unterminated quoted string");
4700 switch (s.front)
4701 {
4702 case '\"':
4703 s.popFront();
5fee5ec3
IB
4704 static if (doCount)
4705 {
4706 return tuple!("data", "count")(result.data, ++count);
4707 }
4708 else
4709 {
4710 return result.data;
4711 }
b4c522fa 4712 case '\\':
5fee5ec3
IB
4713 auto r = parseEscape!(typeof(s), Yes.doCount)(s);
4714 result.put(r[0]);
4715 count += r[1];
b4c522fa
IB
4716 break;
4717 default:
4718 result.put(s.front);
5fee5ec3 4719 ++count;
b4c522fa
IB
4720 s.popFront();
4721 break;
4722 }
4723 }
5fee5ec3 4724 assert(false, "Unexpected fallthrough");
b4c522fa
IB
4725}
4726
4727// ditto
5fee5ec3 4728auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
b4c522fa 4729if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) &&
5fee5ec3 4730 is(CharTypeOf!Target == dchar) && !is(Target == enum))
b4c522fa 4731{
5fee5ec3 4732 Unqual!Target c;
b4c522fa
IB
4733
4734 parseCheck!s('\'');
5fee5ec3 4735 size_t count = 1;
b4c522fa
IB
4736 if (s.empty)
4737 throw convError!(Source, Target)(s);
5fee5ec3 4738 ++count; // for the following if-else sequence
b4c522fa
IB
4739 if (s.front != '\\')
4740 {
4741 c = s.front;
4742 s.popFront();
4743 }
4744 else
4745 c = parseEscape(s);
4746 parseCheck!s('\'');
5fee5ec3
IB
4747 static if (doCount)
4748 {
4749 return tuple!("data", "count")(c, ++count);
4750 }
4751 else
4752 {
4753 return c;
4754 }
b4c522fa
IB
4755}
4756
4757// ditto
5fee5ec3 4758auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
b4c522fa
IB
4759if (isInputRange!Source && isSomeChar!(ElementType!Source) &&
4760 !isSomeString!Target && !isSomeChar!Target)
4761{
5fee5ec3
IB
4762 return parse!(Target, Source, doCount)(s);
4763}
4764
4765// Use this when parsing a type that will ultimately be appended to a
4766// string.
4767package template WideElementType(T)
4768{
4769 alias E = ElementType!T;
4770 static if (isSomeChar!E)
4771 alias WideElementType = dchar;
4772 else
4773 alias WideElementType = E;
b4c522fa
IB
4774}
4775
4776
4777/***************************************************************
4778 * Convenience functions for converting one or more arguments
4779 * of any type into _text (the three character widths).
4780 */
4781string text(T...)(T args)
4782if (T.length > 0) { return textImpl!string(args); }
4783
b4c522fa
IB
4784///ditto
4785wstring wtext(T...)(T args)
4786if (T.length > 0) { return textImpl!wstring(args); }
4787
b4c522fa
IB
4788///ditto
4789dstring dtext(T...)(T args)
4790if (T.length > 0) { return textImpl!dstring(args); }
4791
4792///
4793@safe unittest
4794{
4795 assert( text(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"c);
4796 assert(wtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"w);
4797 assert(dtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"d);
4798}
4799
9fa27ed0
IB
4800@safe unittest
4801{
4802 char c = 'h';
4803 wchar w = '你';
4804 dchar d = 'እ';
4805
4806 assert( text(c, "ello", ' ', w, "好 ", d, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"c);
4807 assert(wtext(c, "ello", ' ', w, "好 ", d, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"w);
4808 assert(dtext(c, "ello", ' ', w, "好 ", d, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"d);
4809
4810 string cs = "今日は";
4811 wstring ws = "여보세요";
4812 dstring ds = "Здравствуйте";
4813
4814 assert( text(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"c);
4815 assert(wtext(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"w);
4816 assert(dtext(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"d);
4817}
b4c522fa
IB
4818
4819private S textImpl(S, U...)(U args)
4820{
4821 static if (U.length == 0)
4822 {
4823 return null;
4824 }
4825 else static if (U.length == 1)
4826 {
4827 return to!S(args[0]);
4828 }
4829 else
4830 {
4831 import std.array : appender;
5fee5ec3 4832 import std.traits : isSomeChar, isSomeString;
b4c522fa
IB
4833
4834 auto app = appender!S();
4835
5fee5ec3
IB
4836 // assume that on average, parameters will have less
4837 // than 20 elements
4838 app.reserve(U.length * 20);
4839 // Must be static foreach because of https://issues.dlang.org/show_bug.cgi?id=21209
4840 static foreach (arg; args)
4841 {
4842 static if (
4843 isSomeChar!(typeof(arg)) || isSomeString!(typeof(arg)) ||
4844 ( isInputRange!(typeof(arg)) && isSomeChar!(ElementType!(typeof(arg))) )
4845 )
4846 app.put(arg);
4847 else static if (
4848
4849 is(immutable typeof(arg) == immutable uint) || is(immutable typeof(arg) == immutable ulong) ||
4850 is(immutable typeof(arg) == immutable int) || is(immutable typeof(arg) == immutable long)
4851 )
4852 // https://issues.dlang.org/show_bug.cgi?id=17712#c15
4853 app.put(textImpl!(S)(arg));
4854 else
4855 app.put(to!S(arg));
4856 }
4857
b4c522fa
IB
4858 return app.data;
4859 }
4860}
4861
4862
4863/***************************************************************
5fee5ec3
IB
4864The `octal` facility provides a means to declare a number in base 8.
4865Using `octal!177` or `octal!"177"` for 127 represented in octal
b4c522fa
IB
4866(same as 0177 in C).
4867
4868The rules for strings are the usual for literals: If it can fit in an
5fee5ec3
IB
4869`int`, it is an `int`. Otherwise, it is a `long`. But, if the
4870user specifically asks for a `long` with the `L` suffix, always
4871give the `long`. Give an unsigned iff it is asked for with the $(D
4872U) or `u` suffix. _Octals created from integers preserve the type
b4c522fa
IB
4873of the passed-in integral.
4874
4875See_Also:
4876 $(LREF parse) for parsing octal strings at runtime.
4877 */
4878template octal(string num)
4879if (isOctalLiteral(num))
4880{
4881 static if ((octalFitsInInt!num && !literalIsLong!num) && !literalIsUnsigned!num)
4882 enum octal = octal!int(num);
4883 else static if ((!octalFitsInInt!num || literalIsLong!num) && !literalIsUnsigned!num)
4884 enum octal = octal!long(num);
4885 else static if ((octalFitsInInt!num && !literalIsLong!num) && literalIsUnsigned!num)
4886 enum octal = octal!uint(num);
4887 else static if ((!octalFitsInInt!(num) || literalIsLong!(num)) && literalIsUnsigned!(num))
4888 enum octal = octal!ulong(num);
4889 else
5fee5ec3 4890 static assert(false, "Unusable input " ~ num);
b4c522fa
IB
4891}
4892
4893/// Ditto
4894template octal(alias decimalInteger)
5a0aa603 4895if (is(typeof(decimalInteger)) && isIntegral!(typeof(decimalInteger)))
b4c522fa 4896{
c8dfa79c 4897 enum octal = convertToOctal(decimalInteger);
b4c522fa
IB
4898}
4899
4900///
4901@safe unittest
4902{
5fee5ec3
IB
4903 // Same as 0177
4904 auto a = octal!177;
b4c522fa 4905 // octal is a compile-time device
5fee5ec3 4906 enum b = octal!160;
b4c522fa 4907 // Create an unsigned octal
5fee5ec3
IB
4908 auto c = octal!"1_000_000u";
4909 // Leading zeros are allowed when converting from a string
4910 auto d = octal!"0001_200_000";
b4c522fa
IB
4911}
4912
c8dfa79c
IB
4913/*************************************
4914 * Convert a decimal integer to an octal integer with the same digits.
4915 * Params:
4916 * i = integer to convert
4917 * Returns:
4918 * octal integer with the same type and same digits
4919 */
4920private T convertToOctal(T)(T i)
4921{
4922 assert((i % 10) < 8);
4923 return i ? convertToOctal(i / 10) * 8 + i % 10 : 0;
4924}
4925
b4c522fa
IB
4926/*
4927 Takes a string, num, which is an octal literal, and returns its
4928 value, in the type T specified.
4929*/
4930private T octal(T)(const string num)
4931{
5fee5ec3 4932 assert(isOctalLiteral(num), num ~ " is not an octal literal");
b4c522fa
IB
4933
4934 T value = 0;
4935
4936 foreach (const char s; num)
4937 {
4938 if (s < '0' || s > '7') // we only care about digits; skip the rest
4939 // safe to skip - this is checked out in the assert so these
4940 // are just suffixes
4941 continue;
4942
4943 value *= 8;
4944 value += s - '0';
4945 }
4946
4947 return value;
4948}
4949
4950@safe unittest
4951{
4952 int a = octal!int("10");
4953 assert(a == 8);
5fee5ec3
IB
4954
4955 int b = octal!int("000137");
4956 assert(b == 95);
b4c522fa
IB
4957}
4958
4959/*
4960Take a look at int.max and int.max+1 in octal and the logic for this
4961function follows directly.
4962 */
4963private template octalFitsInInt(string octalNum)
4964{
4965 // note it is important to strip the literal of all
4966 // non-numbers. kill the suffix and underscores lest they mess up
4967 // the number of digits here that we depend on.
4968 enum bool octalFitsInInt = strippedOctalLiteral(octalNum).length < 11 ||
4969 strippedOctalLiteral(octalNum).length == 11 &&
4970 strippedOctalLiteral(octalNum)[0] == '1';
4971}
4972
4973private string strippedOctalLiteral(string original)
4974{
4975 string stripped = "";
5fee5ec3 4976 bool leading_zeros = true;
b4c522fa 4977 foreach (c; original)
5fee5ec3
IB
4978 {
4979 if (!('0' <= c && c <= '7'))
4980 continue;
4981 if (c == '0')
4982 {
4983 if (leading_zeros)
4984 continue;
4985 }
4986 else
4987 {
4988 leading_zeros = false;
4989 }
4990 stripped ~= c;
4991 }
4992 if (stripped.length == 0)
4993 {
4994 assert(leading_zeros);
4995 return "0";
4996 }
b4c522fa
IB
4997 return stripped;
4998}
4999
5fee5ec3
IB
5000@safe unittest
5001{
5002 static assert(strippedOctalLiteral("7") == "7");
5003 static assert(strippedOctalLiteral("123") == "123");
5004 static assert(strippedOctalLiteral("00123") == "123");
5005 static assert(strippedOctalLiteral("01230") == "1230");
5006 static assert(strippedOctalLiteral("0") == "0");
5007 static assert(strippedOctalLiteral("00_000") == "0");
5008 static assert(strippedOctalLiteral("000_000_12_300") == "12300");
5009}
5010
b4c522fa
IB
5011private template literalIsLong(string num)
5012{
5013 static if (num.length > 1)
5014 // can be xxL or xxLu according to spec
5015 enum literalIsLong = (num[$-1] == 'L' || num[$-2] == 'L');
5016 else
5017 enum literalIsLong = false;
5018}
5019
5020private template literalIsUnsigned(string num)
5021{
5022 static if (num.length > 1)
5023 // can be xxU or xxUL according to spec
5024 enum literalIsUnsigned = (num[$-1] == 'u' || num[$-2] == 'u')
5025 // both cases are allowed too
5026 || (num[$-1] == 'U' || num[$-2] == 'U');
5027 else
5028 enum literalIsUnsigned = false;
5029}
5030
5031/*
5032Returns if the given string is a correctly formatted octal literal.
5033
5fee5ec3
IB
5034The format is specified in spec/lex.html. The leading zeros are allowed,
5035but not required.
b4c522fa
IB
5036 */
5037@safe pure nothrow @nogc
5038private bool isOctalLiteral(const string num)
5039{
5040 if (num.length == 0)
5041 return false;
5042
5fee5ec3 5043 // Must start with a digit.
b4c522fa
IB
5044 if (num[0] < '0' || num[0] > '7')
5045 return false;
5046
5047 foreach (i, c; num)
5048 {
5fee5ec3
IB
5049 if (('0' <= c && c <= '7') || c == '_') // a legal character
5050 continue;
5051
5052 if (i < num.length - 2)
5053 return false;
5054
5055 // gotta check for those suffixes
5056 if (c != 'U' && c != 'u' && c != 'L')
5057 return false;
5058 if (i != num.length - 1)
b4c522fa 5059 {
5fee5ec3
IB
5060 // if we're not the last one, the next one must
5061 // also be a suffix to be valid
5062 char c2 = num[$-1];
5063 if (c2 != 'U' && c2 != 'u' && c2 != 'L')
5064 return false; // spam at the end of the string
5065 if (c2 == c)
5066 return false; // repeats are disallowed
b4c522fa
IB
5067 }
5068 }
5069
5070 return true;
5071}
5072
5073@safe unittest
5074{
5075 // ensure that you get the right types, even with embedded underscores
5076 auto w = octal!"100_000_000_000";
5077 static assert(!is(typeof(w) == int));
5078 auto w2 = octal!"1_000_000_000";
5079 static assert(is(typeof(w2) == int));
5080
5081 static assert(octal!"45" == 37);
5082 static assert(octal!"0" == 0);
5083 static assert(octal!"7" == 7);
5084 static assert(octal!"10" == 8);
5085 static assert(octal!"666" == 438);
5fee5ec3
IB
5086 static assert(octal!"0004001" == 2049);
5087 static assert(octal!"00" == 0);
5088 static assert(octal!"0_0" == 0);
b4c522fa
IB
5089
5090 static assert(octal!45 == 37);
5091 static assert(octal!0 == 0);
5092 static assert(octal!7 == 7);
5093 static assert(octal!10 == 8);
5094 static assert(octal!666 == 438);
5095
5096 static assert(octal!"66_6" == 438);
5fee5ec3 5097 static assert(octal!"0_0_66_6" == 438);
b4c522fa
IB
5098
5099 static assert(octal!2520046213 == 356535435);
5100 static assert(octal!"2520046213" == 356535435);
5101
5102 static assert(octal!17777777777 == int.max);
5103
5104 static assert(!__traits(compiles, octal!823));
5105
5106 static assert(!__traits(compiles, octal!"823"));
5107
5108 static assert(!__traits(compiles, octal!"_823"));
5109 static assert(!__traits(compiles, octal!"spam"));
5110 static assert(!__traits(compiles, octal!"77%"));
5111
5112 static assert(is(typeof(octal!"17777777777") == int));
5113 static assert(octal!"17777777777" == int.max);
5114
5115 static assert(is(typeof(octal!"20000000000U") == ulong)); // Shouldn't this be uint?
5116 static assert(octal!"20000000000" == uint(int.max) + 1);
5117
5118 static assert(is(typeof(octal!"777777777777777777777") == long));
5119 static assert(octal!"777777777777777777777" == long.max);
5120
5121 static assert(is(typeof(octal!"1000000000000000000000U") == ulong));
5122 static assert(octal!"1000000000000000000000" == ulong(long.max) + 1);
5123
5124 int a;
5125 long b;
5126
5127 // biggest value that should fit in an it
5128 a = octal!"17777777777";
5129 assert(a == int.max);
5130 // should not fit in the int
5131 static assert(!__traits(compiles, a = octal!"20000000000"));
5132 // ... but should fit in a long
5133 b = octal!"20000000000";
5134 assert(b == 1L + int.max);
5135
5136 b = octal!"1L";
5137 assert(b == 1);
5138 b = octal!1L;
5139 assert(b == 1);
5140}
5141
5fee5ec3
IB
5142// emplace() used to be here but was moved to druntime
5143public import core.lifetime : emplace;
b4c522fa 5144
5fee5ec3
IB
5145// https://issues.dlang.org/show_bug.cgi?id=9559
5146@safe unittest
5147{
5148 import std.algorithm.iteration : map;
5149 import std.array : array;
5150 import std.typecons : Nullable;
5151 alias I = Nullable!int;
5152 auto ints = [0, 1, 2].map!(i => i & 1 ? I.init : I(i))();
5153 auto asArray = array(ints);
5154}
b4c522fa 5155
5fee5ec3 5156@system unittest //http://forum.dlang.org/post/nxbdgtdlmwscocbiypjs@forum.dlang.org
b4c522fa 5157{
5fee5ec3
IB
5158 import std.array : array;
5159 import std.datetime : SysTime, UTC;
5160 import std.math.traits : isNaN;
5161
5162 static struct A
b4c522fa
IB
5163 {
5164 double i;
5165 }
5166
5167 static struct B
5168 {
5169 invariant()
5170 {
5171 if (j == 0)
5172 assert(a.i.isNaN(), "why is 'j' zero?? and i is not NaN?");
5173 else
5174 assert(!a.i.isNaN());
5175 }
5176 SysTime when; // comment this line avoid the breakage
5177 int j;
5178 A a;
5179 }
5180
5181 B b1 = B.init;
5182 assert(&b1); // verify that default eyes invariants are ok;
5183
5184 auto b2 = B(SysTime(0, UTC()), 1, A(1));
5185 assert(&b2);
5186 auto b3 = B(SysTime(0, UTC()), 1, A(1));
5187 assert(&b3);
5188
5189 auto arr = [b2, b3];
5190
5191 assert(arr[0].j == 1);
5192 assert(arr[1].j == 1);
5193 auto a2 = arr.array(); // << bang, invariant is raised, also if b2 and b3 are good
5194}
5195
b4c522fa
IB
5196@safe unittest
5197{
5198 import std.algorithm.comparison : equal;
5199 import std.algorithm.iteration : map;
5fee5ec3 5200 // Check fix for https://issues.dlang.org/show_bug.cgi?id=2971
b4c522fa
IB
5201 assert(equal(map!(to!int)(["42", "34", "345"]), [42, 34, 345]));
5202}
5203
5204// Undocumented for the time being
5205void toTextRange(T, W)(T value, W writer)
5206if (isIntegral!T && isOutputRange!(W, char))
5207{
5208 import core.internal.string : SignedStringBuf, signedToTempString,
5209 UnsignedStringBuf, unsignedToTempString;
5210
5211 if (value < 0)
5212 {
5213 SignedStringBuf buf = void;
5fee5ec3 5214 put(writer, signedToTempString(value, buf));
b4c522fa
IB
5215 }
5216 else
5217 {
5218 UnsignedStringBuf buf = void;
5fee5ec3 5219 put(writer, unsignedToTempString(value, buf));
b4c522fa
IB
5220 }
5221}
5222
5223@safe unittest
5224{
5225 import std.array : appender;
5226 auto result = appender!(char[])();
5227 toTextRange(-1, result);
5228 assert(result.data == "-1");
5229}
5230
5231
5232/**
5fee5ec3
IB
5233 Returns the corresponding _unsigned value for `x` (e.g. if `x` has type
5234 `int`, it returns $(D cast(uint) x)). The advantage compared to the cast
5235 is that you do not need to rewrite the cast if `x` later changes type
5236 (e.g from `int` to `long`).
b4c522fa
IB
5237
5238 Note that the result is always mutable even if the original type was const
5239 or immutable. In order to retain the constness, use $(REF Unsigned, std,traits).
5240 */
5241auto unsigned(T)(T x)
5242if (isIntegral!T)
5243{
5244 return cast(Unqual!(Unsigned!T))x;
5245}
5246
5247///
5248@safe unittest
5249{
5250 import std.traits : Unsigned;
5251 immutable int s = 42;
5252 auto u1 = unsigned(s); //not qualified
5253 static assert(is(typeof(u1) == uint));
5254 Unsigned!(typeof(s)) u2 = unsigned(s); //same qualification
5255 static assert(is(typeof(u2) == immutable uint));
5256 immutable u3 = unsigned(s); //explicitly qualified
5257}
5258
5fee5ec3
IB
5259/// Ditto
5260auto unsigned(T)(T x)
5261if (isSomeChar!T)
5262{
5263 // All characters are unsigned
5264 static assert(T.min == 0, T.stringof ~ ".min must be zero");
5265 return cast(Unqual!T) x;
5266}
5267
b4c522fa
IB
5268@safe unittest
5269{
5fee5ec3 5270 static foreach (T; AliasSeq!(byte, ubyte))
b4c522fa
IB
5271 {
5272 static assert(is(typeof(unsigned(cast(T) 1)) == ubyte));
5273 static assert(is(typeof(unsigned(cast(const T) 1)) == ubyte));
5274 static assert(is(typeof(unsigned(cast(immutable T) 1)) == ubyte));
5275 }
5276
5fee5ec3 5277 static foreach (T; AliasSeq!(short, ushort))
b4c522fa
IB
5278 {
5279 static assert(is(typeof(unsigned(cast(T) 1)) == ushort));
5280 static assert(is(typeof(unsigned(cast(const T) 1)) == ushort));
5281 static assert(is(typeof(unsigned(cast(immutable T) 1)) == ushort));
5282 }
5283
5fee5ec3 5284 static foreach (T; AliasSeq!(int, uint))
b4c522fa
IB
5285 {
5286 static assert(is(typeof(unsigned(cast(T) 1)) == uint));
5287 static assert(is(typeof(unsigned(cast(const T) 1)) == uint));
5288 static assert(is(typeof(unsigned(cast(immutable T) 1)) == uint));
5289 }
5290
5fee5ec3 5291 static foreach (T; AliasSeq!(long, ulong))
b4c522fa
IB
5292 {
5293 static assert(is(typeof(unsigned(cast(T) 1)) == ulong));
5294 static assert(is(typeof(unsigned(cast(const T) 1)) == ulong));
5295 static assert(is(typeof(unsigned(cast(immutable T) 1)) == ulong));
5296 }
5297}
5298
b4c522fa
IB
5299@safe unittest
5300{
5fee5ec3 5301 static foreach (T; AliasSeq!(char, wchar, dchar))
b4c522fa
IB
5302 {
5303 static assert(is(typeof(unsigned(cast(T)'A')) == T));
5304 static assert(is(typeof(unsigned(cast(const T)'A')) == T));
5305 static assert(is(typeof(unsigned(cast(immutable T)'A')) == T));
5306 }
5307}
5308
5309
5310/**
5fee5ec3
IB
5311 Returns the corresponding _signed value for `x` (e.g. if `x` has type
5312 `uint`, it returns $(D cast(int) x)). The advantage compared to the cast
5313 is that you do not need to rewrite the cast if `x` later changes type
5314 (e.g from `uint` to `ulong`).
b4c522fa
IB
5315
5316 Note that the result is always mutable even if the original type was const
5317 or immutable. In order to retain the constness, use $(REF Signed, std,traits).
5318 */
5319auto signed(T)(T x)
5320if (isIntegral!T)
5321{
5322 return cast(Unqual!(Signed!T))x;
5323}
5324
5325///
5326@safe unittest
5327{
5328 import std.traits : Signed;
5329
5330 immutable uint u = 42;
5331 auto s1 = signed(u); //not qualified
5332 static assert(is(typeof(s1) == int));
5333 Signed!(typeof(u)) s2 = signed(u); //same qualification
5334 static assert(is(typeof(s2) == immutable int));
5335 immutable s3 = signed(u); //explicitly qualified
5336}
5337
5338@system unittest
5339{
5fee5ec3 5340 static foreach (T; AliasSeq!(byte, ubyte))
b4c522fa
IB
5341 {
5342 static assert(is(typeof(signed(cast(T) 1)) == byte));
5343 static assert(is(typeof(signed(cast(const T) 1)) == byte));
5344 static assert(is(typeof(signed(cast(immutable T) 1)) == byte));
5345 }
5346
5fee5ec3 5347 static foreach (T; AliasSeq!(short, ushort))
b4c522fa
IB
5348 {
5349 static assert(is(typeof(signed(cast(T) 1)) == short));
5350 static assert(is(typeof(signed(cast(const T) 1)) == short));
5351 static assert(is(typeof(signed(cast(immutable T) 1)) == short));
5352 }
5353
5fee5ec3 5354 static foreach (T; AliasSeq!(int, uint))
b4c522fa
IB
5355 {
5356 static assert(is(typeof(signed(cast(T) 1)) == int));
5357 static assert(is(typeof(signed(cast(const T) 1)) == int));
5358 static assert(is(typeof(signed(cast(immutable T) 1)) == int));
5359 }
5360
5fee5ec3 5361 static foreach (T; AliasSeq!(long, ulong))
b4c522fa
IB
5362 {
5363 static assert(is(typeof(signed(cast(T) 1)) == long));
5364 static assert(is(typeof(signed(cast(const T) 1)) == long));
5365 static assert(is(typeof(signed(cast(immutable T) 1)) == long));
5366 }
5367}
5368
5fee5ec3 5369// https://issues.dlang.org/show_bug.cgi?id=10874
b4c522fa
IB
5370@safe unittest
5371{
b4c522fa
IB
5372 enum Test { a = 0 }
5373 ulong l = 0;
5374 auto t = l.to!Test;
5375}
5376
5377// asOriginalType
5378/**
5379Returns the representation of an enumerated value, i.e. the value converted to
5380the base type of the enumeration.
5381*/
5fee5ec3
IB
5382OriginalType!E asOriginalType(E)(E value)
5383if (is(E == enum))
b4c522fa
IB
5384{
5385 return value;
5386}
5387
5388///
5389@safe unittest
5390{
5391 enum A { a = 42 }
5392 static assert(is(typeof(A.a.asOriginalType) == int));
5393 assert(A.a.asOriginalType == 42);
5394 enum B : double { a = 43 }
5395 static assert(is(typeof(B.a.asOriginalType) == double));
5396 assert(B.a.asOriginalType == 43);
5397}
5398
5399/**
5400 A wrapper on top of the built-in cast operator that allows one to restrict
5401 casting of the original type of the value.
5402
5403 A common issue with using a raw cast is that it may silently continue to
5404 compile even if the value's type has changed during refactoring,
5405 which breaks the initial assumption about the cast.
5406
5407 Params:
5408 From = The type to cast from. The programmer must ensure it is legal
5409 to make this cast.
5410 */
5411template castFrom(From)
5412{
5413 /**
5414 Params:
5415 To = The type _to cast _to.
5fee5ec3 5416 value = The value _to cast. It must be of type `From`,
b4c522fa
IB
5417 otherwise a compile-time error is emitted.
5418
5419 Returns:
5420 the value after the cast, returned by reference if possible.
5421 */
5422 auto ref to(To, T)(auto ref T value) @system
5423 {
5424 static assert(
5425 is(From == T),
5426 "the value to cast is not of specified type '" ~ From.stringof ~
5427 "', it is of type '" ~ T.stringof ~ "'"
5428 );
5429
5430 static assert(
5431 is(typeof(cast(To) value)),
5432 "can't cast from '" ~ From.stringof ~ "' to '" ~ To.stringof ~ "'"
5433 );
5434
5435 return cast(To) value;
5436 }
5437}
5438
5439///
5440@system unittest
5441{
5442 // Regular cast, which has been verified to be legal by the programmer:
5443 {
5444 long x;
5445 auto y = cast(int) x;
5446 }
5447
5448 // However this will still compile if 'x' is changed to be a pointer:
5449 {
5450 long* x;
5451 auto y = cast(int) x;
5452 }
5453
5454 // castFrom provides a more reliable alternative to casting:
5455 {
5456 long x;
5457 auto y = castFrom!long.to!int(x);
5458 }
5459
5460 // Changing the type of 'x' will now issue a compiler error,
5461 // allowing bad casts to be caught before it's too late:
5462 {
5463 long* x;
5464 static assert(
5465 !__traits(compiles, castFrom!long.to!int(x))
5466 );
5467
5468 // if cast is still needed, must be changed to:
5469 auto y = castFrom!(long*).to!int(x);
5470 }
5471}
5472
5473// https://issues.dlang.org/show_bug.cgi?id=16667
5474@system unittest
5475{
5476 ubyte[] a = ['a', 'b', 'c'];
5477 assert(castFrom!(ubyte[]).to!(string)(a) == "abc");
5478}
5479
5480/**
5fee5ec3 5481Check the correctness of a string for `hexString`.
b4c522fa
IB
5482The result is true if and only if the input string is composed of whitespace
5483characters (\f\n\r\t\v lineSep paraSep nelSep) and
5484an even number of hexadecimal digits (regardless of the case).
5485*/
5486@safe pure @nogc
5487private bool isHexLiteral(String)(scope const String hexData)
5488{
5489 import std.ascii : isHexDigit;
5490 import std.uni : lineSep, paraSep, nelSep;
5491 size_t i;
5492 foreach (const dchar c; hexData)
5493 {
5494 switch (c)
5495 {
5496 case ' ':
5497 case '\t':
5498 case '\v':
5499 case '\f':
5500 case '\r':
5501 case '\n':
5502 case lineSep:
5503 case paraSep:
5504 case nelSep:
5505 continue;
5506
5507 default:
5508 break;
5509 }
5510 if (c.isHexDigit)
5511 ++i;
5512 else
5513 return false;
5514 }
5515 return !(i & 1);
5516}
5517
5518@safe unittest
5519{
5520 // test all the hex digits
5521 static assert( ("0123456789abcdefABCDEF").isHexLiteral);
5522 // empty or white strings are not valid
5523 static assert( "\r\n\t".isHexLiteral);
5524 // but are accepted if the count of hex digits is even
5525 static assert( "A\r\n\tB".isHexLiteral);
5526}
5527
5528@safe unittest
5529{
5530 import std.ascii;
5531 // empty/whites
5532 static assert( "".isHexLiteral);
5533 static assert( " \r".isHexLiteral);
5534 static assert( whitespace.isHexLiteral);
5535 static assert( ""w.isHexLiteral);
5536 static assert( " \r"w.isHexLiteral);
5537 static assert( ""d.isHexLiteral);
5538 static assert( " \r"d.isHexLiteral);
5539 static assert( "\u2028\u2029\u0085"d.isHexLiteral);
5540 // odd x strings
5541 static assert( !("5" ~ whitespace).isHexLiteral);
5542 static assert( !"123".isHexLiteral);
5543 static assert( !"1A3".isHexLiteral);
5544 static assert( !"1 23".isHexLiteral);
5545 static assert( !"\r\n\tC".isHexLiteral);
5546 static assert( !"123"w.isHexLiteral);
5547 static assert( !"1A3"w.isHexLiteral);
5548 static assert( !"1 23"w.isHexLiteral);
5549 static assert( !"\r\n\tC"w.isHexLiteral);
5550 static assert( !"123"d.isHexLiteral);
5551 static assert( !"1A3"d.isHexLiteral);
5552 static assert( !"1 23"d.isHexLiteral);
5553 static assert( !"\r\n\tC"d.isHexLiteral);
5554 // even x strings with invalid charset
5555 static assert( !"12gG".isHexLiteral);
5556 static assert( !"2A 3q".isHexLiteral);
5557 static assert( !"12gG"w.isHexLiteral);
5558 static assert( !"2A 3q"w.isHexLiteral);
5559 static assert( !"12gG"d.isHexLiteral);
5560 static assert( !"2A 3q"d.isHexLiteral);
5561 // valid x strings
5562 static assert( ("5A" ~ whitespace).isHexLiteral);
5563 static assert( ("5A 01A C FF de 1b").isHexLiteral);
5564 static assert( ("0123456789abcdefABCDEF").isHexLiteral);
5565 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF").isHexLiteral);
5566 static assert( ("5A 01A C FF de 1b"w).isHexLiteral);
5567 static assert( ("0123456789abcdefABCDEF"w).isHexLiteral);
5568 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"w).isHexLiteral);
5569 static assert( ("5A 01A C FF de 1b"d).isHexLiteral);
5570 static assert( ("0123456789abcdefABCDEF"d).isHexLiteral);
5571 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"d).isHexLiteral);
5572 // library version allows what's pointed by issue 10454
5573 static assert( ("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").isHexLiteral);
5574}
5575
5576/**
5577Converts a hex literal to a string at compile time.
5578
5579Takes a string made of hexadecimal digits and returns
5580the matching string by converting each pair of digits to a character.
5581The input string can also include white characters, which can be used
5582to keep the literal string readable in the source code.
5583
5584The function is intended to replace the hexadecimal literal strings
5fee5ec3 5585starting with `'x'`, which could be removed to simplify the core language.
b4c522fa
IB
5586
5587Params:
5588 hexData = string to be converted.
5589
5590Returns:
5fee5ec3 5591 a `string`, a `wstring` or a `dstring`, according to the type of hexData.
b4c522fa
IB
5592 */
5593template hexString(string hexData)
5594if (hexData.isHexLiteral)
5595{
5fee5ec3 5596 enum hexString = mixin(hexToString(hexData));
b4c522fa
IB
5597}
5598
5599/// ditto
5600template hexString(wstring hexData)
5601if (hexData.isHexLiteral)
5602{
5fee5ec3 5603 enum wstring hexString = mixin(hexToString(hexData));
b4c522fa
IB
5604}
5605
5606/// ditto
5607template hexString(dstring hexData)
5608if (hexData.isHexLiteral)
5609{
5fee5ec3 5610 enum dstring hexString = mixin(hexToString(hexData));
b4c522fa
IB
5611}
5612
5613///
5614@safe unittest
5615{
5616 // conversion at compile time
5617 auto string1 = hexString!"304A314B";
5618 assert(string1 == "0J1K");
5619 auto string2 = hexString!"304A314B"w;
5620 assert(string2 == "0J1K"w);
5621 auto string3 = hexString!"304A314B"d;
5622 assert(string3 == "0J1K"d);
5623}
5624
5fee5ec3
IB
5625@safe nothrow pure private
5626{
5627 /* These are meant to be used with CTFE.
5628 * They cause the instantiations of hexStrLiteral()
5629 * to be in Phobos, not user code.
5630 */
5631 string hexToString(string s)
5632 {
5633 return hexStrLiteral(s);
5634 }
5635
5636 wstring hexToString(wstring s)
5637 {
5638 return hexStrLiteral(s);
5639 }
5640
5641 dstring hexToString(dstring s)
5642 {
5643 return hexStrLiteral(s);
5644 }
5645}
5646
b4c522fa 5647/*
5fee5ec3
IB
5648 Turn a hexadecimal string into a regular string literal.
5649 I.e. "dead beef" is transformed into "\xde\xad\xbe\xef"
5650 suitable for use in a mixin.
5651 Params:
5652 hexData is string, wstring, or dstring and validated by isHexLiteral()
5653 */
5654@trusted nothrow pure
5655private auto hexStrLiteral(String)(scope String hexData)
b4c522fa
IB
5656{
5657 import std.ascii : isHexDigit;
5fee5ec3 5658 alias C = Unqual!(ElementEncodingType!String); // char, wchar or dchar
b4c522fa 5659 C[] result;
5fee5ec3
IB
5660 result.length = 1 + hexData.length * 2 + 1; // don't forget the " "
5661 /* Use a pointer because we know it won't overrun,
5662 * and this will reduce the size of the function substantially
5663 * by not doing the array bounds checks.
5664 * This is why this function is @trusted.
5665 */
5666 auto r = result.ptr;
5667 r[0] = '"';
5668 size_t cnt = 0;
b4c522fa
IB
5669 foreach (c; hexData)
5670 {
5671 if (c.isHexDigit)
5672 {
5fee5ec3 5673 if ((cnt & 1) == 0)
b4c522fa 5674 {
5fee5ec3
IB
5675 r[1 + cnt] = '\\';
5676 r[1 + cnt + 1] = 'x';
5677 cnt += 2;
b4c522fa 5678 }
5fee5ec3 5679 r[1 + cnt] = c;
b4c522fa
IB
5680 ++cnt;
5681 }
5682 }
5fee5ec3
IB
5683 r[1 + cnt] = '"';
5684 result.length = 1 + cnt + 1; // trim off any excess length
b4c522fa
IB
5685 return result;
5686}
5687
5fee5ec3 5688
b4c522fa
IB
5689@safe unittest
5690{
5691 // compile time
5692 assert(hexString!"46 47 48 49 4A 4B" == "FGHIJK");
5693 assert(hexString!"30\r\n\t\f\v31 32 33 32 31 30" == "0123210");
5694 assert(hexString!"ab cd" == hexString!"ABCD");
5695}
5696
5697
5698/**
5699 * Convert integer to a range of characters.
5700 * Intended to be lightweight and fast.
5701 *
5702 * Params:
5703 * radix = 2, 8, 10, 16
5704 * Char = character type for output
5705 * letterCase = lower for deadbeef, upper for DEADBEEF
5706 * value = integer to convert. Can be uint or ulong. If radix is 10, can also be
5707 * int or long.
5708 * Returns:
5709 * Random access range with slicing and everything
5710 */
5711
5712auto toChars(ubyte radix = 10, Char = char, LetterCase letterCase = LetterCase.lower, T)(T value)
5713 pure nothrow @nogc @safe
5714if ((radix == 2 || radix == 8 || radix == 10 || radix == 16) &&
5fee5ec3
IB
5715 (is(immutable T == immutable uint) || is(immutable T == immutable ulong) ||
5716 radix == 10 && (is(immutable T == immutable int) || is(immutable T == immutable long))))
b4c522fa
IB
5717{
5718 alias UT = Unqual!T;
5719
5720 static if (radix == 10)
5721 {
5722 /* uint.max is 42_9496_7295
5723 * int.max is 21_4748_3647
5724 * ulong.max is 1844_6744_0737_0955_1615
5725 * long.max is 922_3372_0368_5477_5807
5726 */
5727 static struct Result
5728 {
5729 void initialize(UT value)
5730 {
5731 bool neg = false;
5732 if (value < 10)
5733 {
5734 if (value >= 0)
5735 {
5736 lwr = 0;
5737 upr = 1;
5738 buf[0] = cast(char)(cast(uint) value + '0');
5739 return;
5740 }
5741 value = -value;
5742 neg = true;
5743 }
5744 auto i = cast(uint) buf.length - 1;
5745 while (cast(Unsigned!UT) value >= 10)
5746 {
5747 buf[i] = cast(ubyte)('0' + cast(Unsigned!UT) value % 10);
5748 value = unsigned(value) / 10;
5749 --i;
5750 }
5751 buf[i] = cast(char)(cast(uint) value + '0');
5752 if (neg)
5753 {
5754 buf[i - 1] = '-';
5755 --i;
5756 }
5757 lwr = i;
5758 upr = cast(uint) buf.length;
5759 }
5760
5761 @property size_t length() { return upr - lwr; }
5762
5763 alias opDollar = length;
5764
5765 @property bool empty() { return upr == lwr; }
5766
5767 @property Char front() { return buf[lwr]; }
5768
5769 void popFront() { ++lwr; }
5770
5771 @property Char back() { return buf[upr - 1]; }
5772
5773 void popBack() { --upr; }
5774
5775 @property Result save() { return this; }
5776
5777 Char opIndex(size_t i) { return buf[lwr + i]; }
5778
5779 Result opSlice(size_t lwr, size_t upr)
5780 {
5781 Result result = void;
5782 result.buf = buf;
5783 result.lwr = cast(uint)(this.lwr + lwr);
5784 result.upr = cast(uint)(this.lwr + upr);
5785 return result;
5786 }
5787
5788 private:
5789 uint lwr = void, upr = void;
5790 char[(UT.sizeof == 4) ? 10 + isSigned!T : 20] buf = void;
5791 }
5792
5fee5ec3 5793 Result result;
b4c522fa
IB
5794 result.initialize(value);
5795 return result;
5796 }
5797 else
5798 {
5799 static if (radix == 2)
5800 enum SHIFT = 1;
5801 else static if (radix == 8)
5802 enum SHIFT = 3;
5803 else static if (radix == 16)
5804 enum SHIFT = 4;
5805 else
5fee5ec3 5806 static assert(false, "radix must be 2, 8, 10, or 16");
b4c522fa
IB
5807 static struct Result
5808 {
5809 this(UT value)
5810 {
5811 this.value = value;
5812
5813 ubyte len = 1;
5814 while (value >>>= SHIFT)
5815 ++len;
5816 this.len = len;
5817 }
5818
5819 @property size_t length() { return len; }
5820
5821 @property bool empty() { return len == 0; }
5822
5823 @property Char front() { return opIndex(0); }
5824
5825 void popFront() { --len; }
5826
5827 @property Char back() { return opIndex(len - 1); }
5828
5829 void popBack()
5830 {
5831 value >>>= SHIFT;
5832 --len;
5833 }
5834
5835 @property Result save() { return this; }
5836
5837 Char opIndex(size_t i)
5838 {
5839 Char c = (value >>> ((len - i - 1) * SHIFT)) & ((1 << SHIFT) - 1);
5840 return cast(Char)((radix < 10 || c < 10) ? c + '0'
5841 : (letterCase == LetterCase.upper ? c + 'A' - 10
5842 : c + 'a' - 10));
5843 }
5844
5845 Result opSlice(size_t lwr, size_t upr)
5846 {
5847 Result result = void;
5848 result.value = value >>> ((len - upr) * SHIFT);
5849 result.len = cast(ubyte)(upr - lwr);
5850 return result;
5851 }
5852
5853 private:
5854 UT value;
5855 ubyte len;
5856 }
5857
5858 return Result(value);
5859 }
5860}
5861
5fee5ec3
IB
5862///
5863@safe unittest
5864{
5865 import std.algorithm.comparison : equal;
5866
5867 assert(toChars(1).equal("1"));
5868 assert(toChars(1_000_000).equal("1000000"));
5869
5870 assert(toChars!(2)(2U).equal("10"));
5871 assert(toChars!(16)(255U).equal("ff"));
5872 assert(toChars!(16, char, LetterCase.upper)(255U).equal("FF"));
5873}
5874
b4c522fa
IB
5875
5876@safe unittest
5877{
5878 import std.array;
5879 import std.range;
5880
5fee5ec3
IB
5881 assert(toChars(123) == toChars(123));
5882
b4c522fa
IB
5883 {
5884 assert(toChars!2(0u).array == "0");
5885 assert(toChars!2(0Lu).array == "0");
5886 assert(toChars!2(1u).array == "1");
5887 assert(toChars!2(1Lu).array == "1");
5888
5889 auto r = toChars!2(2u);
5890 assert(r.length == 2);
5891 assert(r[0] == '1');
5892 assert(r[1 .. 2].array == "0");
5893 auto s = r.save;
5894 assert(r.array == "10");
5895 assert(s.retro.array == "01");
5896 }
5897 {
5898 assert(toChars!8(0u).array == "0");
5899 assert(toChars!8(0Lu).array == "0");
5900 assert(toChars!8(1u).array == "1");
5901 assert(toChars!8(1234567Lu).array == "4553207");
5902
5903 auto r = toChars!8(8u);
5904 assert(r.length == 2);
5905 assert(r[0] == '1');
5906 assert(r[1 .. 2].array == "0");
5907 auto s = r.save;
5908 assert(r.array == "10");
5909 assert(s.retro.array == "01");
5910 }
5911 {
5912 assert(toChars!10(0u).array == "0");
5913 assert(toChars!10(0Lu).array == "0");
5914 assert(toChars!10(1u).array == "1");
5915 assert(toChars!10(1234567Lu).array == "1234567");
5916 assert(toChars!10(uint.max).array == "4294967295");
5917 assert(toChars!10(ulong.max).array == "18446744073709551615");
5918
5919 auto r = toChars(10u);
5920 assert(r.length == 2);
5921 assert(r[0] == '1');
5922 assert(r[1 .. 2].array == "0");
5923 auto s = r.save;
5924 assert(r.array == "10");
5925 assert(s.retro.array == "01");
5926 }
5927 {
5928 assert(toChars!10(0).array == "0");
5929 assert(toChars!10(0L).array == "0");
5930 assert(toChars!10(1).array == "1");
5931 assert(toChars!10(1234567L).array == "1234567");
5932 assert(toChars!10(int.max).array == "2147483647");
5933 assert(toChars!10(long.max).array == "9223372036854775807");
5934 assert(toChars!10(-int.max).array == "-2147483647");
5935 assert(toChars!10(-long.max).array == "-9223372036854775807");
5936 assert(toChars!10(int.min).array == "-2147483648");
5937 assert(toChars!10(long.min).array == "-9223372036854775808");
5938
5939 auto r = toChars!10(10);
5940 assert(r.length == 2);
5941 assert(r[0] == '1');
5942 assert(r[1 .. 2].array == "0");
5943 auto s = r.save;
5944 assert(r.array == "10");
5945 assert(s.retro.array == "01");
5946 }
5947 {
5948 assert(toChars!(16)(0u).array == "0");
5949 assert(toChars!(16)(0Lu).array == "0");
5950 assert(toChars!(16)(10u).array == "a");
5951 assert(toChars!(16, char, LetterCase.upper)(0x12AF34567Lu).array == "12AF34567");
5952
5953 auto r = toChars!(16)(16u);
5954 assert(r.length == 2);
5955 assert(r[0] == '1');
5956 assert(r[1 .. 2].array == "0");
5957 auto s = r.save;
5958 assert(r.array == "10");
5959 assert(s.retro.array == "01");
5960 }
5961}
5962
5963@safe unittest // opSlice (issue 16192)
5964{
5965 import std.meta : AliasSeq;
5966
5967 static struct Test { ubyte radix; uint number; }
5968
5969 alias tests = AliasSeq!(
5970 Test(2, 0b1_0110_0111u),
5971 Test(2, 0b10_1100_1110u),
5972 Test(8, octal!123456701u),
5973 Test(8, octal!1234567012u),
5974 Test(10, 123456789u),
5975 Test(10, 1234567890u),
5976 Test(16, 0x789ABCDu),
5977 Test(16, 0x789ABCDEu),
5978 );
5979
5980 foreach (test; tests)
5981 {
5982 enum ubyte radix = test.radix;
5983 auto original = toChars!radix(test.number);
5984
5985 // opSlice vs popFront
5986 auto r = original.save;
5987 size_t i = 0;
5988 for (; !r.empty; r.popFront(), ++i)
5989 {
5990 assert(original[i .. original.length].tupleof == r.tupleof);
5991 // tupleof is used to work around issue 16216.
5992 }
5993
5994 // opSlice vs popBack
5995 r = original.save;
5996 i = 0;
5997 for (; !r.empty; r.popBack(), ++i)
5998 {
5999 assert(original[0 .. original.length - i].tupleof == r.tupleof);
6000 }
6001
6002 // opSlice vs both popFront and popBack
6003 r = original.save;
6004 i = 0;
6005 for (; r.length >= 2; r.popFront(), r.popBack(), ++i)
6006 {
6007 assert(original[i .. original.length - i].tupleof == r.tupleof);
6008 }
6009 }
6010}
235d5a96
IB
6011
6012// Converts an unsigned integer to a compile-time string constant.
6013package enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length];
6014
6015// Check that .stringof does what we expect, since it's not guaranteed by the
6016// language spec.
6017@safe /*@betterC*/ unittest
6018{
6019 assert(toCtString!0 == "0");
6020 assert(toCtString!123456 == "123456");
6021}
This page took 1.114294 seconds and 5 git commands to generate.