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 }