328 lines
7.9 KiB
C++
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 {};
|
|
} |