duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

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_ */