AK: Add support for Little/BigEndian<UFixedBigInteger<M>>

This commit is contained in:
Ali Mohammad Pur 2023-06-12 13:37:50 +03:30 committed by Ali Mohammad Pur
parent 94f5389934
commit 4f0f1c7c72
Notes: sideshowbarker 2024-07-17 18:06:52 +09:00
2 changed files with 85 additions and 0 deletions

View file

@ -11,6 +11,7 @@
#include <AK/BuiltinWrappers.h>
#include <AK/Checked.h>
#include <AK/Concepts.h>
#include <AK/Endian.h>
#include <AK/Format.h>
#include <AK/NumericLimits.h>
#include <AK/StdLibExtraDetails.h>
@ -513,6 +514,78 @@ struct NumericLimits<UFixedBigInt<bit_size>> {
static constexpr bool is_signed() { return false; }
};
template<size_t N>
class LittleEndian<UFixedBigInt<N>> {
template<size_t M>
constexpr static auto byte_swap_if_not_little_endian(UFixedBigInt<M> value)
{
if constexpr (HostIsLittleEndian) {
return value;
} else {
auto words = value.span();
auto front_it = words.begin();
auto ending_half_words = words.slice(ceil_div(words.size(), static_cast<size_t>(2)));
for (size_t i = 0; i < ending_half_words.size(); ++i, ++front_it)
*front_it = convert_between_host_and_little_endian(exchange(ending_half_words[ending_half_words.size() - i - 1], convert_between_host_and_little_endian(*front_it)));
if (words.size() % 2)
words[words.size() / 2] = convert_between_host_and_little_endian(*front_it);
return value;
}
}
public:
constexpr LittleEndian() = default;
constexpr LittleEndian(UFixedBigInt<N> value)
: m_value(byte_swap_if_not_little_endian(value))
{
}
constexpr operator UFixedBigInt<N>() const { return byte_swap_if_not_little_endian(m_value); }
private:
UFixedBigInt<N> m_value { 0 };
};
template<size_t N>
class BigEndian<UFixedBigInt<N>> {
template<size_t M>
constexpr static auto byte_swap_if_not_big_endian(UFixedBigInt<M> value)
{
if constexpr (!HostIsLittleEndian) {
return value;
} else {
auto words = value.span();
auto front_it = words.begin();
auto ending_half_words = words.slice(ceil_div(words.size(), static_cast<size_t>(2)));
for (size_t i = 0; i < ending_half_words.size(); ++i, ++front_it)
*front_it = convert_between_host_and_big_endian(exchange(ending_half_words[ending_half_words.size() - i - 1], convert_between_host_and_big_endian(*front_it)));
if (words.size() % 2)
words[words.size() / 2] = convert_between_host_and_big_endian(*front_it);
return value;
}
}
public:
constexpr BigEndian() = default;
constexpr BigEndian(UFixedBigInt<N> value)
: m_value(byte_swap_if_not_big_endian(value))
{
}
constexpr operator UFixedBigInt<N>() const { return byte_swap_if_not_big_endian(m_value); }
private:
UFixedBigInt<N> m_value { 0 };
};
template<size_t M>
struct Traits<UFixedBigInt<M>> : public GenericTraits<UFixedBigInt<M>> {
static constexpr bool is_trivially_serializable() { return true; }
static constexpr bool is_trivial() { return true; }
};
// ===== Formatting =====
// FIXME: This does not work for size != 2 ** x
template<Detail::NotBuiltInUFixedInt T>

View file

@ -185,3 +185,15 @@ TEST_CASE(mod_hardcoded)
EXPECT_EQ(u256(u128 { 0x7f13e232d82a24c6ULL, 0x23d41447dd7f5bc6ULL }, u128 { 0xd89a3ed8b30527caULL, 0xa98ef2cc01e83685ULL }) % u256(u128 { 0x8d4f5b1983fc1f0eULL, 0xf54102ece15fb0faULL }, u128 { 0x17b8aec68556a16dULL, 0x4e1e5bea70cb9398ULL }), u256(u128 { 0x64752bffd031e6aaULL, 0x39520e6e1abff9d1ULL }, u128 { 0xa928e14ba857e4eeULL, 0x0d523af720510f55ULL }));
EXPECT_EQ(u256(u128 { 0x49750d7f39d61607ULL, 0x58bdef1c3e00d18eULL }, u128 { 0xa651479cd1fd1933ULL, 0xd1834bc3d654b633ULL }) % u256(u128 { 0x1bda34f5ec68ef3bULL, 0x12c65ce5363a7616ULL }, u128 { 0x5a79c4d85da0071aULL, 0xffa6b6284559d1aaULL }), u256(u128 { 0x49750d7f39d61607ULL, 0x58bdef1c3e00d18eULL }, u128 { 0xa651479cd1fd1933ULL, 0xd1834bc3d654b633ULL }));
}
TEST_CASE(endian_swap)
{
constexpr u128 const a { 0x1234567890abcdefULL, 0xfedcba0987654321ULL };
constexpr u128 const a_swapped { 0x2143658709badcfeull, 0xefcdab9078563412ull };
static_assert(!AK::HostIsLittleEndian || bit_cast<u128>(BigEndian { a }) == a_swapped);
static_assert(AK::HostIsLittleEndian || bit_cast<u128>(LittleEndian { a }) == a_swapped);
static_assert(!AK::HostIsLittleEndian || bit_cast<u128>(LittleEndian { a }) == a);
static_assert(AK::HostIsLittleEndian || bit_cast<u128>(BigEndian { a }) == a);
}