// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once #include "types.h" #include #include #ifdef _MSC_VER #include #endif // Zero-extending helper template ALWAYS_INLINE static constexpr TReturn ZeroExtend(TValue value) { return static_cast(static_cast::type>( static_cast::type>(value))); } // Sign-extending helper template ALWAYS_INLINE static constexpr TReturn SignExtend(TValue value) { return static_cast( static_cast::type>(static_cast::type>(value))); } // Type-specific helpers template ALWAYS_INLINE static constexpr u16 ZeroExtend16(TValue value) { return ZeroExtend(value); } template ALWAYS_INLINE static constexpr u32 ZeroExtend32(TValue value) { return ZeroExtend(value); } template ALWAYS_INLINE static constexpr u64 ZeroExtend64(TValue value) { return ZeroExtend(value); } template ALWAYS_INLINE static constexpr u16 SignExtend16(TValue value) { return SignExtend(value); } template ALWAYS_INLINE static constexpr u32 SignExtend32(TValue value) { return SignExtend(value); } template ALWAYS_INLINE static constexpr u64 SignExtend64(TValue value) { return SignExtend(value); } template ALWAYS_INLINE static constexpr u8 Truncate8(TValue value) { return static_cast(static_cast::type>(value)); } template ALWAYS_INLINE static constexpr u16 Truncate16(TValue value) { return static_cast(static_cast::type>(value)); } template ALWAYS_INLINE static constexpr u32 Truncate32(TValue value) { return static_cast(static_cast::type>(value)); } // BCD helpers ALWAYS_INLINE static constexpr u8 BinaryToBCD(u8 value) { return ((value / 10) << 4) + (value % 10); } ALWAYS_INLINE static constexpr u8 PackedBCDToBinary(u8 value) { return ((value >> 4) * 10) + (value % 16); } ALWAYS_INLINE static constexpr u8 IsValidBCDDigit(u8 digit) { return (digit <= 9); } ALWAYS_INLINE static constexpr u8 IsValidPackedBCD(u8 value) { return IsValidBCDDigit(value & 0x0F) && IsValidBCDDigit(value >> 4); } // Boolean to integer ALWAYS_INLINE static constexpr u8 BoolToUInt8(bool value) { return static_cast(value); } ALWAYS_INLINE static constexpr u16 BoolToUInt16(bool value) { return static_cast(value); } ALWAYS_INLINE static constexpr u32 BoolToUInt32(bool value) { return static_cast(value); } ALWAYS_INLINE static constexpr u64 BoolToUInt64(bool value) { return static_cast(value); } // Integer to boolean template ALWAYS_INLINE static constexpr bool ConvertToBool(TValue value) { return static_cast(value); } // Unsafe integer to boolean template ALWAYS_INLINE static bool ConvertToBoolUnchecked(TValue value) { // static_assert(sizeof(uint8) == sizeof(bool)); bool ret; std::memcpy(&ret, &value, sizeof(bool)); return ret; } // Generic sign extension template ALWAYS_INLINE static constexpr T SignExtendN(T value) { // http://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend constexpr int shift = 8 * sizeof(T) - NBITS; return static_cast((static_cast>(value) << shift) >> shift); } /// Returns the number of zero bits before the first set bit, going MSB->LSB. template ALWAYS_INLINE static unsigned CountLeadingZeros(T value) { #ifdef _MSC_VER if constexpr (sizeof(value) >= sizeof(u64)) { unsigned long index; _BitScanReverse64(&index, ZeroExtend64(value)); return static_cast(index) ^ static_cast((sizeof(value) * 8u) - 1u); } else { unsigned long index; _BitScanReverse(&index, ZeroExtend32(value)); return static_cast(index) ^ static_cast((sizeof(value) * 8u) - 1u); } #else if constexpr (sizeof(value) >= sizeof(u64)) return static_cast(__builtin_clzl(ZeroExtend64(value))); else if constexpr (sizeof(value) == sizeof(u32)) return static_cast(__builtin_clz(ZeroExtend32(value))); else return static_cast(__builtin_clz(ZeroExtend32(value))) & static_cast((sizeof(value) * 8u) - 1u); #endif } /// Returns the number of zero bits before the first set bit, going LSB->MSB. template ALWAYS_INLINE static unsigned CountTrailingZeros(T value) { #ifdef _MSC_VER if constexpr (sizeof(value) >= sizeof(u64)) { unsigned long index; _BitScanForward64(&index, ZeroExtend64(value)); return index; } else { unsigned long index; _BitScanForward(&index, ZeroExtend32(value)); return index; } #else if constexpr (sizeof(value) >= sizeof(u64)) return static_cast(__builtin_ctzl(ZeroExtend64(value))); else return static_cast(__builtin_ctz(ZeroExtend32(value))); #endif } // C++23-like std::byteswap() template ALWAYS_INLINE static T ByteSwap(T value) { if constexpr (std::is_signed_v) { return static_cast(ByteSwap(std::make_unsigned_t(value))); } else if constexpr (std::is_same_v) { #ifdef _MSC_VER return _byteswap_ushort(value); #else return __builtin_bswap16(value); #endif } else if constexpr (std::is_same_v) { #ifdef _MSC_VER return _byteswap_ulong(value); #else return __builtin_bswap32(value); #endif } else if constexpr (std::is_same_v) { #ifdef _MSC_VER return _byteswap_uint64(value); #else return __builtin_bswap64(value); #endif } }