Everett
Loading...
Searching...
No Matches
registry.h
Go to the documentation of this file.
1
13#pragma once
14
16
17#include <cstddef>
18#include <cstdint>
19#include <functional>
20#include <limits>
21#include <optional>
22#include <stdexcept>
23#include <string>
24#include <tuple>
25#include <type_traits>
26#include <utility>
27
28namespace everett {
29 enum class profile_unit : std::uint8_t { byte, bit };
30
31 struct variable_values {};
32 template <std::uint64_t N> struct fixed_values {
33 static constexpr std::uint64_t width = N;
34 };
35
36 namespace registry_detail {
37 template <class Values, unsigned Shift> struct value_bits;
38 template <unsigned Shift> struct value_bits<variable_values, Shift> {
39 static constexpr std::optional<std::uint64_t> value = std::nullopt;
40 };
41 template <std::uint64_t N, unsigned Shift> struct value_bits<fixed_values<N>, Shift> {
42 static_assert(N <= (std::numeric_limits<std::uint64_t>::max() >> Shift),
43 "fixed value width exceeds a bit extent");
44 static constexpr std::optional<std::uint64_t> value = N << Shift;
45 };
46 }
47
48 // Requirements for already encoded key/value payloads. These helpers do not
49 // supply a semantic key codec or establish prefix freedom of caller data.
50 // Widths in Values are bytes here and bits in bit_encoding respectively.
51 template <class Values = variable_values> struct byte_encoding {
54 };
55 template <class Values = variable_values> struct bit_encoding {
58 };
59 template <class Codec> struct encoded_sort { using encoding = Codec; };
60
61 // Layout inference only: semantic value encoding and tombstone meaning stay
62 // with the value codec. Specialize this trait for types without T::encoding.
63 template <class T> struct value_encoding { using type = typename T::encoding; };
64 template <> struct value_encoding<std::string> { using type = byte_encoding<>; };
65 template <> struct value_encoding<std::optional<std::string>> { using type = byte_encoding<>; };
66
67 // One tagless sort. Its type still identifies the sort to a typed visitor;
68 // no discriminator precedes its key. Native keys must still be sorted.
69 // This does not implement value codecs.
70 template <class T> struct unsorted {
71 using value_type = T;
73 };
74 // A reserved hole admits no sort and imposes neither unit nor value width.
75 // As a standalone empty registry it defaults to byte addressing.
76 struct sort_undefined {};
77 template <class S> struct tip { using sort_type = S; };
78 template <class L, class R> struct bin { using left_type = L; using right_type = R; };
79 template <class... S> struct sort_list {};
80
81 namespace registry_detail {
82 template <class T> inline constexpr bool hole = std::is_same_v<T, sort_undefined>;
83 template <class... S> struct sorts {};
84 template <class A, class B> struct concatenate;
85 template <class... A, class... B> struct concatenate<sorts<A...>, sorts<B...>> {
86 using type = sorts<A..., B...>;
87 };
88 template <class S> struct unique;
89 template <> struct unique<sorts<>> : std::true_type {};
90 template <class A, class... B> struct unique<sorts<A, B...>>
91 : std::bool_constant<(!std::is_same_v<A, B> && ...) && unique<sorts<B...>>::value> {};
92
93 struct empty {
94 using leaves = sorts<>;
95 static constexpr std::size_t count = 0;
96 static constexpr std::optional<profile_unit> mode = std::nullopt;
97 static constexpr std::optional<std::uint64_t> width = std::nullopt;
98 };
99 template <class S, bool = hole<S>> struct leaf : empty {};
100 template <class S> struct leaf<S, false> {
101 using encoding = typename S::encoding;
103 static constexpr std::size_t count = 1;
104 static constexpr std::optional<profile_unit> mode = encoding::unit;
105 static_assert(*mode == profile_unit::byte || *mode == profile_unit::bit,
106 "sort encoding has an invalid storage unit");
107 static constexpr std::optional<std::uint64_t> width = encoding::fixed_value_bits;
108 static_assert(*mode != profile_unit::byte || !width || (*width & 7) == 0,
109 "byte sort values must occupy whole bytes");
110 };
111
112 template <class A, class B> struct join {
114 static constexpr std::size_t count = A::count + B::count;
115 static constexpr std::optional<std::uint64_t> width = !A::count ? B::width :
116 !B::count ? A::width : A::width && B::width && A::width == B::width ? A::width : std::nullopt;
117 };
118 template <class... S> struct list_width : empty {};
119 template <class S, class... Rest> struct list_width<S, Rest...>
120 : join<leaf<S>, list_width<Rest...>> {};
121
122 template <class R> struct info;
123 template <class T> struct info<unsorted<T>> : leaf<unsorted<T>> {};
124 template <> struct info<sort_undefined> : empty {};
125 template <class S> struct info<tip<S>> : leaf<S> {};
126 template <class L, class R> struct info<bin<L, R>> : join<info<L>, info<R>> {
127 static constexpr std::optional<profile_unit> mode = profile_unit::bit;
128 };
129 template <class... S> struct info<sort_list<S...>> : list_width<S...> {
130 static_assert(sizeof...(S) <= 256, "a byte sort list has at most 256 codes");
131 static_assert(((!leaf<S>::mode || *leaf<S>::mode == profile_unit::byte) && ...),
132 "a byte sort list requires byte encodings");
133 static constexpr std::optional<profile_unit> mode = profile_unit::byte;
134 };
135
136 template <class Old, class New> struct extends;
137 template <class Old, class New> struct extension : std::is_same<Old, New> {};
138 template <class T> struct extension<unsorted<T>, tip<unsorted<T>>> : std::true_type {};
139 template <class T> struct extension<tip<unsorted<T>>, unsorted<T>> : std::true_type {};
140 template <class OL, class OR, class NL, class NR>
141 struct extension<bin<OL, OR>, bin<NL, NR>>
142 : std::bool_constant<extends<OL, NL>::value && extends<OR, NR>::value> {};
143 template <class Old, class New, std::size_t... I>
144 consteval bool list_extends(std::index_sequence<I...>) {
145 if constexpr (std::tuple_size_v<Old> > std::tuple_size_v<New>) return false;
146 else return ((hole<std::tuple_element_t<I, Old>> ||
147 std::is_same_v<std::tuple_element_t<I, Old>, std::tuple_element_t<I, New>>) && ...);
148 }
149 template <class... O, class... N> struct extension<sort_list<O...>, sort_list<N...>>
150 : std::bool_constant<list_extends<std::tuple<O...>, std::tuple<N...>>(std::index_sequence_for<O...>{})> {};
151 template <class Old, class New> struct extends
152 : std::bool_constant<info<Old>::count == 0 || extension<Old, New>::value> {};
153
154 template <class Leaves, class Reader, class Visitor> struct result;
155 template <class Reader, class Visitor> struct result<sorts<>, Reader, Visitor> { using type = void; };
156 template <class S, class... Rest, class Reader, class Visitor>
157 struct result<sorts<S, Rest...>, Reader, Visitor> {
158 using type = std::invoke_result_t<Visitor, std::type_identity<S>, Reader &>;
159 static_assert((std::is_same_v<type,
160 std::invoke_result_t<Visitor, std::type_identity<Rest>, Reader &>> && ...),
161 "sort visitor branches must have the same return type");
162 };
163 inline std::uint64_t discriminator(auto & reader, unsigned width) {
164 auto code = static_cast<std::uint64_t>(reader.read_bits(width));
165 if (code >= (std::uint64_t{1} << width))
166 error_detail::raise<std::invalid_argument>("sort discriminator exceeds its width");
167 return code;
168 }
169 template <class S, class Result, class Reader, class Visitor>
170 Result visit(Reader & reader, Visitor && visitor) {
171 if constexpr (hole<S>) error_detail::raise<std::invalid_argument>("undefined sort code");
172 else return std::invoke(std::forward<Visitor>(visitor), std::type_identity<S>{}, reader);
173 }
174 template <class R> struct dispatch;
175 template <> struct dispatch<sort_undefined> {
176 template <class Result, class Reader, class Visitor>
177 static Result run(Reader &, Visitor &&) {
178 error_detail::raise<std::invalid_argument>("empty sort registry");
179 }
180 };
181 template <class S> struct dispatch<tip<S>> {
182 template <class Result, class Reader, class Visitor>
183 static Result run(Reader & reader, Visitor && visitor) {
184 return visit<S, Result>(reader, std::forward<Visitor>(visitor));
185 }
186 };
187 template <class T> struct dispatch<unsorted<T>> : dispatch<tip<unsorted<T>>> {};
188 template <class L, class R> struct dispatch<bin<L, R>> {
189 template <class Result, class Reader, class Visitor>
190 static Result run(Reader & reader, Visitor && visitor) {
191 if (discriminator(reader, 1))
192 return dispatch<R>::template run<Result>(reader, std::forward<Visitor>(visitor));
193 return dispatch<L>::template run<Result>(reader, std::forward<Visitor>(visitor));
194 }
195 };
196 template <class... S> struct dispatch<sort_list<S...>> {
197 template <std::size_t I, class Result, class Reader, class Visitor>
198 static Result at(std::uint64_t code, Reader & reader, Visitor && visitor) {
199 if constexpr (I == sizeof...(S))
200 error_detail::raise<std::invalid_argument>("undefined sort code");
201 else {
202 if (code == I) return visit<std::tuple_element_t<I, std::tuple<S...>>, Result>(reader,
203 std::forward<Visitor>(visitor));
204 return at<I + 1, Result>(code, reader, std::forward<Visitor>(visitor));
205 }
206 }
207 template <class Result, class Reader, class Visitor>
208 static Result run(Reader & reader, Visitor && visitor) {
209 return at<0, Result>(discriminator(reader, 8), reader, std::forward<Visitor>(visitor));
210 }
211 };
212 }
213
214 // A codec's unit promises payload alignment, not alignment of its address:
215 // a byte codec nested under bin may start at any bit offset. Its reader and
216 // writer must support that offset. Empty registries have no common width.
217 template <class R> struct registry_traits {
218 using registry_type = R;
220 "each sort type must have exactly one registry code");
221 static constexpr std::size_t sort_count = registry_detail::info<R>::count;
224 static constexpr auto value_width = fixed_value_bits ?
225 std::optional<std::uint64_t>(*fixed_value_bits >> (unit == profile_unit::byte ? 3 : 0)) : std::nullopt;
226 static constexpr bool fixed_width = value_width.has_value();
227 };
228
229 // Old -> New preserves all previously occupied codes and leaf sort types.
230 // This is code/interpretation compatibility, not a persisted-header check:
231 // extending a registry can change its inferred common value width.
232 template <class Old, class New> struct registry_extends
233 : std::bool_constant<(sizeof(registry_traits<Old>) != 0) && (sizeof(registry_traits<New>) != 0) &&
234 registry_detail::extends<Old, New>::value> {};
235 template <class Old, class New>
237
238 // Reader supplies read_bits(unsigned) -> uint64_t and throws on truncation.
239 // Only the sort discriminator is consumed. The visitor receives the chosen
240 // type and the same reader at its key payload, with no implicit alignment.
241 // Unknown codes throw invalid_argument; errors do not roll back consumed
242 // discriminator bits. Visitor branches must have one exact return type.
243 template <class R, class Reader, class Visitor>
244 decltype(auto) dispatch_sort(Reader & reader, Visitor && visitor) {
245 (void)sizeof(registry_traits<R>);
247 Reader, Visitor &&>::type;
248 return registry_detail::dispatch<R>::template run<result>(reader, std::forward<Visitor>(visitor));
249 }
250}
Outlines exceptional check failures while preserving their types and messages.
std::uint64_t discriminator(auto &reader, unsigned width)
Definition registry.h:163
Result visit(Reader &reader, Visitor &&visitor)
Definition registry.h:170
constexpr bool hole
Definition registry.h:82
consteval bool list_extends(std::index_sequence< I... >)
Definition registry.h:144
Definition active_engine.h:18
constexpr bool registry_extends_v
Definition registry.h:236
decltype(auto) dispatch_sort(Reader &reader, Visitor &&visitor)
Definition registry.h:244
profile_unit
Definition registry.h:29
Definition registry.h:78
R right_type
Definition registry.h:78
L left_type
Definition registry.h:78
Definition registry.h:55
static constexpr profile_unit unit
Definition registry.h:56
static constexpr auto fixed_value_bits
Definition registry.h:57
Definition registry.h:51
static constexpr auto fixed_value_bits
Definition registry.h:53
static constexpr profile_unit unit
Definition registry.h:52
Definition registry.h:59
Codec encoding
Definition registry.h:59
Definition registry.h:32
static constexpr std::uint64_t width
Definition registry.h:33
static Result run(Reader &reader, Visitor &&visitor)
Definition registry.h:190
static Result run(Reader &reader, Visitor &&visitor)
Definition registry.h:208
static Result at(std::uint64_t code, Reader &reader, Visitor &&visitor)
Definition registry.h:198
static Result run(Reader &, Visitor &&)
Definition registry.h:177
static Result run(Reader &reader, Visitor &&visitor)
Definition registry.h:183
Definition registry.h:174
Definition registry.h:93
static constexpr std::optional< std::uint64_t > width
Definition registry.h:97
static constexpr std::size_t count
Definition registry.h:95
static constexpr std::optional< profile_unit > mode
Definition registry.h:96
Definition registry.h:152
Definition registry.h:137
Definition registry.h:122
Definition registry.h:112
static constexpr std::size_t count
Definition registry.h:114
typename concatenate< typename A::leaves, typename B::leaves >::type leaves
Definition registry.h:113
static constexpr std::optional< std::uint64_t > width
Definition registry.h:115
typename S::encoding encoding
Definition registry.h:101
Definition registry.h:99
Definition registry.h:118
std::invoke_result_t< Visitor, std::type_identity< S >, Reader & > type
Definition registry.h:158
Definition registry.h:154
Definition registry.h:83
Definition registry.h:88
Definition registry.h:37
Definition registry.h:234
Definition registry.h:217
static constexpr profile_unit unit
Definition registry.h:222
static constexpr std::size_t sort_count
Definition registry.h:221
static constexpr bool fixed_width
Definition registry.h:226
static constexpr auto value_width
Definition registry.h:224
static constexpr auto fixed_value_bits
Definition registry.h:223
R registry_type
Definition registry.h:218
Definition registry.h:79
Definition registry.h:76
Definition registry.h:77
S sort_type
Definition registry.h:77
Definition registry.h:70
T value_type
Definition registry.h:71
typename value_encoding< T >::type encoding
Definition registry.h:72
Definition registry.h:63
typename T::encoding type
Definition registry.h:63
Definition registry.h:31