cpp_mp/mp/utils.hpp
2025-11-23 19:00:32 +01:00

160 lines
3.4 KiB
C++

#pragma once
#include <concepts>
#include <cstdint>
#include <type_traits>
namespace mp
{
//======================== DoubleWidthType =======================//
template <typename T>
struct DoubleWidth;
template <>
struct DoubleWidth<uint8_t>
{
using type = uint16_t;
};
template <>
struct DoubleWidth<uint16_t>
{
using type = uint32_t;
};
template <>
struct DoubleWidth<uint32_t>
{
using type = uint64_t;
};
#ifdef __SIZEOF_INT128__
template <>
struct DoubleWidth<uint64_t>
{
using type = __uint128_t;
};
#endif
template <typename T>
using DoubleWidthType = typename DoubleWidth<T>::type;
//======================== HalfWidthType =======================//
template <typename T>
struct HalfWidth;
template <>
struct HalfWidth<uint16_t>
{
using type = uint8_t;
};
template <>
struct HalfWidth<uint32_t>
{
using type = uint16_t;
};
template <>
struct HalfWidth<uint64_t>
{
using type = uint32_t;
};
template <typename T>
using HalfWidthType = typename HalfWidth<T>::type;
//======================== Concepts =======================//
template <typename T>
concept ElementSuitable = std::unsigned_integral<T>;
template <typename T, typename TElem = T::ElementType>
concept AnyConstMpInt = requires(T t, TElem a, size_t i) {
{ t[i] } -> std::convertible_to<TElem>;
{ t.get(i) } -> std::convertible_to<TElem>;
{ t.size_elems() } -> std::convertible_to<size_t>;
{ T::MAX_BYTES } -> std::convertible_to<size_t>;
{ T::MAX_ELEMS } -> std::convertible_to<size_t>;
{ T::ELEMENT_BYTES } -> std::convertible_to<size_t>;
{ T::LAST_ELEM_MASK } -> std::convertible_to<TElem>;
};
template <typename T, typename TElem = T::ElementType>
concept AnyMpInt = AnyConstMpInt<T> && requires(T t, TElem a, size_t i) {
{ t.set(i, a) };
{ t.try_set(i, a) } -> std::convertible_to<bool>;
{ t.zero() };
};
template <typename T>
concept AnyRegularInt = std::unsigned_integral<T>;
template <typename T>
concept AnyConstInt = AnyRegularInt<T> || AnyConstMpInt<T>;
//======================== Utils =======================//
#ifdef __SIZEOF_INT128__
using LongestElementSuitableType = uint64_t;
#else
using LongestElementSuitableType = HalfWidthType<size_t>;
#endif
template <AnyConstMpInt TA, AnyConstMpInt TB>
constexpr bool ElementTypesMatch = std::is_same_v<typename TA::ElementType, typename TB::ElementType>;
template <ElementSuitable TElem, size_t MaxBytes>
constexpr TElem calculate_last_elem_mask()
{
size_t last_elem_bytes = MaxBytes % sizeof(TElem);
TElem mask = 0;
if (last_elem_bytes == 0)
{
return ~mask;
}
for (size_t i = 0; i < last_elem_bytes; ++i)
{
mask <<= 8;
mask |= 0xFF;
}
return mask;
}
inline char hex_digit(uint8_t bits)
{
return bits > 9 ? 'a' + (bits - 10) : '0' + bits;
}
template <AnyConstMpInt T>
std::string to_hex_string(const T& number)
{
constexpr size_t ELEMENT_DIGITS = T::ELEMENT_BYTES * 2;
std::string str(number.size_elems() * ELEMENT_DIGITS, '-');
for (size_t elem = 0; elem < number.size_elems(); ++elem)
{
auto v = number[elem];
for (size_t digit = 0; digit < ELEMENT_DIGITS; ++digit)
{
str[str.size() - 1 - (elem * ELEMENT_DIGITS) - digit] = hex_digit((v >> (digit * 4)) & 0xF);
}
}
auto first = str.find_first_not_of('0');
if (first != std::string::npos)
{
return "0x" + str.substr(first);
}
return "0x0";
}
}