10#ifndef BOTAN_LOAD_STORE_H_
11#define BOTAN_LOAD_STORE_H_
13#include <botan/concepts.h>
14#include <botan/mem_ops.h>
15#include <botan/strong_type.h>
16#include <botan/types.h>
17#include <botan/internal/bswap.h>
66 return static_cast<uint8_t
>(input >> (((~byte_num) & (
sizeof(
T) - 1)) << 3));
74template <
size_t B,
typename T>
76 requires(B <
sizeof(
T))
78 const size_t shift = ((~B) & (
sizeof(
T) - 1)) << 3;
79 return static_cast<uint8_t
>((input >> shift) & 0xFF);
88inline constexpr uint16_t
make_uint16(uint8_t i0, uint8_t i1) {
89 return static_cast<uint16_t
>((
static_cast<uint16_t
>(i0) << 8) | i1);
100inline constexpr uint32_t
make_uint32(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3) {
101 return ((
static_cast<uint32_t
>(i0) << 24) | (
static_cast<uint32_t
>(i1) << 16) | (
static_cast<uint32_t
>(i2) << 8) |
102 (
static_cast<uint32_t
>(i3)));
118 uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4, uint8_t i5, uint8_t i6, uint8_t i7) {
119 return ((
static_cast<uint64_t
>(i0) << 56) | (
static_cast<uint64_t
>(i1) << 48) | (
static_cast<uint64_t
>(i2) << 40) |
120 (
static_cast<uint64_t
>(i3) << 32) | (
static_cast<uint64_t
>(i4) << 24) | (
static_cast<uint64_t
>(i5) << 16) |
121 (
static_cast<uint64_t
>(i6) << 8) | (
static_cast<uint64_t
>(i7)));
136#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
138#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
151#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
153#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
161template <Endianness endianness>
163#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) || defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
176 { T::load_be(data) } -> std::same_as<T>;
177 { T::load_le(data) } -> std::same_as<T>;
186 { value.store_be(data) };
187 { value.store_le(data) };
195 std::unsigned_integral<strong_type_wrapped_type<T>> ||
196 (std::is_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>) ||
205 requires std::is_enum_v<T>
207 using type = std::underlying_type_t<T>;
210template <
unsigned_
integralish T>
213template <
unsigned_
integralish InT>
215 if constexpr(std::is_enum_v<InT>) {
217 return static_cast<std::underlying_type_t<InT>
>(t);
223template <
unsigned_
integralish OutT, std::
unsigned_
integral T>
225 if constexpr(std::is_enum_v<OutT>) {
226 return static_cast<OutT
>(t);
237template <Endianness endianness, std::
unsigned_
integral OutT, ranges::contiguous_range<u
int8_t> InR>
239 std::span in{in_range};
242 return [&]<
size_t... i>(std::index_sequence<i...>) {
243 return static_cast<OutT
>(((
static_cast<OutT
>(in[i]) << ((
sizeof(OutT) - i - 1) * 8)) | ...));
244 } (std::make_index_sequence<sizeof(OutT)>());
247 return [&]<
size_t... i>(std::index_sequence<i...>) {
248 return static_cast<OutT
>(((
static_cast<OutT
>(in[i]) << (i * 8)) | ...));
249 } (std::make_index_sequence<sizeof(OutT)>());
259template <Endianness endianness, std::
unsigned_
integral InT, ranges::contiguous_output_range<u
int8_t> OutR>
261 std::span out{out_range};
264 [&]<
size_t... i>(std::index_sequence<i...>) {
266 } (std::make_index_sequence<sizeof(InT)>());
269 [&]<
size_t... i>(std::index_sequence<i...>) {
270 ((out[i] =
get_byte<
sizeof(InT) - i - 1>(in)), ...);
271 } (std::make_index_sequence<sizeof(InT)>());
300template <Endianness endianness,
unsigned_
integralish WrappedOutT, ranges::contiguous_range<u
int8_t> InR>
301 requires(!custom_loadable<strong_type_wrapped_type<WrappedOutT>>)
302inline constexpr WrappedOutT
load_any(InR&& in_range) {
310 if(std::is_constant_evaluated()) {
313 std::span in{in_range};
314 if constexpr(
sizeof(OutT) == 1) {
315 return static_cast<OutT
>(in[0]);
316 }
else if constexpr(
is_native(endianness)) {
337template <Endianness endianness,
unsigned_
integralish WrappedOutT, ranges::contiguous_range<u
int8_t> InR>
338 requires(custom_loadable<strong_type_wrapped_type<WrappedOutT>>)
339inline constexpr WrappedOutT
load_any(InR&& in_range) {
342 std::span<
const uint8_t,
sizeof(OutT)> ins{in_range};
355template <Endianness endianness,
typename OutT, ranges::contiguous_range<u
int8_t> InR,
unsigned_
integralish... Ts>
356 requires(
sizeof...(Ts) > 0) && ((std::same_as<AutoDetect, OutT> && all_same_v<Ts...>) ||
357 (unsigned_integralish<OutT> && all_same_v<OutT, Ts...>))
358inline constexpr
void load_any(InR&& in, Ts&... outs) {
360 auto load_one = [off = 0]<
typename T>(
auto i,
T& o)
mutable {
365 (load_one(std::span{in}, outs), ...);
379 requires(unsigned_integralish<std::ranges::range_value_t<OutR>> &&
380 (std::same_as<AutoDetect, OutT> || std::same_as<OutT, std::ranges::range_value_t<OutR>>))
381inline constexpr void load_any(OutR&& out, InR&& in) {
383 using element_type = std::ranges::range_value_t<OutR>;
385 auto load_elementwise = [&] {
386 constexpr size_t bytes_per_element =
sizeof(element_type);
387 std::span<const uint8_t> in_s(in);
388 for(
auto& out_elem : out) {
390 in_s = in_s.subspan(bytes_per_element);
397 if(std::is_constant_evaluated()) {
419template <Endianness endianness,
typename OutT, ranges::contiguous_range<u
int8_t> InR>
420 requires(std::same_as<AutoDetect, OutT> ||
422 unsigned_integralish<typename OutT::value_type>))
424 auto out = []([[maybe_unused]]
const auto& in) {
425 if constexpr(std::same_as<AutoDetect, OutT>) {
427 constexpr size_t extent =
decltype(std::span{in})::extent;
431 std::conditional_t<extent == 1, uint8_t,
432 std::conditional_t<extent == 2, uint16_t,
433 std::conditional_t<extent == 4, uint32_t,
434 std::conditional_t<extent == 8, uint64_t, void>>>>;
438 !std::is_void_v<type>,
439 "Cannot determine the output type based on a statically sized bytearray with length other than those: 1, 2, 4, 8");
444 !std::same_as<AutoDetect, OutT>,
445 "cannot infer return type from a dynamic range at compile time, please specify it explicitly");
448 const size_t in_bytes = std::span{in}.size_bytes();
449 constexpr size_t out_elem_bytes =
sizeof(
typename OutT::value_type);
451 "Input range is not word-aligned with the requested output range");
452 return OutT(in_bytes / out_elem_bytes);
458 using out_type =
decltype(out);
463 using out_range_type = std::ranges::range_value_t<out_type>;
479template <Endianness endianness,
unsigned_
integralish OutT>
480inline constexpr OutT
load_any(
const uint8_t in[],
size_t off) {
482 constexpr size_t out_size =
sizeof(OutT);
491template <
Endianness endianness,
typename OutT, unsigned_integralish... Ts>
492 requires(
sizeof...(Ts) > 0 && all_same_v<Ts...> &&
493 ((std::same_as<AutoDetect, OutT> && all_same_v<Ts...>) ||
494 (unsigned_integralish<OutT> && all_same_v<OutT, Ts...>)))
495inline constexpr void load_any(
const uint8_t in[], Ts&... outs) {
496 constexpr auto bytes = (
sizeof(outs) + ...);
507template <Endianness endianness,
typename OutT,
unsigned_
integralish T>
508 requires(std::same_as<AutoDetect, OutT> || std::same_as<T, OutT>)
509inline constexpr void load_any(
T out[],
const uint8_t in[],
size_t count) {
520template <
typename OutT = detail::AutoDetect,
typename... ParamTs>
521inline constexpr auto load_le(ParamTs&&... params) {
529template <
typename OutT = detail::AutoDetect,
typename... ParamTs>
530inline constexpr auto load_be(ParamTs&&... params) {
549template <Endianness endianness,
unsigned_
integralish WrappedInT, ranges::contiguous_output_range<u
int8_t> OutR>
550 requires(!custom_storable<strong_type_wrapped_type<WrappedInT>>)
551inline constexpr void store_any(WrappedInT wrapped_in, OutR&& out_range) {
553 using InT =
decltype(in);
555 std::span out{out_range};
560 if(std::is_constant_evaluated()) {
563 if constexpr(
sizeof(InT) == 1) {
564 out[0] =
static_cast<uint8_t
>(in);
565 }
else if constexpr(
is_native(endianness)) {
585template <Endianness endianness,
unsigned_
integralish WrappedInT, ranges::contiguous_output_range<u
int8_t> OutR>
586 requires(custom_storable<strong_type_wrapped_type<WrappedInT>>)
587inline constexpr void store_any(WrappedInT wrapped_in, OutR&& out_range) {
589 using InT =
decltype(in);
591 std::span<uint8_t,
sizeof(InT)> outs{out_range};
607 unsigned_integralish... Ts>
608 requires(
sizeof...(Ts) > 0) && ((std::same_as<AutoDetect, InT> && all_same_v<Ts...>) ||
609 (unsigned_integralish<InT> && all_same_v<InT, Ts...>))
612 auto store_one = [off = 0]<
typename T>(
auto o,
T i)
mutable {
617 (store_one(std::span{out}, ins), ...);
630 requires(std::same_as<AutoDetect, InT> || std::same_as<InT, std::ranges::range_value_t<InR>>)
633 using element_type = std::ranges::range_value_t<InR>;
635 auto store_elementwise = [&] {
636 constexpr size_t bytes_per_element =
sizeof(element_type);
637 std::span<uint8_t> out_s(out);
638 for(
auto in_elem : in) {
640 out_s = out_s.subspan(bytes_per_element);
647 if(std::is_constant_evaluated()) {
672template <Endianness endianness,
typename InT,
unsigned_
integralish T, ranges::contiguous_output_range<u
int8_t> OutR>
673 requires std::same_as<AutoDetect, InT>
687template <Endianness endianness,
typename OutR, ranges::spanable_range InR>
688 requires(std::same_as<AutoDetect, OutR> ||
692 auto out = []([[maybe_unused]]
const auto& in) {
693 if constexpr(std::same_as<AutoDetect, OutR>) {
695 constexpr size_t bytes =
decltype(std::span{in})::extent *
sizeof(std::ranges::range_value_t<InR>);
696 return std::array<uint8_t, bytes>();
699 !std::same_as<AutoDetect, OutR>,
700 "cannot infer a suitable result container type from the given parameters at compile time, please specify it explicitly");
703 return OutR(std::span{in}.size_bytes());
722template <
Endianness endianness,
typename OutR, unsigned_integralish... Ts>
723 requires all_same_v<Ts...>
737template <Endianness endianness,
typename InT,
unsigned_
integralish T>
738 requires(std::same_as<AutoDetect, InT> || std::same_as<T, InT>)
749template <
Endianness endianness,
typename InT, unsigned_integralish T0, unsigned_integralish... Ts>
750 requires(std::same_as<AutoDetect, InT> || std::same_as<T0, InT>) && all_same_v<T0, Ts...>
751inline constexpr void store_any(uint8_t out[], T0 in0, Ts... ins) {
752 constexpr auto bytes =
sizeof(in0) + (
sizeof(ins) + ... + 0);
763template <
typename ModifierT = detail::AutoDetect,
typename... ParamTs>
764inline constexpr auto store_le(ParamTs&&... params) {
772template <
typename ModifierT = detail::AutoDetect,
typename... ParamTs>
773inline constexpr auto store_be(ParamTs&&... params) {
779template <Endianness endianness,
unsigned_
integralish T>
781 const size_t full_words = out.size() /
sizeof(
T);
782 const size_t full_word_bytes = full_words *
sizeof(
T);
783 const size_t remaining_bytes = out.size() - full_word_bytes;
788 out = out.subspan(full_word_bytes);
789 in = in.subspan(full_words);
791 return remaining_bytes;
800template <ranges::spanable_range InR>
802 using T = std::ranges::range_value_t<InR>;
803 std::span<const T> in_s{in};
807 for(
size_t i = 0; i < remaining_bytes; ++i) {
816template <ranges::spanable_range InR>
818 using T = std::ranges::range_value_t<InR>;
819 std::span<const T> in_s{in};
823 for(
size_t i = 0; i < remaining_bytes; ++i) {
#define BOTAN_ASSERT_NOMSG(expr)
#define BOTAN_ARG_CHECK(expr, msg)
constexpr bool native_endianness_is_unknown()
constexpr bool is_opposite(Endianness endianness)
constexpr bool is_native(Endianness endianness)
constexpr void fallback_store_any(InT in, OutR &&out_range)
size_t copy_out_any_word_aligned_portion(std::span< uint8_t > &out, std::span< const T > &in)
typename wrapped_type_helper_with_enum< T >::type wrapped_type
constexpr WrappedOutT load_any(InR &&in_range)
constexpr OutT fallback_load_any(InR &&in_range)
constexpr auto unwrap_strong_type_or_enum(InT t)
constexpr auto wrap_strong_type_or_enum(T t)
constexpr void store_any(WrappedInT wrapped_in, OutR &&out_range)
constexpr void assert_exact_byte_length(R &&r)
constexpr void assert_equal_byte_lengths(R0 &&r0, Rs &&... rs)
void copy_out_be(std::span< uint8_t > out, InR &&in)
constexpr uint8_t get_byte(T input)
constexpr uint64_t make_uint64(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4, uint8_t i5, uint8_t i6, uint8_t i7)
constexpr void typecast_copy(ToR &&out, FromR &&in)
constexpr uint32_t make_uint32(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3)
constexpr decltype(auto) unwrap_strong_type(T &&t)
Generically unwraps a strong type to its underlying type.
constexpr T reverse_bytes(T x)
constexpr auto store_le(ParamTs &&... params)
typename detail::wrapped_type_helper< std::remove_cvref_t< T > >::type strong_type_wrapped_type
Extracts the wrapped type from a strong type.
constexpr decltype(auto) wrap_strong_type(ParamT &&t)
Wraps a value into a caller-defined (strong) type.
void copy_out_le(std::span< uint8_t > out, InR &&in)
constexpr auto load_le(ParamTs &&... params)
constexpr uint8_t get_byte_var(size_t byte_num, T input)
constexpr auto store_be(ParamTs &&... params)
constexpr auto load_be(ParamTs &&... params)
constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1)
std::underlying_type_t< T > type
strong_type_wrapped_type< T > type