Botan 3.5.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
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 return false;
142#endif
143}
144
145/**
146 * @warning This function may return false if the native endianness is unknown
147 * @returns true iff the native endianness does not match the given endianness
148 */
149constexpr bool is_opposite(Endianness endianness) {
150#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
151 return endianness == Endianness::Little;
152#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
153 return endianness == Endianness::Big;
154#else
155 return false;
156#endif
157}
158
159template <Endianness endianness>
161#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) || defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
162 return false;
163#else
164 return true;
165#endif
166}
167
168/**
169 * Models a type that can be loaded/stored from/to a byte range.
170 */
171template <typename T>
173 (std::is_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>);
174
175/**
176 * Manually load a word from a range in either big or little endian byte order.
177 * This will be used only if the endianness of the target platform is unknown at
178 * compile time.
179 */
180template <Endianness endianness, std::unsigned_integral OutT, ranges::contiguous_range<uint8_t> InR>
181inline constexpr OutT fallback_load_any(InR&& in_range) {
182 std::span in{in_range};
183 // clang-format off
184 if constexpr(endianness == Endianness::Big) {
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)>());
188 } else {
189 static_assert(endianness == Endianness::Little);
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)>());
193 }
194 // clang-format on
195}
196
197/**
198 * Manually store a word into a range in either big or little endian byte order.
199 * This will be used only if the endianness of the target platform is unknown at
200 * compile time.
201 */
202template <Endianness endianness, std::unsigned_integral InT, ranges::contiguous_output_range<uint8_t> OutR>
203inline constexpr void fallback_store_any(InT in, OutR&& out_range) {
204 std::span out{out_range};
205 // clang-format off
206 if constexpr(endianness == Endianness::Big) {
207 [&]<size_t... i>(std::index_sequence<i...>) {
208 ((out[i] = get_byte<i>(in)), ...);
209 } (std::make_index_sequence<sizeof(InT)>());
210 } else {
211 static_assert(endianness == Endianness::Little);
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)>());
215 }
216 // clang-format on
217}
218
219/**
220 * Load a word from a range in either big or little endian byte order
221 *
222 * This is the base implementation, all other overloads are just convenience
223 * wrappers. It is assumed that the range has the correct size for the word.
224 *
225 * Template arguments of all overloads of load_any() share the same semantics:
226 *
227 * 1. Endianness Either `Endianness::Big` or `Endianness::Little`, that
228 * will eventually select the byte order translation mode
229 * implemented in this base function.
230 *
231 * 2. Output type Either `AutoDetect`, an unsigned integer or a container
232 * holding an unsigned integer type. `AutoDetect` means
233 * that the caller did not explicitly specify the type and
234 * expects the type to be inferred from the input.
235 *
236 * 3+. Argument types Typically, those are input and output ranges of bytes
237 * or unsigned integers. Or one or more unsigned integers
238 * acting as output parameters.
239 *
240 * @param in_range a fixed-length byte range
241 * @return T loaded from @p in_range, as a big-endian value
242 */
243template <Endianness endianness, std::unsigned_integral OutT, ranges::contiguous_range<uint8_t> InR>
244inline constexpr OutT load_any(InR&& in_range) {
246 std::span in{in_range};
247
248 // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
249 // internally to copy ranges on a byte-by-byte basis, which is not allowed
250 // in a `constexpr` context.
251 if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
252 return fallback_load_any<endianness, OutT>(std::forward<InR>(in_range));
253 } else {
254 if constexpr(sizeof(OutT) == 1) {
255 return static_cast<OutT>(in[0]);
256 } else if constexpr(is_native(endianness)) {
257 return typecast_copy<OutT>(in);
258 } else if constexpr(is_opposite(endianness)) {
260 } else {
262 return fallback_load_any<endianness, OutT>(std::forward<InR>(in_range));
263 }
264 }
265}
266
267/**
268 * Overload for loading into a strong type holding an unsigned integer
269 */
270template <Endianness endianness, concepts::unsigned_integral_strong_type OutT, ranges::contiguous_range<uint8_t> InR>
271inline constexpr OutT load_any(InR&& in_range) {
272 using underlying_type = typename OutT::wrapped_type;
273 return OutT{load_any<endianness, underlying_type>(std::forward<InR>(in_range))};
274}
275
276/**
277 * Overload for loading into an enum type that uses an unsigned integer as its
278 * underlying type.
279 */
280template <Endianness endianness, typename OutT, ranges::contiguous_range<uint8_t> InR>
281 requires(std::is_enum_v<OutT> && std::unsigned_integral<std::underlying_type_t<OutT>>)
282inline constexpr OutT load_any(InR&& in_range) {
283 using underlying_type = std::underlying_type_t<OutT>;
284 return static_cast<OutT>(load_any<endianness, underlying_type>(std::forward<InR>(in_range)));
285}
286
287/**
288 * Load many unsigned integers
289 * @param in a fixed-length span to some bytes
290 * @param outs a arbitrary-length parameter list of unsigned integers to be loaded
291 */
292template <Endianness endianness, typename OutT, ranges::contiguous_range<uint8_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) {
296 ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(in);
297 auto load_one = [off = 0]<typename T>(auto i, T& o) mutable {
298 o = load_any<endianness, T>(i.subspan(off).template first<sizeof(T)>());
299 off += sizeof(T);
300 };
301
302 (load_one(std::span{in}, outs), ...);
303}
304
305/**
306 * Load a variable number of words from @p in into @p out.
307 * The byte length of the @p out and @p in ranges must match.
308 *
309 * @param out the output range of words
310 * @param in the input range of bytes
311 */
312template <Endianness endianness,
313 typename OutT,
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) {
320
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) {
326 out_elem = load_any<endianness, element_type>(in_s.template first<bytes_per_element>());
327 in_s = in_s.subspan(bytes_per_element);
328 }
329 };
330
331 // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
332 // internally to copy ranges on a byte-by-byte basis, which is not allowed
333 // in a `constexpr` context.
334 if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
335 load_elementwise();
336 } else {
337 if constexpr(is_native(endianness)) {
338 typecast_copy(out, in);
339 } else {
340 load_elementwise();
341 }
342 }
343}
344
345//
346// Type inference overloads
347//
348
349/**
350 * Load one or more unsigned integers, auto-detect the output type if
351 * possible. Otherwise, use the specified integer or integer container type.
352 *
353 * @param in_range a statically-sized range with some bytes
354 * @return T loaded from in
355 */
356template <Endianness endianness, typename OutT, ranges::contiguous_range<uint8_t> InR>
357 requires(std::same_as<AutoDetect, OutT> ||
359 unsigned_integralish<typename OutT::value_type>))
360inline constexpr auto load_any(InR&& in_range) {
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;
365
366 // clang-format off
367 using type =
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>>>>;
372 // clang-format on
373
374 static_assert(
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");
377
378 return type{};
379 } else {
380 static_assert(
381 !std::same_as<AutoDetect, OutT>,
382 "cannot infer return type from a dynamic range at compile time, please specify it explicitly");
383 }
384 } else if constexpr(concepts::resizable_container<OutT>) {
385 const size_t in_bytes = std::span{in}.size_bytes();
386 constexpr size_t out_elem_bytes = sizeof(typename OutT::value_type);
387 BOTAN_ARG_CHECK(in_bytes % out_elem_bytes == 0,
388 "Input range is not word-aligned with the requested output range");
389 return OutT(in_bytes / out_elem_bytes);
390 } else {
391 return OutT{};
392 }
393 }(in_range);
394
395 using out_type = decltype(out);
396 if constexpr(unsigned_integralish<out_type>) {
397 out = load_any<endianness, out_type>(std::forward<InR>(in_range));
398 } else {
400 using out_range_type = std::ranges::range_value_t<out_type>;
401 load_any<endianness, out_range_type>(out, std::forward<InR>(in_range));
402 }
403 return out;
404}
405
406//
407// Legacy load functions that work on raw pointers and arrays
408//
409
410/**
411 * Load a word from @p in at some offset @p off
412 * @param in a pointer to some bytes
413 * @param off an offset into the array
414 * @return off'th T of in, as a big-endian value
415 */
416template <Endianness endianness, unsigned_integralish OutT>
417inline constexpr OutT load_any(const uint8_t in[], size_t off) {
418 // asserts that *in points to enough bytes to read at offset off
419 constexpr size_t out_size = sizeof(OutT);
420 return load_any<endianness, OutT>(std::span<const uint8_t, out_size>(in + off * out_size, out_size));
421}
422
423/**
424 * Load many words from @p in
425 * @param in a pointer to some bytes
426 * @param outs a arbitrary-length parameter list of unsigned integers to be loaded
427 */
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) + ...);
434 // asserts that *in points to the correct amount of memory
435 load_any<endianness, OutT>(std::span<const uint8_t, bytes>(in, bytes), outs...);
436}
437
438/**
439 * Load a variable number of words from @p in into @p out.
440 * @param out the output array of words
441 * @param in the input array of bytes
442 * @param count how many words are in in
443 */
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) {
447 // asserts that *in and *out point to the correct amount of memory
448 load_any<endianness, OutT>(std::span<T>(out, count), std::span<const uint8_t>(in, count * sizeof(T)));
449}
450
451} // namespace detail
452
453/**
454 * Load "something" in little endian byte order
455 * See the documentation of this file for more details.
456 */
457template <typename OutT = detail::AutoDetect, typename... ParamTs>
458inline constexpr auto load_le(ParamTs&&... params) {
459 return detail::load_any<detail::Endianness::Little, OutT>(std::forward<ParamTs>(params)...);
460}
461
462/**
463 * Load "something" in big endian byte order
464 * See the documentation of this file for more details.
465 */
466template <typename OutT = detail::AutoDetect, typename... ParamTs>
467inline constexpr auto load_be(ParamTs&&... params) {
468 return detail::load_any<detail::Endianness::Big, OutT>(std::forward<ParamTs>(params)...);
469}
470
471namespace detail {
472
473/**
474 * Store a word in either big or little endian byte order into a range
475 *
476 * This is the base implementation, all other overloads are just convenience
477 * wrappers. It is assumed that the range has the correct size for the word.
478 *
479 * Template arguments of all overloads of store_any() share the same semantics
480 * as those of load_any(). See the documentation of this function for more
481 * details.
482 *
483 * @param in an unsigned integral to be stored
484 * @param out_range a byte range to store the word into
485 */
486template <Endianness endianness, std::unsigned_integral InT, ranges::contiguous_output_range<uint8_t> OutR>
487inline constexpr void store_any(InT in, OutR&& out_range) {
489 std::span out{out_range};
490
491 // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
492 // internally to copy ranges on a byte-by-byte basis, which is not allowed
493 // in a `constexpr` context.
494 if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
495 return fallback_store_any<endianness, InT>(in, std::forward<OutR>(out_range));
496 } else {
497 if constexpr(sizeof(InT) == 1) {
498 out[0] = static_cast<uint8_t>(in);
499 } else if constexpr(is_native(endianness)) {
500 typecast_copy(out, in);
501 } else if constexpr(is_opposite(endianness)) {
503 } else {
505 return fallback_store_any<endianness, InT>(in, std::forward<OutR>(out_range));
506 }
507 }
508}
509
510/**
511 * Overload for loading into a strong type holding an unsigned integer
512 */
513template <Endianness endianness,
516inline constexpr void store_any(InT in, OutR&& out_range) {
517 using underlying_type = typename InT::wrapped_type;
518 store_any<endianness, underlying_type>(in.get(), std::forward<OutR>(out_range));
519}
520
521/**
522 * Overload for storing an enum type that uses an unsigned integer as its
523 * underlying type.
524 */
525template <Endianness endianness, typename InT, ranges::contiguous_output_range<uint8_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>;
529 // TODO: C++23: use std::to_underlying(in) instead
530 store_any<endianness, underlying_type>(static_cast<underlying_type>(in), std::forward<OutR>(out_range));
531}
532
533/**
534 * Store many unsigned integers words into a byte range
535 * @param out a sized range of some bytes
536 * @param ins a arbitrary-length parameter list of unsigned integers to be stored
537 */
538template <Endianness endianness,
539 typename 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...>))
544inline constexpr void store_any(OutR&& out, Ts... ins) {
545 ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(out);
546 auto store_one = [off = 0]<typename T>(auto o, T i) mutable {
547 store_any<endianness, T>(i, o.subspan(off).template first<sizeof(T)>());
548 off += sizeof(T);
549 };
550
551 (store_one(std::span{out}, ins), ...);
552}
553
554/**
555 * Store a variable number of words given in @p in into @p out.
556 * The byte lengths of @p in and @p out must be consistent.
557 * @param out the output range of bytes
558 * @param in the input range of words
559 */
560template <Endianness endianness,
561 typename InT,
564 requires(std::same_as<AutoDetect, InT> || std::same_as<InT, std::ranges::range_value_t<InR>>)
565inline constexpr void store_any(OutR&& out, InR&& in) {
567
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) {
573 store_any<endianness, element_type>(out_s.template first<bytes_per_element>(), in_elem);
574 out_s = out_s.subspan(bytes_per_element);
575 }
576 };
577
578 // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
579 // internally to copy ranges on a byte-by-byte basis, which is not allowed
580 // in a `constexpr` context.
581 if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
582 store_elementwise();
583 } else {
584 if constexpr(is_native(endianness)) {
585 typecast_copy(out, in);
586 } else {
587 store_elementwise();
588 }
589 }
590}
591
592//
593// Type inference overloads
594//
595
596/**
597 * Infer InT from a single unsigned integer input parameter.
598 *
599 * TODO: we might consider dropping this overload (i.e. out-range as second
600 * parameter) and make this a "special case" of the overload below, that
601 * takes a variadic number of input parameters.
602 *
603 * @param in an unsigned integer to be stored
604 * @param out_range a range of bytes to store the word into
605 */
606template <Endianness endianness, typename InT, unsigned_integralish T, ranges::contiguous_output_range<uint8_t> OutR>
607 requires std::same_as<AutoDetect, InT>
608inline constexpr void store_any(T in, OutR&& out_range) {
609 store_any<endianness, T>(in, std::forward<OutR>(out_range));
610}
611
612/**
613 * The caller provided some integer values in a collection but did not provide
614 * the output container. Let's create one for them, fill it with one of the
615 * overloads above and return it. This will default to a std::array if the
616 * caller did not specify the desired output container type.
617 *
618 * @param in_range a range of words that should be stored
619 * @return a container of bytes that contains the stored words
620 */
621template <Endianness endianness, typename OutR, ranges::spanable_range InR>
622 requires(std::same_as<AutoDetect, OutR> ||
623 (ranges::statically_spanable_range<OutR> && std::default_initializable<OutR>) ||
625inline constexpr auto store_any(InR&& in_range) {
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>();
631 } else {
632 static_assert(
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");
635 }
636 } else if constexpr(concepts::resizable_byte_buffer<OutR>) {
637 return OutR(std::span{in}.size_bytes());
638 } else {
639 return OutR{};
640 }
641 }(in_range);
642
643 store_any<endianness, std::ranges::range_value_t<InR>>(out, std::forward<InR>(in_range));
644 return out;
645}
646
647/**
648 * The caller provided some integer values but did not provide the output
649 * container. Let's create one for them, fill it with one of the overloads above
650 * and return it. This will default to a std::array if the caller did not
651 * specify the desired output container type.
652 *
653 * @param ins some words that should be stored
654 * @return a container of bytes that contains the stored words
655 */
656template <Endianness endianness, typename OutR, unsigned_integralish... Ts>
657 requires all_same_v<Ts...>
658inline constexpr auto store_any(Ts... ins) {
659 return store_any<endianness, OutR>(std::array{ins...});
660}
661
662//
663// Legacy store functions that work on raw pointers and arrays
664//
665
666/**
667 * Store a single unsigned integer into a raw pointer
668 * @param in the input unsigned integer
669 * @param out the byte array to write to
670 */
671template <Endianness endianness, typename InT, unsigned_integralish T>
672 requires(std::same_as<AutoDetect, InT> || std::same_as<T, InT>)
673inline constexpr void store_any(T in, uint8_t out[]) {
674 // asserts that *out points to enough bytes to write into
675 store_any<endianness, InT>(in, std::span<uint8_t, sizeof(T)>(out, sizeof(T)));
676}
677
678/**
679 * Store many unsigned integers words into a raw pointer
680 * @param ins a arbitrary-length parameter list of unsigned integers to be stored
681 * @param out the byte array to write to
682 */
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);
687 // asserts that *out points to the correct amount of memory
688 store_any<endianness, T0>(std::span<uint8_t, bytes>(out, bytes), in0, ins...);
689}
690
691} // namespace detail
692
693/**
694 * Store "something" in little endian byte order
695 * See the documentation of this file for more details.
696 */
697template <typename ModifierT = detail::AutoDetect, typename... ParamTs>
698inline constexpr auto store_le(ParamTs&&... params) {
699 return detail::store_any<detail::Endianness::Little, ModifierT>(std::forward<ParamTs>(params)...);
700}
701
702/**
703 * Store "something" in big endian byte order
704 * See the documentation of this file for more details.
705 */
706template <typename ModifierT = detail::AutoDetect, typename... ParamTs>
707inline constexpr auto store_be(ParamTs&&... params) {
708 return detail::store_any<detail::Endianness::Big, ModifierT>(std::forward<ParamTs>(params)...);
709}
710
711namespace detail {
712
713template <Endianness endianness, unsigned_integralish T>
714inline size_t copy_out_any_word_aligned_portion(std::span<uint8_t>& out, std::span<const T>& in) {
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;
718 BOTAN_ASSERT_NOMSG(in.size_bytes() >= full_word_bytes + remaining_bytes);
719
720 // copy full words
721 store_any<endianness, T>(out.first(full_word_bytes), in.first(full_words));
722 out = out.subspan(full_word_bytes);
723 in = in.subspan(full_words);
724
725 return remaining_bytes;
726}
727
728} // namespace detail
729
730/**
731 * Partially copy a subset of @p in into @p out using big-endian
732 * byte order.
733 */
734template <ranges::spanable_range InR>
735inline void copy_out_be(std::span<uint8_t> out, InR&& in) {
736 using T = std::ranges::range_value_t<InR>;
737 std::span<const T> in_s{in};
738 const auto remaining_bytes = detail::copy_out_any_word_aligned_portion<detail::Endianness::Big>(out, in_s);
739
740 // copy remaining bytes as a partial word
741 for(size_t i = 0; i < remaining_bytes; ++i) {
742 out[i] = get_byte_var(i, in_s.front());
743 }
744}
745
746/**
747 * Partially copy a subset of @p in into @p out using little-endian
748 * byte order.
749 */
750template <ranges::spanable_range InR>
751inline void copy_out_le(std::span<uint8_t> out, InR&& in) {
752 using T = std::ranges::range_value_t<InR>;
753 std::span<const T> in_s{in};
754 const auto remaining_bytes = detail::copy_out_any_word_aligned_portion<detail::Endianness::Little>(out, in_s);
755
756 // copy remaining bytes as a partial word
757 for(size_t i = 0; i < remaining_bytes; ++i) {
758 out[i] = get_byte_var(sizeof(T) - 1 - i, in_s.front());
759 }
760}
761
762} // namespace Botan
763
764#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:160
constexpr bool is_opposite(Endianness endianness)
Definition loadstor.h:149
constexpr bool is_native(Endianness endianness)
Definition loadstor.h:135
constexpr void fallback_store_any(InT in, OutR &&out_range)
Definition loadstor.h:203
size_t copy_out_any_word_aligned_portion(std::span< uint8_t > &out, std::span< const T > &in)
Definition loadstor.h:714
constexpr void store_any(InT in, OutR &&out_range)
Definition loadstor.h:487
constexpr OutT fallback_load_any(InR &&in_range)
Definition loadstor.h:181
constexpr OutT load_any(InR &&in_range)
Definition loadstor.h:244
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:735
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 T reverse_bytes(T x)
Definition bswap.h:24
constexpr auto store_le(ParamTs &&... params)
Definition loadstor.h:698
void copy_out_le(std::span< uint8_t > out, InR &&in)
Definition loadstor.h:751
constexpr auto load_le(ParamTs &&... params)
Definition loadstor.h:458
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:707
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:467
constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1)
Definition loadstor.h:88