Everett
Loading...
Searching...
No Matches
sort_codec.h
Go to the documentation of this file.
1
13#pragma once
14
15#include <everett/profile.h>
16#include <everett/registry.h>
17
18#include <functional>
19#include <optional>
20#include <string>
21#include <type_traits>
22#include <variant>
23
24namespace everett {
25 namespace sort_codec_detail {
26 template <class Code> struct count_policy {
27 static constexpr auto unit = profile_unit::bit;
30 };
31 inline bit_view string_bits(std::string const & value) {
32 return {std::as_bytes(std::span(value.data(), value.size())),
33 profile_detail::multiply(value.size(), 8)};
34 }
35 }
36
37 // These streams have bit addresses even when an individual codec writes bytes.
39 explicit sort_bit_writer(bit_string & data) : data_(data) { data.validate(); }
40 std::uint64_t position() const noexcept { return data_.bit_size; }
41 void write_bits(std::uint64_t value, unsigned width) {
42 if (width > 64 || (width < 64 && (value >> width)))
43 error_detail::raise<std::invalid_argument>("value exceeds bit field");
44 auto at = position();
46 profile_detail::put_fixed(data_, at, value, width);
47 }
49 template <class Code = exponential_golomb<0>> void write_count(std::uint64_t value) {
50 profile_detail::write_backspace<sort_codec_detail::count_policy<Code>>(data_, value);
51 }
52 private:
54 };
55
57 explicit sort_bit_reader(bit_view data) : data_(data) {}
58 std::uint64_t position() const noexcept { return at_; }
59 std::uint64_t remaining() const noexcept { return data_.size() - at_; }
60 bool empty() const noexcept { return remaining() == 0; }
61 std::uint64_t read_bits(unsigned width) {
62 if (width > 64 || width > remaining())
63 error_detail::raise<std::invalid_argument>("truncated backspace remainder");
64 if (!width) return 0;
65 if (!cached_) refill();
66 if (width <= cached_) {
67 auto result = width == 64 ? reservoir_ : reservoir_ >> (64 - width);
68 consume(width);
69 return result;
70 }
71 // The first nonempty fragment is shorter than width, so neither shift
72 // below can be 64. One refill supplies the entire second fragment.
73 auto result = reservoir_ >> (64 - cached_);
74 width -= cached_;
76 refill();
77 result = (result << width) | (reservoir_ >> (64 - width));
78 consume(width);
79 return result;
80 }
81 bit_view take_bits(std::uint64_t count) {
82 if (count > remaining()) error_detail::raise<std::invalid_argument>("truncated sort payload");
83 auto result = data_.subview(at_, count);
84 skip(count);
85 return result;
86 }
87 void skip_bits(std::uint64_t count) {
88 if (count > remaining()) error_detail::raise<std::invalid_argument>("truncated sort payload");
89 skip(count);
90 }
91 template <class Code = exponential_golomb<0>> std::uint64_t read_count() {
93 if constexpr (policy::backspace_code == bit_backspace_code::exponential_golomb) {
94 auto quotient = read_exponential();
95 if (quotient > (std::numeric_limits<std::uint64_t>::max() >> policy::backspace_parameter))
96 error_detail::raise<std::invalid_argument>("overflowing exponential-Golomb backspace");
97 auto remainder = read_bits(unsigned(policy::backspace_parameter));
98 return (quotient << policy::backspace_parameter) | remainder;
99 } else {
100 constexpr auto modulus = policy::backspace_parameter;
101 constexpr auto width = unsigned(std::bit_width(modulus - 1));
102 constexpr auto cutoff = profile_detail::golomb_cutoff<policy>();
103 constexpr auto limit = std::numeric_limits<std::uint64_t>::max() / modulus;
104 auto quotient = read_zero_run(limit, "truncated backspace remainder", "overflowing Golomb quotient");
105 std::uint64_t remainder = 0;
106 if constexpr (width != 0) {
107 remainder = read_bits(width - 1);
108 if (remainder >= cutoff) remainder = ((remainder << 1) | read_bits(1)) - cutoff;
109 }
110 auto base = quotient * modulus;
111 if (remainder > std::numeric_limits<std::uint64_t>::max() - base)
112 error_detail::raise<std::invalid_argument>("overflowing Golomb backspace");
113 return base + remainder;
114 }
115 }
116 private:
118 std::uint64_t at_ = 0;
119 // Valid bits are left aligned. Refill is lazy: a payload skip need not
120 // touch any payload pages, and an empty input performs no memory access.
121 std::uint64_t reservoir_ = 0;
122 unsigned cached_ = 0;
123 void refill() noexcept {
124 auto offset = data_.offset() + at_;
125 auto shift = unsigned(offset & 7);
126 auto width = 64 - shift;
127 cached_ = unsigned(std::min<std::uint64_t>(remaining(), width));
128 if (cached_ == width)
129 reservoir_ = key_detail::load_big(data_.storage().data() + (offset >> 3)) << shift;
131 }
132 void consume(unsigned count) noexcept {
133 reservoir_ = count == 64 ? 0 : reservoir_ << count;
134 cached_ -= count;
135 at_ += count;
136 }
137 void skip(std::uint64_t count) noexcept {
138 if (count <= cached_) consume(unsigned(count));
139 else { at_ += count; cached_ = 0; reservoir_ = 0; }
140 }
141 std::uint64_t read_zero_run(std::uint64_t limit, char const *truncated, char const *overflow) {
142 std::uint64_t zeros = 0;
143 for (;;) {
144 if (empty()) error_detail::raise<std::invalid_argument>(truncated);
145 if (!cached_) refill();
146 auto width = cached_;
147 auto leading = std::min(unsigned(std::countl_zero(reservoir_)), width);
148 if (leading > limit - zeros) {
149 consume(unsigned(limit - zeros + 1));
150 error_detail::raise<std::invalid_argument>(overflow);
151 }
152 zeros += leading;
153 consume(leading);
154 if (leading != width) { consume(1); return zeros; }
155 }
156 }
157 std::uint64_t read_exponential() {
158 if (!empty() && !cached_) refill();
159 auto zeros = unsigned(std::countl_zero(reservoir_));
160 // Decode consecutive short fields directly from the cached word.
161 if (zeros < 32 && 2 * zeros + 1 <= cached_) {
162 auto width = 2 * zeros + 1;
163 auto result = (reservoir_ >> (64 - width)) - 1;
164 consume(width);
165 return result;
166 }
167 return read_exponential_slow();
168 }
169 std::uint64_t read_exponential_slow() {
170 auto zeros = unsigned(read_zero_run(64,
171 "truncated exponential-Golomb count", "overflowing exponential-Golomb count"));
172 if (zeros > remaining()) {
173 skip(remaining());
174 error_detail::raise<std::invalid_argument>("truncated exponential-Golomb count");
175 }
176 auto suffix = read_bits(zeros);
177 if (zeros == 64) {
178 if (suffix) error_detail::raise<std::invalid_argument>("overflowing exponential-Golomb count");
179 return std::numeric_limits<std::uint64_t>::max();
180 }
181 return ((std::uint64_t{1} << zeros) - 1) + suffix;
182 }
183 };
184
185 // A frame borrows literal bits and keeps the inherited prefix implicit.
187 std::uint64_t retained_bits = 0;
189 std::uint64_t size() const { return profile_detail::add(retained_bits, literal.size()); }
191 if (previous.common_bits < retained_bits) return previous;
192 if (retained_bits > query.size())
193 error_detail::raise<std::invalid_argument>("FC comparison lacks inherited prefix");
194 auto result = compare_common_bits(literal, query.subview(retained_bits, query.size() - retained_bits));
195 result.common_bits += retained_bits;
196 return result;
197 }
198 };
199
200 // Strings compare as unsigned bytes. Their ordered key representation escapes
201 // zero as 00 ff and terminates with 00 00; it is distinct from FC wire framing.
203 using value_type = std::string;
204 static bool less(value_type const & a, value_type const & b) {
206 }
207 static void write_ordered(sort_bit_writer & out, value_type const & value) {
208 for (unsigned char c : value) {
209 out.write_bits(c, 8);
210 if (!c) out.write_bits(255, 8);
211 }
212 out.write_bits(0, 16);
213 }
215 value_type result;
216 for (;;) {
217 auto c = in.read_bits(8);
218 if (!c) {
219 auto escape = in.read_bits(8);
220 if (!escape) return result;
221 if (escape != 255) error_detail::raise<std::invalid_argument>("noncanonical string escape");
222 }
223 result.push_back(static_cast<char>(c));
224 }
225 }
226 };
227
228 template <class CountCode = exponential_golomb<0>> struct fc_string_key : ordered_string_key {
229 static fc_key_frame read_frame(sort_bit_reader & in, std::uint64_t previous_bits) {
230 auto backspace = in.template read_count<CountCode>();
231 if (backspace > previous_bits) error_detail::raise<std::invalid_argument>("FC backspace exceeds key");
232 auto retained = previous_bits - backspace;
233 auto literal = in.take_bits(in.template read_count<CountCode>());
234 if ((profile_detail::add(retained, literal.size()) & 7) != 0)
235 error_detail::raise<std::invalid_argument>("FC string ends inside a byte");
236 return {retained, literal};
237 }
238 static void write(sort_bit_writer & out, value_type const & value, value_type const * previous = nullptr) {
239 auto key = sort_codec_detail::string_bits(value);
240 auto old = previous ? sort_codec_detail::string_bits(*previous) : bit_view{};
241 auto retained = compare_common_bits(old, key).common_bits;
242 out.template write_count<CountCode>(old.size() - retained);
243 out.template write_count<CountCode>(key.size() - retained);
244 out.append(key.subview(retained, key.size() - retained));
245 }
246 static value_type read(sort_bit_reader & in, value_type const * previous = nullptr) {
247 auto old = previous ? sort_codec_detail::string_bits(*previous) : bit_view{};
248 auto frame = read_frame(in, old.size());
249 value_type result(static_cast<std::size_t>(frame.size() >> 3), '\0');
250 auto target = reinterpret_cast<std::byte *>(result.data());
251 profile_detail::copy_bits(target, 0, old.prefix(frame.retained_bits));
252 profile_detail::copy_bits(target, frame.retained_bits, frame.literal);
253 return result;
254 }
255 };
256
257 template <class CountCode = exponential_golomb<0>> struct fc_bit_key {
259 static bool less(value_type const & a, value_type const & b) { return compare_bits(a.view(), b.view()) < 0; }
260 static fc_key_frame read_frame(sort_bit_reader & in, std::uint64_t previous_bits) {
261 auto backspace = in.template read_count<CountCode>();
262 if (backspace > previous_bits) error_detail::raise<std::invalid_argument>("FC backspace exceeds key");
263 auto retained = previous_bits - backspace;
264 auto literal = in.take_bits(in.template read_count<CountCode>());
265 (void)profile_detail::add(retained, literal.size());
266 return {retained, literal};
267 }
268 static void write(sort_bit_writer & out, value_type const & value, value_type const * previous = nullptr) {
269 auto key = value.view(), old = previous ? previous->view() : bit_view{};
270 auto retained = compare_common_bits(old, key).common_bits;
271 out.template write_count<CountCode>(old.size() - retained);
272 out.template write_count<CountCode>(key.size() - retained);
273 out.append(key.subview(retained, key.size() - retained));
274 }
275 static value_type read(sort_bit_reader & in, value_type const * previous = nullptr) {
276 auto old = previous ? previous->view() : bit_view{};
277 auto frame = read_frame(in, old.size());
278 auto result = bit_string::copy(old.prefix(frame.retained_bits));
279 profile_detail::append(result, frame.literal);
280 return result;
281 }
282 // Canonical bit keys use 1b per input bit, followed by a zero terminator.
283 static void write_ordered(sort_bit_writer & out, value_type const & value) {
284 auto bits = value.view();
285 for (std::uint64_t i = 0; i != bits.size(); ++i) out.write_bits(2 | bits.at(i), 2);
286 out.write_bits(0, 1);
287 }
289 value_type result;
290 sort_bit_writer out(result);
291 while (in.read_bits(1)) out.write_bits(in.read_bits(1), 1);
292 return result;
293 }
294 };
295
296 template <class CountCode = exponential_golomb<0>> struct string_value {
297 using value_type = std::string;
298 static constexpr std::optional<std::uint64_t> fixed_value_bits = std::nullopt;
299 static void write(sort_bit_writer & out, value_type const & value) {
300 out.template write_count<CountCode>(value.size());
302 }
304 auto bytes = in.template read_count<CountCode>();
305 if (bytes > (in.remaining() >> 3)) error_detail::raise<std::invalid_argument>("truncated string value");
306 auto bits = in.take_bits(bytes << 3);
307 value_type result(static_cast<std::size_t>(bytes), '\0');
308 profile_detail::copy_bits(reinterpret_cast<std::byte *>(result.data()), 0, bits);
309 return result;
310 }
311 static void skip(sort_bit_reader & in) {
312 auto bytes = in.template read_count<CountCode>();
313 if (bytes > (in.remaining() >> 3)) error_detail::raise<std::invalid_argument>("truncated string value");
314 (void)in.take_bits(bytes << 3);
315 }
316 };
317
318 template <class CountCode = exponential_golomb<0>> struct bit_value {
320 static constexpr std::optional<std::uint64_t> fixed_value_bits = std::nullopt;
321 static void write(sort_bit_writer & out, value_type const & value) {
322 auto bits = value.view();
323 out.template write_count<CountCode>(bits.size());
324 out.append(bits);
325 }
326 static value_type read(sort_bit_reader & in) { return bit_string::copy(in.take_bits(in.template read_count<CountCode>())); }
327 static void skip(sort_bit_reader & in) { (void)in.take_bits(in.template read_count<CountCode>()); }
328 };
329
330 template <class CountCode = exponential_golomb<0>> struct raw_string_key : ordered_string_key {
331 static void write(sort_bit_writer & out, value_type const & value, value_type const * = nullptr) {
333 }
334 static value_type read(sort_bit_reader & in, value_type const * = nullptr) {
336 }
337 };
338
339 template <unsigned Bits> struct unsigned_value {
340 static_assert(Bits >= 1 && Bits <= 64);
341 using value_type = std::uint64_t;
342 static constexpr std::optional<std::uint64_t> fixed_value_bits = Bits;
343 static void write(sort_bit_writer & out, value_type value) { out.write_bits(value, Bits); }
344 static value_type read(sort_bit_reader & in) { return in.read_bits(Bits); }
345 static void skip(sort_bit_reader & in) { (void)in.take_bits(Bits); }
346 };
347
348 template <unsigned Bits> struct unsigned_key {
349 using value_type = std::uint64_t;
350 static bool less(value_type a, value_type b) noexcept { return a < b; }
351 static void write(sort_bit_writer & out, value_type value, value_type const * = nullptr) {
352 unsigned_value<Bits>::write(out, value);
353 }
354 static value_type read(sort_bit_reader & in, value_type const * = nullptr) {
356 }
357 static void write_ordered(sort_bit_writer & out, value_type value) { write(out, value); }
358 static value_type read_ordered(sort_bit_reader & in) { return read(in); }
359 };
360
361 // The absent case is one bit, regardless of the present codec's width.
362 template <class Codec> struct tombstone_value {
363 using value_type = std::optional<typename Codec::value_type>;
364 static constexpr std::optional<std::uint64_t> fixed_value_bits =
365 Codec::fixed_value_bits == 0 ? std::optional<std::uint64_t>{1} : std::nullopt;
366 static void write(sort_bit_writer & out, value_type const & value) {
367 out.write_bits(value.has_value(), 1);
368 if (value) Codec::write(out, *value);
369 }
371 if (!in.read_bits(1)) return std::nullopt;
372 return Codec::read(in);
373 }
374 static void skip(sort_bit_reader & in) { if (in.read_bits(1)) Codec::skip(in); }
375 };
376
377 template <class Codec, auto Sentinel> struct niche_value {
378 using value_type = std::optional<typename Codec::value_type>;
379 static constexpr auto fixed_value_bits = Codec::fixed_value_bits;
380 static void write(sort_bit_writer & out, value_type const & value) {
381 if (value && *value == Sentinel) error_detail::raise<std::invalid_argument>("value occupies tombstone niche");
382 Codec::write(out, value ? *value : Sentinel);
383 }
385 auto value = Codec::read(in);
386 if (value == Sentinel) return std::nullopt;
387 return value;
388 }
389 static void skip(sort_bit_reader & in) { Codec::skip(in); }
390 };
391
392 struct no_value {
393 using value_type = std::monostate;
394 static constexpr std::optional<std::uint64_t> fixed_value_bits = 0;
395 static void write(sort_bit_writer &, value_type) noexcept {}
396 static value_type read(sort_bit_reader &) noexcept { return {}; }
397 static void skip(sort_bit_reader &) noexcept {}
398 };
399
400 // The sort may specialize this trait rather than declaring codec aliases.
401 template <class S> struct sort_codec {
402 using key_codec = typename S::key_codec;
403 using value_codec = typename S::value_codec;
404 };
405
406 template <> struct sort_codec<unsorted<std::optional<std::string>>> {
409 };
410 template <> struct sort_codec<unsorted<std::string>> {
413 };
414
415 namespace sort_codec_detail {
416 template <class S> constexpr void validate_value_width() {
417 static_assert(!S::encoding::fixed_value_bits ||
418 S::encoding::fixed_value_bits == sort_codec<S>::value_codec::fixed_value_bits,
419 "sort encoding promises a value width its codec does not supply");
420 }
421 template <class S, class Leaves> struct contains;
422 template <class S, class... T> struct contains<S, registry_detail::sorts<T...>>
423 : std::bool_constant<(std::is_same_v<S, T> || ...)> {};
424 template <class R, class S> inline constexpr bool contains_sort =
426 template <class R, class S> struct code;
427 template <class S> struct code<tip<S>, S> {
428 static constexpr std::uint64_t size = 0;
429 static void write(sort_bit_writer &) {}
430 };
431 template <class T> struct code<unsorted<T>, unsorted<T>> : code<tip<unsorted<T>>, unsorted<T>> {};
432 template <class L, class R, class S> struct code<bin<L, R>, S> {
433 static constexpr std::uint64_t size = 1 + [] {
434 if constexpr (contains_sort<L, S>) return code<L, S>::size;
435 else return code<R, S>::size;
436 }();
437 static void write(sort_bit_writer & out) {
438 constexpr bool right = !contains_sort<L, S>;
439 out.write_bits(right, 1);
440 if constexpr (right) code<R, S>::write(out);
441 else code<L, S>::write(out);
442 }
443 };
444 template <class... T, class S> struct code<sort_list<T...>, S> {
445 static constexpr std::uint64_t size = 8;
446 static void write(sort_bit_writer & out) {
447 std::uint64_t i = 0, index = 0;
448 ((std::is_same_v<T, S> ? (index = i, ++i) : ++i), ...);
449 out.write_bits(index, 8);
450 }
451 };
452 template <class S> struct key_state {
453 using sort_type = S;
456 };
457 template <class Leaves> struct states;
458 template <class... S> struct states<registry_detail::sorts<S...>> {
459 using type = std::variant<std::monostate, key_state<S>...>;
460 };
461 // Replay retained discriminator bits, then consume exactly enough new bits
462 // to reach a leaf. A leaf may not terminate inside the retained path.
467 std::uint64_t at = 0;
468 std::uint64_t read_bits(unsigned width) {
469 std::uint64_t value = 0;
470 for (unsigned i = 0; i != width; ++i) {
472 path.write_bits(bit, 1);
473 value = (value << 1) | bit;
474 }
475 return value;
476 }
477 };
478 }
479
480 template <class Registry, class S> void write_sort_code(sort_bit_writer & out) {
481 (void)sizeof(registry_traits<Registry>);
482 static_assert(sort_codec_detail::contains_sort<Registry, S>, "sort is absent from registry");
484 }
485 template <class Registry, class S> bit_string sort_code() {
486 bit_string result;
487 sort_bit_writer out(result);
488 write_sort_code<Registry, S>(out);
489 return result;
490 }
491
493 std::uint64_t start = 0;
494 std::uint64_t sort_retained = 0;
495 std::uint64_t sort_bits = 0;
496 std::uint64_t key_start = 0;
497 std::uint64_t value_start = 0;
498 std::uint64_t end = 0;
499 };
500
501 // A checkpoint contains the previous discriminator and typed key. It is an
502 // in-memory decoding anchor, not a serialized schema or an index restart.
503 template <class Registry> struct sort_record_state {
506 void validate() const {
507 path.validate();
508 std::visit([&](auto const & value) {
509 using state = std::remove_cvref_t<decltype(value)>;
510 if constexpr (std::is_same_v<state, std::monostate>) {
511 if (path.bit_size) error_detail::raise<std::invalid_argument>("sort anchor has no key");
512 } else if (path != sort_code<Registry, typename state::sort_type>())
513 error_detail::raise<std::invalid_argument>("sort anchor path disagrees with key type");
514 }, previous);
515 }
516 };
517
518 template <class Registry, class TreeCode = exponential_golomb<0>, stream_role Role = stream_role::native>
521 "sort record streams require a bit registry");
523 template <class S> using value_codec = std::conditional_t<Role == stream_role::native,
527
528 template <class S> sort_record_control append(
529 typename sort_codec<S>::key_codec::value_type const & key,
530 typename value_codec<S>::value_type const & value) {
531 if (failed_) error_detail::raise<std::logic_error>("failed sort record writer");
532 try {
533 sort_codec_detail::validate_value_width<S>();
534 using key_codec = typename sort_codec<S>::key_codec;
535 auto previous = std::get_if<sort_codec_detail::key_state<S>>(&state_.previous);
536 bool same_sort = previous != nullptr;
537 auto path = same_sort ? bit_string{} : sort_code<Registry, S>();
538 auto path_bits = same_sort ? state_.path.view() : path.view();
539 auto comparison = same_sort ? bit_comparison{state_.path.bit_size, 0} :
540 compare_common_bits(state_.path.view(), path_bits);
541 if ((!std::holds_alternative<std::monostate>(state_.previous) && comparison.order > 0) ||
542 (previous && !key_codec::less(previous->key, key)))
543 error_detail::raise<std::invalid_argument>("sort records must have unique sorted keys");
546 sort_record_control control{data_.bit_size, comparison.common_bits, path_bits.size()};
547 out.template write_count<TreeCode>(state_.path.bit_size - control.sort_retained);
548 out.append(path_bits.subview(control.sort_retained, path_bits.size() - control.sort_retained));
549 control.key_start = control.start + out.position();
550 key_codec::write(out, key, previous ? &previous->key : nullptr);
551 control.value_start = control.start + out.position();
552 value_codec<S>::write(out, value);
553 control.end = control.start + out.position();
554 // All state construction precedes publication of these bytes.
557 state_.previous = std::move(next);
558 if (!same_sort) state_.path = std::move(path);
559 return control;
560 } catch (...) { failed_ = true; throw; }
561 }
562 template <class S> sort_record_control append(typename sort_codec<S>::key_codec::value_type const & key)
563 requires std::is_same_v<value_codec<S>, no_value> { return append<S>(key, {}); }
564 bit_string const & data() const & {
565 if (failed_) error_detail::raise<std::logic_error>("failed sort record writer");
566 return data_;
567 }
568 bit_string const & data() const && = delete;
569 state_type const & state() const & noexcept { return state_; }
570 bool failed() const noexcept { return failed_; }
571 private:
575 bool failed_ = false;
576 };
577
578 template <class Registry, class TreeCode = exponential_golomb<0>, stream_role Role = stream_role::native>
581 "sort record streams require a bit registry");
583 explicit sort_record_reader(bit_view data, state_type state = {}) : input_(data), state_(std::move(state)) {
585 }
586 bool empty() const noexcept { return input_.empty(); }
587 bool failed() const noexcept { return failed_; }
588 std::uint64_t position() const noexcept { return input_.position(); }
589 state_type const & state() const & noexcept { return state_; }
590
591 // Callback receives ephemeral references. Its exception consumes this record
592 // and poisons the reader; copied anchors can resume on the remaining slice.
593 template <class Visitor> bool next(Visitor && visitor) {
594 if (failed_) error_detail::raise<std::logic_error>("failed sort record reader");
595 if (empty()) return false;
596 try {
597 sort_record_control control;
598 control.start = input_.position();
599 auto backspace = input_.template read_count<TreeCode>();
600 if (backspace > state_.path.bit_size)
601 error_detail::raise<std::invalid_argument>("sort backspace exceeds path");
602 control.sort_retained = state_.path.bit_size - backspace;
603 bit_string path;
604 bool reuse = backspace == 0 && !std::holds_alternative<std::monostate>(state_.previous);
605 auto decode = [&]<class S>(std::type_identity<S> tag) {
606 sort_codec_detail::validate_value_width<S>();
607 auto path_bits = reuse ? state_.path.view() : path.view();
608 control.sort_bits = path_bits.size();
609 control.key_start = input_.position();
610 using key_codec = typename sort_codec<S>::key_codec;
611 using value_codec = std::conditional_t<Role == stream_role::native,
613 auto previous = std::get_if<sort_codec_detail::key_state<S>>(&state_.previous);
614 auto key = key_codec::read(input_, previous ? &previous->key : nullptr);
615 control.value_start = input_.position();
616 auto value = value_codec::read(input_);
617 control.end = input_.position();
618 if ((!std::holds_alternative<std::monostate>(state_.previous) &&
619 !reuse && compare_bits(state_.path.view(), path_bits) > 0) ||
620 (previous && !key_codec::less(previous->key, key)))
621 error_detail::raise<std::invalid_argument>("sort records must have unique sorted keys");
622 state_.previous = sort_codec_detail::key_state<S>{std::move(key)};
623 if (!reuse) state_.path = std::move(path);
624 auto const & stored = std::get<sort_codec_detail::key_state<S>>(state_.previous);
625 std::invoke(std::forward<Visitor>(visitor), tag, stored.key, value, control);
626 };
627 if (reuse) {
628 // Retaining the complete code already identifies the leaf. Do not
629 // walk a deep tree or copy its path again for every key of that sort.
630 std::visit([&](auto const & previous) {
631 using state = std::remove_cvref_t<decltype(previous)>;
632 if constexpr (!std::is_same_v<state, std::monostate>)
633 decode(std::type_identity<typename state::sort_type>{});
634 }, state_.previous);
635 } else {
636 sort_bit_writer path_writer(path);
638 dispatch_sort<Registry>(prefix, [&]<class S>(std::type_identity<S> tag, auto &) {
639 if (prefix.at != control.sort_retained)
640 error_detail::raise<std::invalid_argument>("retained sort prefix crosses leaf");
641 decode(tag);
642 });
643 }
644 return true;
645 } catch (...) { failed_ = true; throw; }
646 }
647 private:
650 bool failed_ = false;
651 };
652}
std::uint64_t load_big(void const *source) noexcept
Definition key_detail.h:57
void put_fixed(bit_string &target, std::uint64_t &at, std::uint64_t value, unsigned width)
Definition profile.h:504
std::uint64_t add(std::uint64_t a, std::uint64_t b)
Definition profile.h:39
std::uint64_t multiply(std::uint64_t a, std::uint64_t b)
Definition profile.h:44
void resize(bit_string &value, std::uint64_t bits)
Definition profile.h:465
void copy_bits(std::byte *target, std::uint64_t first, bit_view source) noexcept
Definition profile.h:123
std::uint64_t load_bits(bit_view data, std::uint64_t first, unsigned width) noexcept
Definition profile.h:93
void append(bit_string &target, bit_view source)
Definition profile.h:480
constexpr void validate_value_width()
Definition sort_codec.h:416
constexpr bool contains_sort
Definition sort_codec.h:424
bit_view string_bits(std::string const &value)
Definition sort_codec.h:31
Definition active_engine.h:18
bit_string sort_code()
Definition sort_codec.h:485
bit_comparison compare_common_bits(bit_view a, bit_view b)
Definition profile.h:215
int compare_bits(bit_view a, bit_view b)
Definition profile.h:243
void write_sort_code(sort_bit_writer &out)
Definition sort_codec.h:480
Declares Everett's profile support.
Describes sort codes, encoding requirements and typed registry dispatch.
Definition registry.h:78
Definition profile.h:209
std::uint64_t common_bits
Definition profile.h:210
Definition profile.h:166
void validate() const
Definition profile.h:170
bit_view view() const &
Definition profile.h:178
static bit_string copy(bit_view source)
Definition profile.h:181
std::uint64_t bit_size
Definition profile.h:168
Definition sort_codec.h:318
static void write(sort_bit_writer &out, value_type const &value)
Definition sort_codec.h:321
static value_type read(sort_bit_reader &in)
Definition sort_codec.h:326
static constexpr std::optional< std::uint64_t > fixed_value_bits
Definition sort_codec.h:320
static void skip(sort_bit_reader &in)
Definition sort_codec.h:327
Definition profile.h:56
std::span< std::byte const > storage() const noexcept
Definition profile.h:67
std::uint64_t size() const noexcept
Definition profile.h:64
std::uint64_t offset() const noexcept
Definition profile.h:66
bit_view subview(std::uint64_t first, std::uint64_t count) const
Definition profile.h:73
bit_view prefix(std::uint64_t count) const
Definition profile.h:80
Definition sort_codec.h:257
static void write(sort_bit_writer &out, value_type const &value, value_type const *previous=nullptr)
Definition sort_codec.h:268
static fc_key_frame read_frame(sort_bit_reader &in, std::uint64_t previous_bits)
Definition sort_codec.h:260
static bool less(value_type const &a, value_type const &b)
Definition sort_codec.h:259
static void write_ordered(sort_bit_writer &out, value_type const &value)
Definition sort_codec.h:283
static value_type read(sort_bit_reader &in, value_type const *previous=nullptr)
Definition sort_codec.h:275
static value_type read_ordered(sort_bit_reader &in)
Definition sort_codec.h:288
Definition sort_codec.h:186
std::uint64_t size() const
Definition sort_codec.h:189
bit_comparison compare(bit_view query, bit_comparison previous) const
Definition sort_codec.h:190
bit_view literal
Definition sort_codec.h:188
std::uint64_t retained_bits
Definition sort_codec.h:187
Definition sort_codec.h:228
static fc_key_frame read_frame(sort_bit_reader &in, std::uint64_t previous_bits)
Definition sort_codec.h:229
static value_type read(sort_bit_reader &in, value_type const *previous=nullptr)
Definition sort_codec.h:246
static void write(sort_bit_writer &out, value_type const &value, value_type const *previous=nullptr)
Definition sort_codec.h:238
Definition sort_codec.h:377
static void skip(sort_bit_reader &in)
Definition sort_codec.h:389
static void write(sort_bit_writer &out, value_type const &value)
Definition sort_codec.h:380
static value_type read(sort_bit_reader &in)
Definition sort_codec.h:384
static constexpr auto fixed_value_bits
Definition sort_codec.h:379
std::optional< typename Codec::value_type > value_type
Definition sort_codec.h:378
Definition sort_codec.h:392
static void skip(sort_bit_reader &) noexcept
Definition sort_codec.h:397
static void write(sort_bit_writer &, value_type) noexcept
Definition sort_codec.h:395
std::monostate value_type
Definition sort_codec.h:393
static value_type read(sort_bit_reader &) noexcept
Definition sort_codec.h:396
static constexpr std::optional< std::uint64_t > fixed_value_bits
Definition sort_codec.h:394
Definition sort_codec.h:202
static bool less(value_type const &a, value_type const &b)
Definition sort_codec.h:204
std::string value_type
Definition sort_codec.h:203
static value_type read_ordered(sort_bit_reader &in)
Definition sort_codec.h:214
static void write_ordered(sort_bit_writer &out, value_type const &value)
Definition sort_codec.h:207
Definition sort_codec.h:330
static void write(sort_bit_writer &out, value_type const &value, value_type const *=nullptr)
Definition sort_codec.h:331
static value_type read(sort_bit_reader &in, value_type const *=nullptr)
Definition sort_codec.h:334
Definition registry.h:217
Definition sort_codec.h:56
std::uint64_t at_
Definition sort_codec.h:118
std::uint64_t remaining() const noexcept
Definition sort_codec.h:59
std::uint64_t read_count()
Definition sort_codec.h:91
bool empty() const noexcept
Definition sort_codec.h:60
sort_bit_reader(bit_view data)
Definition sort_codec.h:57
std::uint64_t read_bits(unsigned width)
Definition sort_codec.h:61
void skip(std::uint64_t count) noexcept
Definition sort_codec.h:137
std::uint64_t read_zero_run(std::uint64_t limit, char const *truncated, char const *overflow)
Definition sort_codec.h:141
std::uint64_t reservoir_
Definition sort_codec.h:121
bit_view data_
Definition sort_codec.h:117
std::uint64_t position() const noexcept
Definition sort_codec.h:58
std::uint64_t read_exponential_slow()
Definition sort_codec.h:169
unsigned cached_
Definition sort_codec.h:122
bit_view take_bits(std::uint64_t count)
Definition sort_codec.h:81
std::uint64_t read_exponential()
Definition sort_codec.h:157
void consume(unsigned count) noexcept
Definition sort_codec.h:132
void skip_bits(std::uint64_t count)
Definition sort_codec.h:87
void refill() noexcept
Definition sort_codec.h:123
Definition sort_codec.h:38
bit_string & data_
Definition sort_codec.h:53
void append(bit_view bits)
Definition sort_codec.h:48
std::uint64_t position() const noexcept
Definition sort_codec.h:40
void write_bits(std::uint64_t value, unsigned width)
Definition sort_codec.h:41
void write_count(std::uint64_t value)
Definition sort_codec.h:49
sort_bit_writer(bit_string &data)
Definition sort_codec.h:39
static void write(sort_bit_writer &out)
Definition sort_codec.h:437
static void write(sort_bit_writer &out)
Definition sort_codec.h:446
static void write(sort_bit_writer &)
Definition sort_codec.h:429
Definition sort_codec.h:426
Definition sort_codec.h:421
static constexpr auto backspace_code
Definition sort_codec.h:28
static constexpr auto unit
Definition sort_codec.h:27
static constexpr auto backspace_parameter
Definition sort_codec.h:29
Definition sort_codec.h:452
key_type key
Definition sort_codec.h:455
S sort_type
Definition sort_codec.h:453
typename sort_codec< S >::key_codec::value_type key_type
Definition sort_codec.h:454
sort_bit_reader & input
Definition sort_codec.h:464
std::uint64_t at
Definition sort_codec.h:467
bit_view retained
Definition sort_codec.h:465
std::uint64_t read_bits(unsigned width)
Definition sort_codec.h:468
sort_bit_writer & path
Definition sort_codec.h:466
std::variant< std::monostate, key_state< S >... > type
Definition sort_codec.h:459
Definition sort_codec.h:457
Definition sort_codec.h:401
typename S::key_codec key_codec
Definition sort_codec.h:402
typename S::value_codec value_codec
Definition sort_codec.h:403
Definition registry.h:79
Definition sort_codec.h:492
std::uint64_t end
Definition sort_codec.h:498
std::uint64_t sort_retained
Definition sort_codec.h:494
std::uint64_t value_start
Definition sort_codec.h:497
std::uint64_t start
Definition sort_codec.h:493
std::uint64_t sort_bits
Definition sort_codec.h:495
std::uint64_t key_start
Definition sort_codec.h:496
Definition sort_codec.h:579
state_type const & state() const &noexcept
Definition sort_codec.h:589
bool failed_
Definition sort_codec.h:650
state_type state_
Definition sort_codec.h:649
bool failed() const noexcept
Definition sort_codec.h:587
std::uint64_t position() const noexcept
Definition sort_codec.h:588
bool empty() const noexcept
Definition sort_codec.h:586
sort_record_reader(bit_view data, state_type state={})
Definition sort_codec.h:583
bool next(Visitor &&visitor)
Definition sort_codec.h:593
sort_bit_reader input_
Definition sort_codec.h:648
Definition sort_codec.h:503
void validate() const
Definition sort_codec.h:506
bit_string path
Definition sort_codec.h:504
sort_codec_detail::states< typenameregistry_detail::info< Registry >::leaves >::type previous
Definition sort_codec.h:505
Definition sort_codec.h:519
bit_string frame_
Definition sort_codec.h:573
bit_string const & data() const &
Definition sort_codec.h:564
state_type state_
Definition sort_codec.h:574
sort_record_writer(state_type state)
Definition sort_codec.h:526
std::conditional_t< Role==stream_role::native, typename sort_codec< S >::value_codec, no_value > value_codec
Definition sort_codec.h:524
state_type const & state() const &noexcept
Definition sort_codec.h:569
sort_record_control append(typename sort_codec< S >::key_codec::value_type const &key, typename value_codec< S >::value_type const &value)
Definition sort_codec.h:528
bit_string data_
Definition sort_codec.h:572
bool failed() const noexcept
Definition sort_codec.h:570
sort_record_control append(typename sort_codec< S >::key_codec::value_type const &key)
Definition sort_codec.h:562
bit_string const & data() const &&=delete
bool failed_
Definition sort_codec.h:575
Definition sort_codec.h:296
static value_type read(sort_bit_reader &in)
Definition sort_codec.h:303
static void write(sort_bit_writer &out, value_type const &value)
Definition sort_codec.h:299
static void skip(sort_bit_reader &in)
Definition sort_codec.h:311
std::string value_type
Definition sort_codec.h:297
static constexpr std::optional< std::uint64_t > fixed_value_bits
Definition sort_codec.h:298
Definition registry.h:77
Definition sort_codec.h:362
static void write(sort_bit_writer &out, value_type const &value)
Definition sort_codec.h:366
static value_type read(sort_bit_reader &in)
Definition sort_codec.h:370
std::optional< typename Codec::value_type > value_type
Definition sort_codec.h:363
static constexpr std::optional< std::uint64_t > fixed_value_bits
Definition sort_codec.h:364
static void skip(sort_bit_reader &in)
Definition sort_codec.h:374
Definition sort_codec.h:348
static value_type read(sort_bit_reader &in, value_type const *=nullptr)
Definition sort_codec.h:354
static void write_ordered(sort_bit_writer &out, value_type value)
Definition sort_codec.h:357
std::uint64_t value_type
Definition sort_codec.h:349
static bool less(value_type a, value_type b) noexcept
Definition sort_codec.h:350
static value_type read_ordered(sort_bit_reader &in)
Definition sort_codec.h:358
static void write(sort_bit_writer &out, value_type value, value_type const *=nullptr)
Definition sort_codec.h:351
Definition sort_codec.h:339
static void write(sort_bit_writer &out, value_type value)
Definition sort_codec.h:343
static void skip(sort_bit_reader &in)
Definition sort_codec.h:345
static value_type read(sort_bit_reader &in)
Definition sort_codec.h:344
static constexpr std::optional< std::uint64_t > fixed_value_bits
Definition sort_codec.h:342
std::uint64_t value_type
Definition sort_codec.h:341
Definition registry.h:70