AuROXTL/Include/auROXTL/auBitsUtils.hpp
2023-11-18 02:13:57 +00:00

328 lines
7.9 KiB
C++

/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: auBitsUtils.hpp
Date: 2022-2-1
Author: Reece
***/
#pragma once
template <class T>
static auline bool AuTestBit(T value, AuUInt8 idx)
{
return value & (T(1) << T(idx));
}
template <class T>
static auline bool AuBitTest(T value, AuUInt8 idx)
{
return value & (T(1) << T(idx));
}
template <class T>
static auline void AuSetBit(T &value, AuUInt8 idx)
{
value |= T(1) << T(idx);
}
template <class T>
static auline void AuBitSet(T &value, AuUInt8 idx)
{
value |= T(1) << T(idx);
}
template <class T>
static auline void AuClearBit(T &value, AuUInt8 idx)
{
value &= ~(T(1) << T(idx));
}
template <class T>
static auline void AuBitClear(T &value, AuUInt8 idx)
{
value &= ~(T(1) << T(idx));
}
template <class T>
static auline bool AuBitScanForward(AuUInt8 &index, T value)
{
unsigned long ret;
bool success;
success = false;
index = 0;
#if defined(AURORA_COMPILER_MSVC)
if constexpr (sizeof(T) == sizeof(AuUInt64))
{
#if defined(AURORA_IS_32BIT)
if (!(success = _BitScanForward(&ret, static_cast<AuUInt32>(value & 0xffffffff))))
{
if (!_BitScanForward(&ret, static_cast<AuUInt32>((value >> 32) & 0xffffffff)))
{
return false;
}
ret += 32;
}
#else
success = _BitScanForward64(&ret, static_cast<AuUInt64>(value));
#endif
}
else
{
success = _BitScanForward(&ret, static_cast<unsigned long>(value));
}
#elif defined(AURORA_COMPILER_GCC) || defined(AURORA_COMPILER_CLANG)
if (value == 0)
{
return false;
}
if constexpr (sizeof(T) == sizeof(AuUInt64))
{
#if defined(AURORA_IS_32BIT)
auto lower = static_cast<AuUInt32>(value & 0xffffffff));
if (lower == 0)
{
ret = __builtin_ctzl(static_cast<AuUInt32>((value >> 32) & 0xffffffff));
ret += 32;
}
else
{
ret = __builtin_ctzl(static_cast<AuUInt32>(lower));
}
#else
ret = __builtin_ctzll(static_cast<AuUInt64>(value));
#endif
}
else if constexpr (sizeof(T) == sizeof(unsigned long))
{
ret = __builtin_ctzl(static_cast<unsigned long>(value));
}
else if constexpr (sizeof(T) == sizeof(unsigned int))
{
ret = __builtin_ctz(static_cast<unsigned int>(value));
}
success = true;
#endif
index = ret;
return success;
}
template <class T>
static auline bool AuBitScanReverse(AuUInt8 &index, T value)
{
unsigned long ret;
bool success;
success = false;
index = 0;
#if defined(AURORA_COMPILER_MSVC)
if constexpr (sizeof(T) == sizeof(AuUInt64))
{
#if defined(AURORA_IS_32BIT)
if (!(success = _BitScanReverse(&ret, static_cast<AuUInt32>((value >> 32) & 0xffffffff))))
{
if (!_BitScanReverse(&ret, static_cast<AuUInt32>(value & 0xffffffff)))
{
return false;
}
ret += 32;
}
#else
success = _BitScanReverse64(&ret, static_cast<AuUInt64>(value));
#endif
}
else
{
success = _BitScanReverse(&ret, static_cast<unsigned long>(value));
}
#elif defined(AURORA_COMPILER_GCC) || defined(AURORA_COMPILER_CLANG)
if (value == 0)
{
return false;
}
if constexpr (sizeof(T) == sizeof(AuUInt64))
{
#if defined(AURORA_IS_32BIT)
auto lower = static_cast<AuUInt32>((value >> 32) & 0xffffffff);
if (lower == 0)
{
ret = __builtin_clzl(static_cast<AuUInt32>(value & 0xffffffff)));
ret = 63 - ret;
}
else
{
ret = __builtin_clzl(static_cast<AuUInt32>(lower));
ret = 31 - ret;
}
#else
ret = __builtin_clzll(static_cast<AuUInt64>(value));
ret = 63 - ret;
#endif
}
else if constexpr (sizeof(T) == sizeof(unsigned long))
{
ret = __builtin_clzl(static_cast<unsigned long>(value));
if constexpr (sizeof(unsigned long) == 4)
{
ret = 31 - ret;
}
else if constexpr (sizeof(unsigned long) == 8)
{
ret = 63 - ret;
}
}
else// if constexpr (sizeof(T) == sizeof(unsigned int))
{
ret = __builtin_clz(static_cast<unsigned int>(value));
if constexpr (sizeof(unsigned int) == 4)
{
ret = 31 - ret;
}
else if constexpr (sizeof(unsigned int) == 8)
{
ret = 63 - ret;
}
}
success = true;
#endif
index = ret;
return success;
}
template <class T>
struct AuHalfWord
{
using ReturnType_t = AuConditional_t<AuIsSame_v<T, AuUInt64>, AuUInt32, AuConditional_t<AuIsSame_v<T, AuUInt32>, AuUInt32, AuConditional_t<AuIsSame_v<T, AuUInt16>, AuUInt8, AuFalseType>>>;
static ReturnType_t ToLower(T in)
{
if constexpr (AuIsSame_v<T, AuUInt64>)
{
return in & AuUInt64(0xFFFFFFFF);
}
else if constexpr (AuIsSame_v<T, AuUInt32>)
{
return in & 0xFFFF;
}
else if constexpr (AuIsSame_v<T, AuUInt16>)
{
return in & 0xFF;
}
else
{
return {};
}
}
static ReturnType_t ToHigher(T in)
{
if constexpr (AuIsSame_v<T, AuUInt64>)
{
return (in >> AuUInt64(32)) & AuUInt64(0xFFFFFFFF);
}
else if constexpr (AuIsSame_v<T, AuUInt32>)
{
return (in >> 16) & 0xFFFF;
}
else if constexpr (AuIsSame_v<T, AuUInt16>)
{
return (in >> 8) & 0xFF;
}
else
{
return {};
}
}
};
template <class T>
static auto AuBitsToLower(T in)
{
return AuHalfWord<T>::ToLower(in);
}
template <class T>
static auto AuBitsToHigher(T in)
{
return AuHalfWord<T>::ToHigher(in);
}
template <class T>
static AuUInt8 AuPopCnt(T in)
{
#if defined(AURORA_COMPILER_MSVC)
#if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86)
#if defined(AURORA_ARCH_X64)
if constexpr (sizeof(T) == sizeof(AuUInt64))
{
return _mm_popcnt_u64(static_cast<AuUInt64>(in));
}
else
#endif
if constexpr (sizeof(T) == sizeof(unsigned int))
{
return __popcnt(static_cast<unsigned int>(in));
}
else if constexpr (sizeof(T) <= sizeof(AuUInt16))
{
return __popcnt16(static_cast<AuUInt16>(in));
}
#endif
#else
if constexpr (sizeof(T) == sizeof(unsigned long long))
{
return __builtin_popcountll(static_cast<unsigned long long>(in));
}
else if constexpr (sizeof(T) == sizeof(unsigned long))
{
return __builtin_popcountl(static_cast<unsigned long>(in));
}
else if constexpr (sizeof(T) == sizeof(unsigned int))
{
return __builtin_popcount(static_cast<unsigned int>(in));
}
#endif
#if defined(AU_CPU_ENDIAN_LITTLE)
if constexpr (sizeof(T) == sizeof(AuUInt64))
{
const AuUInt64 m1 = 0x5555555555555555ll;
const AuUInt64 m2 = 0x3333333333333333ll;
const AuUInt64 m4 = 0x0F0F0F0F0F0F0F0Fll;
const AuUInt64 h01 = 0x0101010101010101ll;
in -= (in >> 1) & m1;
in = (in & m2) + ((in >> 2) & m2);
in = (in + (in >> 4)) & m4;
return (in * h01) >> 56;
}
else if constexpr (sizeof(T) == sizeof(AuUInt32))
{
const AuUInt32 m1 = 0x55555555l;
const AuUInt32 m2 = 0x33333333l;
const AuUInt32 m4 = 0x0F0F0F0Fl;
const AuUInt32 h01 = 0x01010101l;
in -= (in >> 1) & m1;
in = (in & m2) + ((in >> 2) & m2);
in = (in + (in >> 4)) & m4;
return (in * h01) >> 24;
}
#endif
if constexpr ((sizeof(T) == sizeof(AuUInt16)) ||
(sizeof(T) == sizeof(AuUInt8)))
{
return AuPopCnt<AuUInt32>(AuUInt32(in));
}
return {};
}