Botan 3.4.0
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/types.h>
16#include <botan/internal/bswap.h>
17#include <vector>
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
132 constexpr AutoDetect() = delete;
133};
134
135/**
136 * @warning This function may return false if the native endianness is unknown
137 * @returns true iff the native endianness matches the given endianness
138 */
139constexpr bool is_native(Endianness endianness) {
140#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
141 return endianness == Endianness::Big;
142#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
143 return endianness == Endianness::Little;
144#else
145 return false;
146#endif
147}
148
149/**
150 * @warning This function may return false if the native endianness is unknown
151 * @returns true iff the native endianness does not match the given endianness
152 */
153constexpr bool is_opposite(Endianness endianness) {
154#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
155 return endianness == Endianness::Little;
156#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
157 return endianness == Endianness::Big;
158#else
159 return false;
160#endif
161}
162
163template <Endianness endianness>
165#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) || defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
166 return false;
167#else
168 return true;
169#endif
170}
171
172/**
173 * Models a type that can be loaded/stored from/to a byte range.
174 */
175template <typename T>
177 (std::is_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>);
178
179/**
180 * Manually load a word from a range in either big or little endian byte order.
181 * This will be used only if the endianness of the target platform is unknown at
182 * compile time.
183 */
184template <Endianness endianness, std::unsigned_integral OutT, ranges::contiguous_range<uint8_t> InR>
185inline constexpr OutT fallback_load_any(InR&& in_range) {
186 std::span in{in_range};
187 // clang-format off
188 if constexpr(endianness == Endianness::Big) {
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)>());
192 } else {
193 static_assert(endianness == Endianness::Little);
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)>());
197 }
198 // clang-format on
199}
200
201/**
202 * Manually store a word into a range in either big or little endian byte order.
203 * This will be used only if the endianness of the target platform is unknown at
204 * compile time.
205 */
206template <Endianness endianness, std::unsigned_integral InT, ranges::contiguous_output_range<uint8_t> OutR>
207inline constexpr void fallback_store_any(InT in, OutR&& out_range) {
208 std::span out{out_range};
209 // clang-format off
210 if constexpr(endianness == Endianness::Big) {
211 [&]<size_t... i>(std::index_sequence<i...>) {
212 ((out[i] = get_byte<i>(in)), ...);
213 } (std::make_index_sequence<sizeof(InT)>());
214 } else {
215 static_assert(endianness == Endianness::Little);
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)>());
219 }
220 // clang-format on
221}
222
223/**
224 * Load a word from a range in either big or little endian byte order
225 *
226 * This is the base implementation, all other overloads are just convenience
227 * wrappers. It is assumed that the range has the correct size for the word.
228 *
229 * Template arguments of all overloads of load_any() share the same semantics:
230 *
231 * 1. Endianness Either `Endianness::Big` or `Endianness::Little`, that
232 * will eventually select the byte order translation mode
233 * implemented in this base function.
234 *
235 * 2. Output type Either `AutoDetect`, an unsigned integer or a container
236 * holding an unsigned integer type. `AutoDetect` means
237 * that the caller did not explicitly specify the type and
238 * expects the type to be inferred from the input.
239 *
240 * 3+. Argument types Typically, those are input and output ranges of bytes
241 * or unsigned integers. Or one or more unsigned integers
242 * acting as output parameters.
243 *
244 * @param in_range a fixed-length byte range
245 * @return T loaded from @p in_range, as a big-endian value
246 */
247template <Endianness endianness, std::unsigned_integral OutT, ranges::contiguous_range<uint8_t> InR>
248inline constexpr OutT load_any(InR&& in_range) {
249 ranges::assert_exact_byte_length<sizeof(OutT)>(in_range);
250 std::span in{in_range};
251
252 // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
253 // internally to copy ranges on a byte-by-byte basis, which is not allowed
254 // in a `constexpr` context.
255 if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
256 return fallback_load_any<endianness, OutT>(std::forward<InR>(in_range));
257 } else {
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);
262 } else if constexpr(is_opposite(endianness)) {
263 return reverse_bytes(typecast_copy<OutT>(in));
264 } else {
265 static_assert(native_endianness_is_unknown<endianness>());
266 return fallback_load_any<endianness, OutT>(std::forward<InR>(in_range));
267 }
268 }
269}
270
271/**
272 * Overload for loading into a strong type holding an unsigned integer
273 */
274template <Endianness endianness, concepts::unsigned_integral_strong_type OutT, ranges::contiguous_range<uint8_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))};
278}
279
280/**
281 * Overload for loading into an enum type that uses an unsigned integer as its
282 * underlying type.
283 */
284template <Endianness endianness, typename OutT, ranges::contiguous_range<uint8_t> InR>
285 requires(std::is_enum_v<OutT> && std::unsigned_integral<std::underlying_type_t<OutT>>)
286inline constexpr OutT load_any(InR&& in_range) {
287 using underlying_type = std::underlying_type_t<OutT>;
288 return static_cast<OutT>(load_any<endianness, underlying_type>(std::forward<InR>(in_range)));
289}
290
291/**
292 * Load many unsigned integers
293 * @param in a fixed-length span to some bytes
294 * @param outs a arbitrary-length parameter list of unsigned integers to be loaded
295 */
296template <Endianness endianness, typename OutT, ranges::contiguous_range<uint8_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) {
300 ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(in);
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)>());
303 off += sizeof(T);
304 };
305
306 (load_one(std::span{in}, outs), ...);
307}
308
309/**
310 * Load a variable number of words from @p in into @p out.
311 * The byte length of the @p out and @p in ranges must match.
312 *
313 * @param out the output range of words
314 * @param in the input range of bytes
315 */
316template <Endianness endianness,
317 typename OutT,
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) {
324
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);
332 }
333 };
334
335 // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
336 // internally to copy ranges on a byte-by-byte basis, which is not allowed
337 // in a `constexpr` context.
338 if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
339 load_elementwise();
340 } else {
341 if constexpr(is_native(endianness)) {
342 typecast_copy(out, in);
343 } else {
344 load_elementwise();
345 }
346 }
347}
348
349//
350// Type inference overloads
351//
352
353/**
354 * Load one or more unsigned integers, auto-detect the output type if
355 * possible. Otherwise, use the specified integer or integer container type.
356 *
357 * @param in_range a statically-sized range with some bytes
358 * @return T loaded from in
359 */
360template <Endianness endianness, typename OutT, ranges::contiguous_range<uint8_t> InR>
361 requires(std::same_as<AutoDetect, OutT> ||
363 concepts::resizable_container<OutT>)&&unsigned_integralish<typename OutT::value_type>))
364inline constexpr auto load_any(InR&& in_range) {
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;
369
370 // clang-format off
371 using type =
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>>>>;
376 // clang-format on
377
378 static_assert(
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");
381
382 return type{};
383 } else {
384 static_assert(
385 !std::same_as<AutoDetect, OutT>,
386 "cannot infer return type from a dynamic range at compile time, please specify it explicitly");
387 }
388 } else if constexpr(concepts::resizable_container<OutT>) {
389 const size_t in_bytes = std::span{in}.size_bytes();
390 constexpr size_t out_elem_bytes = sizeof(typename OutT::value_type);
391 BOTAN_ARG_CHECK(in_bytes % out_elem_bytes == 0,
392 "Input range is not word-aligned with the requested output range");
393 return OutT(in_bytes / out_elem_bytes);
394 } else {
395 return OutT{};
396 }
397 }(in_range);
398
399 using out_type = decltype(out);
400 if constexpr(unsigned_integralish<out_type>) {
401 out = load_any<endianness, out_type>(std::forward<InR>(in_range));
402 } else {
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));
406 }
407 return out;
408}
409
410//
411// Legacy load functions that work on raw pointers and arrays
412//
413
414/**
415 * Load a word from @p in at some offset @p off
416 * @param in a pointer to some bytes
417 * @param off an offset into the array
418 * @return off'th T of in, as a big-endian value
419 */
420template <Endianness endianness, unsigned_integralish OutT>
421inline constexpr OutT load_any(const uint8_t in[], size_t off) {
422 // asserts that *in points to enough bytes to read at offset 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));
425}
426
427/**
428 * Load many words from @p in
429 * @param in a pointer to some bytes
430 * @param outs a arbitrary-length parameter list of unsigned integers to be loaded
431 */
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) + ...);
438 // asserts that *in points to the correct amount of memory
439 load_any<endianness, OutT>(std::span<const uint8_t, bytes>(in, bytes), outs...);
440}
441
442/**
443 * Load a variable number of words from @p in into @p out.
444 * @param out the output array of words
445 * @param in the input array of bytes
446 * @param count how many words are in in
447 */
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) {
451 // asserts that *in and *out point to the correct amount of memory
452 load_any<endianness, OutT>(std::span<T>(out, count), std::span<const uint8_t>(in, count * sizeof(T)));
453}
454
455} // namespace detail
456
457/**
458 * Load "something" in little endian byte order
459 * See the documentation of this file for more details.
460 */
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)...);
464}
465
466/**
467 * Load "something" in big endian byte order
468 * See the documentation of this file for more details.
469 */
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)...);
473}
474
475namespace detail {
476
477/**
478 * Store a word in either big or little endian byte order into a range
479 *
480 * This is the base implementation, all other overloads are just convenience
481 * wrappers. It is assumed that the range has the correct size for the word.
482 *
483 * Template arguments of all overloads of store_any() share the same semantics
484 * as those of load_any(). See the documentation of this function for more
485 * details.
486 *
487 * @param in an unsigned integral to be stored
488 * @param out_range a byte range to store the word into
489 */
490template <Endianness endianness, std::unsigned_integral InT, ranges::contiguous_output_range<uint8_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};
494
495 // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
496 // internally to copy ranges on a byte-by-byte basis, which is not allowed
497 // in a `constexpr` context.
498 if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
499 return fallback_store_any<endianness, InT>(in, std::forward<OutR>(out_range));
500 } else {
501 if constexpr(sizeof(InT) == 1) {
502 out[0] = static_cast<uint8_t>(in);
503 } else if constexpr(is_native(endianness)) {
504 typecast_copy(out, in);
505 } else if constexpr(is_opposite(endianness)) {
507 } else {
508 static_assert(native_endianness_is_unknown<endianness>());
509 return fallback_store_any<endianness, InT>(in, std::forward<OutR>(out_range));
510 }
511 }
512}
513
514/**
515 * Overload for loading into a strong type holding an unsigned integer
516 */
517template <Endianness endianness,
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));
523}
524
525/**
526 * Overload for storing an enum type that uses an unsigned integer as its
527 * underlying type.
528 */
529template <Endianness endianness, typename InT, ranges::contiguous_output_range<uint8_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>;
533 // TODO: C++23: use std::to_underlying(in) instead
534 store_any<endianness, underlying_type>(static_cast<underlying_type>(in), std::forward<OutR>(out_range));
535}
536
537/**
538 * Store many unsigned integers words into a byte range
539 * @param out a sized range of some bytes
540 * @param ins a arbitrary-length parameter list of unsigned integers to be stored
541 */
542template <Endianness endianness,
543 typename InT,
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...>))
548inline constexpr void store_any(OutR&& out, Ts... ins) {
549 ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(out);
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)>());
552 off += sizeof(T);
553 };
554
555 (store_one(std::span{out}, ins), ...);
556}
557
558/**
559 * Store a variable number of words given in @p in into @p out.
560 * The byte lengths of @p in and @p out must be consistent.
561 * @param out the output range of bytes
562 * @param in the input range of words
563 */
564template <Endianness endianness,
565 typename InT,
568 requires(std::same_as<AutoDetect, InT> || std::same_as<InT, std::ranges::range_value_t<InR>>)
569inline constexpr void store_any(OutR&& out, InR&& in) {
571
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);
579 }
580 };
581
582 // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
583 // internally to copy ranges on a byte-by-byte basis, which is not allowed
584 // in a `constexpr` context.
585 if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
586 store_elementwise();
587 } else {
588 if constexpr(is_native(endianness)) {
589 typecast_copy(out, in);
590 } else {
591 store_elementwise();
592 }
593 }
594}
595
596//
597// Type inference overloads
598//
599
600/**
601 * Infer InT from a single unsigned integer input parameter.
602 *
603 * TODO: we might consider dropping this overload (i.e. out-range as second
604 * parameter) and make this a "special case" of the overload below, that
605 * takes a variadic number of input parameters.
606 *
607 * @param in an unsigned integer to be stored
608 * @param out_range a range of bytes to store the word into
609 */
610template <Endianness endianness, typename InT, unsigned_integralish T, ranges::contiguous_output_range<uint8_t> OutR>
611 requires std::same_as<AutoDetect, InT>
612inline constexpr void store_any(T in, OutR&& out_range) {
613 store_any<endianness, T>(in, std::forward<OutR>(out_range));
614}
615
616/**
617 * The caller provided some integer values in a collection but did not provide
618 * the output container. Let's create one for them, fill it with one of the
619 * overloads above and return it. This will default to a std::array if the
620 * caller did not specify the desired output container type.
621 *
622 * @param in_range a range of words that should be stored
623 * @return a container of bytes that contains the stored words
624 */
625template <Endianness endianness, typename OutR, ranges::spanable_range InR>
626 requires(std::same_as<AutoDetect, OutR> ||
627 (ranges::statically_spanable_range<OutR> && std::default_initializable<OutR>) ||
629inline constexpr auto store_any(InR&& in_range) {
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>();
635 } else {
636 static_assert(
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");
639 }
640 } else if constexpr(concepts::resizable_byte_buffer<OutR>) {
641 return OutR(std::span{in}.size_bytes());
642 } else {
643 return OutR{};
644 }
645 }(in_range);
646
647 store_any<endianness, std::ranges::range_value_t<InR>>(out, std::forward<InR>(in_range));
648 return out;
649}
650
651/**
652 * The caller provided some integer values but did not provide the output
653 * container. Let's create one for them, fill it with one of the overloads above
654 * and return it. This will default to a std::array if the caller did not
655 * specify the desired output container type.
656 *
657 * @param ins some words that should be stored
658 * @return a container of bytes that contains the stored words
659 */
660template <Endianness endianness, typename OutR, unsigned_integralish... Ts>
661 requires all_same_v<Ts...>
662inline constexpr auto store_any(Ts... ins) {
663 return store_any<endianness, OutR>(std::array{ins...});
664}
665
666//
667// Legacy store functions that work on raw pointers and arrays
668//
669
670/**
671 * Store a single unsigned integer into a raw pointer
672 * @param in the input unsigned integer
673 * @param out the byte array to write to
674 */
675template <Endianness endianness, typename InT, unsigned_integralish T>
676 requires(std::same_as<AutoDetect, InT> || std::same_as<T, InT>)
677inline constexpr void store_any(T in, uint8_t out[]) {
678 // asserts that *out points to enough bytes to write into
679 store_any<endianness, InT>(in, std::span<uint8_t, sizeof(T)>(out, sizeof(T)));
680}
681
682/**
683 * Store many unsigned integers words into a raw pointer
684 * @param ins a arbitrary-length parameter list of unsigned integers to be stored
685 * @param out the byte array to write to
686 */
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);
691 // asserts that *out points to the correct amount of memory
692 store_any<endianness, T0>(std::span<uint8_t, bytes>(out, bytes), in0, ins...);
693}
694
695} // namespace detail
696
697/**
698 * Store "something" in little endian byte order
699 * See the documentation of this file for more details.
700 */
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)...);
704}
705
706/**
707 * Store "something" in big endian byte order
708 * See the documentation of this file for more details.
709 */
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)...);
713}
714
715namespace detail {
716
717template <Endianness endianness, unsigned_integralish T>
718size_t copy_out_any_word_aligned_portion(std::span<uint8_t>& out, std::span<const T>& in) {
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;
722 BOTAN_ASSERT_NOMSG(in.size_bytes() >= full_word_bytes + remaining_bytes);
723
724 // copy full words
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);
728
729 return remaining_bytes;
730}
731
732} // namespace detail
733
734/**
735 * Partially copy a subset of @p in into @p out using big-endian
736 * byte order.
737 */
738template <ranges::spanable_range InR>
739void copy_out_be(std::span<uint8_t> out, InR&& in) {
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);
743
744 // copy remaining bytes as a partial word
745 for(size_t i = 0; i < remaining_bytes; ++i) {
746 out[i] = get_byte_var(i, in_s.front());
747 }
748}
749
750/**
751 * Partially copy a subset of @p in into @p out using little-endian
752 * byte order.
753 */
754template <ranges::spanable_range InR>
755void copy_out_le(std::span<uint8_t> out, InR&& in) {
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);
759
760 // copy remaining bytes as a partial word
761 for(size_t i = 0; i < remaining_bytes; ++i) {
762 out[i] = get_byte_var(sizeof(T) - 1 - i, in_s.front());
763 }
764}
765
766} // namespace Botan
767
768#endif
#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:164
constexpr bool is_opposite(Endianness endianness)
Definition loadstor.h:153
constexpr bool is_native(Endianness endianness)
Definition loadstor.h:139
constexpr void fallback_store_any(InT in, OutR &&out_range)
Definition loadstor.h:207
size_t copy_out_any_word_aligned_portion(std::span< uint8_t > &out, std::span< const T > &in)
Definition loadstor.h:718
constexpr void store_any(InT in, OutR &&out_range)
Definition loadstor.h:491
constexpr OutT fallback_load_any(InR &&in_range)
Definition loadstor.h:185
constexpr OutT load_any(InR &&in_range)
Definition loadstor.h:248
constexpr void assert_exact_byte_length(R &&r)
Definition concepts.h:97
constexpr void assert_equal_byte_lengths(R0 &&r0, Rs &&... rs)
Definition concepts.h:116
void copy_out_be(std::span< uint8_t > out, InR &&in)
Definition loadstor.h:739
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 auto store_le(ParamTs &&... params)
Definition loadstor.h:702
constexpr uint16_t reverse_bytes(uint16_t x)
Definition bswap.h:21
void copy_out_le(std::span< uint8_t > out, InR &&in)
Definition loadstor.h:755
constexpr auto load_le(ParamTs &&... params)
Definition loadstor.h:462
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:711
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:471
constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1)
Definition loadstor.h:88
constexpr AutoDetect()=delete