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

bitutils.h (6122B)


      1 // SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #pragma once
      5 
      6 #include "types.h"
      7 
      8 #include <cstdint>
      9 #include <type_traits>
     10 
     11 #ifdef _MSC_VER
     12 #include <stdlib.h>
     13 #endif
     14 
     15 // Zero-extending helper
     16 template<typename TReturn, typename TValue>
     17 ALWAYS_INLINE static constexpr TReturn ZeroExtend(TValue value)
     18 {
     19   return static_cast<TReturn>(static_cast<typename std::make_unsigned<TReturn>::type>(
     20     static_cast<typename std::make_unsigned<TValue>::type>(value)));
     21 }
     22 // Sign-extending helper
     23 template<typename TReturn, typename TValue>
     24 ALWAYS_INLINE static constexpr TReturn SignExtend(TValue value)
     25 {
     26   return static_cast<TReturn>(
     27     static_cast<typename std::make_signed<TReturn>::type>(static_cast<typename std::make_signed<TValue>::type>(value)));
     28 }
     29 
     30 // Type-specific helpers
     31 template<typename TValue>
     32 ALWAYS_INLINE static constexpr u16 ZeroExtend16(TValue value)
     33 {
     34   return ZeroExtend<u16, TValue>(value);
     35 }
     36 template<typename TValue>
     37 ALWAYS_INLINE static constexpr u32 ZeroExtend32(TValue value)
     38 {
     39   return ZeroExtend<u32, TValue>(value);
     40 }
     41 template<typename TValue>
     42 ALWAYS_INLINE static constexpr u64 ZeroExtend64(TValue value)
     43 {
     44   return ZeroExtend<u64, TValue>(value);
     45 }
     46 template<typename TValue>
     47 ALWAYS_INLINE static constexpr u16 SignExtend16(TValue value)
     48 {
     49   return SignExtend<u16, TValue>(value);
     50 }
     51 template<typename TValue>
     52 ALWAYS_INLINE static constexpr u32 SignExtend32(TValue value)
     53 {
     54   return SignExtend<u32, TValue>(value);
     55 }
     56 template<typename TValue>
     57 ALWAYS_INLINE static constexpr u64 SignExtend64(TValue value)
     58 {
     59   return SignExtend<u64, TValue>(value);
     60 }
     61 template<typename TValue>
     62 ALWAYS_INLINE static constexpr u8 Truncate8(TValue value)
     63 {
     64   return static_cast<u8>(static_cast<typename std::make_unsigned<decltype(value)>::type>(value));
     65 }
     66 template<typename TValue>
     67 ALWAYS_INLINE static constexpr u16 Truncate16(TValue value)
     68 {
     69   return static_cast<u16>(static_cast<typename std::make_unsigned<decltype(value)>::type>(value));
     70 }
     71 template<typename TValue>
     72 ALWAYS_INLINE static constexpr u32 Truncate32(TValue value)
     73 {
     74   return static_cast<u32>(static_cast<typename std::make_unsigned<decltype(value)>::type>(value));
     75 }
     76 
     77 // BCD helpers
     78 ALWAYS_INLINE static constexpr u8 BinaryToBCD(u8 value)
     79 {
     80   return ((value / 10) << 4) + (value % 10);
     81 }
     82 ALWAYS_INLINE static constexpr u8 PackedBCDToBinary(u8 value)
     83 {
     84   return ((value >> 4) * 10) + (value % 16);
     85 }
     86 ALWAYS_INLINE static constexpr u8 IsValidBCDDigit(u8 digit)
     87 {
     88   return (digit <= 9);
     89 }
     90 ALWAYS_INLINE static constexpr u8 IsValidPackedBCD(u8 value)
     91 {
     92   return IsValidBCDDigit(value & 0x0F) && IsValidBCDDigit(value >> 4);
     93 }
     94 
     95 // Boolean to integer
     96 ALWAYS_INLINE static constexpr u8 BoolToUInt8(bool value)
     97 {
     98   return static_cast<u8>(value);
     99 }
    100 ALWAYS_INLINE static constexpr u16 BoolToUInt16(bool value)
    101 {
    102   return static_cast<u16>(value);
    103 }
    104 ALWAYS_INLINE static constexpr u32 BoolToUInt32(bool value)
    105 {
    106   return static_cast<u32>(value);
    107 }
    108 ALWAYS_INLINE static constexpr u64 BoolToUInt64(bool value)
    109 {
    110   return static_cast<u64>(value);
    111 }
    112 
    113 // Integer to boolean
    114 template<typename TValue>
    115 ALWAYS_INLINE static constexpr bool ConvertToBool(TValue value)
    116 {
    117   return static_cast<bool>(value);
    118 }
    119 
    120 // Unsafe integer to boolean
    121 template<typename TValue>
    122 ALWAYS_INLINE static bool ConvertToBoolUnchecked(TValue value)
    123 {
    124   // static_assert(sizeof(uint8) == sizeof(bool));
    125   bool ret;
    126   std::memcpy(&ret, &value, sizeof(bool));
    127   return ret;
    128 }
    129 
    130 // Generic sign extension
    131 template<int NBITS, typename T>
    132 ALWAYS_INLINE static constexpr T SignExtendN(T value)
    133 {
    134   // http://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend
    135   constexpr int shift = 8 * sizeof(T) - NBITS;
    136   return static_cast<T>((static_cast<std::make_signed_t<T>>(value) << shift) >> shift);
    137 }
    138 
    139 /// Returns the number of zero bits before the first set bit, going MSB->LSB.
    140 template<typename T>
    141 ALWAYS_INLINE static unsigned CountLeadingZeros(T value)
    142 {
    143 #ifdef _MSC_VER
    144   if constexpr (sizeof(value) >= sizeof(u64))
    145   {
    146     unsigned long index;
    147     _BitScanReverse64(&index, ZeroExtend64(value));
    148     return static_cast<unsigned>(index) ^ static_cast<unsigned>((sizeof(value) * 8u) - 1u);
    149   }
    150   else
    151   {
    152     unsigned long index;
    153     _BitScanReverse(&index, ZeroExtend32(value));
    154     return static_cast<unsigned>(index) ^ static_cast<unsigned>((sizeof(value) * 8u) - 1u);
    155   }
    156 #else
    157   if constexpr (sizeof(value) >= sizeof(u64))
    158     return static_cast<unsigned>(__builtin_clzl(ZeroExtend64(value)));
    159   else if constexpr (sizeof(value) == sizeof(u32))
    160     return static_cast<unsigned>(__builtin_clz(ZeroExtend32(value)));
    161   else
    162     return static_cast<unsigned>(__builtin_clz(ZeroExtend32(value))) & static_cast<unsigned>((sizeof(value) * 8u) - 1u);
    163 #endif
    164 }
    165 
    166 /// Returns the number of zero bits before the first set bit, going LSB->MSB.
    167 template<typename T>
    168 ALWAYS_INLINE static unsigned CountTrailingZeros(T value)
    169 {
    170 #ifdef _MSC_VER
    171   if constexpr (sizeof(value) >= sizeof(u64))
    172   {
    173     unsigned long index;
    174     _BitScanForward64(&index, ZeroExtend64(value));
    175     return index;
    176   }
    177   else
    178   {
    179     unsigned long index;
    180     _BitScanForward(&index, ZeroExtend32(value));
    181     return index;
    182   }
    183 #else
    184   if constexpr (sizeof(value) >= sizeof(u64))
    185     return static_cast<unsigned>(__builtin_ctzl(ZeroExtend64(value)));
    186   else
    187     return static_cast<unsigned>(__builtin_ctz(ZeroExtend32(value)));
    188 #endif
    189 }
    190 
    191 // C++23-like std::byteswap()
    192 template<typename T>
    193 ALWAYS_INLINE static T ByteSwap(T value)
    194 {
    195   if constexpr (std::is_signed_v<T>)
    196   {
    197     return static_cast<T>(ByteSwap(std::make_unsigned_t<T>(value)));
    198   }
    199   else if constexpr (std::is_same_v<T, std::uint16_t>)
    200   {
    201 #ifdef _MSC_VER
    202     return _byteswap_ushort(value);
    203 #else
    204     return __builtin_bswap16(value);
    205 #endif
    206   }
    207   else if constexpr (std::is_same_v<T, std::uint32_t>)
    208   {
    209 #ifdef _MSC_VER
    210     return _byteswap_ulong(value);
    211 #else
    212     return __builtin_bswap32(value);
    213 #endif
    214   }
    215   else if constexpr (std::is_same_v<T, std::uint64_t>)
    216   {
    217 #ifdef _MSC_VER
    218     return _byteswap_uint64(value);
    219 #else
    220     return __builtin_bswap64(value);
    221 #endif
    222   }
    223 }