Botan 3.8.1
Crypto and TLS for C&
concepts.h
Go to the documentation of this file.
1/**
2 * Useful concepts that are available throughout the library
3 * (C) 2023 Jack Lloyd
4 * 2023 René Meusel - Rohde & Schwarz Cybersecurity
5 *
6 * Botan is released under the Simplified BSD License (see license.txt)
7 */
8
9#ifndef BOTAN_CONCEPTS_H_
10#define BOTAN_CONCEPTS_H_
11
12#include <botan/exceptn.h>
13
14#include <concepts>
15#include <cstdint>
16#include <iosfwd>
17#include <ranges>
18#include <span>
19#include <type_traits>
20
21namespace Botan {
22
23template <typename T, typename Tag, typename... Capabilities>
24class Strong;
25
26template <typename... Ts>
27struct is_strong_type : std::false_type {};
28
29template <typename... Ts>
30struct is_strong_type<Strong<Ts...>> : std::true_type {};
31
32template <typename... Ts>
34
35template <typename T0 = void, typename... Ts>
36struct all_same {
37 static constexpr bool value = (std::is_same_v<T0, Ts> && ... && true);
38};
39
40template <typename... Ts>
41static constexpr bool all_same_v = all_same<Ts...>::value;
42
43namespace detail {
44
45/**
46 * Helper type to indicate that a certain type should be automatically
47 * detected based on the context.
48 */
49struct AutoDetect {
50 constexpr AutoDetect() = delete;
51};
52
53} // namespace detail
54
55namespace ranges {
56
57/**
58 * Models a std::ranges::contiguous_range that (optionally) restricts its
59 * value_type to ValueT. In other words: a stretch of contiguous memory of
60 * a certain type (optional ValueT).
61 */
62template <typename T, typename ValueT = std::ranges::range_value_t<T>>
63concept contiguous_range = std::ranges::contiguous_range<T> && std::same_as<ValueT, std::ranges::range_value_t<T>>;
64
65/**
66 * Models a std::ranges::contiguous_range that satisfies
67 * std::ranges::output_range with an arbitrary value_type. In other words: a
68 * stretch of contiguous memory of a certain type (optional ValueT) that can be
69 * written to.
70 */
71template <typename T, typename ValueT = std::ranges::range_value_t<T>>
72concept contiguous_output_range = contiguous_range<T, ValueT> && std::ranges::output_range<T, ValueT>;
73
74/**
75 * Models a range that can be turned into a std::span<>. Typically, this is some
76 * form of ranges::contiguous_range.
77 */
78template <typename T>
79concept spanable_range = std::constructible_from<std::span<const std::ranges::range_value_t<T>>, T>;
80
81/**
82 * Models a range that can be turned into a std::span<> with a static extent.
83 * Typically, this is a std::array or a std::span derived from an array.
84 */
85// clang-format off
86template <typename T>
88 decltype(std::span{std::declval<T&>()})::extent != std::dynamic_extent;
89
90// clang-format on
91
92/**
93 * Find the length in bytes of a given contiguous range @p r.
94 */
95inline constexpr size_t size_bytes(spanable_range auto&& r) {
96 return std::span{r}.size_bytes();
97}
98
99/**
100 * Check that a given range @p r has a certain statically-known byte length. If
101 * the range's extent is known at compile time, this is a static check,
102 * otherwise a runtime argument check will be added.
103 *
104 * @throws Invalid_Argument if range @p r has a dynamic extent and does not
105 * feature the expected byte length.
106 */
107template <size_t expected, spanable_range R>
108inline constexpr void assert_exact_byte_length(R&& r) {
109 const std::span s{r};
110 if constexpr(statically_spanable_range<R>) {
111 static_assert(s.size_bytes() == expected, "memory region does not have expected byte lengths");
112 } else {
113 if(s.size_bytes() != expected) {
114 throw Invalid_Argument("Memory regions did not have expected byte lengths");
115 }
116 }
117}
118
119/**
120 * Check that a list of ranges (in @p r0 and @p rs) all have the same byte
121 * lengths. If the first range's extent is known at compile time, this will be a
122 * static check for all other ranges whose extents are known at compile time,
123 * otherwise a runtime argument check will be added.
124 *
125 * @throws Invalid_Argument if any range has a dynamic extent and not all
126 * ranges feature the same byte length.
127 */
128template <spanable_range R0, spanable_range... Rs>
129inline constexpr void assert_equal_byte_lengths(R0&& r0, Rs&&... rs)
130 requires(sizeof...(Rs) > 0)
131{
132 const std::span s0{r0};
133
134 if constexpr(statically_spanable_range<R0>) {
135 constexpr size_t expected_size = s0.size_bytes();
137 } else {
138 const size_t expected_size = s0.size_bytes();
139 const bool correct_size =
140 ((std::span<const std::ranges::range_value_t<Rs>>{rs}.size_bytes() == expected_size) && ...);
141
142 if(!correct_size) {
143 throw Invalid_Argument("Memory regions did not have equal lengths");
144 }
145 }
146}
147
148} // namespace ranges
149
150namespace concepts {
151
152// TODO: C++20 provides concepts like std::ranges::range or ::sized_range
153// but at the time of this writing clang had not caught up on all
154// platforms. E.g. clang 14 on Xcode does not support ranges properly.
155
156template <typename IterT, typename ContainerT>
158 std::same_as<IterT, typename ContainerT::iterator> || std::same_as<IterT, typename ContainerT::const_iterator>;
159
160template <typename PtrT, typename ContainerT>
162 std::same_as<PtrT, typename ContainerT::pointer> || std::same_as<PtrT, typename ContainerT::const_pointer>;
163
164template <typename T>
165concept container = requires(T a) {
166 { a.begin() } -> container_iterator<T>;
167 { a.end() } -> container_iterator<T>;
168 { a.cbegin() } -> container_iterator<T>;
169 { a.cend() } -> container_iterator<T>;
170 { a.size() } -> std::same_as<typename T::size_type>;
171 typename T::value_type;
172};
173
174template <typename T>
175concept contiguous_container = container<T> && requires(T a) {
176 { a.data() } -> container_pointer<T>;
177};
178
179template <typename T>
180concept has_empty = requires(T a) {
181 { a.empty() } -> std::same_as<bool>;
182};
183
184// clang-format off
185template <typename T>
187 requires(T a, const T ac, typename T::size_type s) {
188 { a.at(s) } -> std::same_as<typename T::value_type&>;
189 { ac.at(s) } -> std::same_as<const typename T::value_type&>;
190 } ||
191 requires(T a, const T ac, typename T::key_type k) {
192 { a.at(k) } -> std::same_as<typename T::mapped_type&>;
193 { ac.at(k) } -> std::same_as<const typename T::mapped_type&>;
194 });
195// clang-format on
196
197template <typename T>
198concept resizable_container = container<T> && requires(T& c, typename T::size_type s) {
199 T(s);
200 c.resize(s);
201};
202
203template <typename T>
204concept reservable_container = container<T> && requires(T& c, typename T::size_type s) { c.reserve(s); };
205
206template <typename T>
208 contiguous_container<T> && resizable_container<T> && std::same_as<typename T::value_type, uint8_t>;
209
210template <typename T>
211concept streamable = requires(std::ostream& os, T a) { os << a; };
212
213template <class T>
215
216template <class T>
218
219template <class T>
220concept integral_strong_type = strong_type<T> && std::integral<typename T::wrapped_type>;
221
222template <class T>
223concept unsigned_integral_strong_type = strong_type<T> && std::unsigned_integral<typename T::wrapped_type>;
224
225template <typename T, typename Capability>
226concept strong_type_with_capability = T::template has_capability<Capability>();
227
228} // namespace concepts
229
230} // namespace Botan
231
232#endif
constexpr void assert_exact_byte_length(R &&r)
Definition concepts.h:108
constexpr size_t size_bytes(spanable_range auto &&r)
Definition concepts.h:95
constexpr void assert_equal_byte_lengths(R0 &&r0, Rs &&... rs)
Definition concepts.h:129
constexpr bool is_strong_type_v
Definition concepts.h:33
static constexpr bool value
Definition concepts.h:37
constexpr AutoDetect()=delete