diff --git a/src/lib/utils/loadstor.h b/src/lib/utils/loadstor.h index bf895ded2c6..3cd9af796b0 100644 --- a/src/lib/utils/loadstor.h +++ b/src/lib/utils/loadstor.h @@ -167,12 +167,34 @@ constexpr bool native_endianness_is_unknown() { #endif } +/** + * Models a custom type that provides factory methods to be loaded in big- or + * little-endian byte order. + */ +template +concept custom_loadable = requires(std::span data) { + { T::load_be(data) } -> std::same_as; + { T::load_le(data) } -> std::same_as; +}; + +/** + * Models a custom type that provides store methods to be stored in big- or + * little-endian byte order. + */ +template +concept custom_storable = requires(std::span data, const T value) { + { value.store_be(data) }; + { value.store_le(data) }; +}; + /** * Models a type that can be loaded/stored from/to a byte range. */ template -concept unsigned_integralish = std::unsigned_integral> || - (std::is_enum_v && std::unsigned_integral>); +concept unsigned_integralish = + std::unsigned_integral> || + (std::is_enum_v && std::unsigned_integral>) || + (custom_loadable> || custom_storable>); template struct wrapped_type_helper_with_enum { @@ -276,6 +298,7 @@ inline constexpr void fallback_store_any(InT in, OutR&& out_range) { * @return T loaded from @p in_range, as a big-endian value */ template InR> + requires(!custom_loadable>) inline constexpr WrappedOutT load_any(InR&& in_range) { using OutT = detail::wrapped_type; ranges::assert_exact_byte_length(in_range); @@ -302,6 +325,28 @@ inline constexpr WrappedOutT load_any(InR&& in_range) { }()); } +/** + * Load a custom object from a range in either big or little endian byte order + * + * This is the base implementation for custom objects (e.g. SIMD type wrappres), + * all other overloads are just convenience overloads. + * + * @param in_range a fixed-length byte range + * @return T loaded from @p in_range, as a big-endian value + */ +template InR> + requires(custom_loadable>) +inline constexpr WrappedOutT load_any(InR&& in_range) { + using OutT = detail::wrapped_type; + ranges::assert_exact_byte_length(in_range); + std::span ins{in_range}; + if(endianness == Endianness::Big) { + return wrap_strong_type(OutT::load_be(ins)); + } else { + return wrap_strong_type(OutT::load_le(ins)); + } +} + /** * Load many unsigned integers * @param in a fixed-length span to some bytes @@ -502,6 +547,7 @@ namespace detail { * @param out_range a byte range to store the word into */ template OutR> + requires(!custom_storable>) inline constexpr void store_any(WrappedInT wrapped_in, OutR&& out_range) { const auto in = detail::unwrap_strong_type_or_enum(wrapped_in); using InT = decltype(in); @@ -527,6 +573,29 @@ inline constexpr void store_any(WrappedInT wrapped_in, OutR&& out_range) { } } +/** + * Store a custom word in either big or little endian byte order into a range + * + * This is the base implementation for storing custom objects, all other + * overloads are just convenience overloads. + * + * @param wrapped_in a custom object to be stored + * @param out_range a byte range to store the word into + */ +template OutR> + requires(custom_storable>) +inline constexpr void store_any(WrappedInT wrapped_in, OutR&& out_range) { + const auto in = detail::unwrap_strong_type_or_enum(wrapped_in); + using InT = decltype(in); + ranges::assert_exact_byte_length(out_range); + std::span outs{out_range}; + if(endianness == Endianness::Big) { + in.store_be(outs); + } else { + in.store_le(outs); + } +} + /** * Store many unsigned integers words into a byte range * @param out a sized range of some bytes diff --git a/src/tests/test_utils.cpp b/src/tests/test_utils.cpp index e2a7b112f7e..16212ffc165 100644 --- a/src/tests/test_utils.cpp +++ b/src/tests/test_utils.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -466,6 +467,26 @@ class Utility_Function_Tests final : public Test { result.test_is_eq(Botan::store_le>(TestEnum32::_1), Botan::hex_decode("78563412")); result.test_is_eq>(Botan::store_be(TestEnum32::_2), {0x78, 0x56, 0x34, 0x12}); + // Test load/stores SIMD wrapper types + auto simd_le_in = Botan::hex_decode("ABCDEF01234567890123456789ABCDEF"); + auto simd_be_in = Botan::hex_decode("0123456789ABCDEFABCDEF0123456789"); + + auto simd_le = Botan::load_le(simd_le_in); + auto simd_be = Botan::load_le(simd_be_in); + auto simd_le_array = Botan::load_le>(Botan::concat(simd_le_in, simd_be_in)); + auto simd_be_array = Botan::load_be>(Botan::concat(simd_be_in, simd_le_in)); + + auto simd_le_vec = Botan::store_le>(simd_le); + auto simd_be_vec = Botan::store_be(simd_be); + auto simd_le_array_vec = Botan::store_le>(simd_le_array); + auto simd_be_array_vec = Botan::store_be(simd_be_array); + + result.test_is_eq(simd_le_vec, simd_le_in); + result.test_is_eq(std::vector(simd_be_vec.begin(), simd_be_vec.end()), simd_be_in); + result.test_is_eq(simd_le_array_vec, Botan::concat(simd_le_in, simd_be_in)); + result.test_is_eq(std::vector(simd_be_array_vec.begin(), simd_be_array_vec.end()), + Botan::concat(simd_be_in, simd_le_in)); + return result; }