Everett
Loading...
Searching...
No Matches
sort_profile.h
Go to the documentation of this file.
1
12#pragma once
13
14#include <everett/sort_codec.h>
15#include <everett/cola_index.h>
16
17namespace everett {
18 // Selection is a protocol: the declarative registry is one implementation.
19 // A custom selector may use a generated table rather than a recursive tree.
20 template <class Registry> struct registry_selector {
21 template <class S> static constexpr std::uint64_t code_size = sort_codec_detail::code<Registry, S>::size;
22 template <class Input, class Visitor> static decltype(auto) select(Input & in, Visitor && visitor) {
23 return dispatch_sort<Registry>(in, std::forward<Visitor>(visitor));
24 }
25 template <class S, class Output> static void write(Output & out) { write_sort_code<Registry, S>(out); }
26 };
27
28 // A navigable key codec supplies an order-bit representation and frames that
29 // expose inherited prefix length plus literal spans. Values keep their own
30 // complete grammar; value_codec::skip must find their end without allocation.
31 // Optional order_view borrows the caller's key for synchronous query encoding.
32 template <class Codec> struct sort_profile_key;
33 template <class C> struct sort_profile_key<fc_string_key<C>> {
34 static constexpr bool front_coded = true;
35 static bit_view order_view(std::string const & key) { return sort_codec_detail::string_bits(key); }
36 static bit_string order(std::string const & key) { return bit_string::copy(sort_codec_detail::string_bits(key)); }
37 static std::string decode_order(bit_view key) {
38 if (key.size() & 7) throw std::invalid_argument("string order key ends inside byte");
39 std::string result(static_cast<std::size_t>(key.size() >> 3), '\0');
40 profile_detail::copy_bits(reinterpret_cast<std::byte *>(result.data()), 0, key);
41 return result;
42 }
43 static fc_key_frame read(sort_bit_reader & in, std::uint64_t retained) {
44 auto literal = in.take_bits(in.template read_count<C>());
45 if ((retained + literal.size()) & 7) throw std::invalid_argument("string key ends inside byte");
46 return {retained, literal};
47 }
48 template <class Output> static void header(Output & out, std::uint64_t retained, std::uint64_t suffix_bits) {
49 if ((retained + suffix_bits) & 7) throw std::invalid_argument("string key ends inside byte");
50 out.template write_count<C>(suffix_bits);
51 }
52 template <class Output> static void write(Output & out, std::uint64_t retained, bit_view suffix) {
53 header(out, retained, suffix.size()); out.append(suffix);
54 }
55 };
56 template <class C> struct sort_profile_key<fc_bit_key<C>> {
57 static constexpr bool front_coded = true;
58 static bit_view order_view(bit_string const & key) { return key.view(); }
59 static bit_string order(bit_string const & key) { return key; }
60 static bit_string decode_order(bit_view key) { return bit_string::copy(key); }
61 static fc_key_frame read(sort_bit_reader & in, std::uint64_t retained) {
62 return {retained, in.take_bits(in.template read_count<C>())};
63 }
64 template <class Output> static void header(Output & out, std::uint64_t, std::uint64_t suffix_bits) {
65 out.template write_count<C>(suffix_bits);
66 }
67 template <class Output> static void write(Output & out, std::uint64_t retained, bit_view suffix) {
68 header(out, retained, suffix.size()); out.append(suffix);
69 }
70 };
71 template <unsigned N> struct sort_profile_key<unsigned_key<N>> {
72 static constexpr bool front_coded = false;
73 static bit_string order(std::uint64_t key) {
74 bit_string result; sort_bit_writer out(result); out.write_bits(key, N); return result;
75 }
76 static std::uint64_t decode_order(bit_view key) {
77 if (key.size() != N) throw std::invalid_argument("integer order key width");
78 sort_bit_reader input(key); return input.read_bits(N);
79 }
80 static fc_key_frame read(sort_bit_reader & in, std::uint64_t retained) {
81 if (retained) throw std::invalid_argument("raw integer has an inherited prefix");
82 return {0, in.take_bits(N)};
83 }
84 template <class Output> static void header(Output &, std::uint64_t retained, std::uint64_t suffix_bits) {
85 if (retained || suffix_bits != N) throw std::invalid_argument("raw integer frame width");
86 }
87 template <class Output> static void write(Output & out, std::uint64_t retained, bit_view suffix) {
88 header(out, retained, suffix.size()); out.append(suffix);
89 }
90 };
91 template <class C> struct sort_profile_key<raw_string_key<C>> {
92 static constexpr bool front_coded = false;
93 static bit_view order_view(std::string const & key) { return sort_codec_detail::string_bits(key); }
94 static bit_string order(std::string const & key) { return bit_string::copy(sort_codec_detail::string_bits(key)); }
95 static std::string decode_order(bit_view key) {
96 if (key.size() & 7) throw std::invalid_argument("string order key ends inside byte");
97 std::string result(static_cast<std::size_t>(key.size() >> 3), '\0');
98 profile_detail::copy_bits(reinterpret_cast<std::byte *>(result.data()), 0, key);
99 return result;
100 }
101 static fc_key_frame read(sort_bit_reader & in, std::uint64_t retained) {
102 if (retained) throw std::invalid_argument("raw string has an inherited prefix");
103 return {0, in.take_bits(profile_detail::multiply(in.template read_count<C>(), 8))};
104 }
105 template <class Output> static void header(Output & out, std::uint64_t retained, std::uint64_t suffix_bits) {
106 if (retained || (suffix_bits & 7)) throw std::invalid_argument("raw string frame width");
107 out.template write_count<C>(suffix_bits >> 3);
108 }
109 template <class Output> static void write(Output & out, std::uint64_t retained, bit_view suffix) {
110 header(out, retained, suffix.size()); out.append(suffix);
111 }
112 };
113
114 namespace sort_profile_detail {
115 template <simd::architecture Arch = simd::scalar>
116 inline bit_comparison compare_spans(std::span<bit_view const> a, std::span<bit_view const> b,
117 std::uint64_t first_a = 0, std::uint64_t first_b = 0) {
118 std::size_t i = 0, j = 0;
119 while (i != a.size() && first_a >= a[i].size()) first_a -= a[i++].size();
120 while (j != b.size() && first_b >= b[j].size()) first_b -= b[j++].size();
121 std::uint64_t common = 0;
122 while (i != a.size() && j != b.size()) {
123 auto width = std::min(a[i].size() - first_a, b[j].size() - first_b);
124 auto cmp = compare_common_bits<Arch>(a[i].subview(first_a, width), b[j].subview(first_b, width));
125 common += cmp.common_bits;
126 if (cmp.order) return {common, cmp.order};
127 first_a += width; first_b += width;
128 if (first_a == a[i].size()) { ++i; first_a = 0; }
129 if (first_b == b[j].size()) { ++j; first_b = 0; }
130 }
131 while (i != a.size() && !a[i].size()) ++i;
132 while (j != b.size() && !b[j].size()) ++j;
133 return {common, i == a.size() ? j == b.size() ? 0 : -1 : 1};
134 }
135 template <class List> struct visit;
136 template <class S, class... Rest> struct visit<registry_detail::sorts<S, Rest...>> {
137 template <class F> static decltype(auto) at(std::size_t index, F && f) {
138 if (!index) return f(std::type_identity<S>{});
139 if constexpr (sizeof...(Rest)) return visit<registry_detail::sorts<Rest...>>::at(index - 1, std::forward<F>(f));
140 else throw std::invalid_argument("sort profile leaf index");
141 }
142 };
143 template <class List, class S> struct ordinal;
144 template <class S, class... Rest> struct ordinal<registry_detail::sorts<S, Rest...>, S>
145 : std::integral_constant<std::size_t, 0> {};
146 template <class T, class... Rest, class S> struct ordinal<registry_detail::sorts<T, Rest...>, S>
147 : std::integral_constant<std::size_t, 1 + ordinal<registry_detail::sorts<Rest...>, S>::value> {};
149 auto result = bit_string::copy(a); profile_detail::append(result, b); return result;
150 }
151 }
152
153 template <class P, class Selector = registry_selector<typename P::registry_type>> struct sort_profile_array;
154 template <class P, class Selector = registry_selector<typename P::registry_type>> struct sort_profile_view;
155 template <class P, class Selector = registry_selector<typename P::registry_type>> struct sort_profile_cursor;
156 template <class P, class Selector = registry_selector<typename P::registry_type>> struct sort_profile_writer;
157 namespace sort_profile_detail { template <class P, class Selector> struct encoder; }
158
161 std::uint64_t value_start = 0, end = 0;
162 };
164 std::uint64_t ordinal = 0, retained = 0, key_units = 0, next_offset = 0;
165 std::array<bit_view, 2> literal;
168 std::size_t leaf = 0;
169 bool front_coded = false;
170 // Transient output hint, not a field in the encoded grammar.
171 std::optional<std::uint64_t> retained_limit_bits{};
172 sort_profile_payload (*parse)(sort_bit_reader &, std::uint64_t) = nullptr;
173 std::uint64_t continuation() const noexcept { return front_coded ? key_units : path.size(); }
174 };
175
176 template <class P, class Selector> struct sort_profile_view {
177 static_assert(P::unit == profile_unit::bit, "sort profiles use bit addresses");
178 using policy_type = P;
181 // dictionary_offsets contains the start and EOF bit positions of prefix-free
182 // selector codes. Seeds are packed IDs; one occupied sort takes zero bits.
187 if (metadata.version != 3 || metadata.key_unit != profile_unit::bit || metadata.group_size != P::group_size ||
188 metadata.codec_block_size != P::codec_block_size || metadata.extent != data.size() ||
190 throw std::invalid_argument("sort profile metadata mismatch");
194 metadata.backspace_code != P::backspace_code || metadata.backspace_parameter != P::backspace_parameter ||
198 throw std::invalid_argument("noncanonical sort profile metadata");
199 auto m = dictionary_size();
200 if (bool(size()) != bool(m) || offsets.size() != block_count() + 1 ||
204 throw std::invalid_argument("sort profile section shape mismatch");
205 }
206 std::uint64_t size() const noexcept { return metadata_.record_count; }
207 std::uint64_t block_count() const noexcept { return size() / P::codec_block_size + (size() % P::codec_block_size != 0); }
208 std::uint64_t dictionary_size() const noexcept { return dictionary_offsets_.size() - 1; }
209 unsigned seed_width() const noexcept { return dictionary_size() < 2 ? 0 : std::bit_width(dictionary_size() - 1); }
210 std::uint64_t block_offset(std::uint64_t block) const {
211 if (block > block_count()) throw std::out_of_range("sort profile block");
212 auto ordinal = block == block_count() ? size() : block * P::codec_block_size;
213 return offsets_.template select<typename P::architecture>(block) + ordinal * metadata_.common_value_width.value_or(0);
214 }
215 profile_metadata const & metadata() const noexcept { return metadata_; }
216 bit_view data() const noexcept { return data_; }
217 bit_view dictionary() const noexcept { return dictionary_; }
219 bit_view seeds() const noexcept { return seeds_; }
220 elias_fano_view group_offsets() const noexcept { return offsets_; }
221 cursor_type cursor() const { return cursor_type(*this); }
222 sort_profile_frame encoded_at(std::uint64_t ordinal) const {
223 if (ordinal >= size()) throw std::out_of_range("sort profile ordinal");
224 auto block = ordinal / P::codec_block_size;
225 auto result = start_block(block);
226 while (result.ordinal < ordinal) result = next(result);
227 return result;
228 }
230 auto ordinal = previous.ordinal + 1;
231 if (ordinal >= size()) throw std::out_of_range("sort profile successor");
232 if (!(ordinal % P::codec_block_size)) {
233 if (block_offset(ordinal / P::codec_block_size) != previous.next_offset)
234 throw std::invalid_argument("sort profile block offset mismatch");
235 return start_block(ordinal / P::codec_block_size, &previous);
236 }
237 auto at = previous.next_offset;
238 auto backspace = profile_detail::read_backspace<P>(data_, at);
239 if (backspace > previous.continuation()) throw std::invalid_argument("sort profile backspace exceeds cursor");
240 auto retained = previous.continuation() - backspace;
241 sort_profile_frame result;
242 result.ordinal = ordinal;
243 result.leaf = previous.leaf;
244 result.path = previous.path;
245 result.parse = previous.parse; result.front_coded = previous.front_coded;
246 bool same = retained >= previous.path.size();
247 if (!same) {
248 sort_bit_reader input(data_.subview(at, data_.size() - at));
249 bit_string path; sort_bit_writer out(path);
250 sort_codec_detail::prefix_reader prefix{input, previous.path.prefix(retained), out};
251 Selector::select(prefix, [&]<class S>(std::type_identity<S>, auto &) {
252 select<S>(result);
253 });
254 if (prefix.at != retained) throw std::invalid_argument("sort profile prefix crosses selector leaf");
255 at += input.position();
256 bool found = false;
257 for (std::uint64_t i = 0; i != dictionary_size(); ++i) {
258 auto first = dictionary_offsets_[i], last = dictionary_offsets_[i + 1];
259 if (first > last || last > dictionary_.size()) throw std::invalid_argument("sort profile dictionary range");
260 auto candidate = dictionary_.subview(first, last - first);
261 if (!compare_bits<typename P::architecture>(candidate, path.view())) { result.path = candidate; found = true; break; }
262 }
263 if (!found) throw std::invalid_argument("sort transition absent from dictionary");
264 }
265 return payload(at, retained, std::move(result), same);
266 }
267 template <class F> void compare_window(std::uint64_t first, std::uint64_t last,
268 profile_query_context<P> context, F && callback, profile_comparison_work * work = nullptr) const {
269 if (first > last || last > size() || last - first > P::group_size)
270 throw std::out_of_range("sort profile query window");
271 if (first == last) return;
272 auto record = encoded_at(first);
273 if (work) work->skipped_headers += first % P::codec_block_size;
274 for (auto i = first; i != last; ++i) {
275 auto count = context.advance_parts(record.retained, record.key_units, record.literal);
276 if (work) { ++work->visited_headers; work->compared_bits += count; }
277 if (!callback(profile_comparison_item<P>{i, context, record.value})) return;
278 if (i + 1 != last) record = next(record);
279 }
280 }
281 void scan() const {
284 throw std::invalid_argument("sort profile section endpoints");
285 if (!size()) return;
286 auto c = cursor();
287 while (!c.done()) {
288 auto comparison = c.advance_comparison();
289 if (comparison && comparison->order >= 0) throw std::invalid_argument("unsorted sort profile");
290 }
291 }
292 private:
299 sort_profile_frame start_block(std::uint64_t block, sort_profile_frame const * previous = nullptr) const {
300 auto id = seed_width() ? profile_detail::load_bits(seeds_, block * seed_width(), seed_width()) : 0;
301 if (id >= dictionary_size()) throw std::invalid_argument("sort profile seed ID");
302 auto first = dictionary_offsets_[id], last = dictionary_offsets_[id + 1];
303 if (first > last || last > dictionary_.size()) throw std::invalid_argument("sort profile dictionary range");
304 auto path = dictionary_.subview(first, last - first);
305 sort_bit_reader code(path);
306 sort_profile_frame result;
307 if (previous && previous->path.storage().data() == path.storage().data() &&
308 previous->path.offset() == path.offset() && previous->path.size() == path.size()) {
309 result.leaf = previous->leaf; result.parse = previous->parse; result.front_coded = previous->front_coded;
310 } else {
311 Selector::select(code, [&]<class S>(std::type_identity<S>, auto &) { select<S>(result); });
312 if (!code.empty()) throw std::invalid_argument("trailing selector code bits");
313 }
314 result.path = path;
315 auto at = block_offset(block);
316 auto retained = profile_detail::read_count<P>(data_, at);
317 result.ordinal = block * P::codec_block_size;
318 return payload(at, retained, std::move(result), true, true);
319 }
320 template <class S> static void select(sort_profile_frame & frame) {
323 frame.parse = [](sort_bit_reader & in, std::uint64_t retained) {
324 auto key = sort_profile_key<typename sort_codec<S>::key_codec>::read(in, retained);
325 auto start = in.position(); sort_codec<S>::value_codec::skip(in);
326 return sort_profile_payload{key, start, in.position()};
327 };
328 }
329 sort_profile_frame payload(std::uint64_t at, std::uint64_t retained, sort_profile_frame result, bool same, bool restart = false) const {
330 sort_bit_reader in(data_.subview(at, data_.size() - at));
331 auto local = same ? retained : result.path.size();
332 if (local < result.path.size()) throw std::invalid_argument("sort profile retained position precedes leaf");
333 // The selected handler survives every same-sort frame. Selector dispatch
334 // occurs only on a sort transition or independently entered block.
335 auto parsed = result.parse(in, local - result.path.size());
336 auto frame = parsed.key;
337 bool inherit = same && (frame.retained_bits || !restart);
338 result.retained = inherit ? retained : same ? 0 : retained;
339 result.literal = {inherit ? bit_view{} :
340 result.path.subview(result.retained, result.path.size() - result.retained), frame.literal};
341 result.key_units = profile_detail::add(result.path.size(), frame.size());
342 result.value = data_.subview(at + parsed.value_start, parsed.end - parsed.value_start);
344 throw std::invalid_argument("sort profile fixed value width mismatch");
345 result.next_offset = at + parsed.end;
346 return result;
347 }
348 };
349
350 template <class P, class Selector> struct sort_profile_cursor {
352 // ordinal must be the native lower bound of query, as for profile_cursor.
354 : view_(view), ordinal_(ordinal) {
355 if (ordinal > view.size()) throw std::out_of_range("sort profile cursor ordinal");
356 if (!done()) {
357 frame_ = view_.encoded_at(ordinal);
358 if (frame_.retained > query.size()) throw std::invalid_argument("sort cursor missing query prefix");
360 for (auto part : frame_.literal) profile_detail::append(key_, part);
361 }
362 }
363 bool done() const noexcept { return ordinal_ == view_.size(); }
365 if (done()) throw std::out_of_range("sort profile cursor end");
367 }
368 std::uint64_t retained_bits() const {
369 if (done()) throw std::out_of_range("sort profile cursor end");
370 return frame_.retained;
371 }
372 void advance() { (void)advance_comparison(); }
373 std::optional<bit_comparison> advance_comparison() {
374 if (done()) throw std::out_of_range("sort profile cursor end");
375 ++ordinal_;
376 if (done()) {
377 if (frame_.next_offset != view_.data().size() || frame_.key_units != view_.metadata().terminal_key_units)
378 throw std::invalid_argument("sort profile terminal framing mismatch");
379 return std::nullopt;
380 }
381 frame_ = view_.next(frame_);
382 if (frame_.retained > key_.bit_size) throw std::invalid_argument("sort cursor missing inherited prefix");
383 std::array<bit_view, 1> before{key_.view()};
384 auto comparison = sort_profile_detail::compare_spans<typename P::architecture>(before, frame_.literal, frame_.retained);
385 comparison.common_bits += frame_.retained;
387 for (auto part : frame_.literal) profile_detail::append(key_, part);
388 return comparison;
389 }
390 private:
394 std::uint64_t ordinal_ = 0;
395 };
396
404
405 template <class P, class Selector> struct sort_profile_array {
406 using policy_type = P;
409 auto view() const & {
411 word_view(std::span<std::uint64_t const>(dictionary_offsets_)), seeds_.view());
412 }
413 auto view() const && = delete;
414 std::uint64_t size() const noexcept { return metadata_.record_count; }
415 auto const & metadata() const noexcept { return metadata_; }
416 auto const & group_offsets() const & noexcept { return offsets_; }
417 auto const & data() const & noexcept { return data_; }
418 auto const & dictionary() const & noexcept { return dictionary_; }
419 std::span<std::uint64_t const> dictionary_offsets() const noexcept { return dictionary_offsets_; }
420 auto const & seeds() const & noexcept { return seeds_; }
421 // Allocated output capacities, excluding allocator/control-block overhead.
422 std::size_t retained_bytes() const noexcept {
423 return sizeof(*this) + data_.bytes.capacity() + dictionary_.bytes.capacity() + seeds_.bytes.capacity() +
424 8 * (dictionary_offsets_.capacity() + offsets_.low.capacity() + offsets_.high.capacity() + offsets_.sparse.capacity()) +
425 sizeof(elias_fano_sample) * offsets_.samples.capacity();
426 }
427 private:
428 friend struct sort_profile_writer<P, Selector>;
429 friend struct sort_profile_detail::encoder<P, Selector>;
431 std::vector<std::uint64_t> dictionary_offsets_{0};
432 elias_fano offsets_ = elias_fano::build<typename P::architecture>(std::array<std::uint64_t, 1>{0});
433 profile_metadata metadata_ = [] { auto m = profile_detail::initial_metadata<P, stream_role::native>(); m.version = 3; return m; }();
435 std::vector<std::uint64_t> dictionary_offsets, elias_fano offsets, profile_metadata metadata)
436 : data_(std::move(data)), dictionary_(std::move(dictionary)), seeds_(std::move(seeds)),
437 dictionary_offsets_(std::move(dictionary_offsets)), offsets_(std::move(offsets)), metadata_(metadata) {}
438 };
439
440 namespace sort_profile_detail {
441 // Shared wire framing. A sink supplies position, append and write_count;
442 // payload ownership and sealing stay outside this small navigation state.
443 template <class P, class Selector> struct encoder {
444 static_assert(P::unit == profile_unit::bit, "sort profile framing uses bit addresses");
446 profile_metadata metadata = [] { auto m = profile_detail::initial_metadata<P, stream_role::native>(); m.version = 3; return m; }();
448 std::vector<std::uint64_t> dictionary_offsets{0};
450 std::uint64_t size() const noexcept { return metadata.record_count; }
451 template <class Output> void append_frame(Output & out, sort_profile_frame const & frame,
452 std::span<bit_view const> spans, std::uint64_t common, bit_view value) {
453 visit<leaves>::at(frame.leaf, [&]<class S>(std::type_identity<S>) {
454 append<S>(out, frame.path, frame.key_units - frame.path.size(), spans, value, common, frame.retained_limit_bits);
455 });
456 }
457 template <class S, class Output> void append(Output & out, bit_view path, std::uint64_t key_bits,
458 std::span<bit_view const> key, bit_view value, std::uint64_t common,
459 std::optional<std::uint64_t> retained_limit_bits = {}) {
461 sort_bit_reader validate(value);
463 if (!validate.empty()) throw std::invalid_argument("trailing sort profile value bits");
464 auto total = std::uint64_t{0};
465 for (auto part : key) total = profile_detail::add(total, part.size());
466 if (total != profile_detail::add(path.size(), key_bits) || common > total)
467 throw std::invalid_argument("sort merge key spans");
468 constexpr auto leaf = ordinal<leaves, S>::value;
469 auto found = std::find(ids_.begin(), ids_.end(), leaf);
470 auto id = std::size_t(found - ids_.begin());
471 if (found == ids_.end()) {
472 ids_.push_back(leaf); profile_detail::append(dictionary, path);
474 }
475 auto same = size() && compare_bits<typename P::architecture>(previous_path_.view(), path) == 0;
476 auto retained = same && codec::front_coded ? common : path.size();
477 if (retained < path.size() || retained > total) throw std::invalid_argument("sort retained prefix outside key");
478 if (retained_limit_bits)
479 retained = std::min(retained, std::max(path.size(), *retained_limit_bits));
480 if (!(size() % P::codec_block_size)) {
481 raw_offsets_.push_back(out.position()); block_seeds_.push_back(id);
482 out.template write_count<exponential_golomb<0>>(retained);
483 } else {
484 auto joint = same ? retained : compare_common_bits<typename P::architecture>(previous_path_.view(), path).common_bits;
485 if (joint > previous_continuation_) throw std::invalid_argument("sort backspace exceeds continuation");
486 out.template write_count<typename P::backspace_encoding>(previous_continuation_ - joint);
487 if (!same) out.append(path.subview(joint, path.size() - joint));
488 }
489 auto local = retained - path.size();
490 codec::header(out, local, key_bits - local);
491 auto skip = retained;
492 for (auto part : key) {
493 auto prefix = std::min(skip, part.size()); skip -= prefix;
494 out.append(part.subview(prefix, part.size() - prefix));
495 }
496 out.append(value);
497 if (!size()) metadata.common_value_width = value.size();
498 else if (metadata.common_value_width != value.size()) metadata.common_value_width.reset();
502 previous_continuation_ = path.size() + (codec::front_coded ? key_bits : 0);
503 }
504 void finish(std::uint64_t extent) {
505 raw_offsets_.push_back(extent);
506 auto common = metadata.common_value_width.value_or(0);
507 for (std::size_t i = 0; i != raw_offsets_.size(); ++i) {
508 auto ordinal = i + 1 == raw_offsets_.size() ? size() : i * P::codec_block_size;
509 raw_offsets_[i] -= ordinal * common;
510 }
511 offsets = elias_fano::build<typename P::architecture>(raw_offsets_);
512 auto width = ids_.size() < 2 ? 0u : std::bit_width(ids_.size() - 1);
514 for (auto id : block_seeds_) out.write_bits(id, unsigned(width));
515 metadata.extent = extent;
516 }
517 // The same completed framing state supplies owning and streamed output.
518 // Neither adoption nor serialization repeats key/value composition.
520 return {std::move(data), std::move(dictionary), std::move(seeds),
521 std::move(dictionary_offsets), std::move(offsets), metadata};
522 }
523 private:
525 std::vector<std::size_t> ids_;
526 std::vector<std::uint64_t> raw_offsets_, block_seeds_;
527 std::uint64_t previous_continuation_ = 0;
528 };
529 }
530
531 template <class P, class Selector> struct sort_profile_writer {
532 using policy_type = P;
534 template <class S> void append(typename sort_codec<S>::key_codec::value_type const & key,
535 typename sort_codec<S>::value_codec::value_type const & value,
536 std::optional<std::uint64_t> retained_limit_bits = {}) {
538 if (encoded_only_) throw std::logic_error("typed append after encoded sort frames");
539 sort_codec_detail::validate_value_width<S>();
540 bit_string path, encoded_value;
541 sort_bit_writer path_out(path), value_out(encoded_value);
542 Selector::template write<S>(path_out);
543 sort_codec<S>::value_codec::write(value_out, value);
545 auto logical = sort_profile_detail::concatenate(path.view(), bits.view());
546 auto comparison = compare_common_bits<typename P::architecture>(previous_.view(), logical.view());
547 if (size() && comparison.order >= 0) throw std::invalid_argument("sort profile requires unique sorted keys");
548 std::array<bit_view, 1> spans{logical.view()};
549 try {
550 sort_bit_writer out(data_);
551 encoder_.template append<S>(out, path.view(), bits.bit_size, spans, encoded_value.view(), comparison.common_bits, retained_limit_bits);
552 previous_ = std::move(logical);
553 } catch (...) { failed_ = true; throw; }
554 }
555 // Trusted sorted merge output: spans describe the full logical key, but
556 // only the suffix after the known output LCP is copied to the file.
557 void append_frame(sort_profile_frame const & frame, std::span<bit_view const> spans,
558 std::uint64_t common, bit_view value) {
560 try {
562 encoder_.append_frame(out, frame, spans, common, value);
563 encoded_only_ = true;
564 } catch (...) { failed_ = true; throw; }
565 }
566 std::uint64_t size() const noexcept { return encoder_.size(); }
567 bool failed() const noexcept { return failed_; }
568 bool finished() const noexcept { return finished_; }
571 try {
572 encoder_.finish(data_.bit_size);
573 auto result = encoder_.take(std::move(data_));
574 finished_ = true; return result;
575 } catch (...) { failed_ = true; throw; }
576 }
577 private:
581 bool failed_ = false, finished_ = false, encoded_only_ = false;
582 void require_active() const {
583 if (failed_ || finished_) throw std::logic_error("inactive sort profile writer");
584 }
585 };
586
587 template <class P, class S, class Selector = registry_selector<typename P::registry_type>>
590 auto encode = [](bit_view bits) {
591 bit_string result;
592 // Width is an optional selector hint. Selection still runs for each query.
593 if constexpr (requires { typename std::integral_constant<std::uint64_t, Selector::template code_size<S>>; }) {
594 auto size = profile_detail::add(Selector::template code_size<S>, bits.size());
595 auto bytes = profile_detail::add(size, 7) >> 3;
596 if (bytes > result.bytes.max_size()) throw std::length_error("sort query key too large");
597 result.bytes.reserve(static_cast<std::size_t>(bytes));
598 }
599 sort_bit_writer out(result);
600 Selector::template write<S>(out);
601 out.append(bits);
602 return result;
603 };
604 if constexpr (requires { key_codec::order_view(key); }) return encode(key_codec::order_view(key));
605 else {
606 auto bits = key_codec::order(key);
607 return encode(bits.view());
608 }
609 }
610}
Declares dual-target main/secondary fractional indexes for COLA.
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
unsigned prefix(std::uint64_t const *words, unsigned count) noexcept
Definition rank_groups.h:113
bit_view string_bits(std::string const &value)
Definition sort_codec.h:31
bit_string concatenate(bit_view a, bit_view b)
Definition sort_profile.h:148
bit_comparison compare_spans(std::span< bit_view const > a, std::span< bit_view const > b, std::uint64_t first_a=0, std::uint64_t first_b=0)
Definition sort_profile.h:116
bit_string value(arrow_t< S > const &value)
Definition typed_world.h:99
bit_string key(key_t< S > const &value)
Definition typed_world.h:89
Definition active_engine.h:18
bit_string sort_profile_query(typename sort_codec< S >::key_codec::value_type const &key)
Definition sort_profile.h:588
Encodes typed bit records with inherited sort prefixes and sort-owned grammars.
Definition profile.h:209
Definition profile.h:166
std::vector< std::byte > bytes
Definition profile.h:167
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 profile.h:56
std::uint64_t size() const noexcept
Definition profile.h:64
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 elias_fano.h:195
Definition elias_fano.h:212
std::uint64_t size() const noexcept
Definition elias_fano.h:256
std::uint64_t universe() const noexcept
Definition elias_fano.h:254
Definition elias_fano.h:397
std::vector< std::uint64_t > sparse
Definition elias_fano.h:445
std::vector< std::uint64_t > high
Definition elias_fano.h:443
std::vector< std::uint64_t > low
Definition elias_fano.h:442
std::vector< elias_fano_sample > samples
Definition elias_fano.h:444
elias_fano_view view() const &
Definition elias_fano.h:434
Definition sort_codec.h:257
Definition sort_codec.h:186
Definition sort_codec.h:228
static profile_anchor complete(bit_view key)
Definition profile.h:253
Definition profile.h:1280
Definition profile.h:458
Definition profile.h:452
Definition profile.h:302
Definition profile.h:273
profile_unit offset_unit
Definition profile.h:278
std::uint64_t policy_value_width
Definition profile.h:285
profile_unit key_unit
Definition profile.h:275
std::uint64_t extent
Definition profile.h:291
profile_unit value_unit
Definition profile.h:276
profile_count_code count_code
Definition profile.h:279
profile_bit_order bit_order
Definition profile.h:282
std::uint64_t backspace_parameter
Definition profile.h:281
std::uint64_t terminal_key_units
Definition profile.h:289
stream_role role
Definition profile.h:283
std::uint32_t version
Definition profile.h:274
profile_unit count_unit
Definition profile.h:277
bool policy_fixed_values
Definition profile.h:284
bit_backspace_code backspace_code
Definition profile.h:280
std::uint64_t record_count
Definition profile.h:290
std::uint64_t codec_block_size
Definition profile.h:288
std::optional< std::uint64_t > common_value_width
Definition profile.h:286
std::uint64_t group_size
Definition profile.h:287
Definition profile.h:328
std::uint64_t advance_parts(std::uint64_t retained_bits, std::uint64_t full_bits, std::span< bit_view const > literal)
Definition profile.h:357
Definition sort_codec.h:330
Definition registry.h:122
Definition registry.h:83
Definition sort_profile.h:20
static decltype(auto) select(Input &in, Visitor &&visitor)
Definition sort_profile.h:22
static void write(Output &out)
Definition sort_profile.h:25
static constexpr std::uint64_t code_size
Definition sort_profile.h:21
Definition sort_codec.h:56
bool empty() const noexcept
Definition sort_codec.h:60
std::uint64_t read_bits(unsigned width)
Definition sort_codec.h:61
std::uint64_t position() const noexcept
Definition sort_codec.h:58
bit_view take_bits(std::uint64_t count)
Definition sort_codec.h:81
Definition sort_codec.h:38
void append(bit_view bits)
Definition sort_codec.h:48
void write_bits(std::uint64_t value, unsigned width)
Definition sort_codec.h:41
Definition sort_codec.h:426
Definition sort_codec.h:401
Definition sort_profile.h:405
auto const & data() const &noexcept
Definition sort_profile.h:417
std::uint64_t size() const noexcept
Definition sort_profile.h:414
auto view() const &&=delete
bit_string data_
Definition sort_profile.h:430
std::vector< std::uint64_t > dictionary_offsets_
Definition sort_profile.h:431
bit_string seeds_
Definition sort_profile.h:430
std::span< std::uint64_t const > dictionary_offsets() const noexcept
Definition sort_profile.h:419
std::size_t retained_bytes() const noexcept
Definition sort_profile.h:422
auto view() const &
Definition sort_profile.h:409
P policy_type
Definition sort_profile.h:406
elias_fano offsets_
Definition sort_profile.h:432
sort_profile_array(bit_string data, bit_string dictionary, bit_string seeds, std::vector< std::uint64_t > dictionary_offsets, elias_fano offsets, profile_metadata metadata)
Definition sort_profile.h:434
auto const & metadata() const noexcept
Definition sort_profile.h:415
auto const & dictionary() const &noexcept
Definition sort_profile.h:418
auto const & seeds() const &noexcept
Definition sort_profile.h:420
profile_metadata metadata_
Definition sort_profile.h:433
bit_string dictionary_
Definition sort_profile.h:430
auto const & group_offsets() const &noexcept
Definition sort_profile.h:416
Definition sort_profile.h:350
bool done() const noexcept
Definition sort_profile.h:363
profile_item< P > peek() const
Definition sort_profile.h:364
std::uint64_t retained_bits() const
Definition sort_profile.h:368
sort_profile_view< P, Selector > view_
Definition sort_profile.h:391
sort_profile_cursor(sort_profile_view< P, Selector > view, std::uint64_t ordinal, bit_view query)
Definition sort_profile.h:353
sort_profile_cursor(sort_profile_view< P, Selector > view)
Definition sort_profile.h:351
std::uint64_t ordinal_
Definition sort_profile.h:394
sort_profile_frame frame_
Definition sort_profile.h:392
void advance()
Definition sort_profile.h:372
bit_string key_
Definition sort_profile.h:393
std::optional< bit_comparison > advance_comparison()
Definition sort_profile.h:373
Definition sort_profile.h:443
void finish(std::uint64_t extent)
Definition sort_profile.h:504
bit_string dictionary
Definition sort_profile.h:447
profile_metadata metadata
Definition sort_profile.h:446
typename registry_detail::info< typename P::registry_type >::leaves leaves
Definition sort_profile.h:445
void append_frame(Output &out, sort_profile_frame const &frame, std::span< bit_view const > spans, std::uint64_t common, bit_view value)
Definition sort_profile.h:451
std::vector< std::uint64_t > raw_offsets_
Definition sort_profile.h:526
std::vector< std::uint64_t > dictionary_offsets
Definition sort_profile.h:448
elias_fano offsets
Definition sort_profile.h:449
bit_string previous_path_
Definition sort_profile.h:524
bit_string seeds
Definition sort_profile.h:447
std::vector< std::size_t > ids_
Definition sort_profile.h:525
std::uint64_t size() const noexcept
Definition sort_profile.h:450
std::uint64_t previous_continuation_
Definition sort_profile.h:527
std::vector< std::uint64_t > block_seeds_
Definition sort_profile.h:526
void append(Output &out, bit_view path, std::uint64_t key_bits, std::span< bit_view const > key, bit_view value, std::uint64_t common, std::optional< std::uint64_t > retained_limit_bits={})
Definition sort_profile.h:457
sort_profile_array< P, Selector > take(bit_string data)
Definition sort_profile.h:519
Definition sort_profile.h:143
static decltype(auto) at(std::size_t index, F &&f)
Definition sort_profile.h:137
Definition sort_profile.h:135
Definition sort_profile.h:397
Selector selector_type
Definition sort_profile.h:398
Definition sort_profile.h:163
std::array< bit_view, 2 > literal
Definition sort_profile.h:165
std::uint64_t next_offset
Definition sort_profile.h:164
std::uint64_t key_units
Definition sort_profile.h:164
bool front_coded
Definition sort_profile.h:169
std::uint64_t continuation() const noexcept
Definition sort_profile.h:173
std::uint64_t retained
Definition sort_profile.h:164
sort_profile_payload(* parse)(sort_bit_reader &, std::uint64_t)
Definition sort_profile.h:172
std::size_t leaf
Definition sort_profile.h:168
bit_view path
Definition sort_profile.h:167
std::optional< std::uint64_t > retained_limit_bits
Definition sort_profile.h:171
bit_view value
Definition sort_profile.h:166
std::uint64_t ordinal
Definition sort_profile.h:164
static bit_string order(bit_string const &key)
Definition sort_profile.h:59
static bit_string decode_order(bit_view key)
Definition sort_profile.h:60
static bit_view order_view(bit_string const &key)
Definition sort_profile.h:58
static void header(Output &out, std::uint64_t, std::uint64_t suffix_bits)
Definition sort_profile.h:64
static fc_key_frame read(sort_bit_reader &in, std::uint64_t retained)
Definition sort_profile.h:61
static void write(Output &out, std::uint64_t retained, bit_view suffix)
Definition sort_profile.h:67
static bit_view order_view(std::string const &key)
Definition sort_profile.h:35
static bit_string order(std::string const &key)
Definition sort_profile.h:36
static fc_key_frame read(sort_bit_reader &in, std::uint64_t retained)
Definition sort_profile.h:43
static void header(Output &out, std::uint64_t retained, std::uint64_t suffix_bits)
Definition sort_profile.h:48
static void write(Output &out, std::uint64_t retained, bit_view suffix)
Definition sort_profile.h:52
static std::string decode_order(bit_view key)
Definition sort_profile.h:37
static bit_view order_view(std::string const &key)
Definition sort_profile.h:93
static bit_string order(std::string const &key)
Definition sort_profile.h:94
static std::string decode_order(bit_view key)
Definition sort_profile.h:95
static void header(Output &out, std::uint64_t retained, std::uint64_t suffix_bits)
Definition sort_profile.h:105
static fc_key_frame read(sort_bit_reader &in, std::uint64_t retained)
Definition sort_profile.h:101
static void write(Output &out, std::uint64_t retained, bit_view suffix)
Definition sort_profile.h:109
static std::uint64_t decode_order(bit_view key)
Definition sort_profile.h:76
static void write(Output &out, std::uint64_t retained, bit_view suffix)
Definition sort_profile.h:87
static bit_string order(std::uint64_t key)
Definition sort_profile.h:73
static fc_key_frame read(sort_bit_reader &in, std::uint64_t retained)
Definition sort_profile.h:80
static void header(Output &, std::uint64_t retained, std::uint64_t suffix_bits)
Definition sort_profile.h:84
Definition sort_profile.h:32
Definition sort_profile.h:159
fc_key_frame key
Definition sort_profile.h:160
std::uint64_t end
Definition sort_profile.h:161
std::uint64_t value_start
Definition sort_profile.h:161
Definition sort_profile.h:176
void scan() const
Definition sort_profile.h:281
std::uint64_t size() const noexcept
Definition sort_profile.h:206
P policy_type
Definition sort_profile.h:178
sort_profile_frame payload(std::uint64_t at, std::uint64_t retained, sort_profile_frame result, bool same, bool restart=false) const
Definition sort_profile.h:329
sort_profile_view(bit_view data, elias_fano_view offsets, profile_metadata metadata, bit_view dictionary, word_view dictionary_offsets, bit_view seeds)
Definition sort_profile.h:183
typename registry_detail::info< typename P::registry_type >::leaves leaves
Definition sort_profile.h:179
bit_view data_
Definition sort_profile.h:293
bit_view dictionary_
Definition sort_profile.h:296
std::uint64_t dictionary_size() const noexcept
Definition sort_profile.h:208
sort_profile_frame encoded_at(std::uint64_t ordinal) const
Definition sort_profile.h:222
profile_metadata metadata_
Definition sort_profile.h:295
profile_metadata const & metadata() const noexcept
Definition sort_profile.h:215
sort_profile_frame next(sort_profile_frame const &previous) const
Definition sort_profile.h:229
static void select(sort_profile_frame &frame)
Definition sort_profile.h:320
bit_view seeds() const noexcept
Definition sort_profile.h:219
bit_view seeds_
Definition sort_profile.h:298
std::uint64_t block_offset(std::uint64_t block) const
Definition sort_profile.h:210
void compare_window(std::uint64_t first, std::uint64_t last, profile_query_context< P > context, F &&callback, profile_comparison_work *work=nullptr) const
Definition sort_profile.h:267
cursor_type cursor() const
Definition sort_profile.h:221
word_view dictionary_offsets_
Definition sort_profile.h:297
unsigned seed_width() const noexcept
Definition sort_profile.h:209
sort_profile_frame start_block(std::uint64_t block, sort_profile_frame const *previous=nullptr) const
Definition sort_profile.h:299
elias_fano_view group_offsets() const noexcept
Definition sort_profile.h:220
std::uint64_t block_count() const noexcept
Definition sort_profile.h:207
word_view dictionary_offsets() const noexcept
Definition sort_profile.h:218
elias_fano_view offsets_
Definition sort_profile.h:294
bit_view data() const noexcept
Definition sort_profile.h:216
bit_view dictionary() const noexcept
Definition sort_profile.h:217
sort_profile_cursor< P, Selector > cursor_type
Definition sort_profile.h:180
Definition sort_profile.h:531
bool finished() const noexcept
Definition sort_profile.h:568
P policy_type
Definition sort_profile.h:532
void require_active() const
Definition sort_profile.h:582
bit_string previous_
Definition sort_profile.h:580
sort_profile_detail::encoder< P, Selector > encoder_
Definition sort_profile.h:579
void append(typename sort_codec< S >::key_codec::value_type const &key, typename sort_codec< S >::value_codec::value_type const &value, std::optional< std::uint64_t > retained_limit_bits={})
Definition sort_profile.h:534
array_type finish()
Definition sort_profile.h:569
bool failed_
Definition sort_profile.h:581
bool finished_
Definition sort_profile.h:581
bool encoded_only_
Definition sort_profile.h:581
void append_frame(sort_profile_frame const &frame, std::span< bit_view const > spans, std::uint64_t common, bit_view value)
Definition sort_profile.h:557
bit_string data_
Definition sort_profile.h:578
bool failed() const noexcept
Definition sort_profile.h:567
std::uint64_t size() const noexcept
Definition sort_profile.h:566
Definition sort_codec.h:348
Definition word_view.h:31
std::size_t size() const noexcept
Definition word_view.h:41