memory_util.hpp (24156B)
1 #ifndef _C4_MEMORY_UTIL_HPP_ 2 #define _C4_MEMORY_UTIL_HPP_ 3 4 #include "c4/config.hpp" 5 #include "c4/error.hpp" 6 #include "c4/compiler.hpp" 7 #include "c4/cpu.hpp" 8 #ifdef C4_MSVC 9 #include <intrin.h> 10 #endif 11 #include <string.h> 12 13 #if (defined(__GNUC__) && __GNUC__ >= 10) || defined(__has_builtin) 14 #define _C4_USE_LSB_INTRINSIC(which) __has_builtin(which) 15 #define _C4_USE_MSB_INTRINSIC(which) __has_builtin(which) 16 #elif defined(C4_MSVC) 17 #define _C4_USE_LSB_INTRINSIC(which) true 18 #define _C4_USE_MSB_INTRINSIC(which) true 19 #else 20 // let's try our luck 21 #define _C4_USE_LSB_INTRINSIC(which) true 22 #define _C4_USE_MSB_INTRINSIC(which) true 23 #endif 24 25 26 /** @file memory_util.hpp Some memory utilities. */ 27 28 namespace c4 { 29 30 C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast") 31 32 /** set the given memory to zero */ 33 C4_ALWAYS_INLINE void mem_zero(void* mem, size_t num_bytes) 34 { 35 memset(mem, 0, num_bytes); 36 } 37 /** set the given memory to zero */ 38 template<class T> 39 C4_ALWAYS_INLINE void mem_zero(T* mem, size_t num_elms) 40 { 41 memset(mem, 0, sizeof(T) * num_elms); 42 } 43 /** set the given memory to zero */ 44 template<class T> 45 C4_ALWAYS_INLINE void mem_zero(T* mem) 46 { 47 memset(mem, 0, sizeof(T)); 48 } 49 50 C4_ALWAYS_INLINE C4_CONST bool mem_overlaps(void const* a, void const* b, size_t sza, size_t szb) 51 { 52 // thanks @timwynants 53 return (((const char*)b + szb) > a && b < ((const char*)a+sza)); 54 } 55 56 void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times); 57 58 59 //----------------------------------------------------------------------------- 60 //----------------------------------------------------------------------------- 61 //----------------------------------------------------------------------------- 62 63 template<class T> 64 C4_ALWAYS_INLINE C4_CONST bool is_aligned(T *ptr, uintptr_t alignment=alignof(T)) 65 { 66 return (uintptr_t(ptr) & (alignment - uintptr_t(1))) == uintptr_t(0); 67 } 68 69 70 //----------------------------------------------------------------------------- 71 //----------------------------------------------------------------------------- 72 //----------------------------------------------------------------------------- 73 // least significant bit 74 75 /** @name msb Compute the least significant bit 76 * @note the input value must be nonzero 77 * @note the input type must be unsigned 78 */ 79 /** @{ */ 80 81 // https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightLinear 82 #define _c4_lsb_fallback \ 83 unsigned c = 0; \ 84 v = (v ^ (v - 1)) >> 1; /* Set v's trailing 0s to 1s and zero rest */ \ 85 for(; v; ++c) \ 86 v >>= 1; \ 87 return (unsigned) c 88 89 // u8 90 template<class I> 91 C4_CONSTEXPR14 92 auto lsb(I v) noexcept 93 -> typename std::enable_if<sizeof(I) == 1u, unsigned>::type 94 { 95 C4_STATIC_ASSERT(std::is_unsigned<I>::value); 96 C4_ASSERT(v != 0); 97 #if _C4_USE_LSB_INTRINSIC(__builtin_ctz) 98 // upcast to use the intrinsic, it's cheaper. 99 #ifdef C4_MSVC 100 #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) 101 unsigned long bit; 102 _BitScanForward(&bit, (unsigned long)v); 103 return bit; 104 #else 105 _c4_lsb_fallback; 106 #endif 107 #else 108 return (unsigned)__builtin_ctz((unsigned)v); 109 #endif 110 #else 111 _c4_lsb_fallback; 112 #endif 113 } 114 115 // u16 116 template<class I> 117 C4_CONSTEXPR14 118 auto lsb(I v) noexcept 119 -> typename std::enable_if<sizeof(I) == 2u, unsigned>::type 120 { 121 C4_STATIC_ASSERT(std::is_unsigned<I>::value); 122 C4_ASSERT(v != 0); 123 #if _C4_USE_LSB_INTRINSIC(__builtin_ctz) 124 // upcast to use the intrinsic, it's cheaper. 125 // Then remember that the upcast makes it to 31bits 126 #ifdef C4_MSVC 127 #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) 128 unsigned long bit; 129 _BitScanForward(&bit, (unsigned long)v); 130 return bit; 131 #else 132 _c4_lsb_fallback; 133 #endif 134 #else 135 return (unsigned)__builtin_ctz((unsigned)v); 136 #endif 137 #else 138 _c4_lsb_fallback; 139 #endif 140 } 141 142 // u32 143 template<class I> 144 C4_CONSTEXPR14 145 auto lsb(I v) noexcept 146 -> typename std::enable_if<sizeof(I) == 4u, unsigned>::type 147 { 148 C4_STATIC_ASSERT(std::is_unsigned<I>::value); 149 C4_ASSERT(v != 0); 150 #if _C4_USE_LSB_INTRINSIC(__builtin_ctz) 151 #ifdef C4_MSVC 152 #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) 153 unsigned long bit; 154 _BitScanForward(&bit, v); 155 return bit; 156 #else 157 _c4_lsb_fallback; 158 #endif 159 #else 160 return (unsigned)__builtin_ctz((unsigned)v); 161 #endif 162 #else 163 _c4_lsb_fallback; 164 #endif 165 } 166 167 // u64 in 64bits 168 template<class I> 169 C4_CONSTEXPR14 170 auto lsb(I v) noexcept 171 -> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type 172 { 173 C4_STATIC_ASSERT(std::is_unsigned<I>::value); 174 C4_ASSERT(v != 0); 175 #if _C4_USE_LSB_INTRINSIC(__builtin_ctzl) 176 #if defined(C4_MSVC) 177 #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) 178 unsigned long bit; 179 _BitScanForward64(&bit, v); 180 return bit; 181 #else 182 _c4_lsb_fallback; 183 #endif 184 #else 185 return (unsigned)__builtin_ctzl((unsigned long)v); 186 #endif 187 #else 188 _c4_lsb_fallback; 189 #endif 190 } 191 192 // u64 in 32bits 193 template<class I> 194 C4_CONSTEXPR14 195 auto lsb(I v) noexcept 196 -> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type 197 { 198 C4_STATIC_ASSERT(std::is_unsigned<I>::value); 199 C4_ASSERT(v != 0); 200 #if _C4_USE_LSB_INTRINSIC(__builtin_ctzll) 201 #if defined(C4_MSVC) 202 #if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) 203 unsigned long bit; 204 _BitScanForward64(&bit, v); 205 return bit; 206 #else 207 _c4_lsb_fallback; 208 #endif 209 #else 210 return (unsigned)__builtin_ctzll((unsigned long long)v); 211 #endif 212 #else 213 _c4_lsb_fallback; 214 #endif 215 } 216 217 #undef _c4_lsb_fallback 218 219 /** @} */ 220 221 222 namespace detail { 223 template<class I, I val, unsigned num_bits, bool finished> struct _lsb11; 224 template<class I, I val, unsigned num_bits> 225 struct _lsb11<I, val, num_bits, false> 226 { 227 enum : unsigned { num = _lsb11<I, (val>>1), num_bits+I(1), (((val>>1)&I(1))!=I(0))>::num }; 228 }; 229 template<class I, I val, unsigned num_bits> 230 struct _lsb11<I, val, num_bits, true> 231 { 232 enum : unsigned { num = num_bits }; 233 }; 234 } // namespace detail 235 236 237 /** TMP version of lsb(); this needs to be implemented with template 238 * meta-programming because C++11 cannot use a constexpr function with 239 * local variables 240 * @see lsb */ 241 template<class I, I number> 242 struct lsb11 243 { 244 static_assert(number != 0, "lsb: number must be nonzero"); 245 enum : unsigned { value = detail::_lsb11<I, number, 0, ((number&I(1))!=I(0))>::num}; 246 }; 247 248 249 //----------------------------------------------------------------------------- 250 //----------------------------------------------------------------------------- 251 //----------------------------------------------------------------------------- 252 // most significant bit 253 254 255 /** @name msb Compute the most significant bit 256 * @note the input value must be nonzero 257 * @note the input type must be unsigned 258 */ 259 /** @{ */ 260 261 262 #define _c4_msb8_fallback \ 263 unsigned n = 0; \ 264 if(v & I(0xf0)) v >>= 4, n |= I(4); \ 265 if(v & I(0x0c)) v >>= 2, n |= I(2); \ 266 if(v & I(0x02)) v >>= 1, n |= I(1); \ 267 return n 268 269 #define _c4_msb16_fallback \ 270 unsigned n = 0; \ 271 if(v & I(0xff00)) v >>= 8, n |= I(8); \ 272 if(v & I(0x00f0)) v >>= 4, n |= I(4); \ 273 if(v & I(0x000c)) v >>= 2, n |= I(2); \ 274 if(v & I(0x0002)) v >>= 1, n |= I(1); \ 275 return n 276 277 #define _c4_msb32_fallback \ 278 unsigned n = 0; \ 279 if(v & I(0xffff0000)) v >>= 16, n |= 16; \ 280 if(v & I(0x0000ff00)) v >>= 8, n |= 8; \ 281 if(v & I(0x000000f0)) v >>= 4, n |= 4; \ 282 if(v & I(0x0000000c)) v >>= 2, n |= 2; \ 283 if(v & I(0x00000002)) v >>= 1, n |= 1; \ 284 return n 285 286 #define _c4_msb64_fallback \ 287 unsigned n = 0; \ 288 if(v & I(0xffffffff00000000)) v >>= 32, n |= I(32); \ 289 if(v & I(0x00000000ffff0000)) v >>= 16, n |= I(16); \ 290 if(v & I(0x000000000000ff00)) v >>= 8, n |= I(8); \ 291 if(v & I(0x00000000000000f0)) v >>= 4, n |= I(4); \ 292 if(v & I(0x000000000000000c)) v >>= 2, n |= I(2); \ 293 if(v & I(0x0000000000000002)) v >>= 1, n |= I(1); \ 294 return n 295 296 297 // u8 298 template<class I> 299 C4_CONSTEXPR14 300 auto msb(I v) noexcept 301 -> typename std::enable_if<sizeof(I) == 1u, unsigned>::type 302 { 303 C4_STATIC_ASSERT(std::is_unsigned<I>::value); 304 C4_ASSERT(v != 0); 305 #if _C4_USE_MSB_INTRINSIC(__builtin_clz) 306 // upcast to use the intrinsic, it's cheaper. 307 // Then remember that the upcast makes it to 31bits 308 #ifdef C4_MSVC 309 #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) 310 unsigned long bit; 311 _BitScanReverse(&bit, (unsigned long)v); 312 return bit; 313 #else 314 _c4_msb8_fallback; 315 #endif 316 #else 317 return 31u - (unsigned)__builtin_clz((unsigned)v); 318 #endif 319 #else 320 _c4_msb8_fallback; 321 #endif 322 } 323 324 // u16 325 template<class I> 326 C4_CONSTEXPR14 327 auto msb(I v) noexcept 328 -> typename std::enable_if<sizeof(I) == 2u, unsigned>::type 329 { 330 C4_STATIC_ASSERT(std::is_unsigned<I>::value); 331 C4_ASSERT(v != 0); 332 #if _C4_USE_MSB_INTRINSIC(__builtin_clz) 333 // upcast to use the intrinsic, it's cheaper. 334 // Then remember that the upcast makes it to 31bits 335 #ifdef C4_MSVC 336 #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) 337 unsigned long bit; 338 _BitScanReverse(&bit, (unsigned long)v); 339 return bit; 340 #else 341 _c4_msb16_fallback; 342 #endif 343 #else 344 return 31u - (unsigned)__builtin_clz((unsigned)v); 345 #endif 346 #else 347 _c4_msb16_fallback; 348 #endif 349 } 350 351 // u32 352 template<class I> 353 C4_CONSTEXPR14 354 auto msb(I v) noexcept 355 -> typename std::enable_if<sizeof(I) == 4u, unsigned>::type 356 { 357 C4_STATIC_ASSERT(std::is_unsigned<I>::value); 358 C4_ASSERT(v != 0); 359 #if _C4_USE_MSB_INTRINSIC(__builtin_clz) 360 #ifdef C4_MSVC 361 #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) 362 unsigned long bit; 363 _BitScanReverse(&bit, v); 364 return bit; 365 #else 366 _c4_msb32_fallback; 367 #endif 368 #else 369 return 31u - (unsigned)__builtin_clz((unsigned)v); 370 #endif 371 #else 372 _c4_msb32_fallback; 373 #endif 374 } 375 376 // u64 in 64bits 377 template<class I> 378 C4_CONSTEXPR14 379 auto msb(I v) noexcept 380 -> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type 381 { 382 C4_STATIC_ASSERT(std::is_unsigned<I>::value); 383 C4_ASSERT(v != 0); 384 #if _C4_USE_MSB_INTRINSIC(__builtin_clzl) 385 #ifdef C4_MSVC 386 #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) 387 unsigned long bit; 388 _BitScanReverse64(&bit, v); 389 return bit; 390 #else 391 _c4_msb64_fallback; 392 #endif 393 #else 394 return 63u - (unsigned)__builtin_clzl((unsigned long)v); 395 #endif 396 #else 397 _c4_msb64_fallback; 398 #endif 399 } 400 401 // u64 in 32bits 402 template<class I> 403 C4_CONSTEXPR14 404 auto msb(I v) noexcept 405 -> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type 406 { 407 C4_STATIC_ASSERT(std::is_unsigned<I>::value); 408 C4_ASSERT(v != 0); 409 #if _C4_USE_MSB_INTRINSIC(__builtin_clzll) 410 #ifdef C4_MSVC 411 #if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) 412 unsigned long bit; 413 _BitScanReverse64(&bit, v); 414 return bit; 415 #else 416 _c4_msb64_fallback; 417 #endif 418 #else 419 return 63u - (unsigned)__builtin_clzll((unsigned long long)v); 420 #endif 421 #else 422 _c4_msb64_fallback; 423 #endif 424 } 425 426 #undef _c4_msb8_fallback 427 #undef _c4_msb16_fallback 428 #undef _c4_msb32_fallback 429 #undef _c4_msb64_fallback 430 431 /** @} */ 432 433 434 namespace detail { 435 template<class I, I val, I num_bits, bool finished> struct _msb11; 436 template<class I, I val, I num_bits> 437 struct _msb11< I, val, num_bits, false> 438 { 439 enum : unsigned { num = _msb11<I, (val>>1), num_bits+I(1), ((val>>1)==I(0))>::num }; 440 }; 441 template<class I, I val, I num_bits> 442 struct _msb11<I, val, num_bits, true> 443 { 444 static_assert(val == 0, "bad implementation"); 445 enum : unsigned { num = (unsigned)(num_bits-1) }; 446 }; 447 } // namespace detail 448 449 450 /** TMP version of msb(); this needs to be implemented with template 451 * meta-programming because C++11 cannot use a constexpr function with 452 * local variables 453 * @see msb */ 454 template<class I, I number> 455 struct msb11 456 { 457 enum : unsigned { value = detail::_msb11<I, number, 0, (number==I(0))>::num }; 458 }; 459 460 461 462 #undef _C4_USE_LSB_INTRINSIC 463 #undef _C4_USE_MSB_INTRINSIC 464 465 //----------------------------------------------------------------------------- 466 //----------------------------------------------------------------------------- 467 //----------------------------------------------------------------------------- 468 469 // there is an implicit conversion below; it happens when E or B are 470 // narrower than int, and thus any operation will upcast the result to 471 // int, and then downcast to assign 472 C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wconversion") 473 474 /** integer power; this function is constexpr-14 because of the local 475 * variables */ 476 template<class B, class E> 477 C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type 478 { 479 C4_STATIC_ASSERT(std::is_integral<E>::value); 480 B r = B(1); 481 if(exponent >= 0) 482 { 483 for(E e = 0; e < exponent; ++e) 484 r *= base; 485 } 486 else 487 { 488 exponent *= E(-1); 489 for(E e = 0; e < exponent; ++e) 490 r /= base; 491 } 492 return r; 493 } 494 495 /** integer power; this function is constexpr-14 because of the local 496 * variables */ 497 template<class B, B base, class E> 498 C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type 499 { 500 C4_STATIC_ASSERT(std::is_integral<E>::value); 501 B r = B(1); 502 if(exponent >= 0) 503 { 504 for(E e = 0; e < exponent; ++e) 505 r *= base; 506 } 507 else 508 { 509 exponent *= E(-1); 510 for(E e = 0; e < exponent; ++e) 511 r /= base; 512 } 513 return r; 514 } 515 516 /** integer power; this function is constexpr-14 because of the local 517 * variables */ 518 template<class B, class Base, Base base, class E> 519 C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type 520 { 521 C4_STATIC_ASSERT(std::is_integral<E>::value); 522 B r = B(1); 523 B bbase = B(base); 524 if(exponent >= 0) 525 { 526 for(E e = 0; e < exponent; ++e) 527 r *= bbase; 528 } 529 else 530 { 531 exponent *= E(-1); 532 for(E e = 0; e < exponent; ++e) 533 r /= bbase; 534 } 535 return r; 536 } 537 538 /** integer power; this function is constexpr-14 because of the local 539 * variables */ 540 template<class B, class E> 541 C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type 542 { 543 C4_STATIC_ASSERT(std::is_integral<E>::value); 544 B r = B(1); 545 for(E e = 0; e < exponent; ++e) 546 r *= base; 547 return r; 548 } 549 550 /** integer power; this function is constexpr-14 because of the local 551 * variables */ 552 template<class B, B base, class E> 553 C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type 554 { 555 C4_STATIC_ASSERT(std::is_integral<E>::value); 556 B r = B(1); 557 for(E e = 0; e < exponent; ++e) 558 r *= base; 559 return r; 560 } 561 /** integer power; this function is constexpr-14 because of the local 562 * variables */ 563 template<class B, class Base, Base base, class E> 564 C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type 565 { 566 C4_STATIC_ASSERT(std::is_integral<E>::value); 567 B r = B(1); 568 B bbase = B(base); 569 for(E e = 0; e < exponent; ++e) 570 r *= bbase; 571 return r; 572 } 573 574 C4_SUPPRESS_WARNING_GCC_CLANG_POP 575 576 577 //----------------------------------------------------------------------------- 578 //----------------------------------------------------------------------------- 579 //----------------------------------------------------------------------------- 580 581 /** return a mask with all bits set [first_bit,last_bit[; this function 582 * is constexpr-14 because of the local variables */ 583 template<class I> 584 C4_CONSTEXPR14 I contiguous_mask(I first_bit, I last_bit) 585 { 586 I r = 0; 587 for(I i = first_bit; i < last_bit; ++i) 588 { 589 r |= (I(1) << i); 590 } 591 return r; 592 } 593 594 595 namespace detail { 596 597 template<class I, I val, I first, I last, bool finished> 598 struct _ctgmsk11; 599 600 template<class I, I val, I first, I last> 601 struct _ctgmsk11< I, val, first, last, true> 602 { 603 enum : I { value = _ctgmsk11<I, val|(I(1)<<first), first+I(1), last, (first+1!=last)>::value }; 604 }; 605 606 template<class I, I val, I first, I last> 607 struct _ctgmsk11< I, val, first, last, false> 608 { 609 enum : I { value = val }; 610 }; 611 612 } // namespace detail 613 614 615 /** TMP version of contiguous_mask(); this needs to be implemented with template 616 * meta-programming because C++11 cannot use a constexpr function with 617 * local variables 618 * @see contiguous_mask */ 619 template<class I, I first_bit, I last_bit> 620 struct contiguous_mask11 621 { 622 enum : I { value = detail::_ctgmsk11<I, I(0), first_bit, last_bit, (first_bit!=last_bit)>::value }; 623 }; 624 625 626 //----------------------------------------------------------------------------- 627 //----------------------------------------------------------------------------- 628 //----------------------------------------------------------------------------- 629 /** use Empty Base Class Optimization to reduce the size of a pair of 630 * potentially empty types*/ 631 632 namespace detail { 633 typedef enum { 634 tpc_same, 635 tpc_same_empty, 636 tpc_both_empty, 637 tpc_first_empty, 638 tpc_second_empty, 639 tpc_general 640 } TightPairCase_e; 641 642 template<class First, class Second> 643 constexpr TightPairCase_e tpc_which_case() 644 { 645 return std::is_same<First, Second>::value ? 646 std::is_empty<First>::value ? 647 tpc_same_empty 648 : 649 tpc_same 650 : 651 std::is_empty<First>::value && std::is_empty<Second>::value ? 652 tpc_both_empty 653 : 654 std::is_empty<First>::value ? 655 tpc_first_empty 656 : 657 std::is_empty<Second>::value ? 658 tpc_second_empty 659 : 660 tpc_general 661 ; 662 } 663 664 template<class First, class Second, TightPairCase_e Case> 665 struct tight_pair 666 { 667 private: 668 669 First m_first; 670 Second m_second; 671 672 public: 673 674 using first_type = First; 675 using second_type = Second; 676 677 tight_pair() : m_first(), m_second() {} 678 tight_pair(First const& f, Second const& s) : m_first(f), m_second(s) {} 679 680 C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; } 681 C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; } 682 C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; } 683 C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; } 684 }; 685 686 template<class First, class Second> 687 struct tight_pair<First, Second, tpc_same_empty> : public First 688 { 689 static_assert(std::is_same<First, Second>::value, "bad implementation"); 690 691 using first_type = First; 692 using second_type = Second; 693 694 tight_pair() : First() {} 695 tight_pair(First const& f, Second const& /*s*/) : First(f) {} 696 697 C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); } 698 C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); } 699 C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return reinterpret_cast<Second &>(*this); } 700 C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return reinterpret_cast<Second const&>(*this); } 701 }; 702 703 template<class First, class Second> 704 struct tight_pair<First, Second, tpc_both_empty> : public First, public Second 705 { 706 using first_type = First; 707 using second_type = Second; 708 709 tight_pair() : First(), Second() {} 710 tight_pair(First const& f, Second const& s) : First(f), Second(s) {} 711 712 C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); } 713 C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); } 714 C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return static_cast<Second &>(*this); } 715 C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); } 716 }; 717 718 template<class First, class Second> 719 struct tight_pair<First, Second, tpc_same> : public First 720 { 721 Second m_second; 722 723 using first_type = First; 724 using second_type = Second; 725 726 tight_pair() : First() {} 727 tight_pair(First const& f, Second const& s) : First(f), m_second(s) {} 728 729 C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); } 730 C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); } 731 C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; } 732 C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; } 733 }; 734 735 template<class First, class Second> 736 struct tight_pair<First, Second, tpc_first_empty> : public First 737 { 738 Second m_second; 739 740 using first_type = First; 741 using second_type = Second; 742 743 tight_pair() : First(), m_second() {} 744 tight_pair(First const& f, Second const& s) : First(f), m_second(s) {} 745 746 C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); } 747 C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); } 748 C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; } 749 C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; } 750 }; 751 752 template<class First, class Second> 753 struct tight_pair<First, Second, tpc_second_empty> : public Second 754 { 755 First m_first; 756 757 using first_type = First; 758 using second_type = Second; 759 760 tight_pair() : Second(), m_first() {} 761 tight_pair(First const& f, Second const& s) : Second(s), m_first(f) {} 762 763 C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; } 764 C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; } 765 C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return static_cast<Second &>(*this); } 766 C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); } 767 }; 768 769 } // namespace detail 770 771 template<class First, class Second> 772 using tight_pair = detail::tight_pair<First, Second, detail::tpc_which_case<First,Second>()>; 773 774 C4_SUPPRESS_WARNING_GCC_CLANG_POP 775 776 } // namespace c4 777 778 #endif /* _C4_MEMORY_UTIL_HPP_ */