Botan 3.6.1
Crypto and TLS for C&
stl_util.h
Go to the documentation of this file.
1/*
2* STL Utility Functions
3* (C) 1999-2007 Jack Lloyd
4* (C) 2015 Simon Warta (Kullo GmbH)
5* (C) 2023-2024 René Meusel - Rohde & Schwarz Cybersecurity
6*
7* Botan is released under the Simplified BSD License (see license.txt)
8*/
9
10#ifndef BOTAN_STL_UTIL_H_
11#define BOTAN_STL_UTIL_H_
12
13#include <algorithm>
14#include <functional>
15#include <optional>
16#include <span>
17#include <string>
18#include <tuple>
19#include <variant>
20#include <vector>
21
22#include <botan/concepts.h>
23#include <botan/secmem.h>
24#include <botan/strong_type.h>
25
26namespace Botan {
27
28template <concepts::contiguous_container T = std::vector<uint8_t>>
29inline T to_byte_vector(std::string_view s) {
30 return T(s.cbegin(), s.cend());
31}
32
33inline std::string to_string(std::span<const uint8_t> bytes) {
34 return std::string(bytes.begin(), bytes.end());
35}
36
37/**
38 * Reduce the values of @p keys into an accumulator initialized with @p acc using
39 * the reducer function @p reducer.
40 *
41 * The @p reducer is a function taking the accumulator and a single key to return the
42 * new accumulator. Keys are consecutively reduced into the accumulator.
43 *
44 * @return the accumulator containing the reduction of @p keys
45 */
46template <typename RetT, typename KeyT, typename ReducerT>
47RetT reduce(const std::vector<KeyT>& keys, RetT acc, ReducerT reducer)
48 requires std::is_convertible_v<ReducerT, std::function<RetT(RetT, const KeyT&)>>
49{
50 for(const KeyT& key : keys) {
51 acc = reducer(std::move(acc), key);
52 }
53 return acc;
54}
55
56/**
57* Existence check for values
58*/
59template <typename T, typename OT>
60bool value_exists(const std::vector<T>& vec, const OT& val) {
61 for(size_t i = 0; i != vec.size(); ++i) {
62 if(vec[i] == val) {
63 return true;
64 }
65 }
66 return false;
67}
68
69template <typename T, typename Pred>
70void map_remove_if(Pred pred, T& assoc) {
71 auto i = assoc.begin();
72 while(i != assoc.end()) {
73 if(pred(i->first)) {
74 assoc.erase(i++);
75 } else {
76 i++;
77 }
78 }
79}
80
81/**
82 * Helper class to ease unmarshalling of concatenated fixed-length values
83 */
85 public:
86 BufferSlicer(std::span<const uint8_t> buffer) : m_remaining(buffer) {}
87
88 template <concepts::contiguous_container ContainerT>
89 auto copy(const size_t count) {
90 const auto result = take(count);
91 return ContainerT(result.begin(), result.end());
92 }
93
94 auto copy_as_vector(const size_t count) { return copy<std::vector<uint8_t>>(count); }
95
96 auto copy_as_secure_vector(const size_t count) { return copy<secure_vector<uint8_t>>(count); }
97
98 std::span<const uint8_t> take(const size_t count) {
99 BOTAN_STATE_CHECK(remaining() >= count);
100 auto result = m_remaining.first(count);
101 m_remaining = m_remaining.subspan(count);
102 return result;
103 }
104
105 template <size_t count>
106 std::span<const uint8_t, count> take() {
107 BOTAN_STATE_CHECK(remaining() >= count);
108 auto result = m_remaining.first<count>();
109 m_remaining = m_remaining.subspan(count);
110 return result;
111 }
112
113 template <concepts::contiguous_strong_type T>
114 StrongSpan<const T> take(const size_t count) {
115 return StrongSpan<const T>(take(count));
116 }
117
118 uint8_t take_byte() { return take(1)[0]; }
119
120 void copy_into(std::span<uint8_t> sink) {
121 const auto data = take(sink.size());
122 std::copy(data.begin(), data.end(), sink.begin());
123 }
124
125 void skip(const size_t count) { take(count); }
126
127 size_t remaining() const { return m_remaining.size(); }
128
129 bool empty() const { return m_remaining.empty(); }
130
131 private:
132 std::span<const uint8_t> m_remaining;
133};
134
135/**
136 * @brief Helper class to ease in-place marshalling of concatenated fixed-length
137 * values.
138 *
139 * The size of the final buffer must be known from the start, reallocations are
140 * not performed.
141 */
143 public:
144 constexpr BufferStuffer(std::span<uint8_t> buffer) : m_buffer(buffer) {}
145
146 /**
147 * @returns a span for the next @p bytes bytes in the concatenated buffer.
148 * Checks that the buffer is not exceded.
149 */
150 constexpr std::span<uint8_t> next(size_t bytes) {
151 BOTAN_STATE_CHECK(m_buffer.size() >= bytes);
152
153 auto result = m_buffer.first(bytes);
154 m_buffer = m_buffer.subspan(bytes);
155 return result;
156 }
157
158 template <size_t bytes>
159 constexpr std::span<uint8_t, bytes> next() {
160 BOTAN_STATE_CHECK(m_buffer.size() >= bytes);
161
162 auto result = m_buffer.first<bytes>();
163 m_buffer = m_buffer.subspan(bytes);
164 return result;
165 }
166
167 template <concepts::contiguous_strong_type StrongT>
168 StrongSpan<StrongT> next(size_t bytes) {
169 return StrongSpan<StrongT>(next(bytes));
170 }
171
172 /**
173 * @returns a reference to the next single byte in the buffer
174 */
175 constexpr uint8_t& next_byte() { return next(1)[0]; }
176
177 constexpr void append(std::span<const uint8_t> buffer) {
178 auto sink = next(buffer.size());
179 std::copy(buffer.begin(), buffer.end(), sink.begin());
180 }
181
182 constexpr void append(uint8_t b, size_t repeat = 1) {
183 auto sink = next(repeat);
184 std::fill(sink.begin(), sink.end(), b);
185 }
186
187 constexpr bool full() const { return m_buffer.empty(); }
188
189 constexpr size_t remaining_capacity() const { return m_buffer.size(); }
190
191 private:
192 std::span<uint8_t> m_buffer;
193};
194
195namespace detail {
196
197/**
198 * Helper function that performs range size-checks as required given the
199 * selected output and input range types. If all lengths are known at compile
200 * time, this check will be performed at compile time as well. It will then
201 * instantiate an output range and concatenate the input ranges' contents.
202 */
203template <ranges::spanable_range OutR, ranges::spanable_range... Rs>
204constexpr OutR concatenate(Rs&&... ranges)
206{
207 OutR result;
208
209 // Prepare and validate the output range and construct a lambda that does the
210 // actual filling of the result buffer.
211 // (if no input ranges are given, GCC claims that fill_fn is unused)
212 [[maybe_unused]] auto fill_fn = [&] {
214 // dynamically allocate the correct result byte length
215 const size_t total_size = (ranges.size() + ... + 0);
216 result.reserve(total_size);
217
218 // fill the result buffer using a back-inserter
219 return [&result](auto&& range) {
220 std::copy(
221 std::ranges::begin(range), std::ranges::end(range), std::back_inserter(unwrap_strong_type(result)));
222 };
223 } else {
224 if constexpr((ranges::statically_spanable_range<Rs> && ... && true)) {
225 // all input ranges have a static extent, so check the total size at compile time
226 // (work around an issue in MSVC that warns `total_size` is unused)
227 [[maybe_unused]] constexpr size_t total_size = (decltype(std::span{ranges})::extent + ... + 0);
228 static_assert(result.size() == total_size, "size of result buffer does not match the sum of input buffers");
229 } else {
230 // at least one input range has a dynamic extent, so check the total size at runtime
231 const size_t total_size = (ranges.size() + ... + 0);
232 BOTAN_ARG_CHECK(result.size() == total_size,
233 "result buffer has static extent that does not match the sum of input buffers");
234 }
235
236 // fill the result buffer and hold the current output-iterator position
237 return [itr = std::ranges::begin(result)](auto&& range) mutable {
238 std::copy(std::ranges::begin(range), std::ranges::end(range), itr);
239 std::advance(itr, std::ranges::size(range));
240 };
241 }
242 }();
243
244 // perform the actual concatenation
245 (fill_fn(std::forward<Rs>(ranges)), ...);
246
247 return result;
248}
249
250} // namespace detail
251
252/**
253 * Concatenate an arbitrary number of buffers. Performs range-checks as needed.
254 *
255 * The output type can be auto-detected based on the input ranges, or explicitly
256 * specified by the caller. If all input ranges have a static extent, the total
257 * size is calculated at compile time and a statically sized std::array<> is used.
258 * Otherwise this tries to use the type of the first input range as output type.
259 *
260 * Alternatively, the output container type can be specified explicitly.
261 */
262template <typename OutR = detail::AutoDetect, ranges::spanable_range... Rs>
263constexpr auto concat(Rs&&... ranges)
264 requires(all_same_v<std::ranges::range_value_t<Rs>...>)
265{
266 if constexpr(std::same_as<detail::AutoDetect, OutR>) {
267 // Try to auto-detect a reasonable output type given the input ranges
268 static_assert(sizeof...(Rs) > 0, "Cannot auto-detect the output type if not a single input range is provided.");
269 using candidate_result_t = std::remove_cvref_t<std::tuple_element_t<0, std::tuple<Rs...>>>;
270 using result_range_value_t = std::remove_cvref_t<std::ranges::range_value_t<candidate_result_t>>;
271
272 if constexpr((ranges::statically_spanable_range<Rs> && ...)) {
273 // If all input ranges have a static extent, we can calculate the total size at compile time
274 // and therefore can use a statically sized output container. This is constexpr.
275 constexpr size_t total_size = (decltype(std::span{ranges})::extent + ... + 0);
276 using out_array_t = std::array<result_range_value_t, total_size>;
277 return detail::concatenate<out_array_t>(std::forward<Rs>(ranges)...);
278 } else {
279 // If at least one input range has a dynamic extent, we must use a dynamically allocated output container.
280 // We assume that the user wants to use the first input range's container type as output type.
281 static_assert(
283 "First input range has static extent, but a dynamically allocated output range is required. Please explicitly specify a dynamically allocatable output type.");
284 return detail::concatenate<candidate_result_t>(std::forward<Rs>(ranges)...);
285 }
286 } else {
287 // The caller has explicitly specified the output type
288 return detail::concatenate<OutR>(std::forward<Rs>(ranges)...);
289 }
290}
291
292template <typename... Alts, typename... Ts>
293constexpr bool holds_any_of(const std::variant<Ts...>& v) noexcept {
294 return (std::holds_alternative<Alts>(v) || ...);
295}
296
297template <typename GeneralVariantT, typename SpecialT>
298constexpr bool is_generalizable_to(const SpecialT&) noexcept {
299 return std::is_constructible_v<GeneralVariantT, SpecialT>;
300}
301
302template <typename GeneralVariantT, typename... SpecialTs>
303constexpr bool is_generalizable_to(const std::variant<SpecialTs...>&) noexcept {
304 return (std::is_constructible_v<GeneralVariantT, SpecialTs> && ...);
305}
306
307/**
308 * @brief Converts a given variant into another variant-ish whose type states
309 * are a super set of the given variant.
310 *
311 * This is useful to convert restricted variant types into more general
312 * variants types.
313 */
314template <typename GeneralVariantT, typename SpecialT>
315constexpr GeneralVariantT generalize_to(SpecialT&& specific) noexcept
316 requires(std::is_constructible_v<GeneralVariantT, std::decay_t<SpecialT>>)
317{
318 return std::forward<SpecialT>(specific);
319}
320
321/**
322 * @brief Converts a given variant into another variant-ish whose type states
323 * are a super set of the given variant.
324 *
325 * This is useful to convert restricted variant types into more general
326 * variants types.
327 */
328template <typename GeneralVariantT, typename... SpecialTs>
329constexpr GeneralVariantT generalize_to(std::variant<SpecialTs...> specific) noexcept {
330 static_assert(
332 "Desired general type must be implicitly constructible by all types of the specialized std::variant<>");
333 return std::visit([](auto s) -> GeneralVariantT { return s; }, std::move(specific));
334}
335
336// This is a helper utility to emulate pattern matching with std::visit.
337// See https://en.cppreference.com/w/cpp/utility/variant/visit for more info.
338template <class... Ts>
339struct overloaded : Ts... {
340 using Ts::operator()...;
341};
342// explicit deduction guide (not needed as of C++20)
343template <class... Ts>
344overloaded(Ts...) -> overloaded<Ts...>;
345
346/**
347 * @brief Helper class to create a RAII-style cleanup callback
348 *
349 * Ensures that the cleanup callback given in the object's constructor is called
350 * when the object is destroyed. Use this to ensure some cleanup code runs when
351 * leaving the current scope.
352 */
353template <std::invocable FunT>
355 public:
356 explicit scoped_cleanup(FunT cleanup) : m_cleanup(std::move(cleanup)) {}
357
360
361 scoped_cleanup(scoped_cleanup&& other) : m_cleanup(std::move(other.m_cleanup)) { other.disengage(); }
362
364 if(this != &other) {
365 m_cleanup = std::move(other.m_cleanup);
366 other.disengage();
367 }
368 return *this;
369 }
370
372 if(m_cleanup.has_value()) {
373 m_cleanup.value()();
374 }
375 }
376
377 /**
378 * Disengage the cleanup callback, i.e., prevent it from being called
379 */
380 void disengage() { m_cleanup.reset(); }
381
382 private:
383 std::optional<FunT> m_cleanup;
384};
385
386/**
387* Define BOTAN_ASSERT_IS_SOME
388*/
389template <typename T>
390T assert_is_some(std::optional<T> v, const char* expr, const char* func, const char* file, int line) {
391 if(v) {
392 return *v;
393 } else {
394 Botan::assertion_failure(expr, "optional had value", func, file, line);
395 }
396}
397
398#define BOTAN_ASSERT_IS_SOME(v) assert_is_some(v, #v, __func__, __FILE__, __LINE__)
399
400/*
401 * @brief Helper class to pass literal strings to C++ templates
402 */
403template <size_t N>
405 public:
406 constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); }
407
408 char value[N];
409};
410
411// TODO: C++23: replace with std::to_underlying
412template <typename T>
413 requires std::is_enum_v<T>
414auto to_underlying(T e) noexcept {
415 return static_cast<std::underlying_type_t<T>>(e);
416}
417
418// TODO: C++23 - use std::out_ptr
419template <typename T>
420[[nodiscard]] constexpr auto out_ptr(T& outptr) noexcept {
421 class out_ptr_t {
422 public:
423 constexpr ~out_ptr_t() noexcept {
424 m_ptr.reset(m_rawptr);
425 m_rawptr = nullptr;
426 }
427
428 constexpr out_ptr_t(T& outptr) noexcept : m_ptr(outptr), m_rawptr(nullptr) {}
429
430 out_ptr_t(const out_ptr_t&) = delete;
431 out_ptr_t(out_ptr_t&&) = delete;
432 out_ptr_t& operator=(const out_ptr_t&) = delete;
433 out_ptr_t& operator=(out_ptr_t&&) = delete;
434
435 [[nodiscard]] constexpr operator typename T::element_type **() && noexcept { return &m_rawptr; }
436
437 private:
438 T& m_ptr;
439 typename T::element_type* m_rawptr;
440 };
441
442 return out_ptr_t{outptr};
443}
444
445template <typename T>
446 requires std::is_default_constructible_v<T>
447[[nodiscard]] constexpr auto out_opt(std::optional<T>& outopt) noexcept {
448 class out_opt_t {
449 public:
450 constexpr ~out_opt_t() noexcept { m_opt = m_raw; }
451
452 constexpr out_opt_t(std::optional<T>& outopt) noexcept : m_opt(outopt) {}
453
454 out_opt_t(const out_opt_t&) = delete;
455 out_opt_t(out_opt_t&&) = delete;
456 out_opt_t& operator=(const out_opt_t&) = delete;
457 out_opt_t& operator=(out_opt_t&&) = delete;
458
459 [[nodiscard]] constexpr operator T*() && noexcept { return &m_raw; }
460
461 private:
462 std::optional<T>& m_opt;
463 T m_raw;
464 };
465
466 return out_opt_t{outopt};
467}
468
469} // namespace Botan
470
471#endif
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:41
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
size_t remaining() const
Definition stl_util.h:127
void skip(const size_t count)
Definition stl_util.h:125
auto copy_as_secure_vector(const size_t count)
Definition stl_util.h:96
void copy_into(std::span< uint8_t > sink)
Definition stl_util.h:120
uint8_t take_byte()
Definition stl_util.h:118
BufferSlicer(std::span< const uint8_t > buffer)
Definition stl_util.h:86
std::span< const uint8_t, count > take()
Definition stl_util.h:106
auto copy(const size_t count)
Definition stl_util.h:89
bool empty() const
Definition stl_util.h:129
std::span< const uint8_t > take(const size_t count)
Definition stl_util.h:98
StrongSpan< const T > take(const size_t count)
Definition stl_util.h:114
auto copy_as_vector(const size_t count)
Definition stl_util.h:94
Helper class to ease in-place marshalling of concatenated fixed-length values.
Definition stl_util.h:142
constexpr void append(std::span< const uint8_t > buffer)
Definition stl_util.h:177
constexpr void append(uint8_t b, size_t repeat=1)
Definition stl_util.h:182
constexpr size_t remaining_capacity() const
Definition stl_util.h:189
StrongSpan< StrongT > next(size_t bytes)
Definition stl_util.h:168
constexpr std::span< uint8_t > next(size_t bytes)
Definition stl_util.h:150
constexpr std::span< uint8_t, bytes > next()
Definition stl_util.h:159
constexpr uint8_t & next_byte()
Definition stl_util.h:175
constexpr BufferStuffer(std::span< uint8_t > buffer)
Definition stl_util.h:144
constexpr bool full() const
Definition stl_util.h:187
constexpr StringLiteral(const char(&str)[N])
Definition stl_util.h:406
Helper class to create a RAII-style cleanup callback.
Definition stl_util.h:354
scoped_cleanup(const scoped_cleanup &)=delete
scoped_cleanup & operator=(const scoped_cleanup &)=delete
scoped_cleanup(FunT cleanup)
Definition stl_util.h:356
scoped_cleanup & operator=(scoped_cleanup &&other)
Definition stl_util.h:363
scoped_cleanup(scoped_cleanup &&other)
Definition stl_util.h:361
int(* final)(unsigned char *, CTX *)
FE_25519 T
Definition ge.cpp:34
constexpr OutR concatenate(Rs &&... ranges)
Definition stl_util.h:204
constexpr GeneralVariantT generalize_to(SpecialT &&specific) noexcept
Converts a given variant into another variant-ish whose type states are a super set of the given vari...
Definition stl_util.h:315
void map_remove_if(Pred pred, T &assoc)
Definition stl_util.h:70
T to_byte_vector(std::string_view s)
Definition stl_util.h:29
constexpr auto out_ptr(T &outptr) noexcept
Definition stl_util.h:420
constexpr bool holds_any_of(const std::variant< Ts... > &v) noexcept
Definition stl_util.h:293
RetT reduce(const std::vector< KeyT > &keys, RetT acc, ReducerT reducer)
Definition stl_util.h:47
constexpr decltype(auto) unwrap_strong_type(T &&t)
Generically unwraps a strong type to its underlying type.
constexpr auto out_opt(std::optional< T > &outopt) noexcept
Definition stl_util.h:447
T assert_is_some(std::optional< T > v, const char *expr, const char *func, const char *file, int line)
Definition stl_util.h:390
constexpr auto concat(Rs &&... ranges)
Definition stl_util.h:263
bool value_exists(const std::vector< T > &vec, const OT &val)
Definition stl_util.h:60
const SIMD_8x32 & b
constexpr bool is_generalizable_to(const SpecialT &) noexcept
Definition stl_util.h:298
std::string to_string(ErrorType type)
Convert an ErrorType to string.
Definition exceptn.cpp:13
auto to_underlying(T e) noexcept
Definition stl_util.h:414
void assertion_failure(const char *expr_str, const char *assertion_made, const char *func, const char *file, int line)
Definition assert.cpp:29
overloaded(Ts...) -> overloaded< Ts... >