Botan 3.4.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*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#ifndef BOTAN_STL_UTIL_H_
10#define BOTAN_STL_UTIL_H_
11
12#include <algorithm>
13#include <functional>
14#include <map>
15#include <optional>
16#include <set>
17#include <span>
18#include <string>
19#include <tuple>
20#include <variant>
21#include <vector>
22
23#include <botan/concepts.h>
24#include <botan/secmem.h>
25#include <botan/strong_type.h>
26
27namespace Botan {
28
29template <concepts::contiguous_container T = std::vector<uint8_t>>
30inline T to_byte_vector(std::string_view s) {
31 return T(s.cbegin(), s.cend());
32}
33
34inline std::string to_string(std::span<const uint8_t> bytes) {
35 return std::string(bytes.begin(), bytes.end());
36}
37
38/**
39 * Reduce the values of @p keys into an accumulator initialized with @p acc using
40 * the reducer function @p reducer.
41 *
42 * The @p reducer is a function taking the accumulator and a single key to return the
43 * new accumulator. Keys are consecutively reduced into the accumulator.
44 *
45 * @return the accumulator containing the reduction of @p keys
46 */
47template <typename RetT, typename KeyT, typename ReducerT>
48RetT reduce(const std::vector<KeyT>& keys, RetT acc, ReducerT reducer)
49 requires std::is_convertible_v<ReducerT, std::function<RetT(RetT, const KeyT&)>>
50{
51 for(const KeyT& key : keys) {
52 acc = reducer(std::move(acc), key);
53 }
54 return acc;
55}
56
57/**
58* Return the keys of a map as a std::set
59*/
60template <typename K, typename V>
61std::set<K> map_keys_as_set(const std::map<K, V>& kv) {
62 std::set<K> s;
63 for(auto&& i : kv) {
64 s.insert(i.first);
65 }
66 return s;
67}
68
69/**
70* Return the keys of a multimap as a std::set
71*/
72template <typename K, typename V>
73std::set<K> map_keys_as_set(const std::multimap<K, V>& kv) {
74 std::set<K> s;
75 for(auto&& i : kv) {
76 s.insert(i.first);
77 }
78 return s;
79}
80
81/*
82* Searching through a std::map
83* @param mapping the map to search
84* @param key is what to look for
85* @param null_result is the value to return if key is not in mapping
86* @return mapping[key] or null_result
87*/
88template <typename K, typename V>
89inline V search_map(const std::map<K, V>& mapping, const K& key, const V& null_result = V()) {
90 auto i = mapping.find(key);
91 if(i == mapping.end()) {
92 return null_result;
93 }
94 return i->second;
95}
96
97template <typename K, typename V, typename R>
98inline R search_map(const std::map<K, V>& mapping, const K& key, const R& null_result, const R& found_result) {
99 auto i = mapping.find(key);
100 if(i == mapping.end()) {
101 return null_result;
102 }
103 return found_result;
104}
105
106/*
107* Insert a key/value pair into a multimap
108*/
109template <typename K, typename V>
110void multimap_insert(std::multimap<K, V>& multimap, const K& key, const V& value) {
111 multimap.insert(std::make_pair(key, value));
112}
113
114/**
115* Existence check for values
116*/
117template <typename T, typename OT>
118bool value_exists(const std::vector<T>& vec, const OT& val) {
119 for(size_t i = 0; i != vec.size(); ++i) {
120 if(vec[i] == val) {
121 return true;
122 }
123 }
124 return false;
125}
126
127template <typename T, typename Pred>
128void map_remove_if(Pred pred, T& assoc) {
129 auto i = assoc.begin();
130 while(i != assoc.end()) {
131 if(pred(i->first)) {
132 assoc.erase(i++);
133 } else {
134 i++;
135 }
136 }
137}
138
139/**
140 * Helper class to ease unmarshalling of concatenated fixed-length values
141 */
143 public:
144 BufferSlicer(std::span<const uint8_t> buffer) : m_remaining(buffer) {}
145
146 template <concepts::contiguous_container ContainerT>
147 auto copy(const size_t count) {
148 const auto result = take(count);
149 return ContainerT(result.begin(), result.end());
150 }
151
152 auto copy_as_vector(const size_t count) { return copy<std::vector<uint8_t>>(count); }
153
154 auto copy_as_secure_vector(const size_t count) { return copy<secure_vector<uint8_t>>(count); }
155
156 std::span<const uint8_t> take(const size_t count) {
157 BOTAN_STATE_CHECK(remaining() >= count);
158 auto result = m_remaining.first(count);
159 m_remaining = m_remaining.subspan(count);
160 return result;
161 }
162
163 template <size_t count>
164 std::span<const uint8_t, count> take() {
165 BOTAN_STATE_CHECK(remaining() >= count);
166 auto result = m_remaining.first<count>();
167 m_remaining = m_remaining.subspan(count);
168 return result;
169 }
170
171 template <concepts::contiguous_strong_type T>
172 StrongSpan<const T> take(const size_t count) {
173 return StrongSpan<const T>(take(count));
174 }
175
176 uint8_t take_byte() { return take(1)[0]; }
177
178 void copy_into(std::span<uint8_t> sink) {
179 const auto data = take(sink.size());
180 std::copy(data.begin(), data.end(), sink.begin());
181 }
182
183 void skip(const size_t count) { take(count); }
184
185 size_t remaining() const { return m_remaining.size(); }
186
187 bool empty() const { return m_remaining.empty(); }
188
189 private:
190 std::span<const uint8_t> m_remaining;
191};
192
193/**
194 * @brief Helper class to ease in-place marshalling of concatenated fixed-length
195 * values.
196 *
197 * The size of the final buffer must be known from the start, reallocations are
198 * not performed.
199 */
201 public:
202 constexpr BufferStuffer(std::span<uint8_t> buffer) : m_buffer(buffer) {}
203
204 /**
205 * @returns a span for the next @p bytes bytes in the concatenated buffer.
206 * Checks that the buffer is not exceded.
207 */
208 constexpr std::span<uint8_t> next(size_t bytes) {
209 BOTAN_STATE_CHECK(m_buffer.size() >= bytes);
210
211 auto result = m_buffer.first(bytes);
212 m_buffer = m_buffer.subspan(bytes);
213 return result;
214 }
215
216 template <size_t bytes>
217 constexpr std::span<uint8_t, bytes> next() {
218 BOTAN_STATE_CHECK(m_buffer.size() >= bytes);
219
220 auto result = m_buffer.first<bytes>();
221 m_buffer = m_buffer.subspan(bytes);
222 return result;
223 }
224
225 template <concepts::contiguous_strong_type StrongT>
226 StrongSpan<StrongT> next(size_t bytes) {
227 return StrongSpan<StrongT>(next(bytes));
228 }
229
230 /**
231 * @returns a reference to the next single byte in the buffer
232 */
233 constexpr uint8_t& next_byte() { return next(1)[0]; }
234
235 constexpr void append(std::span<const uint8_t> buffer) {
236 auto sink = next(buffer.size());
237 std::copy(buffer.begin(), buffer.end(), sink.begin());
238 }
239
240 constexpr void append(uint8_t b, size_t repeat = 1) {
241 auto sink = next(repeat);
242 std::fill(sink.begin(), sink.end(), b);
243 }
244
245 constexpr bool full() const { return m_buffer.empty(); }
246
247 constexpr size_t remaining_capacity() const { return m_buffer.size(); }
248
249 private:
250 std::span<uint8_t> m_buffer;
251};
252
253/**
254 * Concatenate an arbitrary number of buffers.
255 * @return the concatenation of \p buffers as the container type of the first buffer
256 */
257template <typename... Ts>
258decltype(auto) concat(Ts&&... buffers) {
259 static_assert(sizeof...(buffers) > 0, "concat requires at least one buffer");
260
261 using result_t = std::remove_cvref_t<std::tuple_element_t<0, std::tuple<Ts...>>>;
262 result_t result;
263 result.reserve((buffers.size() + ...));
264 (result.insert(result.end(), buffers.begin(), buffers.end()), ...);
265 return result;
266}
267
268/**
269 * Concatenate an arbitrary number of buffers and define the output buffer
270 * type as a mandatory template parameter.
271 * @return the concatenation of \p buffers as the user-defined container type
272 */
273template <typename ResultT, typename... Ts>
274ResultT concat_as(Ts&&... buffers) {
275 return concat(ResultT(), std::forward<Ts>(buffers)...);
276}
277
278template <typename... Alts, typename... Ts>
279constexpr bool holds_any_of(const std::variant<Ts...>& v) noexcept {
280 return (std::holds_alternative<Alts>(v) || ...);
281}
282
283template <typename GeneralVariantT, typename SpecialT>
284constexpr bool is_generalizable_to(const SpecialT&) noexcept {
285 return std::is_constructible_v<GeneralVariantT, SpecialT>;
286}
287
288template <typename GeneralVariantT, typename... SpecialTs>
289constexpr bool is_generalizable_to(const std::variant<SpecialTs...>&) noexcept {
290 return (std::is_constructible_v<GeneralVariantT, SpecialTs> && ...);
291}
292
293/**
294 * @brief Converts a given variant into another variant-ish whose type states
295 * are a super set of the given variant.
296 *
297 * This is useful to convert restricted variant types into more general
298 * variants types.
299 */
300template <typename GeneralVariantT, typename SpecialT>
301constexpr GeneralVariantT generalize_to(SpecialT&& specific) noexcept
302 requires(std::is_constructible_v<GeneralVariantT, std::decay_t<SpecialT>>)
303{
304 return std::forward<SpecialT>(specific);
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... SpecialTs>
315constexpr GeneralVariantT generalize_to(std::variant<SpecialTs...> specific) noexcept {
316 static_assert(
317 is_generalizable_to<GeneralVariantT>(specific),
318 "Desired general type must be implicitly constructible by all types of the specialized std::variant<>");
319 return std::visit([](auto s) -> GeneralVariantT { return s; }, std::move(specific));
320}
321
322// This is a helper utility to emulate pattern matching with std::visit.
323// See https://en.cppreference.com/w/cpp/utility/variant/visit for more info.
324template <class... Ts>
325struct overloaded : Ts... {
326 using Ts::operator()...;
327};
328// explicit deduction guide (not needed as of C++20)
329template <class... Ts>
330overloaded(Ts...) -> overloaded<Ts...>;
331
332/**
333 * @brief Helper class to create a RAII-style cleanup callback
334 *
335 * Ensures that the cleanup callback given in the object's constructor is called
336 * when the object is destroyed. Use this to ensure some cleanup code runs when
337 * leaving the current scope.
338 */
339template <std::invocable FunT>
341 public:
342 explicit scoped_cleanup(FunT cleanup) : m_cleanup(std::move(cleanup)) {}
343
348
350 if(m_cleanup.has_value()) {
351 m_cleanup.value()();
352 }
353 }
354
355 /**
356 * Disengage the cleanup callback, i.e., prevent it from being called
357 */
358 void disengage() { m_cleanup.reset(); }
359
360 private:
361 std::optional<FunT> m_cleanup;
362};
363
364} // namespace Botan
365
366#endif
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:41
size_t remaining() const
Definition stl_util.h:185
void skip(const size_t count)
Definition stl_util.h:183
auto copy_as_secure_vector(const size_t count)
Definition stl_util.h:154
void copy_into(std::span< uint8_t > sink)
Definition stl_util.h:178
uint8_t take_byte()
Definition stl_util.h:176
BufferSlicer(std::span< const uint8_t > buffer)
Definition stl_util.h:144
std::span< const uint8_t, count > take()
Definition stl_util.h:164
auto copy(const size_t count)
Definition stl_util.h:147
bool empty() const
Definition stl_util.h:187
std::span< const uint8_t > take(const size_t count)
Definition stl_util.h:156
StrongSpan< const T > take(const size_t count)
Definition stl_util.h:172
auto copy_as_vector(const size_t count)
Definition stl_util.h:152
Helper class to ease in-place marshalling of concatenated fixed-length values.
Definition stl_util.h:200
constexpr void append(std::span< const uint8_t > buffer)
Definition stl_util.h:235
constexpr void append(uint8_t b, size_t repeat=1)
Definition stl_util.h:240
constexpr size_t remaining_capacity() const
Definition stl_util.h:247
StrongSpan< StrongT > next(size_t bytes)
Definition stl_util.h:226
constexpr std::span< uint8_t > next(size_t bytes)
Definition stl_util.h:208
constexpr std::span< uint8_t, bytes > next()
Definition stl_util.h:217
constexpr uint8_t & next_byte()
Definition stl_util.h:233
constexpr BufferStuffer(std::span< uint8_t > buffer)
Definition stl_util.h:202
constexpr bool full() const
Definition stl_util.h:245
Helper class to create a RAII-style cleanup callback.
Definition stl_util.h:340
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:342
scoped_cleanup(scoped_cleanup &&)=delete
int(* final)(unsigned char *, CTX *)
FE_25519 T
Definition ge.cpp:34
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:301
void map_remove_if(Pred pred, T &assoc)
Definition stl_util.h:128
T to_byte_vector(std::string_view s)
Definition stl_util.h:30
ResultT concat_as(Ts &&... buffers)
Definition stl_util.h:274
constexpr bool holds_any_of(const std::variant< Ts... > &v) noexcept
Definition stl_util.h:279
RetT reduce(const std::vector< KeyT > &keys, RetT acc, ReducerT reducer)
Definition stl_util.h:48
std::set< K > map_keys_as_set(const std::map< K, V > &kv)
Definition stl_util.h:61
V search_map(const std::map< K, V > &mapping, const K &key, const V &null_result=V())
Definition stl_util.h:89
void multimap_insert(std::multimap< K, V > &multimap, const K &key, const V &value)
Definition stl_util.h:110
bool value_exists(const std::vector< T > &vec, const OT &val)
Definition stl_util.h:118
constexpr bool is_generalizable_to(const SpecialT &) noexcept
Definition stl_util.h:284
std::string to_string(ErrorType type)
Convert an ErrorType to string.
Definition exceptn.cpp:13
decltype(auto) concat(Ts &&... buffers)
Definition stl_util.h:258
overloaded(Ts...) -> overloaded< Ts... >