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 {
reverse_(const (ubyte)[]arr)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
version(LittleEndian)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
FloatTraits(T)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
binPow2(int pow)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
shiftrRound(ulong x)328 private ulong shiftrRound(ulong x)
329 {
330 return (x >> 1) + (x & 1);
331 }
332
333 @safe pure nothrow @nogc
binLog2(T)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
version(unittest)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
floatFormat(T)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;
foreach(ref cur;arr)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.
OriginalType(T)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 }
773