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