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>
59static_assert(std::endian::native == std::endian::big || std::endian::native == std::endian::little,
60 "Mixed endian systems are not supported");
70 return static_cast<uint8_t
>(input >> (((~byte_num) & (
sizeof(T) - 1)) << 3));
78template <
size_t B,
typename T>
80 requires(B <
sizeof(T))
82 const size_t shift = ((~B) & (
sizeof(T) - 1)) << 3;
83 return static_cast<uint8_t
>((input >> shift) & 0xFF);
92inline constexpr uint16_t
make_uint16(uint8_t i0, uint8_t i1) {
93 return static_cast<uint16_t
>((
static_cast<uint16_t
>(i0) << 8) | i1);
104inline constexpr uint32_t
make_uint32(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3) {
105 return ((
static_cast<uint32_t
>(i0) << 24) | (
static_cast<uint32_t
>(i1) << 16) | (
static_cast<uint32_t
>(i2) << 8) |
106 (
static_cast<uint32_t
>(i3)));
122 uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4, uint8_t i5, uint8_t i6, uint8_t i7) {
123 return ((
static_cast<uint64_t
>(i0) << 56) | (
static_cast<uint64_t
>(i1) << 48) | (
static_cast<uint64_t
>(i2) << 40) |
124 (
static_cast<uint64_t
>(i3) << 32) | (
static_cast<uint64_t
>(i4) << 24) | (
static_cast<uint64_t
>(i5) << 16) |
125 (
static_cast<uint64_t
>(i6) << 8) | (
static_cast<uint64_t
>(i7)));
136consteval std::endian
opposite(std::endian endianness) {
137 if(endianness == std::endian::big) {
138 return std::endian::little;
142 return std::endian::big;
152 { T::load_be(data) } -> std::same_as<T>;
153 { T::load_le(data) } -> std::same_as<T>;
161concept custom_storable =
requires(std::span<uint8_t,
sizeof(T)> data,
const T value) {
162 { value.store_be(data) };
163 { value.store_le(data) };
171 std::unsigned_integral<strong_type_wrapped_type<T>> ||
172 (std::is_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>) ||
181 requires std::is_enum_v<T>
183 using type = std::underlying_type_t<T>;
186template <
unsigned_
integralish T>
189template <
unsigned_
integralish InT>
191 if constexpr(std::is_enum_v<InT>) {
193 return static_cast<std::underlying_type_t<InT>
>(t);
199template <
unsigned_
integralish OutT, std::
unsigned_
integral T>
201 if constexpr(std::is_enum_v<OutT>) {
202 return static_cast<OutT
>(t);
213template <std::endian endianness, std::
unsigned_
integral OutT, ranges::contiguous_range<u
int8_t> InR>
215 std::span in{in_range};
217 if constexpr(endianness == std::endian::big) {
218 return [&]<
size_t... i>(std::index_sequence<i...>) {
219 return static_cast<OutT
>(((
static_cast<OutT
>(in[i]) << ((
sizeof(OutT) - i - 1) * 8)) | ...));
220 } (std::make_index_sequence<sizeof(OutT)>());
222 static_assert(endianness == std::endian::little);
223 return [&]<
size_t... i>(std::index_sequence<i...>) {
224 return static_cast<OutT
>(((
static_cast<OutT
>(in[i]) << (i * 8)) | ...));
225 } (std::make_index_sequence<sizeof(OutT)>());
235template <std::endian endianness, std::
unsigned_
integral InT, ranges::contiguous_output_range<u
int8_t> OutR>
237 std::span out{out_range};
239 if constexpr(endianness == std::endian::big) {
240 [&]<
size_t... i>(std::index_sequence<i...>) {
242 } (std::make_index_sequence<sizeof(InT)>());
244 static_assert(endianness == std::endian::little);
245 [&]<
size_t... i>(std::index_sequence<i...>) {
246 ((out[i] =
get_byte<
sizeof(InT) - i - 1>(in)), ...);
247 } (std::make_index_sequence<sizeof(InT)>());
276template <std::endian endianness,
unsigned_
integralish WrappedOutT, ranges::contiguous_range<u
int8_t> InR>
277 requires(!custom_loadable<strong_type_wrapped_type<WrappedOutT>>)
278inline constexpr WrappedOutT
load_any(InR&& in_range) {
286 if(std::is_constant_evaluated()) {
289 std::span in{in_range};
290 if constexpr(
sizeof(OutT) == 1) {
291 return static_cast<OutT
>(in[0]);
292 }
else if constexpr(endianness == std::endian::native) {
295 static_assert(
opposite(endianness) == std::endian::native);
311template <std::endian endianness,
unsigned_
integralish WrappedOutT, ranges::contiguous_range<u
int8_t> InR>
312 requires(custom_loadable<strong_type_wrapped_type<WrappedOutT>>)
313inline constexpr WrappedOutT
load_any(
const InR& in_range) {
316 std::span<
const uint8_t,
sizeof(OutT)> ins{in_range};
317 if constexpr(endianness == std::endian::big) {
329template <std::endian endianness,
typename OutT, ranges::contiguous_range<u
int8_t> InR,
unsigned_
integralish... Ts>
330 requires(
sizeof...(Ts) > 0) && ((std::same_as<AutoDetect, OutT> && all_same_v<Ts...>) ||
331 (unsigned_integralish<OutT> && all_same_v<OutT, Ts...>))
332inline constexpr
void load_any(const InR& in, Ts&... outs) {
334 auto load_one = [off = 0]<
typename T>(
auto i, T& o)
mutable {
339 (load_one(std::span{in}, outs), ...);
349template <std::endian endianness,
353 requires(unsigned_integralish<std::ranges::range_value_t<OutR>> &&
354 (std::same_as<AutoDetect, OutT> || std::same_as<OutT, std::ranges::range_value_t<OutR>>))
355inline constexpr void load_any(OutR&& out ,
const InR& in) {
357 using element_type = std::ranges::range_value_t<OutR>;
359 auto load_elementwise = [&] {
360 constexpr size_t bytes_per_element =
sizeof(element_type);
361 std::span<const uint8_t> in_s(in);
362 for(
auto& out_elem : out) {
364 in_s = in_s.subspan(bytes_per_element);
371 if(std::is_constant_evaluated()) {
393template <std::endian endianness,
typename OutT, ranges::contiguous_range<u
int8_t> InR>
394 requires(std::same_as<AutoDetect, OutT> ||
396 unsigned_integralish<typename OutT::value_type>))
398 auto out = []([[maybe_unused]]
const auto& in) {
399 if constexpr(std::same_as<AutoDetect, OutT>) {
401 constexpr size_t extent =
decltype(std::span{in})::extent;
405 std::conditional_t<extent == 1, uint8_t,
406 std::conditional_t<extent == 2, uint16_t,
407 std::conditional_t<extent == 4, uint32_t,
408 std::conditional_t<extent == 8, uint64_t, void>>>>;
412 !std::is_void_v<type>,
413 "Cannot determine the output type based on a statically sized bytearray with length other than those: 1, 2, 4, 8");
418 !std::same_as<AutoDetect, OutT>,
419 "cannot infer return type from a dynamic range at compile time, please specify it explicitly");
422 const size_t in_bytes = std::span{in}.size_bytes();
423 constexpr size_t out_elem_bytes =
sizeof(
typename OutT::value_type);
425 "Input range is not word-aligned with the requested output range");
426 return OutT(in_bytes / out_elem_bytes);
432 using out_type =
decltype(out);
437 using out_range_type = std::ranges::range_value_t<out_type>;
453template <std::endian endianness,
unsigned_
integralish OutT>
454inline constexpr OutT
load_any(
const uint8_t in[],
size_t off) {
456 constexpr size_t out_size =
sizeof(OutT);
465template <std::endian endianness,
typename OutT, unsigned_integralish... Ts>
466 requires(
sizeof...(Ts) > 0 && all_same_v<Ts...> &&
467 ((std::same_as<AutoDetect, OutT> && all_same_v<Ts...>) ||
468 (unsigned_integralish<OutT> && all_same_v<OutT, Ts...>)))
469inline constexpr void load_any(
const uint8_t in[], Ts&... outs) {
470 constexpr auto bytes = (
sizeof(outs) + ...);
481template <std::endian endianness,
typename OutT,
unsigned_
integralish T>
482 requires(std::same_as<AutoDetect, OutT> || std::same_as<T, OutT>)
483inline constexpr void load_any(T out[],
const uint8_t in[],
size_t count) {
494template <
typename OutT = detail::AutoDetect,
typename... ParamTs>
495inline constexpr auto load_le(ParamTs&&... params) {
503template <
typename OutT = detail::AutoDetect,
typename... ParamTs>
504inline constexpr auto load_be(ParamTs&&... params) {
523template <std::endian endianness,
unsigned_
integralish WrappedInT, ranges::contiguous_output_range<u
int8_t> OutR>
524 requires(!custom_storable<strong_type_wrapped_type<WrappedInT>>)
525inline constexpr void store_any(WrappedInT wrapped_in, OutR&& out_range) {
527 using InT =
decltype(in);
529 std::span out{out_range};
534 if(std::is_constant_evaluated()) {
537 if constexpr(
sizeof(InT) == 1) {
538 out[0] =
static_cast<uint8_t
>(in);
539 }
else if constexpr(endianness == std::endian::native) {
542 static_assert(
opposite(endianness) == std::endian::native);
557template <std::endian endianness,
unsigned_
integralish WrappedInT, ranges::contiguous_output_range<u
int8_t> OutR>
558 requires(custom_storable<strong_type_wrapped_type<WrappedInT>>)
559inline constexpr void store_any(WrappedInT wrapped_in,
const OutR& out_range) {
561 using InT =
decltype(in);
563 std::span<uint8_t,
sizeof(InT)> outs{out_range};
564 if constexpr(endianness == std::endian::big) {
576template <std::endian endianness,
579 unsigned_integralish... Ts>
580 requires(
sizeof...(Ts) > 0) && ((std::same_as<AutoDetect, InT> && all_same_v<Ts...>) ||
581 (unsigned_integralish<InT> && all_same_v<InT, Ts...>))
582inline constexpr
void store_any(OutR&& out , Ts... ins) {
584 auto store_one = [off = 0]<
typename T>(
auto o, T i)
mutable {
589 (store_one(std::span{out}, ins), ...);
598template <std::endian endianness,
602 requires(std::same_as<AutoDetect, InT> || std::same_as<InT, std::ranges::range_value_t<InR>>)
603inline constexpr void store_any(OutR&& out ,
const InR& in) {
605 using element_type = std::ranges::range_value_t<InR>;
607 auto store_elementwise = [&] {
608 constexpr size_t bytes_per_element =
sizeof(element_type);
609 std::span<uint8_t> out_s(out);
610 for(
auto in_elem : in) {
612 out_s = out_s.subspan(bytes_per_element);
619 if(std::is_constant_evaluated()) {
644template <std::endian endianness,
typename InT,
unsigned_
integralish T, ranges::contiguous_output_range<u
int8_t> OutR>
645 requires std::same_as<AutoDetect, InT>
646inline constexpr void store_any(T in, OutR&& out_range) {
659template <std::endian endianness,
typename OutR, ranges::spanable_range InR>
660 requires(std::same_as<AutoDetect, OutR> ||
664 auto out = []([[maybe_unused]]
const auto& in) {
665 if constexpr(std::same_as<AutoDetect, OutR>) {
667 constexpr size_t bytes =
decltype(std::span{in})::extent *
sizeof(std::ranges::range_value_t<InR>);
668 return std::array<uint8_t, bytes>();
671 !std::same_as<AutoDetect, OutR>,
672 "cannot infer a suitable result container type from the given parameters at compile time, please specify it explicitly");
675 return OutR(std::span{in}.size_bytes());
694template <std::endian endianness,
typename OutR, unsigned_integralish... Ts>
695 requires all_same_v<Ts...>
709template <std::endian endianness,
typename InT,
unsigned_
integralish T>
710 requires(std::same_as<AutoDetect, InT> || std::same_as<T, InT>)
721template <std::endian endianness,
typename InT, unsigned_integralish T0, unsigned_integralish... Ts>
722 requires(std::same_as<AutoDetect, InT> || std::same_as<T0, InT>) && all_same_v<T0, Ts...>
723inline constexpr void store_any(uint8_t out[], T0 in0, Ts... ins) {
724 constexpr auto bytes =
sizeof(in0) + (
sizeof(ins) + ... + 0);
735template <
typename ModifierT = detail::AutoDetect,
typename... ParamTs>
736inline constexpr auto store_le(ParamTs&&... params) {
744template <
typename ModifierT = detail::AutoDetect,
typename... ParamTs>
745inline constexpr auto store_be(ParamTs&&... params) {
751template <std::endian endianness,
unsigned_
integralish T>
753 const size_t full_words = out.size() /
sizeof(T);
754 const size_t full_word_bytes = full_words *
sizeof(T);
755 const size_t remaining_bytes = out.size() - full_word_bytes;
760 out = out.subspan(full_word_bytes);
761 in = in.subspan(full_words);
763 return remaining_bytes;
772template <ranges::spanable_range InR>
774 using T = std::ranges::range_value_t<InR>;
775 std::span<const T> in_s{in};
779 for(
size_t i = 0; i < remaining_bytes; ++i) {
788template <ranges::spanable_range InR>
790 using T = std::ranges::range_value_t<InR>;
791 std::span<const T> in_s{in};
795 for(
size_t i = 0; i < remaining_bytes; ++i) {
#define BOTAN_ASSERT_NOMSG(expr)
#define BOTAN_ARG_CHECK(expr, msg)
consteval std::endian opposite(std::endian 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)
constexpr OutT fallback_load_any(const InR &in_range)
typename wrapped_type_helper_with_enum< T >::type wrapped_type
constexpr WrappedOutT 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_equal_byte_lengths(const R0 &r0, const Rs &... rs)
constexpr void assert_exact_byte_length(const R &r)
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)
void copy_out_le(std::span< uint8_t > out, const InR &in)
void copy_out_be(std::span< uint8_t > out, const InR &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.
constexpr auto load_le(ParamTs &&... params)
constexpr uint8_t get_byte_var(size_t byte_num, T input)
constexpr void typecast_copy(ToR &&out, const FromR &in)
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