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)));
140#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
142#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
154#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
156#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
163template <Endianness endianness>
165#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) || defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
177 (std::is_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>);
184template <Endianness endianness, std::
unsigned_
integral OutT, ranges::contiguous_range<u
int8_t> InR>
186 std::span in{in_range};
189 return [&]<
size_t... i>(std::index_sequence<i...>) {
190 return static_cast<OutT
>(((
static_cast<OutT
>(in[i]) << ((
sizeof(OutT) - i - 1) * 8)) | ...));
191 } (std::make_index_sequence<sizeof(OutT)>());
194 return [&]<
size_t... i>(std::index_sequence<i...>) {
195 return static_cast<OutT
>(((
static_cast<OutT
>(in[i]) << (i * 8)) | ...));
196 } (std::make_index_sequence<sizeof(OutT)>());
206template <Endianness endianness, std::
unsigned_
integral InT, ranges::contiguous_output_range<u
int8_t> OutR>
208 std::span out{out_range};
211 [&]<
size_t... i>(std::index_sequence<i...>) {
212 ((out[i] = get_byte<i>(in)), ...);
213 } (std::make_index_sequence<sizeof(InT)>());
216 [&]<
size_t... i>(std::index_sequence<i...>) {
217 ((out[i] =
get_byte<
sizeof(InT) - i - 1>(in)), ...);
218 } (std::make_index_sequence<sizeof(InT)>());
247template <Endianness endianness, std::
unsigned_
integral OutT, ranges::contiguous_range<u
int8_t> InR>
249 ranges::assert_exact_byte_length<sizeof(OutT)>(in_range);
250 std::span in{in_range};
255 if(std::is_constant_evaluated()) {
256 return fallback_load_any<endianness, OutT>(std::forward<InR>(in_range));
258 if constexpr(
sizeof(OutT) == 1) {
259 return static_cast<OutT
>(in[0]);
260 }
else if constexpr(
is_native(endianness)) {
261 return typecast_copy<OutT>(in);
265 static_assert(native_endianness_is_unknown<endianness>());
266 return fallback_load_any<endianness, OutT>(std::forward<InR>(in_range));
274template <Endianness endianness, concepts::
unsigned_
integral_strong_type OutT, ranges::contiguous_range<u
int8_t> InR>
275inline constexpr OutT
load_any(InR&& in_range) {
276 using underlying_type =
typename OutT::wrapped_type;
277 return OutT{load_any<endianness, underlying_type>(std::forward<InR>(in_range))};
284template <Endianness endianness,
typename OutT, ranges::contiguous_range<u
int8_t> InR>
285 requires(std::is_enum_v<OutT> && std::unsigned_integral<std::underlying_type_t<OutT>>)
287 using underlying_type = std::underlying_type_t<OutT>;
288 return static_cast<OutT
>(load_any<endianness, underlying_type>(std::forward<InR>(in_range)));
296template <Endianness endianness,
typename OutT, ranges::contiguous_range<u
int8_t> InR,
unsigned_
integralish... Ts>
297 requires(
sizeof...(Ts) > 0) && ((std::same_as<AutoDetect, OutT> && all_same_v<Ts...>) ||
298 (unsigned_integralish<OutT> && all_same_v<OutT, Ts...>))
299inline constexpr void load_any(InR&& in, Ts&... outs) {
301 auto load_one = [off = 0]<
typename T>(
auto i,
T& o)
mutable {
302 o = load_any<endianness, T>(i.subspan(off).template first<
sizeof(
T)>());
306 (load_one(std::span{in}, outs), ...);
320 requires(unsigned_integralish<std::ranges::range_value_t<OutR>> &&
321 (std::same_as<AutoDetect, OutT> || std::same_as<OutT, std::ranges::range_value_t<OutR>>))
322inline constexpr
void load_any(OutR&& out, InR&& in) {
325 auto load_elementwise = [&] {
326 using element_type = std::ranges::range_value_t<OutR>;
327 constexpr size_t bytes_per_element =
sizeof(element_type);
328 std::span<const uint8_t> in_s(in);
329 for(
auto& out_elem : out) {
330 out_elem = load_any<endianness, element_type>(in_s.template first<bytes_per_element>());
331 in_s = in_s.subspan(bytes_per_element);
338 if(std::is_constant_evaluated()) {
360template <Endianness endianness,
typename OutT, ranges::contiguous_range<u
int8_t> InR>
361 requires(std::same_as<AutoDetect, OutT> ||
365 auto out = []([[maybe_unused]]
const auto& in) {
366 if constexpr(std::same_as<AutoDetect, OutT>) {
368 constexpr size_t extent =
decltype(std::span{in})::extent;
372 std::conditional_t<extent == 1, uint8_t,
373 std::conditional_t<extent == 2, uint16_t,
374 std::conditional_t<extent == 4, uint32_t,
375 std::conditional_t<extent == 8, uint64_t, void>>>>;
379 !std::is_void_v<type>,
380 "Cannot determine the output type based on a statically sized bytearray with length other than those: 1, 2, 4, 8");
385 !std::same_as<AutoDetect, OutT>,
386 "cannot infer return type from a dynamic range at compile time, please specify it explicitly");
389 const size_t in_bytes = std::span{in}.size_bytes();
390 constexpr size_t out_elem_bytes =
sizeof(
typename OutT::value_type);
392 "Input range is not word-aligned with the requested output range");
393 return OutT(in_bytes / out_elem_bytes);
399 using out_type =
decltype(out);
401 out = load_any<endianness, out_type>(std::forward<InR>(in_range));
404 using out_range_type = std::ranges::range_value_t<out_type>;
405 load_any<endianness, out_range_type>(out, std::forward<InR>(in_range));
420template <Endianness endianness,
unsigned_
integralish OutT>
421inline constexpr OutT
load_any(
const uint8_t in[],
size_t off) {
423 constexpr size_t out_size =
sizeof(OutT);
424 return load_any<endianness, OutT>(std::span<const uint8_t, out_size>(in + off * out_size, out_size));
432template <
Endianness endianness,
typename OutT, unsigned_integralish... Ts>
433 requires(
sizeof...(Ts) > 0 && all_same_v<Ts...> &&
434 ((std::same_as<AutoDetect, OutT> && all_same_v<Ts...>) ||
435 (unsigned_integralish<OutT> && all_same_v<OutT, Ts...>)))
436inline constexpr void load_any(
const uint8_t in[], Ts&... outs) {
437 constexpr auto bytes = (
sizeof(outs) + ...);
439 load_any<endianness, OutT>(std::span<const uint8_t, bytes>(in, bytes), outs...);
448template <Endianness endianness,
typename OutT,
unsigned_
integralish T>
449 requires(std::same_as<AutoDetect, OutT> || std::same_as<T, OutT>)
450inline constexpr void load_any(
T out[],
const uint8_t in[],
size_t count) {
452 load_any<endianness, OutT>(std::span<T>(out, count), std::span<const uint8_t>(in, count *
sizeof(
T)));
461template <
typename OutT = detail::AutoDetect,
typename... ParamTs>
462inline constexpr auto load_le(ParamTs&&... params) {
463 return detail::load_any<detail::Endianness::Little, OutT>(std::forward<ParamTs>(params)...);
470template <
typename OutT = detail::AutoDetect,
typename... ParamTs>
471inline constexpr auto load_be(ParamTs&&... params) {
472 return detail::load_any<detail::Endianness::Big, OutT>(std::forward<ParamTs>(params)...);
490template <Endianness endianness, std::
unsigned_
integral InT, ranges::contiguous_output_range<u
int8_t> OutR>
491inline constexpr void store_any(InT in, OutR&& out_range) {
492 ranges::assert_exact_byte_length<sizeof(InT)>(out_range);
493 std::span out{out_range};
498 if(std::is_constant_evaluated()) {
499 return fallback_store_any<endianness, InT>(in, std::forward<OutR>(out_range));
501 if constexpr(
sizeof(InT) == 1) {
502 out[0] =
static_cast<uint8_t
>(in);
503 }
else if constexpr(
is_native(endianness)) {
508 static_assert(native_endianness_is_unknown<endianness>());
509 return fallback_store_any<endianness, InT>(in, std::forward<OutR>(out_range));
520inline constexpr void store_any(InT in, OutR&& out_range) {
521 using underlying_type =
typename InT::wrapped_type;
522 store_any<endianness, underlying_type>(in.get(), std::forward<OutR>(out_range));
529template <Endianness endianness,
typename InT, ranges::contiguous_output_range<u
int8_t> OutR>
530 requires(std::is_enum_v<InT> && std::unsigned_integral<std::underlying_type_t<InT>>)
531inline constexpr void store_any(InT in, OutR&& out_range) {
532 using underlying_type = std::underlying_type_t<InT>;
534 store_any<endianness, underlying_type>(
static_cast<underlying_type
>(in), std::forward<OutR>(out_range));
545 unsigned_integralish... Ts>
546 requires(
sizeof...(Ts) > 0) && ((std::same_as<AutoDetect, InT> && all_same_v<Ts...>) ||
547 (unsigned_integralish<InT> && all_same_v<InT, Ts...>))
550 auto store_one = [off = 0]<
typename T>(
auto o,
T i)
mutable {
551 store_any<endianness, T>(i, o.subspan(off).template first<
sizeof(
T)>());
555 (store_one(std::span{out}, ins), ...);
568 requires(std::same_as<AutoDetect, InT> || std::same_as<InT, std::ranges::range_value_t<InR>>)
572 auto store_elementwise = [&] {
573 using element_type = std::ranges::range_value_t<InR>;
574 constexpr size_t bytes_per_element =
sizeof(element_type);
575 std::span<uint8_t> out_s(out);
576 for(
auto in_elem : in) {
577 store_any<endianness, element_type>(out_s.template first<bytes_per_element>(), in_elem);
578 out_s = out_s.subspan(bytes_per_element);
585 if(std::is_constant_evaluated()) {
610template <Endianness endianness,
typename InT,
unsigned_
integralish T, ranges::contiguous_output_range<u
int8_t> OutR>
611 requires std::same_as<AutoDetect, InT>
613 store_any<endianness, T>(in, std::forward<OutR>(out_range));
625template <Endianness endianness,
typename OutR, ranges::spanable_range InR>
626 requires(std::same_as<AutoDetect, OutR> ||
630 auto out = []([[maybe_unused]]
const auto& in) {
631 if constexpr(std::same_as<AutoDetect, OutR>) {
633 constexpr size_t bytes =
decltype(std::span{in})::extent *
sizeof(std::ranges::range_value_t<InR>);
634 return std::array<uint8_t, bytes>();
637 !std::same_as<AutoDetect, OutR>,
638 "cannot infer a suitable result container type from the given parameters at compile time, please specify it explicitly");
641 return OutR(std::span{in}.size_bytes());
647 store_any<endianness, std::ranges::range_value_t<InR>>(out, std::forward<InR>(in_range));
660template <
Endianness endianness,
typename OutR, unsigned_integralish... Ts>
661 requires all_same_v<Ts...>
663 return store_any<endianness, OutR>(std::array{ins...});
675template <Endianness endianness,
typename InT,
unsigned_
integralish T>
676 requires(std::same_as<AutoDetect, InT> || std::same_as<T, InT>)
679 store_any<endianness, InT>(in, std::span<uint8_t,
sizeof(
T)>(out,
sizeof(
T)));
687template <
Endianness endianness,
typename InT, unsigned_integralish T0, unsigned_integralish... Ts>
688 requires(std::same_as<AutoDetect, InT> || std::same_as<T0, InT>) && all_same_v<T0, Ts...>
689inline constexpr void store_any(uint8_t out[], T0 in0, Ts... ins) {
690 constexpr auto bytes =
sizeof(in0) + (
sizeof(ins) + ... + 0);
692 store_any<endianness, T0>(std::span<uint8_t, bytes>(out, bytes), in0, ins...);
701template <
typename ModifierT = detail::AutoDetect,
typename... ParamTs>
702inline constexpr auto store_le(ParamTs&&... params) {
703 return detail::store_any<detail::Endianness::Little, ModifierT>(std::forward<ParamTs>(params)...);
710template <
typename ModifierT = detail::AutoDetect,
typename... ParamTs>
711inline constexpr auto store_be(ParamTs&&... params) {
712 return detail::store_any<detail::Endianness::Big, ModifierT>(std::forward<ParamTs>(params)...);
717template <Endianness endianness,
unsigned_
integralish T>
719 const size_t full_words = out.size() /
sizeof(
T);
720 const size_t full_word_bytes = full_words *
sizeof(
T);
721 const size_t remaining_bytes = out.size() - full_word_bytes;
725 store_any<endianness, T>(out.first(full_word_bytes), in.first(full_words));
726 out = out.subspan(full_word_bytes);
727 in = in.subspan(full_words);
729 return remaining_bytes;
738template <ranges::spanable_range InR>
740 using T = std::ranges::range_value_t<InR>;
741 std::span<const T> in_s{in};
742 const auto remaining_bytes = detail::copy_out_any_word_aligned_portion<detail::Endianness::Big>(out, in_s);
745 for(
size_t i = 0; i < remaining_bytes; ++i) {
754template <ranges::spanable_range InR>
756 using T = std::ranges::range_value_t<InR>;
757 std::span<const T> in_s{in};
758 const auto remaining_bytes = detail::copy_out_any_word_aligned_portion<detail::Endianness::Little>(out, in_s);
761 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 auto store_le(ParamTs &&... params)
constexpr uint16_t reverse_bytes(uint16_t x)
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)
constexpr AutoDetect()=delete