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