From 4f0f1c7c72ba50528a9efe3ce734ef8c23acd153 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Mon, 12 Jun 2023 13:37:50 +0330 Subject: [PATCH] AK: Add support for Little/BigEndian> --- AK/UFixedBigInt.h | 73 +++++++++++++++++++++++++++++++++++ Tests/AK/TestUFixedBigInt.cpp | 12 ++++++ 2 files changed, 85 insertions(+) diff --git a/AK/UFixedBigInt.h b/AK/UFixedBigInt.h index 397eaf042b0..3764da9c6b0 100644 --- a/AK/UFixedBigInt.h +++ b/AK/UFixedBigInt.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -513,6 +514,78 @@ struct NumericLimits> { static constexpr bool is_signed() { return false; } }; +template +class LittleEndian> { + template + constexpr static auto byte_swap_if_not_little_endian(UFixedBigInt 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(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 value) + : m_value(byte_swap_if_not_little_endian(value)) + { + } + + constexpr operator UFixedBigInt() const { return byte_swap_if_not_little_endian(m_value); } + +private: + UFixedBigInt m_value { 0 }; +}; + +template +class BigEndian> { + template + constexpr static auto byte_swap_if_not_big_endian(UFixedBigInt 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(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 value) + : m_value(byte_swap_if_not_big_endian(value)) + { + } + + constexpr operator UFixedBigInt() const { return byte_swap_if_not_big_endian(m_value); } + +private: + UFixedBigInt m_value { 0 }; +}; + +template +struct Traits> : public GenericTraits> { + 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 diff --git a/Tests/AK/TestUFixedBigInt.cpp b/Tests/AK/TestUFixedBigInt.cpp index 8e7219d9b3c..c5a5b3cd076 100644 --- a/Tests/AK/TestUFixedBigInt.cpp +++ b/Tests/AK/TestUFixedBigInt.cpp @@ -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(BigEndian { a }) == a_swapped); + static_assert(AK::HostIsLittleEndian || bit_cast(LittleEndian { a }) == a_swapped); + + static_assert(!AK::HostIsLittleEndian || bit_cast(LittleEndian { a }) == a); + static_assert(AK::HostIsLittleEndian || bit_cast(BigEndian { a }) == a); +}