Botan 3.6.1
Crypto and TLS for C&
loadstor.h
Go to the documentation of this file.
1/*
2* Load/Store Operators
3* (C) 1999-2007,2015,2017 Jack Lloyd
4* 2007 Yves Jerschow
5* 2023-2024 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity
6*
7* Botan is released under the Simplified BSD License (see license.txt)
8*/
9
10#ifndef BOTAN_LOAD_STORE_H_
11#define BOTAN_LOAD_STORE_H_
12
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>
18
19/**
20 * @file loadstor.h
21 *
22 * @brief This header contains various helper functions to load and store
23 * unsigned integers in big- or little-endian byte order.
24 *
25 * Storing integer values in various ways (same for BE and LE):
26 * @code {.cpp}
27 *
28 * std::array<uint8_t, 8> bytes = store_le(some_uint64);
29 * std::array<uint8_t, 12> bytes = store_le(some_uint32_1, some_uint32_2, some_uint32_3, ...);
30 * auto bytes = store_le<std::vector<uint8_t>>(some_uint64);
31 * auto bytes = store_le<MyContainerStrongType>(some_uint64);
32 * auto bytes = store_le<std::vector<uint8_t>>(vector_of_ints);
33 * auto bytes = store_le<secure_vector<uint8_t>>(some_uint32_1, some_uint32_2, some_uint32_3, ...);
34 * store_le(bytes, some_uint64);
35 * store_le(concatenated_bytes, some_uint64_1, some_uint64_2, some_uint64_3, ...);
36 * store_le(concatenated_bytes, vector_of_ints);
37 * copy_out_le(short_concated_bytes, vector_of_ints); // stores as many bytes as required in the output buffer
38 *
39 * @endcode
40 *
41 * Loading integer values in various ways (same for BE and LE):
42 * @code {.cpp}
43 *
44 * uint64_t some_uint64 = load_le(bytes_8);
45 * auto some_int32s = load_le<std::vector<uint32_t>>(concatenated_bytes);
46 * auto some_int32s = load_le<std::vector<MyIntStrongType>>(concatenated_bytes);
47 * auto some_int32s = load_le(some_strong_typed_bytes);
48 * auto strong_int = load_le<MyStrongTypedInteger>(concatenated_bytes);
49 * load_le(concatenated_bytes, out_some_uint64);
50 * load_le(concatenated_bytes, out_some_uint64_1, out_some_uint64_2, out_some_uint64_3, ...);
51 * load_le(out_vector_of_ints, concatenated_bytes);
52 *
53 * @endcode
54 */
55
56namespace Botan {
57
58/**
59* Byte extraction
60* @param byte_num which byte to extract, 0 == highest byte
61* @param input the value to extract from
62* @return byte byte_num of input
63*/
64template <typename T>
65inline constexpr uint8_t get_byte_var(size_t byte_num, T input) {
66 return static_cast<uint8_t>(input >> (((~byte_num) & (sizeof(T) - 1)) << 3));
67}
68
69/**
70* Byte extraction
71* @param input the value to extract from
72* @return byte byte number B of input
73*/
74template <size_t B, typename T>
75inline constexpr uint8_t get_byte(T input)
76 requires(B < sizeof(T))
77{
78 const size_t shift = ((~B) & (sizeof(T) - 1)) << 3;
79 return static_cast<uint8_t>((input >> shift) & 0xFF);
80}
81
82/**
83* Make a uint16_t from two bytes
84* @param i0 the first byte
85* @param i1 the second byte
86* @return i0 || i1
87*/
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);
90}
91
92/**
93* Make a uint32_t from four bytes
94* @param i0 the first byte
95* @param i1 the second byte
96* @param i2 the third byte
97* @param i3 the fourth byte
98* @return i0 || i1 || i2 || i3
99*/
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)));
103}
104
105/**
106* Make a uint64_t from eight bytes
107* @param i0 the first byte
108* @param i1 the second byte
109* @param i2 the third byte
110* @param i3 the fourth byte
111* @param i4 the fifth byte
112* @param i5 the sixth byte
113* @param i6 the seventh byte
114* @param i7 the eighth byte
115* @return i0 || i1 || i2 || i3 || i4 || i5 || i6 || i7
116*/
117inline constexpr uint64_t make_uint64(
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)));
122}
123
124namespace detail {
125
126enum class Endianness : bool {
127 Big,
128 Little,
129};
130
131/**
132 * @warning This function may return false if the native endianness is unknown
133 * @returns true iff the native endianness matches the given endianness
134 */
135constexpr bool is_native(Endianness endianness) {
136#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
137 return endianness == Endianness::Big;
138#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
139 return endianness == Endianness::Little;
140#else
141 BOTAN_UNUSED(endianness);
142 return false;
143#endif
144}
145
146/**
147 * @warning This function may return false if the native endianness is unknown
148 * @returns true iff the native endianness does not match the given endianness
149 */
150constexpr bool is_opposite(Endianness endianness) {
151#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
152 return endianness == Endianness::Little;
153#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
154 return endianness == Endianness::Big;
155#else
156 BOTAN_UNUSED(endianness);
157 return false;
158#endif
159}
160
161template <Endianness endianness>
163#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) || defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
164 return false;
165#else
166 return true;
167#endif
168}
169
170/**
171 * Models a custom type that provides factory methods to be loaded in big- or
172 * little-endian byte order.
173 */
174template <typename T>
175concept custom_loadable = requires(std::span<const uint8_t, sizeof(T)> data) {
176 { T::load_be(data) } -> std::same_as<T>;
177 { T::load_le(data) } -> std::same_as<T>;
178};
179
180/**
181 * Models a custom type that provides store methods to be stored in big- or
182 * little-endian byte order.
183 */
184template <typename T>
185concept custom_storable = requires(std::span<uint8_t, sizeof(T)> data, const T value) {
186 { value.store_be(data) };
187 { value.store_le(data) };
188};
189
190/**
191 * Models a type that can be loaded/stored from/to a byte range.
192 */
193template <typename T>
195 std::unsigned_integral<strong_type_wrapped_type<T>> ||
196 (std::is_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>) ||
198
199template <typename T>
203
204template <typename T>
205 requires std::is_enum_v<T>
207 using type = std::underlying_type_t<T>;
208};
209
210template <unsigned_integralish T>
212
213template <unsigned_integralish InT>
214constexpr auto unwrap_strong_type_or_enum(InT t) {
215 if constexpr(std::is_enum_v<InT>) {
216 // TODO: C++23: use std::to_underlying(in) instead
217 return static_cast<std::underlying_type_t<InT>>(t);
218 } else {
220 }
221}
222
223template <unsigned_integralish OutT, std::unsigned_integral T>
224constexpr auto wrap_strong_type_or_enum(T t) {
225 if constexpr(std::is_enum_v<OutT>) {
226 return static_cast<OutT>(t);
227 } else {
229 }
230}
231
232/**
233 * Manually load a word from a range in either big or little endian byte order.
234 * This will be used only if the endianness of the target platform is unknown at
235 * compile time.
236 */
237template <Endianness endianness, std::unsigned_integral OutT, ranges::contiguous_range<uint8_t> InR>
238inline constexpr OutT fallback_load_any(InR&& in_range) {
239 std::span in{in_range};
240 // clang-format off
241 if constexpr(endianness == Endianness::Big) {
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)>());
245 } else {
246 static_assert(endianness == Endianness::Little);
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)>());
250 }
251 // clang-format on
252}
253
254/**
255 * Manually store a word into a range in either big or little endian byte order.
256 * This will be used only if the endianness of the target platform is unknown at
257 * compile time.
258 */
259template <Endianness endianness, std::unsigned_integral InT, ranges::contiguous_output_range<uint8_t> OutR>
260inline constexpr void fallback_store_any(InT in, OutR&& out_range) {
261 std::span out{out_range};
262 // clang-format off
263 if constexpr(endianness == Endianness::Big) {
264 [&]<size_t... i>(std::index_sequence<i...>) {
265 ((out[i] = get_byte<i>(in)), ...);
266 } (std::make_index_sequence<sizeof(InT)>());
267 } else {
268 static_assert(endianness == Endianness::Little);
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)>());
272 }
273 // clang-format on
274}
275
276/**
277 * Load a word from a range in either big or little endian byte order
278 *
279 * This is the base implementation, all other overloads are just convenience
280 * wrappers. It is assumed that the range has the correct size for the word.
281 *
282 * Template arguments of all overloads of load_any() share the same semantics:
283 *
284 * 1. Endianness Either `Endianness::Big` or `Endianness::Little`, that
285 * will eventually select the byte order translation mode
286 * implemented in this base function.
287 *
288 * 2. Output type Either `AutoDetect`, an unsigned integer or a container
289 * holding an unsigned integer type. `AutoDetect` means
290 * that the caller did not explicitly specify the type and
291 * expects the type to be inferred from the input.
292 *
293 * 3+. Argument types Typically, those are input and output ranges of bytes
294 * or unsigned integers. Or one or more unsigned integers
295 * acting as output parameters.
296 *
297 * @param in_range a fixed-length byte range
298 * @return T loaded from @p in_range, as a big-endian value
299 */
300template <Endianness endianness, unsigned_integralish WrappedOutT, ranges::contiguous_range<uint8_t> InR>
301 requires(!custom_loadable<strong_type_wrapped_type<WrappedOutT>>)
302inline constexpr WrappedOutT load_any(InR&& in_range) {
305
307 // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
308 // internally to copy ranges on a byte-by-byte basis, which is not allowed
309 // in a `constexpr` context.
310 if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
311 return fallback_load_any<endianness, OutT>(std::forward<InR>(in_range));
312 } else {
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)) {
317 return typecast_copy<OutT>(in);
318 } else if constexpr(is_opposite(endianness)) {
320 } else {
322 return fallback_load_any<endianness, OutT>(std::forward<InR>(in_range));
323 }
324 }
325 }());
326}
327
328/**
329 * Load a custom object from a range in either big or little endian byte order
330 *
331 * This is the base implementation for custom objects (e.g. SIMD type wrappres),
332 * all other overloads are just convenience overloads.
333 *
334 * @param in_range a fixed-length byte range
335 * @return T loaded from @p in_range, as a big-endian value
336 */
337template <Endianness endianness, unsigned_integralish WrappedOutT, ranges::contiguous_range<uint8_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};
343 if constexpr(endianness == Endianness::Big) {
344 return wrap_strong_type<WrappedOutT>(OutT::load_be(ins));
345 } else {
346 return wrap_strong_type<WrappedOutT>(OutT::load_le(ins));
347 }
348}
349
350/**
351 * Load many unsigned integers
352 * @param in a fixed-length span to some bytes
353 * @param outs a arbitrary-length parameter list of unsigned integers to be loaded
354 */
355template <Endianness endianness, typename OutT, ranges::contiguous_range<uint8_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) {
359 ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(in);
360 auto load_one = [off = 0]<typename T>(auto i, T& o) mutable {
361 o = load_any<endianness, T>(i.subspan(off).template first<sizeof(T)>());
362 off += sizeof(T);
363 };
364
365 (load_one(std::span{in}, outs), ...);
366}
367
368/**
369 * Load a variable number of words from @p in into @p out.
370 * The byte length of the @p out and @p in ranges must match.
371 *
372 * @param out the output range of words
373 * @param in the input range of bytes
374 */
375template <Endianness endianness,
376 typename OutT,
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>;
384
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) {
389 out_elem = load_any<endianness, element_type>(in_s.template first<bytes_per_element>());
390 in_s = in_s.subspan(bytes_per_element);
391 }
392 };
393
394 // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
395 // internally to copy ranges on a byte-by-byte basis, which is not allowed
396 // in a `constexpr` context.
397 if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
398 load_elementwise();
399 } else {
400 if constexpr(is_native(endianness) && !custom_loadable<element_type>) {
401 typecast_copy(out, in);
402 } else {
403 load_elementwise();
404 }
405 }
406}
407
408//
409// Type inference overloads
410//
411
412/**
413 * Load one or more unsigned integers, auto-detect the output type if
414 * possible. Otherwise, use the specified integer or integer container type.
415 *
416 * @param in_range a statically-sized range with some bytes
417 * @return T loaded from in
418 */
419template <Endianness endianness, typename OutT, ranges::contiguous_range<uint8_t> InR>
420 requires(std::same_as<AutoDetect, OutT> ||
422 unsigned_integralish<typename OutT::value_type>))
423inline constexpr auto load_any(InR&& in_range) {
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;
428
429 // clang-format off
430 using type =
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>>>>;
435 // clang-format on
436
437 static_assert(
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");
440
441 return type{};
442 } else {
443 static_assert(
444 !std::same_as<AutoDetect, OutT>,
445 "cannot infer return type from a dynamic range at compile time, please specify it explicitly");
446 }
447 } else if constexpr(concepts::resizable_container<OutT>) {
448 const size_t in_bytes = std::span{in}.size_bytes();
449 constexpr size_t out_elem_bytes = sizeof(typename OutT::value_type);
450 BOTAN_ARG_CHECK(in_bytes % out_elem_bytes == 0,
451 "Input range is not word-aligned with the requested output range");
452 return OutT(in_bytes / out_elem_bytes);
453 } else {
454 return OutT{};
455 }
456 }(in_range);
457
458 using out_type = decltype(out);
459 if constexpr(unsigned_integralish<out_type>) {
460 out = load_any<endianness, out_type>(std::forward<InR>(in_range));
461 } else {
463 using out_range_type = std::ranges::range_value_t<out_type>;
464 load_any<endianness, out_range_type>(out, std::forward<InR>(in_range));
465 }
466 return out;
467}
468
469//
470// Legacy load functions that work on raw pointers and arrays
471//
472
473/**
474 * Load a word from @p in at some offset @p off
475 * @param in a pointer to some bytes
476 * @param off an offset into the array
477 * @return off'th T of in, as a big-endian value
478 */
479template <Endianness endianness, unsigned_integralish OutT>
480inline constexpr OutT load_any(const uint8_t in[], size_t off) {
481 // asserts that *in points to enough bytes to read at offset off
482 constexpr size_t out_size = sizeof(OutT);
483 return load_any<endianness, OutT>(std::span<const uint8_t, out_size>(in + off * out_size, out_size));
484}
485
486/**
487 * Load many words from @p in
488 * @param in a pointer to some bytes
489 * @param outs a arbitrary-length parameter list of unsigned integers to be loaded
490 */
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) + ...);
497 // asserts that *in points to the correct amount of memory
498 load_any<endianness, OutT>(std::span<const uint8_t, bytes>(in, bytes), outs...);
499}
500
501/**
502 * Load a variable number of words from @p in into @p out.
503 * @param out the output array of words
504 * @param in the input array of bytes
505 * @param count how many words are in in
506 */
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) {
510 // asserts that *in and *out point to the correct amount of memory
511 load_any<endianness, OutT>(std::span<T>(out, count), std::span<const uint8_t>(in, count * sizeof(T)));
512}
513
514} // namespace detail
515
516/**
517 * Load "something" in little endian byte order
518 * See the documentation of this file for more details.
519 */
520template <typename OutT = detail::AutoDetect, typename... ParamTs>
521inline constexpr auto load_le(ParamTs&&... params) {
522 return detail::load_any<detail::Endianness::Little, OutT>(std::forward<ParamTs>(params)...);
523}
524
525/**
526 * Load "something" in big endian byte order
527 * See the documentation of this file for more details.
528 */
529template <typename OutT = detail::AutoDetect, typename... ParamTs>
530inline constexpr auto load_be(ParamTs&&... params) {
531 return detail::load_any<detail::Endianness::Big, OutT>(std::forward<ParamTs>(params)...);
532}
533
534namespace detail {
535
536/**
537 * Store a word in either big or little endian byte order into a range
538 *
539 * This is the base implementation, all other overloads are just convenience
540 * wrappers. It is assumed that the range has the correct size for the word.
541 *
542 * Template arguments of all overloads of store_any() share the same semantics
543 * as those of load_any(). See the documentation of this function for more
544 * details.
545 *
546 * @param wrapped_in an unsigned integral to be stored
547 * @param out_range a byte range to store the word into
548 */
549template <Endianness endianness, unsigned_integralish WrappedInT, ranges::contiguous_output_range<uint8_t> OutR>
550 requires(!custom_storable<strong_type_wrapped_type<WrappedInT>>)
551inline constexpr void store_any(WrappedInT wrapped_in, OutR&& out_range) {
552 const auto in = detail::unwrap_strong_type_or_enum(wrapped_in);
553 using InT = decltype(in);
555 std::span out{out_range};
556
557 // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
558 // internally to copy ranges on a byte-by-byte basis, which is not allowed
559 // in a `constexpr` context.
560 if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
561 return fallback_store_any<endianness, InT>(in, std::forward<OutR>(out_range));
562 } else {
563 if constexpr(sizeof(InT) == 1) {
564 out[0] = static_cast<uint8_t>(in);
565 } else if constexpr(is_native(endianness)) {
566 typecast_copy(out, in);
567 } else if constexpr(is_opposite(endianness)) {
569 } else {
571 return fallback_store_any<endianness, InT>(in, std::forward<OutR>(out_range));
572 }
573 }
574}
575
576/**
577 * Store a custom word in either big or little endian byte order into a range
578 *
579 * This is the base implementation for storing custom objects, all other
580 * overloads are just convenience overloads.
581 *
582 * @param wrapped_in a custom object to be stored
583 * @param out_range a byte range to store the word into
584 */
585template <Endianness endianness, unsigned_integralish WrappedInT, ranges::contiguous_output_range<uint8_t> OutR>
586 requires(custom_storable<strong_type_wrapped_type<WrappedInT>>)
587inline constexpr void store_any(WrappedInT wrapped_in, OutR&& out_range) {
588 const auto in = detail::unwrap_strong_type_or_enum(wrapped_in);
589 using InT = decltype(in);
591 std::span<uint8_t, sizeof(InT)> outs{out_range};
592 if constexpr(endianness == Endianness::Big) {
593 in.store_be(outs);
594 } else {
595 in.store_le(outs);
596 }
597}
598
599/**
600 * Store many unsigned integers words into a byte range
601 * @param out a sized range of some bytes
602 * @param ins a arbitrary-length parameter list of unsigned integers to be stored
603 */
604template <Endianness endianness,
605 typename InT,
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...>))
610inline constexpr void store_any(OutR&& out, Ts... ins) {
611 ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(out);
612 auto store_one = [off = 0]<typename T>(auto o, T i) mutable {
613 store_any<endianness, T>(i, o.subspan(off).template first<sizeof(T)>());
614 off += sizeof(T);
615 };
616
617 (store_one(std::span{out}, ins), ...);
618}
619
620/**
621 * Store a variable number of words given in @p in into @p out.
622 * The byte lengths of @p in and @p out must be consistent.
623 * @param out the output range of bytes
624 * @param in the input range of words
625 */
626template <Endianness endianness,
627 typename InT,
630 requires(std::same_as<AutoDetect, InT> || std::same_as<InT, std::ranges::range_value_t<InR>>)
631inline constexpr void store_any(OutR&& out, InR&& in) {
633 using element_type = std::ranges::range_value_t<InR>;
634
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) {
639 store_any<endianness, element_type>(out_s.template first<bytes_per_element>(), in_elem);
640 out_s = out_s.subspan(bytes_per_element);
641 }
642 };
643
644 // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
645 // internally to copy ranges on a byte-by-byte basis, which is not allowed
646 // in a `constexpr` context.
647 if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
648 store_elementwise();
649 } else {
650 if constexpr(is_native(endianness) && !custom_storable<element_type>) {
651 typecast_copy(out, in);
652 } else {
653 store_elementwise();
654 }
655 }
656}
657
658//
659// Type inference overloads
660//
661
662/**
663 * Infer InT from a single unsigned integer input parameter.
664 *
665 * TODO: we might consider dropping this overload (i.e. out-range as second
666 * parameter) and make this a "special case" of the overload below, that
667 * takes a variadic number of input parameters.
668 *
669 * @param in an unsigned integer to be stored
670 * @param out_range a range of bytes to store the word into
671 */
672template <Endianness endianness, typename InT, unsigned_integralish T, ranges::contiguous_output_range<uint8_t> OutR>
673 requires std::same_as<AutoDetect, InT>
674inline constexpr void store_any(T in, OutR&& out_range) {
675 store_any<endianness, T>(in, std::forward<OutR>(out_range));
676}
677
678/**
679 * The caller provided some integer values in a collection but did not provide
680 * the output container. Let's create one for them, fill it with one of the
681 * overloads above and return it. This will default to a std::array if the
682 * caller did not specify the desired output container type.
683 *
684 * @param in_range a range of words that should be stored
685 * @return a container of bytes that contains the stored words
686 */
687template <Endianness endianness, typename OutR, ranges::spanable_range InR>
688 requires(std::same_as<AutoDetect, OutR> ||
689 (ranges::statically_spanable_range<OutR> && std::default_initializable<OutR>) ||
691inline constexpr auto store_any(InR&& in_range) {
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>();
697 } else {
698 static_assert(
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");
701 }
702 } else if constexpr(concepts::resizable_byte_buffer<OutR>) {
703 return OutR(std::span{in}.size_bytes());
704 } else {
705 return OutR{};
706 }
707 }(in_range);
708
709 store_any<endianness, std::ranges::range_value_t<InR>>(out, std::forward<InR>(in_range));
710 return out;
711}
712
713/**
714 * The caller provided some integer values but did not provide the output
715 * container. Let's create one for them, fill it with one of the overloads above
716 * and return it. This will default to a std::array if the caller did not
717 * specify the desired output container type.
718 *
719 * @param ins some words that should be stored
720 * @return a container of bytes that contains the stored words
721 */
722template <Endianness endianness, typename OutR, unsigned_integralish... Ts>
723 requires all_same_v<Ts...>
724inline constexpr auto store_any(Ts... ins) {
725 return store_any<endianness, OutR>(std::array{ins...});
726}
727
728//
729// Legacy store functions that work on raw pointers and arrays
730//
731
732/**
733 * Store a single unsigned integer into a raw pointer
734 * @param in the input unsigned integer
735 * @param out the byte array to write to
736 */
737template <Endianness endianness, typename InT, unsigned_integralish T>
738 requires(std::same_as<AutoDetect, InT> || std::same_as<T, InT>)
739inline constexpr void store_any(T in, uint8_t out[]) {
740 // asserts that *out points to enough bytes to write into
741 store_any<endianness, InT>(in, std::span<uint8_t, sizeof(T)>(out, sizeof(T)));
742}
743
744/**
745 * Store many unsigned integers words into a raw pointer
746 * @param ins a arbitrary-length parameter list of unsigned integers to be stored
747 * @param out the byte array to write to
748 */
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);
753 // asserts that *out points to the correct amount of memory
754 store_any<endianness, T0>(std::span<uint8_t, bytes>(out, bytes), in0, ins...);
755}
756
757} // namespace detail
758
759/**
760 * Store "something" in little endian byte order
761 * See the documentation of this file for more details.
762 */
763template <typename ModifierT = detail::AutoDetect, typename... ParamTs>
764inline constexpr auto store_le(ParamTs&&... params) {
765 return detail::store_any<detail::Endianness::Little, ModifierT>(std::forward<ParamTs>(params)...);
766}
767
768/**
769 * Store "something" in big endian byte order
770 * See the documentation of this file for more details.
771 */
772template <typename ModifierT = detail::AutoDetect, typename... ParamTs>
773inline constexpr auto store_be(ParamTs&&... params) {
774 return detail::store_any<detail::Endianness::Big, ModifierT>(std::forward<ParamTs>(params)...);
775}
776
777namespace detail {
778
779template <Endianness endianness, unsigned_integralish T>
780inline size_t copy_out_any_word_aligned_portion(std::span<uint8_t>& out, std::span<const T>& in) {
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;
784 BOTAN_ASSERT_NOMSG(in.size_bytes() >= full_word_bytes + remaining_bytes);
785
786 // copy full words
787 store_any<endianness, T>(out.first(full_word_bytes), in.first(full_words));
788 out = out.subspan(full_word_bytes);
789 in = in.subspan(full_words);
790
791 return remaining_bytes;
792}
793
794} // namespace detail
795
796/**
797 * Partially copy a subset of @p in into @p out using big-endian
798 * byte order.
799 */
800template <ranges::spanable_range InR>
801inline void copy_out_be(std::span<uint8_t> out, InR&& in) {
802 using T = std::ranges::range_value_t<InR>;
803 std::span<const T> in_s{in};
804 const auto remaining_bytes = detail::copy_out_any_word_aligned_portion<detail::Endianness::Big>(out, in_s);
805
806 // copy remaining bytes as a partial word
807 for(size_t i = 0; i < remaining_bytes; ++i) {
808 out[i] = get_byte_var(i, in_s.front());
809 }
810}
811
812/**
813 * Partially copy a subset of @p in into @p out using little-endian
814 * byte order.
815 */
816template <ranges::spanable_range InR>
817inline void copy_out_le(std::span<uint8_t> out, InR&& in) {
818 using T = std::ranges::range_value_t<InR>;
819 std::span<const T> in_s{in};
820 const auto remaining_bytes = detail::copy_out_any_word_aligned_portion<detail::Endianness::Little>(out, in_s);
821
822 // copy remaining bytes as a partial word
823 for(size_t i = 0; i < remaining_bytes; ++i) {
824 out[i] = get_byte_var(sizeof(T) - 1 - i, in_s.front());
825 }
826}
827
828} // namespace Botan
829
830#endif
#define BOTAN_UNUSED
Definition assert.h:118
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
FE_25519 T
Definition ge.cpp:34
constexpr bool native_endianness_is_unknown()
Definition loadstor.h:162
constexpr bool is_opposite(Endianness endianness)
Definition loadstor.h:150
constexpr bool is_native(Endianness endianness)
Definition loadstor.h:135
constexpr void fallback_store_any(InT in, OutR &&out_range)
Definition loadstor.h:260
size_t copy_out_any_word_aligned_portion(std::span< uint8_t > &out, std::span< const T > &in)
Definition loadstor.h:780
typename wrapped_type_helper_with_enum< T >::type wrapped_type
Definition loadstor.h:211
constexpr WrappedOutT load_any(InR &&in_range)
Definition loadstor.h:302
constexpr OutT fallback_load_any(InR &&in_range)
Definition loadstor.h:238
constexpr auto unwrap_strong_type_or_enum(InT t)
Definition loadstor.h:214
constexpr auto wrap_strong_type_or_enum(T t)
Definition loadstor.h:224
constexpr void store_any(WrappedInT wrapped_in, OutR &&out_range)
Definition loadstor.h:551
constexpr void assert_exact_byte_length(R &&r)
Definition concepts.h:109
constexpr void assert_equal_byte_lengths(R0 &&r0, Rs &&... rs)
Definition concepts.h:128
void copy_out_be(std::span< uint8_t > out, InR &&in)
Definition loadstor.h:801
constexpr uint8_t get_byte(T input)
Definition loadstor.h:75
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)
Definition loadstor.h:117
constexpr void typecast_copy(ToR &&out, FromR &&in)
Definition mem_ops.h:178
constexpr uint32_t make_uint32(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3)
Definition loadstor.h:100
constexpr decltype(auto) unwrap_strong_type(T &&t)
Generically unwraps a strong type to its underlying type.
constexpr T reverse_bytes(T x)
Definition bswap.h:24
constexpr auto store_le(ParamTs &&... params)
Definition loadstor.h:764
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)
Definition loadstor.h:817
constexpr auto load_le(ParamTs &&... params)
Definition loadstor.h:521
constexpr uint8_t get_byte_var(size_t byte_num, T input)
Definition loadstor.h:65
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:773
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:530
constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1)
Definition loadstor.h:88
strong_type_wrapped_type< T > type
Definition loadstor.h:201