Botan 3.5.0
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(std::ranges::begin(range), std::ranges::end(range), std::back_inserter(unpack(result)));
221 };
222 } else {
223 if constexpr((ranges::statically_spanable_range<Rs> && ... && true)) {
224 // all input ranges have a static extent, so check the total size at compile time
225 // (work around an issue in MSVC that warns `total_size` is unused)
226 [[maybe_unused]] constexpr size_t total_size = (decltype(std::span{ranges})::extent + ... + 0);
227 static_assert(result.size() == total_size, "size of result buffer does not match the sum of input buffers");
228 } else {
229 // at least one input range has a dynamic extent, so check the total size at runtime
230 const size_t total_size = (ranges.size() + ... + 0);
231 BOTAN_ARG_CHECK(result.size() == total_size,
232 "result buffer has static extent that does not match the sum of input buffers");
233 }
234
235 // fill the result buffer and hold the current output-iterator position
236 return [itr = std::ranges::begin(result)](auto&& range) mutable {
237 std::copy(std::ranges::begin(range), std::ranges::end(range), itr);
238 std::advance(itr, std::ranges::size(range));
239 };
240 }
241 }();
242
243 // perform the actual concatenation
244 (fill_fn(std::forward<Rs>(ranges)), ...);
245
246 return result;
247}
248
249} // namespace detail
250
251/**
252 * Concatenate an arbitrary number of buffers. Performs range-checks as needed.
253 *
254 * The output type can be auto-detected based on the input ranges, or explicitly
255 * specified by the caller. If all input ranges have a static extent, the total
256 * size is calculated at compile time and a statically sized std::array<> is used.
257 * Otherwise this tries to use the type of the first input range as output type.
258 *
259 * Alternatively, the output container type can be specified explicitly.
260 */
261template <typename OutR = detail::AutoDetect, ranges::spanable_range... Rs>
262constexpr auto concat(Rs&&... ranges)
263 requires(all_same_v<std::ranges::range_value_t<Rs>...>)
264{
265 if constexpr(std::same_as<detail::AutoDetect, OutR>) {
266 // Try to auto-detect a reasonable output type given the input ranges
267 static_assert(sizeof...(Rs) > 0, "Cannot auto-detect the output type if not a single input range is provided.");
268 using candidate_result_t = std::remove_cvref_t<std::tuple_element_t<0, std::tuple<Rs...>>>;
269 using result_range_value_t = std::remove_cvref_t<std::ranges::range_value_t<candidate_result_t>>;
270
271 if constexpr((ranges::statically_spanable_range<Rs> && ...)) {
272 // If all input ranges have a static extent, we can calculate the total size at compile time
273 // and therefore can use a statically sized output container. This is constexpr.
274 constexpr size_t total_size = (decltype(std::span{ranges})::extent + ... + 0);
275 using out_array_t = std::array<result_range_value_t, total_size>;
276 return detail::concatenate<out_array_t>(std::forward<Rs>(ranges)...);
277 } else {
278 // If at least one input range has a dynamic extent, we must use a dynamically allocated output container.
279 // We assume that the user wants to use the first input range's container type as output type.
280 static_assert(
282 "First input range has static extent, but a dynamically allocated output range is required. Please explicitly specify a dynamically allocatable output type.");
283 return detail::concatenate<candidate_result_t>(std::forward<Rs>(ranges)...);
284 }
285 } else {
286 // The caller has explicitly specified the output type
287 return detail::concatenate<OutR>(std::forward<Rs>(ranges)...);
288 }
289}
290
291template <typename... Alts, typename... Ts>
292constexpr bool holds_any_of(const std::variant<Ts...>& v) noexcept {
293 return (std::holds_alternative<Alts>(v) || ...);
294}
295
296template <typename GeneralVariantT, typename SpecialT>
297constexpr bool is_generalizable_to(const SpecialT&) noexcept {
298 return std::is_constructible_v<GeneralVariantT, SpecialT>;
299}
300
301template <typename GeneralVariantT, typename... SpecialTs>
302constexpr bool is_generalizable_to(const std::variant<SpecialTs...>&) noexcept {
303 return (std::is_constructible_v<GeneralVariantT, SpecialTs> && ...);
304}
305
306/**
307 * @brief Converts a given variant into another variant-ish whose type states
308 * are a super set of the given variant.
309 *
310 * This is useful to convert restricted variant types into more general
311 * variants types.
312 */
313template <typename GeneralVariantT, typename SpecialT>
314constexpr GeneralVariantT generalize_to(SpecialT&& specific) noexcept
315 requires(std::is_constructible_v<GeneralVariantT, std::decay_t<SpecialT>>)
316{
317 return std::forward<SpecialT>(specific);
318}
319
320/**
321 * @brief Converts a given variant into another variant-ish whose type states
322 * are a super set of the given variant.
323 *
324 * This is useful to convert restricted variant types into more general
325 * variants types.
326 */
327template <typename GeneralVariantT, typename... SpecialTs>
328constexpr GeneralVariantT generalize_to(std::variant<SpecialTs...> specific) noexcept {
329 static_assert(
331 "Desired general type must be implicitly constructible by all types of the specialized std::variant<>");
332 return std::visit([](auto s) -> GeneralVariantT { return s; }, std::move(specific));
333}
334
335// This is a helper utility to emulate pattern matching with std::visit.
336// See https://en.cppreference.com/w/cpp/utility/variant/visit for more info.
337template <class... Ts>
338struct overloaded : Ts... {
339 using Ts::operator()...;
340};
341// explicit deduction guide (not needed as of C++20)
342template <class... Ts>
343overloaded(Ts...) -> overloaded<Ts...>;
344
345/**
346 * @brief Helper class to create a RAII-style cleanup callback
347 *
348 * Ensures that the cleanup callback given in the object's constructor is called
349 * when the object is destroyed. Use this to ensure some cleanup code runs when
350 * leaving the current scope.
351 */
352template <std::invocable FunT>
354 public:
355 explicit scoped_cleanup(FunT cleanup) : m_cleanup(std::move(cleanup)) {}
356
361
363 if(m_cleanup.has_value()) {
364 m_cleanup.value()();
365 }
366 }
367
368 /**
369 * Disengage the cleanup callback, i.e., prevent it from being called
370 */
371 void disengage() { m_cleanup.reset(); }
372
373 private:
374 std::optional<FunT> m_cleanup;
375};
376
377/**
378* Define BOTAN_ASSERT_IS_SOME
379*/
380template <typename T>
381T assert_is_some(std::optional<T> v, const char* expr, const char* func, const char* file, int line) {
382 if(v) {
383 return *v;
384 } else {
385 Botan::assertion_failure(expr, "optional had value", func, file, line);
386 }
387}
388
389#define BOTAN_ASSERT_IS_SOME(v) assert_is_some(v, #v, __func__, __FILE__, __LINE__)
390
391/*
392 * @brief Helper class to pass literal strings to C++ templates
393 */
394template <size_t N>
396 public:
397 constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); }
398
399 char value[N];
400};
401
402} // namespace Botan
403
404#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:397
Helper class to create a RAII-style cleanup callback.
Definition stl_util.h:353
scoped_cleanup(const scoped_cleanup &)=delete
scoped_cleanup & operator=(const scoped_cleanup &)=delete
scoped_cleanup & operator=(scoped_cleanup &&)=delete
scoped_cleanup(FunT cleanup)
Definition stl_util.h:355
scoped_cleanup(scoped_cleanup &&)=delete
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:314
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 bool holds_any_of(const std::variant< Ts... > &v) noexcept
Definition stl_util.h:292
RetT reduce(const std::vector< KeyT > &keys, RetT acc, ReducerT reducer)
Definition stl_util.h:47
T assert_is_some(std::optional< T > v, const char *expr, const char *func, const char *file, int line)
Definition stl_util.h:381
constexpr auto concat(Rs &&... ranges)
Definition stl_util.h:262
bool value_exists(const std::vector< T > &vec, const OT &val)
Definition stl_util.h:60
constexpr bool is_generalizable_to(const SpecialT &) noexcept
Definition stl_util.h:297
constexpr decltype(auto) unpack(T &t)
std::string to_string(ErrorType type)
Convert an ErrorType to string.
Definition exceptn.cpp:13
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... >