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 }