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 
12 /+
13 A @nogc function can allocate memory during CTFE.
14 +/
15 @nogc nothrow pure @trusted
ctfe_alloc(size_t n)16 private ubyte[] ctfe_alloc(size_t n)
17 {
18     if (!__ctfe)
19     {
20         assert(0, "CTFE only");
21     }
22     else
23     {
24         static ubyte[] alloc(size_t x) nothrow pure
25         {
26             if (__ctfe) // Needed to prevent _d_newarray from appearing in compiled prorgam.
27                 return new ubyte[x];
28             else
29                 assert(0);
30         }
31         return (cast(ubyte[] function(size_t) @nogc nothrow pure) &alloc)(n);
32     }
33 }
34 
35 @trusted pure nothrow @nogc
36 const(ubyte)[] toUbyte(T)(const scope ref T val) if (__traits(isFloating, T) && (is(T : real) || is(T : ireal)))
37 {
38     if (__ctfe)
39     {
40         static if (floatFormat!T == FloatFormat.Float || floatFormat!T == FloatFormat.Double)
41         {
42             static if (is(T : ireal)) // https://issues.dlang.org/show_bug.cgi?id=19932
43                 const f = val.im;
44             else
45                 alias f = val;
46             static if (T.sizeof == uint.sizeof)
47                 uint bits = *cast(const uint*) &f;
48             else static if (T.sizeof == ulong.sizeof)
49                 ulong bits = *cast(const ulong*) &f;
50             ubyte[] result = ctfe_alloc(T.sizeof);
version(BigEndian)51             version (BigEndian)
52             {
53                 foreach_reverse (ref b; result)
54                 {
55                     b = cast(ubyte) bits;
56                     bits >>= 8;
57                 }
58             }
59             else
60             {
foreach(ref b;result)61                 foreach (ref b; result)
62                 {
63                     b = cast(ubyte) bits;
64                     bits >>= 8;
65                 }
66             }
67             return result;
68         }
69         else static if (floatFormat!T == FloatFormat.DoubleDouble)
70         {
71             // Parse DoubleDoubles as a pair of doubles.
72             // The layout of the type is:
73             //
74             //   [1|    11    |       52      ][1|    11    |       52       ]
75             //   [S| Exponent | Fraction (hi) ][S| Exponent | Fraction (low) ]
76             //
77             // We can get the least significant bits by subtracting the IEEE
78             // double precision portion from the real value.
79 
80             import core.math : toPrec;
81 
82             ubyte[] buff = ctfe_alloc(T.sizeof);
83             enum msbSize = double.sizeof;
84 
85             static if (is(T : ireal))
86                 double hi = toPrec!double(val.im);
87             else
88                 double hi = toPrec!double(val);
89             buff[0 .. msbSize] = toUbyte(hi)[];
90 
91             if (val is cast(T)0.0 || val is cast(T)-0.0 ||
92                 val is T.nan || val is -T.nan ||
93                 val is T.infinity || val > T.max ||
94                 val is -T.infinity || val < -T.max)
95             {
96                 // Zero, NaN, and Inf are all representable as doubles, so the
97                 // least significant part can be 0.0.
98                 buff[msbSize .. $] = 0;
99             }
100             else
101             {
102                 static if (is(T : ireal))
103                     double low = toPrec!double(val.im - hi);
104                 else
105                     double low = toPrec!double(val - hi);
106                 buff[msbSize .. $] = toUbyte(low)[];
107             }
108 
109             // Arrays don't index differently between little and big-endian targets.
110             return buff;
111         }
112         else
113         {
114             auto parsed = parse(val);
115 
116             ulong mantissa = parsed.mantissa;
117             uint exp = parsed.exponent;
118             uint sign = parsed.sign;
119 
120             ubyte[] buff = ctfe_alloc(T.sizeof);
121             size_t off_bytes = 0;
122             size_t off_bits  = 0;
123             // Quadruples won't fit in one ulong, so check for that.
124             enum mantissaMax = FloatTraits!T.MANTISSA < ulong.sizeof*8 ?
125                                FloatTraits!T.MANTISSA : ulong.sizeof*8;
126 
127             for (; off_bytes < mantissaMax/8; ++off_bytes)
128             {
129                 buff[off_bytes] = cast(ubyte)mantissa;
130                 mantissa >>= 8;
131             }
132 
133             static if (floatFormat!T == FloatFormat.Quadruple)
134             {
135                 ulong mantissa2 = parsed.mantissa2;
136                 off_bytes--; // go back one, since mantissa only stored data in 56
137                              // bits, ie 7 bytes
138                 for (; off_bytes < FloatTraits!T.MANTISSA/8; ++off_bytes)
139                 {
140                     buff[off_bytes] = cast(ubyte)mantissa2;
141                     mantissa2 >>= 8;
142                 }
143             }
144             else
145             {
146                 off_bits = FloatTraits!T.MANTISSA%8;
147                 buff[off_bytes] = cast(ubyte)mantissa;
148             }
149 
150             for (size_t i=0; i<FloatTraits!T.EXPONENT/8; ++i)
151             {
152                 ubyte cur_exp = cast(ubyte)exp;
153                 exp >>= 8;
154                 buff[off_bytes] |= (cur_exp << off_bits);
155                 ++off_bytes;
156                 buff[off_bytes] |= cur_exp >> 8 - off_bits;
157             }
158 
159 
160             exp <<= 8 - FloatTraits!T.EXPONENT%8 - 1;
161             buff[off_bytes] |= exp;
162             sign <<= 7;
163             buff[off_bytes] |= sign;
164 
version(BigEndian)165             version (BigEndian)
166             {
167                 for (size_t left = 0, right = buff.length - 1; left < right; left++, right--)
168                 {
169                     const swap = buff[left];
170                     buff[left] = buff[right];
171                     buff[right] = swap;
172                 }
173             }
174             return buff;
175         }
176     }
177     else
178     {
179         return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
180     }
181 }
182 
183 @safe pure nothrow @nogc
184 private Float parse(bool is_denormalized = false, T:ireal)(T x)
185 {
186     return parse(x.im);
187 }
188 
189 @safe pure nothrow @nogc
190 private Float parse(bool is_denormalized = false, T:real)(T x_) if (floatFormat!T != FloatFormat.Real80)
191 {
192     import core.internal.traits : Unqual;
193     Unqual!T x = x_;
194     static assert(floatFormat!T != FloatFormat.DoubleDouble,
195            "doubledouble float format not supported in CTFE");
196     if (x is cast(T)0.0) return FloatTraits!T.ZERO;
197     if (x is cast(T)-0.0) return FloatTraits!T.NZERO;
198     if (x is T.nan) return FloatTraits!T.NAN;
199     if (x is -T.nan) return FloatTraits!T.NNAN;
200     if (x is T.infinity || x > T.max) return FloatTraits!T.INF;
201     if (x is -T.infinity || x < -T.max) return FloatTraits!T.NINF;
202 
203     uint sign = x < 0;
204     x = sign ? -x : x;
205     int e = binLog2(x);
206     real x2 = x;
207     uint exp = cast(uint)(e + (2^^(FloatTraits!T.EXPONENT-1) - 1));
208 
209     if (!exp)
210     {
211         if (is_denormalized)
212             return Float(0, 0, sign);
213         else
214             return denormalizedMantissa(x, sign);
215     }
216 
217     x2 /= binPow2(e);
218 
219     static if (!is_denormalized)
220         x2 -= 1.0;
221 
222     static if (floatFormat!T == FloatFormat.Quadruple)
223     {
224         // Store the 112-bit mantissa in two ulongs, specifically the lower 56
225         // bits of each, with the most significant bits in mantissa2. There's
226         // an edge case exposed by the labeled test below, where only a subnormal
227         // with the highest bit set being the 57th bit will "overflow" to the
228         // 57th bit in mantissa2 with the following logic, but that special case
229         // is handled by an additional check in denormalizedMantissa for
230         // Quadruples below.
231 
232         x2 *=  2UL<<(FloatTraits!T.MANTISSA - (ulong.sizeof - 1)*8 - 1);
233         ulong mant2 = cast(ulong) x2;
234         x2 -= mant2;
235 
236         x2 *=  2UL<<((ulong.sizeof - 1)*8 - 1);
237         ulong mant = cast(ulong) x2;
238         return Float(mant, exp, sign, mant2);
239     }
240     else
241     {
242         x2 *=  2UL<<(FloatTraits!T.MANTISSA);
243         ulong mant = shiftrRound(cast(ulong)x2);
244         return Float(mant, exp, sign);
245     }
246 }
247 
248 @safe pure nothrow @nogc
249 private Float parse(bool _ = false, T:real)(T x_) if (floatFormat!T == FloatFormat.Real80)
250 {
251     import core.internal.traits : Unqual;
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 
475 @system unittest
476 {
toUbyte2(T)477     static const(ubyte)[] toUbyte2(T)(T val)
478     {
479         return toUbyte(val).dup;
480     }
481 
testNumberConvert(string v)482     static 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 
testConvert()498     static 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         /**True random values*/
576         testNumberConvert!("-0x9.0f7ee55df77618fp-13829L");
577         testNumberConvert!("0x7.36e6e2640120d28p+8797L");
578         testNumberConvert!("-0x1.05df6ce4702ccf8p+15835L");
579         testNumberConvert!("0x9.54bb0d88806f714p-7088L");
580 
581         testNumberConvert!("-0x9.0f7ee55df7ffp-338");
582         testNumberConvert!("0x7.36e6e264012dp+879");
583         testNumberConvert!("-0x1.05df6ce4708ep+658");
584         testNumberConvert!("0x9.54bb0d888061p-708");
585 
586         testNumberConvert!("-0x9.0f7eefp-101F");
587         testNumberConvert!("0x7.36e6ep+87F");
588         testNumberConvert!("-0x1.05df6p+112F");
589         testNumberConvert!("0x9.54bb0p-70F");
590 
591         /**Big overflow or underflow*/
592         testNumberConvert!("cast(double)-0x9.0f7ee55df77618fp-13829L");
593         testNumberConvert!("cast(double)0x7.36e6e2640120d28p+8797L");
594         testNumberConvert!("cast(double)-0x1.05df6ce4702ccf8p+15835L");
595         testNumberConvert!("cast(double)0x9.54bb0d88806f714p-7088L");
596 
597         testNumberConvert!("cast(float)-0x9.0f7ee55df77618fp-13829L");
598         testNumberConvert!("cast(float)0x7.36e6e2640120d28p+8797L");
599         testNumberConvert!("cast(float)-0x1.05df6ce4702ccf8p+15835L");
600         testNumberConvert!("cast(float)0x9.54bb0d88806f714p-7088L");
601     }
602 
603     testConvert();
604 }
605 
606 
607 
608 private enum FloatFormat
609 {
610     Float,
611     Double,
612     Real80,
613     DoubleDouble,
614     Quadruple
615 }
616 
floatFormat(T)617 template floatFormat(T) if (is(T:real) || is(T:ireal))
618 {
619     static if (T.mant_dig == 24)
620         enum floatFormat = FloatFormat.Float;
621     else static if (T.mant_dig == 53)
622     {
623         // Double precision, or real == double
624         static if (T.sizeof == double.sizeof)
625             enum floatFormat = FloatFormat.Double;
626         // 80-bit real with rounding precision set to 53 bits.
627         else static if (T.sizeof == real.sizeof)
628             enum floatFormat = FloatFormat.Real80;
629     }
630     else static if (T.mant_dig == 64)
631         enum floatFormat = FloatFormat.Real80;
632     else static if (T.mant_dig == 106)
633         enum floatFormat = FloatFormat.DoubleDouble;
634     else static if (T.mant_dig == 113)
635         enum floatFormat = FloatFormat.Quadruple;
636     else
637         static assert(0);
638 
639 }
640 
641 package template floatSize(T) if (is(T:real) || is(T:ireal))
642 {
643     enum floatSize = FloatTraits!(T).DATASIZE;
644 }
645 
646 //  all toUbyte functions must be evaluable at compile time
647 @trusted pure nothrow @nogc
648 const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof == 1)
649 {
650     return cast(const(ubyte)[])arr;
651 }
652 
653 @trusted pure nothrow @nogc
654 const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof > 1)
655 {
656     if (__ctfe)
657     {
658         ubyte[] ret = ctfe_alloc(T.sizeof * arr.length);
659         static if (is(T EType == enum)) // Odd style is to avoid template instantiation in most cases.
660             alias E = OriginalType!EType;
661         else
662             alias E = T;
663         static if (is(E == struct) || is(E == union) || __traits(isStaticArray, E) || !is(typeof(arr[0] is null)))
664         {
665             size_t offset = 0;
foreach(ref cur;arr)666             foreach (ref cur; arr)
667             {
668                 ret[offset .. offset + T.sizeof] = toUbyte(cur)[0 .. T.sizeof];
669                 offset += T.sizeof;
670             }
671         }
672         else
673         {
674             foreach (cur; arr)
675                 assert(cur is null, "Unable to compute byte representation of non-null pointer at compile time");
676         }
677         return ret;
678     }
679     else
680     {
681         return (cast(const(ubyte)*)(arr.ptr))[0 .. T.sizeof*arr.length];
682     }
683 }
684 
685 @trusted pure nothrow @nogc
686 const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) && !is(T == enum) && !is(T == __vector))
687 {
688     static if (T.sizeof == 1)
689     {
690         if (__ctfe)
691         {
692             ubyte[] result = ctfe_alloc(1);
693             result[0] = cast(ubyte) val;
694             return result;
695         }
696         else
697         {
698             return (cast(const(ubyte)*)(&val))[0 .. T.sizeof];
699         }
700     }
701     else if (__ctfe)
702     {
703         import core.internal.traits : Unqual;
704         ubyte[] tmp = ctfe_alloc(T.sizeof);
705         Unqual!T val_ = val;
706         for (size_t i = 0; i < T.sizeof; ++i)
707         {
708             size_t idx;
709             version (LittleEndian) idx = i;
710             else idx = T.sizeof-i-1;
711             tmp[idx] = cast(ubyte)(val_&0xff);
712             val_ >>= 8;
713         }
714         return tmp;
715     }
716     else
717     {
718         return (cast(const(ubyte)*)(&val))[0 .. T.sizeof];
719     }
720 }
721 
722 @trusted pure nothrow @nogc
723 const(ubyte)[] toUbyte(T)(const ref scope T val) if (is(T == __vector))
724 {
725     if (!__ctfe)
726         return (cast(const ubyte*) &val)[0 .. T.sizeof];
727     else static if (is(typeof(val[0]) : void))
728         assert(0, "Unable to compute byte representation of " ~ T.stringof ~ " at compile time.");
729     else
730     {
731         // This code looks like it should work in CTFE but it segfaults:
732         //    auto a = val.array;
733         //    return toUbyte(a);
734         alias E = typeof(val[0]);
735         ubyte[] result = ctfe_alloc(T.sizeof);
736         for (size_t i = 0, j = 0; i < T.sizeof; i += E.sizeof, ++j)
737         {
738             result[i .. i + E.sizeof] = toUbyte(val[j]);
739         }
740         return result;
741     }
742 }
743 
744 // @@@DEPRECATED_2022-02@@@
745 deprecated
746 @trusted pure nothrow @nogc
747 const(ubyte)[] toUbyte(T)(const ref return scope T val) if (__traits(isFloating, T) && is(T : creal))
748 {
749     if (__ctfe)
750     {
751         auto re = val.re;
752         auto im = val.im;
753         auto a = re.toUbyte();
754         auto b = im.toUbyte();
755         ubyte[] result = ctfe_alloc(a.length + b.length);
756         result[0 .. a.length] = a[0 .. a.length];
757         result[a.length .. $] = b[0 .. b.length];
758         return result;
759     }
760     else
761     {
762         return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
763     }
764 }
765 
766 @trusted pure nothrow @nogc
767 const(ubyte)[] toUbyte(T)(const ref return scope T val) if (is(T == enum))
768 {
769     if (__ctfe)
770     {
771         static if (is(T V == enum)){}
772         return toUbyte(*cast(const V*) &val);
773     }
774     else
775     {
776         return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
777     }
778 }
779 
780 nothrow pure @safe unittest
781 {
782     // Issue 19008 - check toUbyte works on enums.
783     enum Month : uint { jan = 1}
784     Month m = Month.jan;
785     const bytes = toUbyte(m);
786     enum ctfe_works = (() { Month x = Month.jan; return toUbyte(x).length > 0; })();
787 }
788 
789 @trusted pure nothrow @nogc
790 const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == delegate) || is(T : V*, V) && __traits(getAliasThis, T).length == 0)
791 {
792     if (__ctfe)
793     {
794         if (val !is null) assert(0, "Unable to compute byte representation of non-null pointer at compile time");
795         return ctfe_alloc(T.sizeof);
796     }
797     else
798     {
799         return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
800     }
801 }
802 
803 @trusted pure nothrow @nogc
804 const(ubyte)[] toUbyte(T)(const ref return scope T val) if (is(T == struct) || is(T == union))
805 {
806     if (__ctfe)
807     {
808         ubyte[] bytes = ctfe_alloc(T.sizeof);
809         foreach (key, ref cur; val.tupleof)
810         {
811             static if (is(typeof(cur) EType == enum)) // Odd style is to avoid template instantiation in most cases.
812                 alias CurType = OriginalType!EType;
813             else
814                 alias CurType = typeof(cur);
815             static if (is(CurType == struct) || is(CurType == union) || __traits(isStaticArray, CurType) || !is(typeof(cur is null)))
816             {
817                 bytes[val.tupleof[key].offsetof .. val.tupleof[key].offsetof + CurType.sizeof] = toUbyte(cur)[];
818             }
819             else
820             {
821                 assert(cur is null, "Unable to compute byte representation of non-null reference field at compile time");
822                 //skip, because val bytes are zeros
823             }
824         }
825         return bytes;
826     }
827     else
828     {
829         return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
830     }
831 }
832 
833 // Strips off all `enum`s from type `T`.
834 // Perhaps move to core.internal.types.
OriginalType(T)835 private template OriginalType(T)
836 {
837     static if (is(T EType == enum))
838         alias OriginalType = .OriginalType!EType;
839     else
840         alias OriginalType = T;
841 }
842