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