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/types.h>
16#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)
150#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
152#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
159template <Endianness endianness>
161#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) || defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
173 (std::is_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>);
180template <Endianness endianness, std::
unsigned_
integral OutT, ranges::contiguous_range<u
int8_t> InR>
182 std::span in{in_range};
185 return [&]<
size_t... i>(std::index_sequence<i...>) {
186 return static_cast<OutT
>(((
static_cast<OutT
>(in[i]) << ((
sizeof(OutT) - i - 1) * 8)) | ...));
187 } (std::make_index_sequence<sizeof(OutT)>());
190 return [&]<
size_t... i>(std::index_sequence<i...>) {
191 return static_cast<OutT
>(((
static_cast<OutT
>(in[i]) << (i * 8)) | ...));
192 } (std::make_index_sequence<sizeof(OutT)>());
202template <Endianness endianness, std::
unsigned_
integral InT, ranges::contiguous_output_range<u
int8_t> OutR>
204 std::span out{out_range};
207 [&]<
size_t... i>(std::index_sequence<i...>) {
209 } (std::make_index_sequence<sizeof(InT)>());
212 [&]<
size_t... i>(std::index_sequence<i...>) {
213 ((out[i] =
get_byte<
sizeof(InT) - i - 1>(in)), ...);
214 } (std::make_index_sequence<sizeof(InT)>());
243template <Endianness endianness, std::
unsigned_
integral OutT, ranges::contiguous_range<u
int8_t> InR>
246 std::span in{in_range};
251 if(std::is_constant_evaluated()) {
254 if constexpr(
sizeof(OutT) == 1) {
255 return static_cast<OutT
>(in[0]);
256 }
else if constexpr(
is_native(endianness)) {
270template <Endianness endianness, concepts::
unsigned_
integral_strong_type OutT, ranges::contiguous_range<u
int8_t> InR>
271inline constexpr OutT
load_any(InR&& in_range) {
272 using underlying_type =
typename OutT::wrapped_type;
280template <Endianness endianness,
typename OutT, ranges::contiguous_range<u
int8_t> InR>
281 requires(std::is_enum_v<OutT> && std::unsigned_integral<std::underlying_type_t<OutT>>)
283 using underlying_type = std::underlying_type_t<OutT>;
292template <Endianness endianness,
typename OutT, ranges::contiguous_range<u
int8_t> InR,
unsigned_
integralish... Ts>
293 requires(
sizeof...(Ts) > 0) && ((std::same_as<AutoDetect, OutT> && all_same_v<Ts...>) ||
294 (unsigned_integralish<OutT> && all_same_v<OutT, Ts...>))
295inline constexpr void load_any(InR&& in, Ts&... outs) {
297 auto load_one = [off = 0]<
typename T>(
auto i,
T& o)
mutable {
302 (load_one(std::span{in}, outs), ...);
316 requires(unsigned_integralish<std::ranges::range_value_t<OutR>> &&
317 (std::same_as<AutoDetect, OutT> || std::same_as<OutT, std::ranges::range_value_t<OutR>>))
318inline constexpr
void load_any(OutR&& out, InR&& in) {
321 auto load_elementwise = [&] {
322 using element_type = std::ranges::range_value_t<OutR>;
323 constexpr size_t bytes_per_element =
sizeof(element_type);
324 std::span<const uint8_t> in_s(in);
325 for(
auto& out_elem : out) {
327 in_s = in_s.subspan(bytes_per_element);
334 if(std::is_constant_evaluated()) {
356template <Endianness endianness,
typename OutT, ranges::contiguous_range<u
int8_t> InR>
357 requires(std::same_as<AutoDetect, OutT> ||
359 unsigned_integralish<typename OutT::value_type>))
361 auto out = []([[maybe_unused]]
const auto& in) {
362 if constexpr(std::same_as<AutoDetect, OutT>) {
364 constexpr size_t extent =
decltype(std::span{in})::extent;
368 std::conditional_t<extent == 1, uint8_t,
369 std::conditional_t<extent == 2, uint16_t,
370 std::conditional_t<extent == 4, uint32_t,
371 std::conditional_t<extent == 8, uint64_t, void>>>>;
375 !std::is_void_v<type>,
376 "Cannot determine the output type based on a statically sized bytearray with length other than those: 1, 2, 4, 8");
381 !std::same_as<AutoDetect, OutT>,
382 "cannot infer return type from a dynamic range at compile time, please specify it explicitly");
385 const size_t in_bytes = std::span{in}.size_bytes();
386 constexpr size_t out_elem_bytes =
sizeof(
typename OutT::value_type);
388 "Input range is not word-aligned with the requested output range");
389 return OutT(in_bytes / out_elem_bytes);
395 using out_type =
decltype(out);
400 using out_range_type = std::ranges::range_value_t<out_type>;
416template <Endianness endianness,
unsigned_
integralish OutT>
417inline constexpr OutT
load_any(
const uint8_t in[],
size_t off) {
419 constexpr size_t out_size =
sizeof(OutT);
428template <
Endianness endianness,
typename OutT, unsigned_integralish... Ts>
429 requires(
sizeof...(Ts) > 0 && all_same_v<Ts...> &&
430 ((std::same_as<AutoDetect, OutT> && all_same_v<Ts...>) ||
431 (unsigned_integralish<OutT> && all_same_v<OutT, Ts...>)))
432inline constexpr void load_any(
const uint8_t in[], Ts&... outs) {
433 constexpr auto bytes = (
sizeof(outs) + ...);
444template <Endianness endianness,
typename OutT,
unsigned_
integralish T>
445 requires(std::same_as<AutoDetect, OutT> || std::same_as<T, OutT>)
446inline constexpr void load_any(
T out[],
const uint8_t in[],
size_t count) {
457template <
typename OutT = detail::AutoDetect,
typename... ParamTs>
458inline constexpr auto load_le(ParamTs&&... params) {
466template <
typename OutT = detail::AutoDetect,
typename... ParamTs>
467inline constexpr auto load_be(ParamTs&&... params) {
486template <Endianness endianness, std::
unsigned_
integral InT, ranges::contiguous_output_range<u
int8_t> OutR>
487inline constexpr void store_any(InT in, OutR&& out_range) {
489 std::span out{out_range};
494 if(std::is_constant_evaluated()) {
497 if constexpr(
sizeof(InT) == 1) {
498 out[0] =
static_cast<uint8_t
>(in);
499 }
else if constexpr(
is_native(endianness)) {
516inline constexpr void store_any(InT in, OutR&& out_range) {
517 using underlying_type =
typename InT::wrapped_type;
525template <Endianness endianness,
typename InT, ranges::contiguous_output_range<u
int8_t> OutR>
526 requires(std::is_enum_v<InT> && std::unsigned_integral<std::underlying_type_t<InT>>)
527inline constexpr void store_any(InT in, OutR&& out_range) {
528 using underlying_type = std::underlying_type_t<InT>;
541 unsigned_integralish... Ts>
542 requires(
sizeof...(Ts) > 0) && ((std::same_as<AutoDetect, InT> && all_same_v<Ts...>) ||
543 (unsigned_integralish<InT> && all_same_v<InT, Ts...>))
546 auto store_one = [off = 0]<
typename T>(
auto o,
T i)
mutable {
551 (store_one(std::span{out}, ins), ...);
564 requires(std::same_as<AutoDetect, InT> || std::same_as<InT, std::ranges::range_value_t<InR>>)
568 auto store_elementwise = [&] {
569 using element_type = std::ranges::range_value_t<InR>;
570 constexpr size_t bytes_per_element =
sizeof(element_type);
571 std::span<uint8_t> out_s(out);
572 for(
auto in_elem : in) {
574 out_s = out_s.subspan(bytes_per_element);
581 if(std::is_constant_evaluated()) {
606template <Endianness endianness,
typename InT,
unsigned_
integralish T, ranges::contiguous_output_range<u
int8_t> OutR>
607 requires std::same_as<AutoDetect, InT>
621template <Endianness endianness,
typename OutR, ranges::spanable_range InR>
622 requires(std::same_as<AutoDetect, OutR> ||
626 auto out = []([[maybe_unused]]
const auto& in) {
627 if constexpr(std::same_as<AutoDetect, OutR>) {
629 constexpr size_t bytes =
decltype(std::span{in})::extent *
sizeof(std::ranges::range_value_t<InR>);
630 return std::array<uint8_t, bytes>();
633 !std::same_as<AutoDetect, OutR>,
634 "cannot infer a suitable result container type from the given parameters at compile time, please specify it explicitly");
637 return OutR(std::span{in}.size_bytes());
656template <
Endianness endianness,
typename OutR, unsigned_integralish... Ts>
657 requires all_same_v<Ts...>
671template <Endianness endianness,
typename InT,
unsigned_
integralish T>
672 requires(std::same_as<AutoDetect, InT> || std::same_as<T, InT>)
683template <
Endianness endianness,
typename InT, unsigned_integralish T0, unsigned_integralish... Ts>
684 requires(std::same_as<AutoDetect, InT> || std::same_as<T0, InT>) && all_same_v<T0, Ts...>
685inline constexpr void store_any(uint8_t out[], T0 in0, Ts... ins) {
686 constexpr auto bytes =
sizeof(in0) + (
sizeof(ins) + ... + 0);
697template <
typename ModifierT = detail::AutoDetect,
typename... ParamTs>
698inline constexpr auto store_le(ParamTs&&... params) {
706template <
typename ModifierT = detail::AutoDetect,
typename... ParamTs>
707inline constexpr auto store_be(ParamTs&&... params) {
713template <Endianness endianness,
unsigned_
integralish T>
715 const size_t full_words = out.size() /
sizeof(
T);
716 const size_t full_word_bytes = full_words *
sizeof(
T);
717 const size_t remaining_bytes = out.size() - full_word_bytes;
722 out = out.subspan(full_word_bytes);
723 in = in.subspan(full_words);
725 return remaining_bytes;
734template <ranges::spanable_range InR>
736 using T = std::ranges::range_value_t<InR>;
737 std::span<const T> in_s{in};
741 for(
size_t i = 0; i < remaining_bytes; ++i) {
750template <ranges::spanable_range InR>
752 using T = std::ranges::range_value_t<InR>;
753 std::span<const T> in_s{in};
757 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)
constexpr void store_any(InT in, OutR &&out_range)
constexpr OutT fallback_load_any(InR &&in_range)
constexpr OutT load_any(InR &&in_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 T reverse_bytes(T x)
constexpr auto store_le(ParamTs &&... params)
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)