1 /**
2  * Written in the D programming language.
3  * This module provides functions to converting different values to const(ubyte)[]
4  *
5  * Copyright: Copyright Igor Stepanov 2013-2013.
6  * License:   $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
7  * Authors:   Igor Stepanov
8  * Source: $(DRUNTIMESRC core/internal/_convert.d)
9  */
10 module core.internal.convert;
11 import core.internal.traits : Unqual;
12 
13 /+
14 A @nogc function can allocate memory during CTFE.
15 +/
16 @nogc nothrow pure @trusted
ctfe_alloc()17 private ubyte[] ctfe_alloc()(size_t n)
18 {
19     if (!__ctfe)
20     {
21         assert(0, "CTFE only");
22     }
23     else
24     {
25         static ubyte[] alloc(size_t x) nothrow pure
26         {
27             if (__ctfe) // Needed to prevent _d_newarray from appearing in compiled prorgam.
28                 return new ubyte[x];
29             else
30                 assert(0);
31         }
32         return (cast(ubyte[] function(size_t) @nogc nothrow pure) &alloc)(n);
33     }
34 }
35 
36 @trusted pure nothrow @nogc
37 const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == float) || is(Unqual!T == double) || is(Unqual!T == real) ||
38                                         is(Unqual!T == ifloat) || is(Unqual!T == idouble) || is(Unqual!T == ireal))
39 {
40     if (__ctfe)
41     {
42         static if (floatFormat!T == FloatFormat.Float || floatFormat!T == FloatFormat.Double)
43         {
44             static if (is(T : ireal)) // https://issues.dlang.org/show_bug.cgi?id=19932
45                 const f = val.im;
46             else
47                 alias f = val;
48             static if (T.sizeof == uint.sizeof)
49                 uint bits = *cast(const uint*) &f;
50             else static if (T.sizeof == ulong.sizeof)
51                 ulong bits = *cast(const ulong*) &f;
52             ubyte[] result = ctfe_alloc(T.sizeof);
version(BigEndian)53             version (BigEndian)
54             {
55                 foreach_reverse (ref b; result)
56                 {
57                     b = cast(ubyte) bits;
58                     bits >>= 8;
59                 }
60             }
61             else
62             {
foreach(ref b;result)63                 foreach (ref b; result)
64                 {
65                     b = cast(ubyte) bits;
66                     bits >>= 8;
67                 }
68             }
69             return result;
70         }
71         else static if (floatFormat!T == FloatFormat.DoubleDouble)
72         {
73             // Parse DoubleDoubles as a pair of doubles.
74             // The layout of the type is:
75             //
76             //   [1|    11    |       52      ][1|    11    |       52       ]
77             //   [S| Exponent | Fraction (hi) ][S| Exponent | Fraction (low) ]
78             //
79             // We can get the least significant bits by subtracting the IEEE
80             // double precision portion from the real value.
81 
82             import core.math : toPrec;
83 
84             ubyte[] buff = ctfe_alloc(T.sizeof);
85             enum msbSize = double.sizeof;
86 
87             static if (is(Unqual!T == ireal))
88                 double hi = toPrec!double(val.im);
89             else
90                 double hi = toPrec!double(val);
91             buff[0 .. msbSize] = toUbyte(hi)[];
92 
93             if (val is cast(T)0.0 || val is cast(T)-0.0 ||
94                 val is T.nan || val is -T.nan ||
95                 val is T.infinity || val > T.max ||
96                 val is -T.infinity || val < -T.max)
97             {
98                 // Zero, NaN, and Inf are all representable as doubles, so the
99                 // least significant part can be 0.0.
100                 buff[msbSize .. $] = 0;
101             }
102             else
103             {
104                 static if (is(Unqual!T == ireal))
105                     double low = toPrec!double(val.im - hi);
106                 else
107                     double low = toPrec!double(val - hi);
108                 buff[msbSize .. $] = toUbyte(low)[];
109             }
110 
111             // Arrays don't index differently between little and big-endian targets.
112             return buff;
113         }
114         else
115         {
116             auto parsed = parse(val);
117 
118             ulong mantissa = parsed.mantissa;
119             uint exp = parsed.exponent;
120             uint sign = parsed.sign;
121 
122             ubyte[] buff = ctfe_alloc(T.sizeof);
123             size_t off_bytes = 0;
124             size_t off_bits  = 0;
125             // Quadruples won't fit in one ulong, so check for that.
126             enum mantissaMax = FloatTraits!T.MANTISSA < ulong.sizeof*8 ?
127                                FloatTraits!T.MANTISSA : ulong.sizeof*8;
128 
129             for (; off_bytes < mantissaMax/8; ++off_bytes)
130             {
131                 buff[off_bytes] = cast(ubyte)mantissa;
132                 mantissa >>= 8;
133             }
134 
135             static if (floatFormat!T == FloatFormat.Quadruple)
136             {
137                 ulong mantissa2 = parsed.mantissa2;
138                 off_bytes--; // go back one, since mantissa only stored data in 56
139                              // bits, ie 7 bytes
140                 for (; off_bytes < FloatTraits!T.MANTISSA/8; ++off_bytes)
141                 {
142                     buff[off_bytes] = cast(ubyte)mantissa2;
143                     mantissa2 >>= 8;
144                 }
145             }
146             else
147             {
148                 off_bits = FloatTraits!T.MANTISSA%8;
149                 buff[off_bytes] = cast(ubyte)mantissa;
150             }
151 
152             for (size_t i=0; i<FloatTraits!T.EXPONENT/8; ++i)
153             {
154                 ubyte cur_exp = cast(ubyte)exp;
155                 exp >>= 8;
156                 buff[off_bytes] |= (cur_exp << off_bits);
157                 ++off_bytes;
158                 buff[off_bytes] |= cur_exp >> 8 - off_bits;
159             }
160 
161 
162             exp <<= 8 - FloatTraits!T.EXPONENT%8 - 1;
163             buff[off_bytes] |= exp;
164             sign <<= 7;
165             buff[off_bytes] |= sign;
166 
version(BigEndian)167             version (BigEndian)
168             {
169                 for (size_t left = 0, right = buff.length - 1; left < right; left++, right--)
170                 {
171                     const swap = buff[left];
172                     buff[left] = buff[right];
173                     buff[right] = swap;
174                 }
175             }
176             return buff;
177         }
178     }
179     else
180     {
181         return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
182     }
183 }
184 
185 @safe pure nothrow @nogc
186 private Float parse(bool is_denormalized = false, T)(T x) if (is(Unqual!T == ifloat) || is(Unqual!T == idouble) || is(Unqual!T == ireal))
187 {
188     return parse(x.im);
189 }
190 
191 @safe pure nothrow @nogc
192 private Float parse(bool is_denormalized = false, T:real)(T x_) if (floatFormat!T != FloatFormat.Real80)
193 {
194     Unqual!T x = x_;
195     static assert(floatFormat!T != FloatFormat.DoubleDouble,
196            "doubledouble float format not supported in CTFE");
197     if (x is cast(T)0.0) return FloatTraits!T.ZERO;
198     if (x is cast(T)-0.0) return FloatTraits!T.NZERO;
199     if (x is T.nan) return FloatTraits!T.NAN;
200     if (x is -T.nan) return FloatTraits!T.NNAN;
201     if (x is T.infinity || x > T.max) return FloatTraits!T.INF;
202     if (x is -T.infinity || x < -T.max) return FloatTraits!T.NINF;
203 
204     uint sign = x < 0;
205     x = sign ? -x : x;
206     int e = binLog2(x);
207     real x2 = x;
208     uint exp = cast(uint)(e + (2^^(FloatTraits!T.EXPONENT-1) - 1));
209 
210     if (!exp)
211     {
212         if (is_denormalized)
213             return Float(0, 0, sign);
214         else
215             return denormalizedMantissa(x, sign);
216     }
217 
218     x2 /= binPow2(e);
219 
220     static if (!is_denormalized)
221         x2 -= 1.0;
222 
223     static if (floatFormat!T == FloatFormat.Quadruple)
224     {
225         // Store the 112-bit mantissa in two ulongs, specifically the lower 56
226         // bits of each, with the most significant bits in mantissa2. There's
227         // an edge case exposed by the labeled test below, where only a subnormal
228         // with the highest bit set being the 57th bit will "overflow" to the
229         // 57th bit in mantissa2 with the following logic, but that special case
230         // is handled by an additional check in denormalizedMantissa for
231         // Quadruples below.
232 
233         x2 *=  2UL<<(FloatTraits!T.MANTISSA - (ulong.sizeof - 1)*8 - 1);
234         ulong mant2 = cast(ulong) x2;
235         x2 -= mant2;
236 
237         x2 *=  2UL<<((ulong.sizeof - 1)*8 - 1);
238         ulong mant = cast(ulong) x2;
239         return Float(mant, exp, sign, mant2);
240     }
241     else
242     {
243         x2 *=  2UL<<(FloatTraits!T.MANTISSA);
244         ulong mant = shiftrRound(cast(ulong)x2);
245         return Float(mant, exp, sign);
246     }
247 }
248 
249 @safe pure nothrow @nogc
250 private Float parse(bool _ = false, T:real)(T x_) if (floatFormat!T == FloatFormat.Real80)
251 {
252     Unqual!T x = x_;
253     //HACK @@@3632@@@
254 
255     if (x == 0.0L)
256     {
257         real y = 1.0L/x;
258         if (y == real.infinity) // -0.0
259             return FloatTraits!T.ZERO;
260         else
261             return FloatTraits!T.NZERO; //0.0
262     }
263 
264     if (x != x) //HACK: should be if (x is real.nan) and if (x is -real.nan)
265     {
266         auto y = cast(double)x;
267         if (y is double.nan)
268             return FloatTraits!T.NAN;
269         else
270             return FloatTraits!T.NNAN;
271     }
272 
273     if (x == real.infinity) return FloatTraits!T.INF;
274     if (x == -real.infinity) return FloatTraits!T.NINF;
275 
276     enum EXPONENT_MED = (2^^(FloatTraits!T.EXPONENT-1) - 1);
277     uint sign = x < 0;
278     x = sign ? -x : x;
279 
280     int e = binLog2(x);
281     uint exp = cast(uint)(e + EXPONENT_MED);
282     if (!exp)
283     {
284         return denormalizedMantissa(x, sign);
285     }
286     int pow = (FloatTraits!T.MANTISSA-1-e);
287     x *=  binPow2((pow / EXPONENT_MED)*EXPONENT_MED); //To avoid overflow in 2.0L ^^ pow
288     x *=  binPow2(pow % EXPONENT_MED);
289     ulong mant = cast(ulong)x;
290     return Float(mant, exp, sign);
291 }
292 
293 private struct Float
294 {
295     ulong mantissa;
296     uint exponent;
297     uint sign;
298     ulong mantissa2;
299 }
300 
FloatTraits(T)301 private template FloatTraits(T) if (floatFormat!T == FloatFormat.Float)
302 {
303     enum DATASIZE = 4;
304     enum EXPONENT = 8;
305     enum MANTISSA = 23;
306     enum ZERO     = Float(0, 0, 0);
307     enum NZERO    = Float(0, 0, 1);
308     enum NAN      = Float(0x400000UL, 0xff, 0);
309     enum NNAN     = Float(0x400000UL, 0xff, 1);
310     enum INF      = Float(0, 255, 0);
311     enum NINF     = Float(0, 255, 1);
312 }
313 
314 private template FloatTraits(T) if (floatFormat!T == FloatFormat.Double)
315 {
316     enum DATASIZE = 8;
317     enum EXPONENT = 11;
318     enum MANTISSA = 52;
319     enum ZERO     = Float(0, 0, 0);
320     enum NZERO    = Float(0, 0, 1);
321     enum NAN      = Float(0x8000000000000UL, 0x7ff, 0);
322     enum NNAN     = Float(0x8000000000000UL, 0x7ff, 1);
323     enum INF      = Float(0, 0x7ff, 0);
324     enum NINF     = Float(0, 0x7ff, 1);
325 }
326 
327 private template FloatTraits(T) if (floatFormat!T == FloatFormat.Real80)
328 {
329     enum DATASIZE = 10;
330     enum EXPONENT = 15;
331     enum MANTISSA = 64;
332     enum ZERO     = Float(0, 0, 0);
333     enum NZERO    = Float(0, 0, 1);
334     enum NAN      = Float(0xC000000000000000UL, 0x7fff, 0);
335     enum NNAN     = Float(0xC000000000000000UL, 0x7fff, 1);
336     enum INF      = Float(0x8000000000000000UL, 0x7fff, 0);
337     enum NINF     = Float(0x8000000000000000UL, 0x7fff, 1);
338 }
339 
340 private template FloatTraits(T) if (floatFormat!T == FloatFormat.DoubleDouble) //Unsupported in CTFE
341 {
342     enum DATASIZE = 16;
343     enum EXPONENT = 11;
344     enum MANTISSA = 106;
345     enum ZERO     = Float(0, 0, 0);
346     enum NZERO    = Float(0, 0, 1);
347     enum NAN      = Float(0x8000000000000UL, 0x7ff, 0);
348     enum NNAN     = Float(0x8000000000000UL, 0x7ff, 1);
349     enum INF      = Float(0, 0x7ff, 0);
350     enum NINF     = Float(0, 0x7ff, 1);
351 }
352 
353 private template FloatTraits(T) if (floatFormat!T == FloatFormat.Quadruple)
354 {
355     enum DATASIZE = 16;
356     enum EXPONENT = 15;
357     enum MANTISSA = 112;
358     enum ZERO     = Float(0, 0, 0);
359     enum NZERO    = Float(0, 0, 1);
360     enum NAN      = Float(0, 0x7fff, 0, 0x80000000000000UL);
361     enum NNAN     = Float(0, 0x7fff, 1, 0x80000000000000UL);
362     enum INF      = Float(0, 0x7fff, 0);
363     enum NINF     = Float(0, 0x7fff, 1);
364 }
365 
366 
367 @safe pure nothrow @nogc
binPow2(int pow)368 private real binPow2(int pow)
369 {
370     static real binPosPow2(int pow) @safe pure nothrow @nogc
371     {
372         assert(pow > 0);
373 
374         if (pow == 1) return 2.0L;
375 
376         int subpow = pow/2;
377         real p = binPosPow2(subpow);
378         real ret = p*p;
379 
380         if (pow%2)
381         {
382             ret *= 2.0L;
383         }
384 
385         return ret;
386     }
387 
388     if (!pow) return 1.0L;
389     if (pow > 0) return binPosPow2(pow);
390     return 1.0L/binPosPow2(-pow);
391 }
392 
393 
394 //Need in CTFE, because CTFE float and double expressions computed more precisely that run-time expressions.
395 @safe pure nothrow @nogc
shiftrRound(ulong x)396 private ulong shiftrRound(ulong x)
397 {
398     return (x >> 1) + (x & 1);
399 }
400 
401 @safe pure nothrow @nogc
binLog2(T)402 private uint binLog2(T)(const T x)
403 {
404     assert(x > 0);
405     int max = 2 ^^ (FloatTraits!T.EXPONENT-1)-1;
406     int min = -max+1;
407     int med = (min + max) / 2;
408 
409     if (x < T.min_normal) return -max;
410 
411     while ((max - min) > 1)
412     {
413         if (binPow2(med) > x)
414         {
415             max = med;
416         }
417         else
418         {
419             min = med;
420         }
421         med = (min + max) / 2;
422     }
423 
424     if (x < binPow2(max))
425         return min;
426     return max;
427 }
428 
429 @safe pure nothrow @nogc
430 private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == FloatFormat.Real80)
431 {
432     x *= 2.0L^^FloatTraits!T.MANTISSA;
433     auto fl = parse(x);
434     uint pow = FloatTraits!T.MANTISSA - fl.exponent + 1;
435     return Float(fl.mantissa >> pow, 0, sign);
436 }
437 
438 @safe pure nothrow @nogc
439 private Float denormalizedMantissa(T)(T x, uint sign)
440     if (floatFormat!T == FloatFormat.Float || floatFormat!T == FloatFormat.Double)
441 {
442     x *= 2.0L^^FloatTraits!T.MANTISSA;
443     auto fl = parse!true(x);
444     ulong mant = fl.mantissa >> (FloatTraits!T.MANTISSA - fl.exponent);
445     return Float(shiftrRound(mant), 0, sign);
446 }
447 
448 @safe pure nothrow @nogc
449 private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == FloatFormat.Quadruple)
450 {
451     x *= 2.0L^^FloatTraits!T.MANTISSA;
452     auto fl = parse!true(x);
453     uint offset = FloatTraits!T.MANTISSA - fl.exponent + 1;
454     enum mantissaSize = (ulong.sizeof - 1) * 8;
455 
456     if (offset < mantissaSize)
457     {   // Create a new mantissa ulong with the trailing mantissa2 bits that
458         // need to be shifted into mantissa, by shifting the needed bits left,
459         // zeroing out the first byte, and then ORing it with mantissa shifted
460         // right by offset.
461 
462         ulong shiftedMantissa = ((fl.mantissa2 << (mantissaSize - offset)) &
463                                  0x00FFFFFFFFFFFFFFUL) | fl.mantissa >> offset;
464         return Float(shiftedMantissa, 0, sign, fl.mantissa2 >> offset);
465     }
466     else if (offset > mantissaSize)
467         return Float(fl.mantissa2 >> offset - mantissaSize , 0, sign, 0);
468     else
469         // Handle special case mentioned in parse() above by zeroing out the
470         // 57'th bit of mantissa2, "shifting" it into mantissa, and setting the
471         // first bit of mantissa2.
472         return Float(fl.mantissa2 & 0x00FFFFFFFFFFFFFFUL , 0, sign, 1);
473 }
474 
version(unittest)475 version (unittest)
476 {
477     private const(ubyte)[] toUbyte2(T)(T val)
478     {
479         return toUbyte(val).dup;
480     }
481 
482     private void testNumberConvert(string v)()
483     {
484         enum ctval = mixin(v);
485 
486         alias TYPE = typeof(ctval);
487         auto rtval = ctval;
488         auto rtbytes = *cast(ubyte[TYPE.sizeof]*)&rtval;
489 
490         enum ctbytes = toUbyte2(ctval);
491 
492         // don't test pad bytes because can be anything
493         enum testsize =
494             (FloatTraits!TYPE.EXPONENT + FloatTraits!TYPE.MANTISSA + 1)/8;
495         assert(rtbytes[0..testsize] == ctbytes[0..testsize]);
496     }
497 
498     private void testConvert()
499     {
500         /**Test special values*/
501         testNumberConvert!("-float.infinity");
502         testNumberConvert!("float.infinity");
503         testNumberConvert!("-0.0F");
504         testNumberConvert!("0.0F");
505         //testNumberConvert!("-float.nan"); //BUG @@@3632@@@
506         testNumberConvert!("float.nan");
507 
508         testNumberConvert!("-double.infinity");
509         testNumberConvert!("double.infinity");
510         testNumberConvert!("-0.0");
511         testNumberConvert!("0.0");
512         //testNumberConvert!("-double.nan"); //BUG @@@3632@@@
513         testNumberConvert!("double.nan");
514 
515         testNumberConvert!("-real.infinity");
516         testNumberConvert!("real.infinity");
517         testNumberConvert!("-0.0L");
518         testNumberConvert!("0.0L");
519         //testNumberConvert!("-real.nan"); //BUG @@@3632@@@
520         testNumberConvert!("real.nan");
521 
522         /**
523             Test min and max values values: min value has an '1' mantissa and minimal exponent,
524             Max value has an all '1' bits mantissa and max exponent.
525         */
526         testNumberConvert!("float.min_normal");
527         testNumberConvert!("float.max");
528 
529         /**Test common values*/
530         testNumberConvert!("-0.17F");
531         testNumberConvert!("3.14F");
532 
533         /**Test immutable and const*/
534         testNumberConvert!("cast(const)3.14F");
535         testNumberConvert!("cast(immutable)3.14F");
536 
537         /**The same tests for double and real*/
538         testNumberConvert!("double.min_normal");
539         testNumberConvert!("double.max");
540         testNumberConvert!("-0.17");
541         testNumberConvert!("3.14");
542         testNumberConvert!("cast(const)3.14");
543         testNumberConvert!("cast(immutable)3.14");
544 
545         testNumberConvert!("real.min_normal");
546         testNumberConvert!("real.max");
547         testNumberConvert!("-0.17L");
548         testNumberConvert!("3.14L");
549         testNumberConvert!("cast(const)3.14L");
550         testNumberConvert!("cast(immutable)3.14L");
551 
552         /**Test denormalized values*/
553 
554         /**Max denormalized value, first bit is 1*/
555         testNumberConvert!("float.min_normal/2");
556         /**Min denormalized value, last bit is 1*/
557         testNumberConvert!("float.min_normal/2UL^^23");
558 
559         /**Denormalized values with round*/
560         testNumberConvert!("float.min_normal/19");
561         testNumberConvert!("float.min_normal/17");
562 
563         testNumberConvert!("double.min_normal/2");
564         testNumberConvert!("double.min_normal/2UL^^52");
565         testNumberConvert!("double.min_normal/19");
566         testNumberConvert!("double.min_normal/17");
567 
568         testNumberConvert!("real.min_normal/2");
569         testNumberConvert!("real.min_normal/2UL^^63");
570         // check subnormal storage edge case for Quadruple
571         testNumberConvert!("real.min_normal/2UL^^56");
572         testNumberConvert!("real.min_normal/19");
573         testNumberConvert!("real.min_normal/17");
574 
575         /**Test imaginary values: convert algorithm is same with real values*/
576         testNumberConvert!("0.0Fi");
577         testNumberConvert!("0.0i");
578         testNumberConvert!("0.0Li");
579 
580         /**True random values*/
581         testNumberConvert!("-0x9.0f7ee55df77618fp-13829L");
582         testNumberConvert!("0x7.36e6e2640120d28p+8797L");
583         testNumberConvert!("-0x1.05df6ce4702ccf8p+15835L");
584         testNumberConvert!("0x9.54bb0d88806f714p-7088L");
585 
586         testNumberConvert!("-0x9.0f7ee55df7ffp-338");
587         testNumberConvert!("0x7.36e6e264012dp+879");
588         testNumberConvert!("-0x1.05df6ce4708ep+658");
589         testNumberConvert!("0x9.54bb0d888061p-708");
590 
591         testNumberConvert!("-0x9.0f7eefp-101F");
592         testNumberConvert!("0x7.36e6ep+87F");
593         testNumberConvert!("-0x1.05df6p+112F");
594         testNumberConvert!("0x9.54bb0p-70F");
595 
596         /**Big overflow or underflow*/
597         testNumberConvert!("cast(double)-0x9.0f7ee55df77618fp-13829L");
598         testNumberConvert!("cast(double)0x7.36e6e2640120d28p+8797L");
599         testNumberConvert!("cast(double)-0x1.05df6ce4702ccf8p+15835L");
600         testNumberConvert!("cast(double)0x9.54bb0d88806f714p-7088L");
601 
602         testNumberConvert!("cast(float)-0x9.0f7ee55df77618fp-13829L");
603         testNumberConvert!("cast(float)0x7.36e6e2640120d28p+8797L");
604         testNumberConvert!("cast(float)-0x1.05df6ce4702ccf8p+15835L");
605         testNumberConvert!("cast(float)0x9.54bb0d88806f714p-7088L");
606     }
607 
608 
609     unittest
610     {
611         testConvert();
612     }
613 }
614 
615 
616 
617 private enum FloatFormat
618 {
619     Float,
620     Double,
621     Real80,
622     DoubleDouble,
623     Quadruple
624 }
625 
floatFormat(T)626 template floatFormat(T) if (is(T:real) || is(T:ireal))
627 {
628     static if (T.mant_dig == 24)
629         enum floatFormat = FloatFormat.Float;
630     else static if (T.mant_dig == 53)
631     {
632         // Double precision, or real == double
633         static if (T.sizeof == double.sizeof)
634             enum floatFormat = FloatFormat.Double;
635         // 80-bit real with rounding precision set to 53 bits.
636         else static if (T.sizeof == real.sizeof)
637             enum floatFormat = FloatFormat.Real80;
638     }
639     else static if (T.mant_dig == 64)
640         enum floatFormat = FloatFormat.Real80;
641     else static if (T.mant_dig == 106)
642         enum floatFormat = FloatFormat.DoubleDouble;
643     else static if (T.mant_dig == 113)
644         enum floatFormat = FloatFormat.Quadruple;
645     else
646         static assert(0);
647 
648 }
649 
650 package template floatSize(T) if (is(T:real) || is(T:ireal))
651 {
652     enum floatSize = FloatTraits!(T).DATASIZE;
653 }
654 
655 //  all toUbyte functions must be evaluable at compile time
656 @trusted pure nothrow @nogc
657 const(ubyte)[] toUbyte(T)(const T[] arr) if (T.sizeof == 1)
658 {
659     return cast(const(ubyte)[])arr;
660 }
661 
662 @trusted pure nothrow @nogc
663 const(ubyte)[] toUbyte(T)(const T[] arr) if (T.sizeof > 1)
664 {
665     if (__ctfe)
666     {
667         ubyte[] ret = ctfe_alloc(T.sizeof * arr.length);
668         static if (is(T EType == enum)) // Odd style is to avoid template instantiation in most cases.
669             alias E = OriginalType!EType;
670         else
671             alias E = T;
672         static if (is(E == struct) || is(E == union) || __traits(isStaticArray, E) || !is(typeof(arr[0] is null)))
673         {
674             size_t offset = 0;
foreach(ref cur;arr)675             foreach (ref cur; arr)
676             {
677                 ret[offset .. offset + T.sizeof] = toUbyte(cur)[0 .. T.sizeof];
678                 offset += T.sizeof;
679             }
680         }
681         else
682         {
683             foreach (cur; arr)
684                 assert(cur is null, "Unable to compute byte representation of non-null pointer at compile time");
685         }
686         return ret;
687     }
688     else
689     {
690         return (cast(const(ubyte)*)(arr.ptr))[0 .. T.sizeof*arr.length];
691     }
692 }
693 
694 @trusted pure nothrow @nogc
695 const(ubyte)[] toUbyte(T)(const ref T val) if (__traits(isIntegral, T) && !is(T == enum) && !is(T == __vector))
696 {
697     static if (T.sizeof == 1)
698     {
699         if (__ctfe)
700         {
701             ubyte[] result = ctfe_alloc(1);
702             result[0] = cast(ubyte) val;
703             return result;
704         }
705         else
706         {
707             return (cast(const(ubyte)*)(&val))[0 .. T.sizeof];
708         }
709     }
710     else if (__ctfe)
711     {
712         ubyte[] tmp = ctfe_alloc(T.sizeof);
713         Unqual!T val_ = val;
714         for (size_t i = 0; i < T.sizeof; ++i)
715         {
716             size_t idx;
717             version (LittleEndian) idx = i;
718             else idx = T.sizeof-i-1;
719             tmp[idx] = cast(ubyte)(val_&0xff);
720             val_ >>= 8;
721         }
722         return tmp;
723     }
724     else
725     {
726         return (cast(const(ubyte)*)(&val))[0 .. T.sizeof];
727     }
728 }
729 
730 @trusted pure nothrow @nogc
731 const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == __vector))
732 {
733     if (!__ctfe)
734         return (cast(const ubyte*) &val)[0 .. T.sizeof];
735     else static if (is(typeof(val[0]) : void))
736         assert(0, "Unable to compute byte representation of " ~ T.stringof ~ " at compile time.");
737     else
738     {
739         // This code looks like it should work in CTFE but it segfaults:
740         //    auto a = val.array;
741         //    return toUbyte(a);
742         alias E = typeof(val[0]);
743         ubyte[] result = ctfe_alloc(T.sizeof);
744         for (size_t i = 0, j = 0; i < T.sizeof; i += E.sizeof, ++j)
745         {
746             result[i .. i + E.sizeof] = toUbyte(val[j]);
747         }
748         return result;
749     }
750 }
751 
752 @trusted pure nothrow @nogc
753 const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == cfloat) || is(Unqual!T == cdouble) ||is(Unqual!T == creal))
754 {
755     if (__ctfe)
756     {
757         auto re = val.re;
758         auto im = val.im;
759         auto a = re.toUbyte();
760         auto b = im.toUbyte();
761         ubyte[] result = ctfe_alloc(a.length + b.length);
762         result[0 .. a.length] = a[0 .. a.length];
763         result[a.length .. $] = b[0 .. b.length];
764         return result;
765     }
766     else
767     {
768         return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
769     }
770 }
771 
772 @trusted pure nothrow @nogc
773 const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == enum))
774 {
775     if (__ctfe)
776     {
777         static if (is(T V == enum)){}
778         return toUbyte(cast(const V) val);
779     }
780     else
781     {
782         return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
783     }
784 }
785 
786 nothrow pure @safe unittest
787 {
788     // Issue 19008 - check toUbyte works on enums.
789     enum Month : uint { jan = 1}
790     Month m = Month.jan;
791     const bytes = toUbyte(m);
792     enum ctfe_works = (() => { Month x = Month.jan; return toUbyte(x).length > 0; })();
793 }
794 
795 @trusted pure nothrow @nogc
796 const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == delegate) || is(T : V*, V) && __traits(getAliasThis, T).length == 0)
797 {
798     if (__ctfe)
799     {
800         if (val !is null) assert(0, "Unable to compute byte representation of non-null pointer at compile time");
801         return ctfe_alloc(T.sizeof);
802     }
803     else
804     {
805         return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
806     }
807 }
808 
809 @trusted pure nothrow @nogc
810 const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == struct) || is(T == union))
811 {
812     if (__ctfe)
813     {
814         ubyte[] bytes = ctfe_alloc(T.sizeof);
815         foreach (key, ref cur; val.tupleof)
816         {
817             static if (is(typeof(cur) EType == enum)) // Odd style is to avoid template instantiation in most cases.
818                 alias CurType = OriginalType!EType;
819             else
820                 alias CurType = typeof(cur);
821             static if (is(CurType == struct) || is(CurType == union) || __traits(isStaticArray, CurType) || !is(typeof(cur is null)))
822             {
823                 bytes[val.tupleof[key].offsetof .. val.tupleof[key].offsetof + CurType.sizeof] = toUbyte(cur)[];
824             }
825             else
826             {
827                 assert(cur is null, "Unable to compute byte representation of non-null reference field at compile time");
828                 //skip, because val bytes are zeros
829             }
830         }
831         return bytes;
832     }
833     else
834     {
835         return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
836     }
837 }
838 
839 // Strips off all `enum`s from type `T`.
840 // Perhaps move to core.internal.types.
OriginalType(T)841 private template OriginalType(T)
842 {
843     static if (is(T EType == enum))
844         alias OriginalType = .OriginalType!EType;
845     else
846         alias OriginalType = T;
847 }
848