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