Everett
Loading...
Searching...
No Matches
cola_index.h
Go to the documentation of this file.
1
13#pragma once
14
17#include <everett/sampling.h>
18
19#include <algorithm>
20#include <array>
21#include <cstddef>
22#include <cstdint>
23#include <limits>
24#include <memory>
25#include <optional>
26#include <span>
27#include <stdexcept>
28#include <type_traits>
29#include <utility>
30#include <vector>
31
32namespace everett {
33 enum class cola_target : unsigned { main = 0, secondary = 1 };
34 enum class cola_origin : unsigned { native = 0, main = 1, secondary = 2 };
35 namespace cola_detail {
36 struct query_access;
37 inline unsigned route(unsigned value) {
38 if (value >= 2) error_detail::raise<std::out_of_range>("COLA target route");
39 return value;
40 }
41 inline std::uint64_t target_ordinal(std::uint64_t ordinal, std::uint64_t stride) {
42 if (ordinal > std::numeric_limits<std::uint64_t>::max() / stride)
43 error_detail::raise<std::overflow_error>("COLA target ordinal overflow");
44 return ordinal * stride;
45 }
47 unsigned origin = 3;
48 std::array<std::uint64_t, 3> common{};
49 };
50 // Every live head follows the same preceding occurrence. Larger LCPs
51 // sort first; equal LCPs need only a suffix comparison. Stable origin order
52 // preserves native, main-borrow, secondary-borrow ties.
53 template <simd::architecture Arch = simd::scalar, class Live, class Head> frontier_selection choose_frontier(
54 std::array<std::uint64_t, 3> const & prefixes, Live live, Head head) {
55 frontier_selection result;
56 bit_view key;
57 for (unsigned origin = 0; origin < 3; ++origin) if (live(origin)) {
58 auto candidate = head(origin);
59 if (result.origin == 3) {
60 result.origin = origin; key = candidate; result.common[origin] = key.size();
61 continue;
62 }
63 auto first = prefixes[origin], second = prefixes[result.origin];
64 bit_comparison comparison;
65 if (first != second) comparison = {std::min(first, second), first > second ? -1 : 1};
66 else {
67 comparison = compare_common_bits<Arch>(candidate.subview(first, candidate.size() - first),
68 key.subview(first, key.size() - first));
69 comparison.common_bits += first;
70 }
71 if (comparison.order < 0) {
72 // New winner <= old winner <= each old loser: ordered LCP minimum.
73 for (unsigned i = 0; i < origin; ++i)
74 result.common[i] = std::min(result.common[i], comparison.common_bits);
75 result.origin = origin; key = candidate;
76 result.common[origin] = key.size();
77 } else result.common[origin] = comparison.common_bits;
78 }
79 if (result.origin == 3) error_detail::raise<std::invalid_argument>("COLA frontier has no live stream");
80 return result;
81 }
82
83 }
84
85 struct cola_window {
86 std::uint64_t native_first = 0, native_last = 0;
87 std::array<std::uint64_t, 2> borrowed_first{}, borrowed_last{};
88 };
89 template <class P> struct cola_window_result {
90 std::optional<profile_blob_native_match<P>> native;
91 std::array<std::optional<profile_blob_borrowed_predecessor<P>>, 2> predecessors;
92 };
93
94 template <class P> struct cola_lower_bound_result {
95 std::uint64_t native_ordinal = 0;
96 std::array<std::optional<profile_blob_borrowed_predecessor<P>>, 2> predecessors;
97 };
98
105 template <class P, class Native, class = void> struct cola_stream_family { using type = profile_stream_family<P>; };
106 template <class P, class Native> struct cola_stream_family<P, Native, std::void_t<typename Native::stream_family>> {
107 using type = typename Native::stream_family;
108 };
109
110 // Both population directories refer to the same three-way augmented order.
111 // Native keys win ties, followed by main borrows, then secondary borrows.
112 // Shapes are checked without scanning payload. Contents, flags and cut LCPs
113 // must come from this builder or an independently validated reader.
114 template <class P, class Family = profile_stream_family<P>> struct cola_index_view {
115 using policy_type = P;
116 using native_view = typename Family::native_view;
117 using borrowed_view = typename Family::borrowed_view;
119 static constexpr auto group_size = P::group_size;
120 cola_index_view(native_view native, std::array<borrowed_view, 2> borrowed,
121 std::array<rank_view, 2> ranks, std::array<std::span<std::byte const>, 2> flags,
122 std::array<word_view, 2> cuts, std::uint64_t count)
123 : native_(native), borrowed_(borrowed), ranks_(ranks), flags_(flags), cuts_(cuts), count_(count) {
124 auto remaining = std::numeric_limits<std::uint64_t>::max() - native.size();
125 if (borrowed[0].size() > remaining || borrowed[1].size() > remaining - borrowed[0].size() ||
126 native.size() + borrowed[0].size() + borrowed[1].size() != count)
127 error_detail::raise<std::invalid_argument>("COLA augmented count mismatch");
128 for (unsigned route = 0; route < 2; ++route) {
129 auto size = borrowed[route].size();
130 if (size > std::numeric_limits<std::uint64_t>::max() - 7 || ranks[route].size() != count ||
131 flags[route].size() != ((size + 7) >> 3) || cuts[route].size() != group_count())
132 error_detail::raise<std::invalid_argument>("COLA section shape mismatch");
133 }
134 }
135 native_view const & native() const noexcept { return native_; }
136 borrowed_view const & borrowed(unsigned route) const { return borrowed_[cola_detail::route(route)]; }
137 borrowed_view const & borrowed(cola_target route) const { return borrowed(unsigned(route)); }
138 rank_view const & interleave(unsigned route) const { return ranks_[cola_detail::route(route)]; }
139 rank_view const & interleave(cola_target route) const { return interleave(unsigned(route)); }
140 std::span<std::byte const> false_borrow_bits(unsigned route) const { return flags_[cola_detail::route(route)]; }
141 std::span<std::byte const> false_borrow_bits(cola_target route) const { return false_borrow_bits(unsigned(route)); }
142 word_view cut_lcps(unsigned route) const { return cuts_[cola_detail::route(route)]; }
143 word_view cut_lcps(cola_target route) const { return cut_lcps(unsigned(route)); }
144 std::uint64_t virtual_size() const noexcept { return count_; }
145 std::uint64_t group_count() const noexcept { return count_ / group_size + (count_ % group_size != 0); }
146 bool false_borrow(unsigned route, std::uint64_t ordinal) const {
147 route = cola_detail::route(route);
148 if (ordinal >= borrowed_[route].size()) error_detail::raise<std::out_of_range>("COLA borrowed ordinal");
149 return (std::to_integer<unsigned>(flags_[route][ordinal >> 3]) >> (ordinal & 7)) & 1;
150 }
151 bool false_borrow(cola_target route, std::uint64_t ordinal) const { return false_borrow(unsigned(route), ordinal); }
152 cola_window project(std::uint64_t group) const {
153 if (group >= group_count()) error_detail::raise<std::out_of_range>("COLA virtual group");
154 auto first = group * group_size;
155 auto width = std::min<std::uint64_t>(group_size, count_ - first);
156 std::array<std::uint64_t, 2> prefix{}, population{};
157 for (unsigned route = 0; route < 2; ++route) {
158 prefix[route] = ranks_[route].template rank<typename P::architecture>(group);
159 population[route] = ranks_[route].class_at(group);
160 if (prefix[route] > first || prefix[route] > borrowed_[route].size() ||
161 population[route] > width || population[route] > borrowed_[route].size() - prefix[route])
162 error_detail::raise<std::invalid_argument>("invalid COLA rank projection");
163 }
164 if (prefix[0] > first - prefix[1] || population[0] > width - population[1])
165 error_detail::raise<std::invalid_argument>("overlapping COLA population directories");
166 auto native_first = first - prefix[0] - prefix[1];
167 auto native_last = native_first + width - population[0] - population[1];
168 if (native_last > native_.size()) error_detail::raise<std::invalid_argument>("invalid COLA native projection");
169 return {native_first, native_last, prefix,
170 {prefix[0] + population[0], prefix[1] + population[1]}};
171 }
173 profile_comparison_work * native_work = nullptr,
174 std::array<profile_comparison_work *, 2> borrowed_work = {}) const {
175 return search_window_with<cola_window_result<P>>(group, lower,
176 [](std::uint64_t ordinal, bit_view value) {
177 return profile_blob_native_match<P>{ordinal, bit_string::copy(value)};
178 }, native_work, borrowed_work);
179 }
180 // The canonical routed window contains the native lower bound, except
181 // that an equal false borrow can follow its native occurrence past a cut.
183 profile_comparison_work * work = nullptr) const {
184 if (lower.order() > 0) error_detail::raise<std::invalid_argument>("query precedes COLA boundary");
185 auto window = project(group);
186 cola_lower_bound_result<P> result{window.native_last, {}};
187 bool equal = false;
188 native_.compare_window(window.native_first, window.native_last, lower,
190 if (item.comparison.order() >= 0) {
191 result.native_ordinal = item.ordinal;
192 equal = !item.comparison.order();
193 return false;
194 }
195 return true;
196 }, work);
197 for (unsigned route = 0; route < 2; ++route) {
198 auto remember = [&](std::uint64_t ordinal, profile_query_context<P> const & comparison) {
199 auto is_false = false_borrow(route, ordinal);
200 result.predecessors[route] = profile_blob_borrowed_predecessor<P>{ordinal,
201 cola_detail::target_ordinal(ordinal, group_size), is_false, comparison};
202 if (!comparison.order() && is_false && !equal) {
203 if (!window.native_first) error_detail::raise<std::invalid_argument>("COLA false borrow has no native predecessor");
204 result.native_ordinal = std::min(result.native_ordinal, window.native_first - 1);
205 }
206 };
207 borrowed_[route].compare_window(window.borrowed_first[route], window.borrowed_last[route], lower,
208 [&](profile_comparison_item<P> item) {
209 if (item.comparison.order() > 0) return false;
210 remember(item.ordinal, item.comparison);
211 return true;
212 }, work);
213 if (!result.predecessors[route] && window.borrowed_first[route])
214 remember(window.borrowed_first[route] - 1, lower.predecessor(cuts_[route][group]));
215 }
216 return result;
217 }
218 private:
220 template <class Result, class Capture> Result search_window_with(std::uint64_t group,
221 profile_query_context<P> const & lower, Capture && capture,
222 profile_comparison_work * native_work = nullptr,
223 std::array<profile_comparison_work *, 2> borrowed_work = {}) const {
224 if (lower.order() > 0) error_detail::raise<std::invalid_argument>("query precedes COLA boundary");
225 auto window = project(group);
226 Result result;
227 native_.compare_window(window.native_first, window.native_last, lower,
229 if (!item.comparison.order())
230 result.native = capture(item.ordinal, item.value);
231 return item.comparison.order() < 0;
232 }, native_work);
233 for (unsigned route = 0; route < 2; ++route) {
234 auto remember = [&](std::uint64_t ordinal, profile_query_context<P> const & comparison) {
235 auto is_false = false_borrow(route, ordinal);
236 result.predecessors[route] = profile_blob_borrowed_predecessor<P>{ordinal,
237 cola_detail::target_ordinal(ordinal, group_size), is_false, comparison};
238 if (!comparison.order() && is_false && !result.native) {
239 if (!window.native_first) error_detail::raise<std::invalid_argument>("COLA false borrow has no native predecessor");
240 auto native_ordinal = window.native_first - 1;
241 result.native = capture(native_ordinal, native_.encoded_at(native_ordinal).value);
242 }
243 };
244 borrowed_[route].compare_window(window.borrowed_first[route], window.borrowed_last[route], lower,
245 [&](profile_comparison_item<P> item) {
246 if (item.comparison.order() > 0) return false;
247 remember(item.ordinal, item.comparison);
248 return true;
249 }, borrowed_work[route]);
250 if (!result.predecessors[route] && window.borrowed_first[route])
251 remember(window.borrowed_first[route] - 1, lower.predecessor(cuts_[route][group]));
252 }
253 return result;
254 }
256 std::array<borrowed_view, 2> borrowed_;
257 std::array<rank_view, 2> ranks_;
258 std::array<std::span<std::byte const>, 2> flags_;
259 std::array<word_view, 2> cuts_;
260 std::uint64_t count_;
261 };
262
263 // A secondary is a native-only leaf. Its sampled ordinal directly names a
264 // native K-window; it has no onward index route.
265 namespace cola_detail {
266 template <class P, class View, class Capture> void visit_secondary(View const & leaf,
267 profile_blob_borrowed_predecessor<P> const & predecessor, Capture && capture,
268 profile_comparison_work * work = nullptr) {
269 auto first = predecessor.target_ordinal;
270 if (first >= leaf.size() || first % P::group_size || predecessor.comparison.order() > 0)
271 error_detail::raise<std::invalid_argument>("invalid COLA secondary route");
272 auto last = first + std::min<std::uint64_t>(P::group_size, leaf.size() - first);
273 leaf.compare_window(first, last, predecessor.comparison, [&](profile_comparison_item<P> item) {
274 if (!item.comparison.order()) capture(item.ordinal, item.value);
275 return item.comparison.order() < 0;
276 }, work);
277 }
278 }
279 template <class P, class View> std::optional<profile_blob_native_match<P>> cola_search_secondary(
280 View leaf, profile_blob_borrowed_predecessor<P> const & predecessor,
281 profile_comparison_work * work = nullptr) {
282 std::optional<profile_blob_native_match<P>> result;
283 cola_detail::visit_secondary<P>(leaf, predecessor, [&](std::uint64_t ordinal, bit_view value) {
284 result = profile_blob_native_match<P>{ordinal, bit_string::copy(value)};
285 }, work);
286 return result;
287 }
288
289 template <class P, class Native = profile_array<P>, class Main = void> struct cola_index;
290 template <class P, class Target = cola_index<P>> struct cola_sample_cursor;
291 namespace cola_detail {
292 template <class P, class Native, class Main> struct index_output;
293 template <class P> struct index_metadata {
294 std::array<rank_groups<P::group_size>, 2> ranks;
295 std::array<std::vector<std::byte>, 2> flags;
296 std::array<std::vector<std::uint64_t>, 2> cuts;
297 std::uint64_t count = 0;
298 };
299 }
300 template <class P, class Native = profile_array<P>, class Main = void,
301 class Output = cola_detail::index_output<P, Native, Main>> struct cola_index_builder;
302 template <class P> struct cola_sample_view {
304 std::uint64_t target_ordinal = 0;
305 cola_origin origin = cola_origin::native;
306 std::uint64_t source_ordinal = 0;
307 };
308
309 // Immutable main node with one recursive main edge and one native-only leaf.
310 // This is a fractional-index representation, not a scheduler or chronology.
311 // Default parameters own native arrays and a homogeneous main chain. A
312 // supplied Native/Main pair instead yields a construction artifact retaining
313 // those exact owners; its borrowed payload/navigation are newly built output.
314 // Persist such a mixed artifact before adopting a homogeneous mapped query.
315 template <class P, class Native, class Main> struct cola_index {
316 using policy_type = P;
317 using native_array = Native;
318 using target_type = std::conditional_t<std::is_void_v<Main>, cola_index, Main>;
321 using borrowed_array = typename stream_family::borrowed_array;
322 using native_pointer = std::shared_ptr<native_array const>;
323 using pair_type = std::shared_ptr<cola_index const>;
324 using main_pointer = std::shared_ptr<target_type const>;
325 static constexpr auto group_size = P::group_size;
326 static cola_index build(std::span<profile_record const> records, main_pointer main = {}, native_pointer secondary = {})
327 requires (std::is_same_v<Native, profile_array<P>> && std::is_void_v<Main>);
328 // The adopted array must be immutable, ordinary-FC and strictly sorted.
329 static cola_index adopt_native(native_pointer native, main_pointer main = {}, native_pointer secondary = {});
330 // Include both level-zero arrays, then add empty routing nodes until the
331 // first augmented group suffices. Neither handle may be silently dropped.
332 static pair_type prepare_root(pair_type main = {}, native_pointer secondary = {})
333 requires (std::is_same_v<Native, profile_array<P>> && std::is_void_v<Main>);
334 cola_index(cola_index const &) = delete;
335 cola_index & operator=(cola_index const &) = delete;
336 cola_index(cola_index &&) = default;
338 native_pointer native_owner() const noexcept { return native_; }
339 native_array const & native() const & { require_active(); return *native_; }
340 native_array const & native() const && = delete;
341 borrowed_array const & borrowed(unsigned route) const & { require_active(); return borrowed_[cola_detail::route(route)]; }
342 borrowed_array const & borrowed(unsigned) const && = delete;
343 borrowed_array const & borrowed(cola_target route) const & { return borrowed(unsigned(route)); }
344 borrowed_array const & borrowed(cola_target) const && = delete;
345 rank_groups<group_size> const & interleave(unsigned route) const & { require_active(); return ranks_[cola_detail::route(route)]; }
346 rank_groups<group_size> const & interleave(unsigned) const && = delete;
347 rank_groups<group_size> const & interleave(cola_target route) const & { return interleave(unsigned(route)); }
349 std::span<std::byte const> false_borrow_bits(unsigned route) const & { require_active(); return flags_[cola_detail::route(route)]; }
350 std::span<std::byte const> false_borrow_bits(unsigned) const && = delete;
351 std::span<std::byte const> false_borrow_bits(cola_target route) const & { return false_borrow_bits(unsigned(route)); }
352 std::span<std::byte const> false_borrow_bits(cola_target) const && = delete;
353 std::span<std::uint64_t const> cut_lcps(unsigned route) const & { require_active(); return cuts_[cola_detail::route(route)]; }
354 std::span<std::uint64_t const> cut_lcps(unsigned) const && = delete;
355 std::span<std::uint64_t const> cut_lcps(cola_target route) const & { return cut_lcps(unsigned(route)); }
356 std::span<std::uint64_t const> cut_lcps(cola_target) const && = delete;
357 main_pointer main_target() const noexcept { return main_; }
358 native_pointer secondary_target() const noexcept { return secondary_; }
359 std::uint64_t virtual_size() const noexcept { return native_ ? count_ : 0; }
360 std::uint64_t group_count() const noexcept { auto n = virtual_size(); return n / group_size + (n % group_size != 0); }
361 view_type view() const & {
362 require_active();
363 return {native_->view(), {borrowed_[0].view(), borrowed_[1].view()}, {ranks_[0].view(), ranks_[1].view()},
364 {flags_[0], flags_[1]}, {word_view(std::span<std::uint64_t const>(cuts_[0])),
365 word_view(std::span<std::uint64_t const>(cuts_[1]))}, count_};
366 }
367 view_type view() const && = delete;
368 cola_window project(std::uint64_t group) const { return view().project(group); }
369 cola_window_result<P> search_window(std::uint64_t group, profile_query_context<P> const & lower,
370 profile_comparison_work * native_work = nullptr,
371 std::array<profile_comparison_work *, 2> borrowed_work = {}) const {
372 return view().search_window(group, lower, native_work, borrowed_work);
373 }
374 private:
375 friend struct cola_detail::index_output<P, Native, Main>;
379 std::array<borrowed_array, 2> borrowed_;
380 std::array<rank_groups<group_size>, 2> ranks_;
381 std::array<std::vector<std::byte>, 2> flags_;
382 std::array<std::vector<std::uint64_t>, 2> cuts_;
383 std::uint64_t count_;
384 void require_active() const {
385 if (!native_) error_detail::raise<std::logic_error>("moved-from COLA index");
386 }
388 std::array<borrowed_array, 2> borrowed, std::array<rank_groups<group_size>, 2> ranks,
389 std::array<std::vector<std::byte>, 2> flags, std::array<std::vector<std::uint64_t>, 2> cuts,
390 std::uint64_t count)
391 : native_(std::move(native)), main_(std::move(main)), secondary_(std::move(secondary)),
392 borrowed_(std::move(borrowed)), ranks_(std::move(ranks)), flags_(std::move(flags)), cuts_(std::move(cuts)), count_(count) {}
393 };
394
395 namespace cola_detail {
396 // Default output owns the two FC payloads. Alternate concrete outputs
397 // consume the same known-prefix stream and final sparse navigation.
398 template <class P, class Native, class Main> struct index_output {
402 bool failed() const noexcept { return false; }
403 void append_known(unsigned route, bit_view key, std::uint64_t common) { writers_[route].append_known(key, common); }
405 std::array<typename index_type::borrowed_array, 2> borrowed{writers_[0].finish(), writers_[1].finish()};
406 return adopt(std::move(native), std::move(main), std::move(secondary), std::move(borrowed), std::move(metadata));
407 }
409 std::array<typename index_type::borrowed_array, 2> borrowed, index_metadata<P> metadata) {
410 return index_type(std::move(native), std::move(main), std::move(secondary), std::move(borrowed),
411 std::move(metadata.ranks), std::move(metadata.flags), std::move(metadata.cuts), metadata.count);
412 }
413 private:
414 std::array<typename index_type::stream_family::borrowed_writer, 2> writers_;
415 };
416 }
417
418 // Samples the exact local three-way augmented order, retaining its owner.
419 // Generic mapped targets provide the same cola_index_view through view().
420 template <class P, class Target> struct cola_sample_cursor {
421 using policy_type = P;
422 explicit cola_sample_cursor(std::shared_ptr<Target const> target) : cola_sample_cursor(bind(std::move(target))) {}
423 bool done() const noexcept { return !target_ || ordinal_ == count_; }
424 std::shared_ptr<Target const> target() const noexcept { return target_; }
426 if (done()) error_detail::raise<std::out_of_range>("COLA sampler at end");
427 auto origin = choose().origin;
428 if (!origin) { auto item = native_.peek(); return {item.key.prefix, ordinal_, cola_origin::native, item.ordinal}; }
429 auto item = borrowed_[origin - 1].peek();
430 return {item.key.prefix, ordinal_, cola_origin(origin), item.ordinal};
431 }
432 cola_sample_view<P> peek() const && = delete;
433 void advance() { (void)advance_impl<false>(); }
434 // Exact old/new sampled-key comparison, accumulated across at most K
435 // adjacent transitions. EOF has no successor and returns nullopt.
436 std::optional<bit_comparison> advance_comparison() { return advance_impl<true>(); }
437
438 private:
439 template <bool Compare> std::optional<bit_comparison> advance_impl() {
440 if (done()) error_detail::raise<std::out_of_range>("COLA sampler at end");
441 auto selected = choose();
442 auto first_length = selected.common[selected.origin];
443 auto common = first_length;
444 auto width = std::min<std::uint64_t>(P::group_size, count_ - ordinal_);
445 for (std::uint64_t i = 0; i < width; ++i) {
446 prefixes_ = selected.common;
447 auto next = !selected.origin ? native_.advance_comparison() :
448 borrowed_[selected.origin - 1].advance_comparison();
449 if (next && (next->order > 0 || (!selected.origin && !next->order)))
450 error_detail::raise<std::invalid_argument>("COLA sample source order mismatch");
451 prefixes_[selected.origin] = next ? next->common_bits : 0;
452 ++ordinal_;
453 if (!done() && (Compare || i + 1 < width)) {
454 selected = choose();
455 if constexpr (Compare) common = std::min(common, prefixes_[selected.origin]);
456 }
457 }
458 if constexpr (Compare) if (!done())
459 return bit_comparison{common, common == first_length &&
460 selected.common[selected.origin] == first_length ? 0 : -1};
461 return std::nullopt;
462 }
463 using view_type = decltype(std::declval<Target const &>().view());
464 using native_cursor = decltype(std::declval<typename view_type::native_view>().cursor());
465 using borrowed_cursor = decltype(std::declval<typename view_type::borrowed_view>().cursor());
466 struct binding { std::shared_ptr<Target const> target; view_type view; };
467 std::shared_ptr<Target const> target_;
469 std::array<borrowed_cursor, 2> borrowed_;
470 std::array<std::uint64_t, 3> prefixes_{};
471 std::uint64_t count_, ordinal_ = 0;
472 static binding bind(std::shared_ptr<Target const> target) {
473 if (!target) error_detail::raise<std::invalid_argument>("null COLA sample target");
474 auto view = target->view();
475 return {std::move(target), std::move(view)};
476 }
478 : target_(std::move(source.target)), native_(source.view.native()),
479 borrowed_{borrowed_cursor(source.view.borrowed(0)), borrowed_cursor(source.view.borrowed(1))},
480 count_(source.view.virtual_size()) {}
482 return cola_detail::choose_frontier<typename P::architecture>(prefixes_,
483 [&](unsigned origin) { return !origin ? !native_.done() : !borrowed_[origin - 1].done(); },
484 [&](unsigned origin) { return !origin ? native_.peek().key.prefix : borrowed_[origin - 1].peek().key.prefix; });
485 }
486 };
487
488 // Each step consumes at most budget merged occurrences. Consuming a sampled
489 // head advances its source by at most K occurrences, and bytes/comparison and
490 // allocation remain additional work. Only current cursor/key contexts are
491 // reconstructed; borrowed payload and compact navigation are output storage.
492 // Without targets only native count is inspected; step charges occurrences
493 // while doing one zero-navigation update per crossed K-group, without keys.
494 // Native contents must already be trusted/admitted; this metadata-only path
495 // deliberately does not revalidate their framing or strictly sorted order.
496 template <class P, class Native, class Main, class Output> struct cola_index_builder {
497 using policy_type = P;
503 explicit cola_index_builder(native_pointer native, main_pointer main = {}, native_pointer secondary = {})
504 : cola_index_builder(Output{}, std::move(native), std::move(main), std::move(secondary)) {}
505 cola_index_builder(Output output, native_pointer native, main_pointer main = {}, native_pointer secondary = {})
506 : native_(checked(std::move(native))), main_(std::move(main)), secondary_(std::move(secondary)), output_(std::move(output)) {
507 auto remaining = std::numeric_limits<std::uint64_t>::max() - native_->size();
508 auto primary_count = main_ ? main_->group_count() : 0;
509 auto secondary_count = secondary_ ? secondary_->size() / P::group_size + (secondary_->size() % P::group_size != 0) : 0;
510 if (primary_count > remaining || secondary_count > remaining - primary_count)
511 error_detail::raise<std::length_error>("COLA augmented count overflow");
512 if (main_ || secondary_) native_cursor_.emplace(native_->view());
513 if (main_) primary_cursor_.emplace(main_);
514 if (secondary_) secondary_cursor_.emplace(secondary_->view());
515 }
520 bool done() const noexcept {
521 return native_ && !failed_ && (native_cursor_ ? !live(0) && !live(1) && !live(2) : count_ == native_->size());
522 }
523 bool failed() const noexcept { return failed_ || output_.failed(); }
524 bool finished() const noexcept { return finished_; }
525 std::uint64_t size() const noexcept { return count_; }
526 native_pointer native_owner() const noexcept { return native_; }
527 main_pointer main_target() const noexcept { return main_; }
528 native_pointer secondary_target() const noexcept { return secondary_; }
529 std::uint64_t step(std::uint64_t budget) {
530 require_active();
531 std::uint64_t consumed = 0;
532 try {
533 // With no targets, all augmented occurrences are native. Navigation
534 // depends only on their count: do not open a payload cursor at all.
535 if (!native_cursor_) return step_terminal(budget);
536 while (consumed < budget && !done()) {
537 auto selected = cola_detail::choose_frontier<typename P::architecture>(prefixes_,
538 [&](unsigned origin) { return live(origin); },
539 [&](unsigned origin) { return head(origin); });
540 auto origin = selected.origin;
541 auto key = head(origin);
542 auto common = prefixes_[origin];
543 bool equal = count_ && common == previous_length_ && key.size() == previous_length_;
544 if (!origin && equal)
545 error_detail::raise<std::invalid_argument>("COLA sources violate sorted native order");
546 for (unsigned route = 0; route < 2; ++route)
547 if (borrowed_count_[route]) cut_common_[route] = std::min(cut_common_[route], common);
548 if (!width_) for (unsigned route = 0; route < 2; ++route)
549 cuts_[route].push_back(borrowed_count_[route] ? cut_common_[route] : 0);
550 native_equal_ = !origin || (equal && native_equal_);
551 if (origin) {
552 auto route = origin - 1;
553 // Along a sorted walk the minimum adjacent LCP since the preceding
554 // borrow is its exact LCP with this key. Reuse that same frontier
555 // for FC output instead of comparing the inherited prefix again.
556 output_.append_known(route, key, borrowed_count_[route] ? cut_common_[route] : 0);
557 auto ordinal = borrowed_count_[route]++;
558 if (!(ordinal & 7)) flags_[route].push_back(std::byte{0});
559 if (native_equal_) flags_[route].back() |= std::byte(1u << (ordinal & 7));
560 ++population_[route];
561 cut_common_[route] = key.size();
562 }
563 previous_length_ = key.size();
564 prefixes_ = selected.common;
565 if (!origin) prefixes_[0] = advance_native(*native_cursor_);
566 else if (origin == 1) {
567 auto next = primary_cursor_->advance_comparison();
568 prefixes_[1] = next ? next->common_bits : 0;
569 } else {
570 auto next_common = key.size();
571 for (std::uint64_t i = 0; i < P::group_size && !secondary_cursor_->done(); ++i)
572 next_common = std::min(next_common, advance_native(*secondary_cursor_));
573 prefixes_[2] = next_common;
574 }
575 ++count_; ++consumed; ++width_;
576 if (width_ == P::group_size) flush_group();
577 }
578 } catch (...) { failed_ = true; throw; }
579 return consumed;
580 }
581 auto finish() {
582 require_active();
583 if (!done()) error_detail::raise<std::logic_error>("COLA builder has remaining input");
584 try {
585 if (width_) flush_group();
586 cola_detail::index_metadata<P> metadata{{ranks_[0].finish(), ranks_[1].finish()},
587 std::move(flags_), std::move(cuts_), count_};
588 auto result = output_.finish(native_, main_, secondary_, std::move(metadata));
589 finished_ = true;
590 return result;
591 } catch (...) { failed_ = true; throw; }
592 }
593 private:
597 using native_cursor_type = decltype(std::declval<Native const &>().view().cursor());
598 std::optional<native_cursor_type> native_cursor_;
599 std::optional<cola_sample_cursor<P, target_type>> primary_cursor_;
600 std::optional<native_cursor_type> secondary_cursor_;
601 Output output_;
602 std::array<rank_groups_builder<P::group_size>, 2> ranks_;
603 std::array<std::vector<std::byte>, 2> flags_;
604 std::array<std::vector<std::uint64_t>, 2> cuts_;
605 std::array<std::uint64_t, 2> borrowed_count_{}, cut_common_{}, population_{};
606 std::array<std::uint64_t, 3> prefixes_{};
607 std::uint64_t previous_length_ = 0, count_ = 0, width_ = 0;
608 bool native_equal_ = false, failed_ = false, finished_ = false;
610 if (!native) error_detail::raise<std::invalid_argument>("null COLA native source");
611 return native;
612 }
613 void require_active() const {
614 if (!native_ || failed() || finished_) error_detail::raise<std::logic_error>("COLA builder is no longer active");
615 }
616 bool live(unsigned origin) const noexcept {
617 if (!origin) return native_cursor_ && !native_cursor_->done();
618 if (origin == 1) return primary_cursor_ && !primary_cursor_->done();
619 return secondary_cursor_ && !secondary_cursor_->done();
620 }
621 bit_view head(unsigned origin) const {
622 if (!origin) return native_cursor_->peek().key.prefix;
623 if (origin == 1) return primary_cursor_->peek().key;
624 return secondary_cursor_->peek().key.prefix;
625 }
626 static std::uint64_t advance_native(native_cursor_type & cursor) {
627 auto next = cursor.advance_comparison();
628 if (next && next->order >= 0)
629 error_detail::raise<std::invalid_argument>("COLA native source order mismatch");
630 return next ? next->common_bits : 0;
631 }
632 std::uint64_t step_terminal(std::uint64_t budget) {
633 std::uint64_t consumed = 0;
634 auto total = native_->size();
635 while (consumed < budget && count_ < total) {
636 if (!width_) for (auto & cuts : cuts_) cuts.push_back(0);
637 auto chunk = std::min({budget - consumed, total - count_, P::group_size - width_});
638 count_ += chunk; consumed += chunk; width_ += chunk;
639 if (width_ == P::group_size) flush_group();
640 }
641 return consumed;
642 }
643 void flush_group() {
644 for (unsigned route = 0; route < 2; ++route) ranks_[route].append(population_[route], width_);
645 width_ = 0; population_ = {};
646 }
647 };
648
649 template <class P, class Native, class Main>
651 std::span<profile_record const> records, main_pointer main, native_pointer secondary)
652 requires (std::is_same_v<Native, profile_array<P>> && std::is_void_v<Main>) {
654 for (auto const & record : records) writer.append(record);
655 return adopt_native(std::make_shared<native_array const>(writer.finish()), std::move(main), std::move(secondary));
656 }
657 template <class P, class Native, class Main>
659 native_pointer native, main_pointer main, native_pointer secondary) {
660 cola_index_builder<P, Native, Main> builder(std::move(native), std::move(main), std::move(secondary));
661 while (!builder.done()) builder.step(4096);
662 return builder.finish();
663 }
664 template <class P, class Native, class Main>
666 pair_type main, native_pointer secondary)
667 requires (std::is_same_v<Native, profile_array<P>> && std::is_void_v<Main>) {
668 if (main && !secondary && main->virtual_size() <= group_size) return main;
669 auto empty = std::make_shared<native_array const>(native_array::build({}));
670 auto root = std::make_shared<cola_index const>(adopt_native(empty, std::move(main), std::move(secondary)));
671 while (root->virtual_size() > group_size)
672 root = std::make_shared<cola_index const>(adopt_native(empty, std::move(root)));
673 return root;
674 }
675}
unsigned route(unsigned value)
Definition cola_index.h:37
frontier_selection choose_frontier(std::array< std::uint64_t, 3 > const &prefixes, Live live, Head head)
Definition cola_index.h:53
std::uint64_t target_ordinal(std::uint64_t ordinal, std::uint64_t stride)
Definition cola_index.h:41
void visit_secondary(View const &leaf, profile_blob_borrowed_predecessor< P > const &predecessor, Capture &&capture, profile_comparison_work *work=nullptr)
Definition cola_index.h:266
Definition active_engine.h:18
cola_target
Definition cola_index.h:33
std::optional< profile_blob_native_match< P > > cola_search_secondary(View leaf, profile_blob_borrowed_predecessor< P > const &predecessor, profile_comparison_work *work=nullptr)
Definition cola_index.h:279
cola_origin
Definition cola_index.h:34
Builds native front-coded profiles incrementally with explicit value widths.
Declares Everett's profile blob support.
Declares Everett's sequential sampling of pinned encoded blob pairs.
Definition profile.h:209
std::uint64_t common_bits
Definition profile.h:210
int order
Definition profile.h:211
static bit_string copy(bit_view source)
Definition profile.h:181
Definition profile.h:56
bit_view prefix(std::uint64_t count) const
Definition profile.h:80
std::array< std::uint64_t, 3 > common
Definition cola_index.h:48
unsigned origin
Definition cola_index.h:47
Definition cola_index.h:293
std::array< std::vector< std::byte >, 2 > flags
Definition cola_index.h:295
std::array< rank_groups< P::group_size >, 2 > ranks
Definition cola_index.h:294
std::array< std::vector< std::uint64_t >, 2 > cuts
Definition cola_index.h:296
std::uint64_t count
Definition cola_index.h:297
Definition cola_index.h:398
static index_type adopt(native_pointer native, main_pointer main, native_pointer secondary, std::array< typename index_type::borrowed_array, 2 > borrowed, index_metadata< P > metadata)
Definition cola_index.h:408
index_type finish(native_pointer native, main_pointer main, native_pointer secondary, index_metadata< P > metadata)
Definition cola_index.h:404
typename index_type::native_pointer native_pointer
Definition cola_index.h:400
typename index_type::main_pointer main_pointer
Definition cola_index.h:401
void append_known(unsigned route, bit_view key, std::uint64_t common)
Definition cola_index.h:403
std::array< typename index_type::stream_family::borrowed_writer, 2 > writers_
Definition cola_index.h:414
bool failed() const noexcept
Definition cola_index.h:402
Definition cola_query.h:85
Definition cola_index.h:496
native_pointer native_
Definition cola_index.h:594
cola_index_builder(cola_index_builder const &)=delete
main_pointer main_
Definition cola_index.h:595
void flush_group()
Definition cola_index.h:643
native_pointer secondary_target() const noexcept
Definition cola_index.h:528
std::optional< native_cursor_type > secondary_cursor_
Definition cola_index.h:600
cola_index_builder(Output output, native_pointer native, main_pointer main={}, native_pointer secondary={})
Definition cola_index.h:505
native_pointer secondary_
Definition cola_index.h:596
bit_view head(unsigned origin) const
Definition cola_index.h:621
cola_index_builder & operator=(cola_index_builder const &)=delete
std::uint64_t size() const noexcept
Definition cola_index.h:525
cola_index_builder(native_pointer native, main_pointer main={}, native_pointer secondary={})
Definition cola_index.h:503
P policy_type
Definition cola_index.h:497
std::uint64_t step_terminal(std::uint64_t budget)
Definition cola_index.h:632
main_pointer main_target() const noexcept
Definition cola_index.h:527
std::optional< cola_sample_cursor< P, target_type > > primary_cursor_
Definition cola_index.h:599
decltype(std::declval< Native const & >().view().cursor()) native_cursor_type
Definition cola_index.h:597
std::optional< native_cursor_type > native_cursor_
Definition cola_index.h:598
bool failed() const noexcept
Definition cola_index.h:523
static native_pointer checked(native_pointer native)
Definition cola_index.h:609
std::array< std::vector< std::uint64_t >, 2 > cuts_
Definition cola_index.h:604
typename index_type::main_pointer main_pointer
Definition cola_index.h:501
bool live(unsigned origin) const noexcept
Definition cola_index.h:616
bool finished() const noexcept
Definition cola_index.h:524
native_pointer native_owner() const noexcept
Definition cola_index.h:526
typename index_type::target_type target_type
Definition cola_index.h:502
cola_index_builder(cola_index_builder &&)=default
std::array< rank_groups_builder< P::group_size >, 2 > ranks_
Definition cola_index.h:602
std::array< std::vector< std::byte >, 2 > flags_
Definition cola_index.h:603
auto finish()
Definition cola_index.h:581
void require_active() const
Definition cola_index.h:613
bool done() const noexcept
Definition cola_index.h:520
std::uint64_t step(std::uint64_t budget)
Definition cola_index.h:529
Output output_
Definition cola_index.h:601
static std::uint64_t advance_native(native_cursor_type &cursor)
Definition cola_index.h:626
typename index_type::pair_type pair_type
Definition cola_index.h:500
typename index_type::native_pointer native_pointer
Definition cola_index.h:499
cola_index_builder & operator=(cola_index_builder &&)=default
Definition cola_index.h:114
native_view native_
Definition cola_index.h:255
word_view cut_lcps(cola_target route) const
Definition cola_index.h:143
typename Family::native_view native_view
Definition cola_index.h:116
std::uint64_t virtual_size() const noexcept
Definition cola_index.h:144
typename Family::borrowed_view borrowed_view
Definition cola_index.h:117
cola_window project(std::uint64_t group) const
Definition cola_index.h:152
std::array< rank_view, 2 > ranks_
Definition cola_index.h:257
borrowed_view const & borrowed(unsigned route) const
Definition cola_index.h:136
std::span< std::byte const > false_borrow_bits(unsigned route) const
Definition cola_index.h:140
rank_view const & interleave(unsigned route) const
Definition cola_index.h:138
P policy_type
Definition cola_index.h:115
bool false_borrow(cola_target route, std::uint64_t ordinal) const
Definition cola_index.h:151
word_view cut_lcps(unsigned route) const
Definition cola_index.h:142
static constexpr auto group_size
Definition cola_index.h:119
std::span< std::byte const > false_borrow_bits(cola_target route) const
Definition cola_index.h:141
cola_window_result< P > search_window(std::uint64_t group, profile_query_context< P > const &lower, profile_comparison_work *native_work=nullptr, std::array< profile_comparison_work *, 2 > borrowed_work={}) const
Definition cola_index.h:172
std::array< word_view, 2 > cuts_
Definition cola_index.h:259
borrowed_view const & borrowed(cola_target route) const
Definition cola_index.h:137
rank_view const & interleave(cola_target route) const
Definition cola_index.h:139
std::uint64_t group_count() const noexcept
Definition cola_index.h:145
std::uint64_t count_
Definition cola_index.h:260
bool false_borrow(unsigned route, std::uint64_t ordinal) const
Definition cola_index.h:146
native_view const & native() const noexcept
Definition cola_index.h:135
std::array< borrowed_view, 2 > borrowed_
Definition cola_index.h:256
cola_index_view(native_view native, std::array< borrowed_view, 2 > borrowed, std::array< rank_view, 2 > ranks, std::array< std::span< std::byte const >, 2 > flags, std::array< word_view, 2 > cuts, std::uint64_t count)
Definition cola_index.h:120
Result search_window_with(std::uint64_t group, profile_query_context< P > const &lower, Capture &&capture, profile_comparison_work *native_work=nullptr, std::array< profile_comparison_work *, 2 > borrowed_work={}) const
Definition cola_index.h:220
cola_lower_bound_result< P > lower_bound_window(std::uint64_t group, profile_query_context< P > const &lower, profile_comparison_work *work=nullptr) const
Definition cola_index.h:182
std::array< std::span< std::byte const >, 2 > flags_
Definition cola_index.h:258
Definition cola_index.h:315
view_type view() const &
Definition cola_index.h:361
std::array< std::vector< std::uint64_t >, 2 > cuts_
Definition cola_index.h:382
native_array const & native() const &
Definition cola_index.h:339
static cola_index build(std::span< profile_record const > records, main_pointer main={}, native_pointer secondary={})
Definition cola_index.h:650
main_pointer main_target() const noexcept
Definition cola_index.h:357
std::span< std::uint64_t const > cut_lcps(cola_target route) const &
Definition cola_index.h:355
std::array< std::vector< std::byte >, 2 > flags_
Definition cola_index.h:381
std::span< std::uint64_t const > cut_lcps(unsigned route) const &
Definition cola_index.h:353
native_pointer secondary_
Definition cola_index.h:378
borrowed_array const & borrowed(unsigned) const &&=delete
std::conditional_t< std::is_void_v< Main >, cola_index, Main > target_type
Definition cola_index.h:318
typename stream_family::borrowed_array borrowed_array
Definition cola_index.h:321
std::span< std::byte const > false_borrow_bits(unsigned route) const &
Definition cola_index.h:349
std::uint64_t group_count() const noexcept
Definition cola_index.h:360
cola_index(cola_index const &)=delete
std::shared_ptr< native_array const > native_pointer
Definition cola_index.h:322
Native native_array
Definition cola_index.h:317
std::uint64_t count_
Definition cola_index.h:383
std::array< borrowed_array, 2 > borrowed_
Definition cola_index.h:379
std::shared_ptr< cola_index const > pair_type
Definition cola_index.h:323
native_pointer native_
Definition cola_index.h:376
borrowed_array const & borrowed(cola_target) const &&=delete
std::span< std::byte const > false_borrow_bits(cola_target route) const &
Definition cola_index.h:351
native_pointer native_owner() const noexcept
Definition cola_index.h:338
rank_groups< group_size > const & interleave(unsigned route) const &
Definition cola_index.h:345
std::span< std::uint64_t const > cut_lcps(cola_target) const &&=delete
static cola_index adopt_native(native_pointer native, main_pointer main={}, native_pointer secondary={})
Definition cola_index.h:658
std::uint64_t virtual_size() const noexcept
Definition cola_index.h:359
cola_index & operator=(cola_index &&)=default
typename cola_stream_family< P, Native >::type stream_family
Definition cola_index.h:319
rank_groups< group_size > const & interleave(cola_target route) const &
Definition cola_index.h:347
rank_groups< group_size > const & interleave(cola_target) const &&=delete
std::shared_ptr< target_type const > main_pointer
Definition cola_index.h:324
main_pointer main_
Definition cola_index.h:377
borrowed_array const & borrowed(cola_target route) const &
Definition cola_index.h:343
std::array< rank_groups< group_size >, 2 > ranks_
Definition cola_index.h:380
native_array const & native() const &&=delete
cola_index(cola_index &&)=default
std::span< std::uint64_t const > cut_lcps(unsigned) const &&=delete
std::span< std::byte const > false_borrow_bits(cola_target) const &&=delete
native_pointer secondary_target() const noexcept
Definition cola_index.h:358
rank_groups< group_size > const & interleave(unsigned) const &&=delete
void require_active() const
Definition cola_index.h:384
std::span< std::byte const > false_borrow_bits(unsigned) const &&=delete
P policy_type
Definition cola_index.h:316
cola_index & operator=(cola_index const &)=delete
static pair_type prepare_root(pair_type main={}, native_pointer secondary={})
Definition cola_index.h:665
cola_index(native_pointer native, main_pointer main, native_pointer secondary, std::array< borrowed_array, 2 > borrowed, std::array< rank_groups< group_size >, 2 > ranks, std::array< std::vector< std::byte >, 2 > flags, std::array< std::vector< std::uint64_t >, 2 > cuts, std::uint64_t count)
Definition cola_index.h:387
view_type view() const &&=delete
Definition cola_index.h:94
std::array< std::optional< profile_blob_borrowed_predecessor< P > >, 2 > predecessors
Definition cola_index.h:96
std::uint64_t native_ordinal
Definition cola_index.h:95
Definition cola_index.h:466
std::shared_ptr< Target const > target
Definition cola_index.h:466
Definition cola_index.h:420
cola_sample_view< P > peek() const &&=delete
cola_sample_cursor(std::shared_ptr< Target const > target)
Definition cola_index.h:422
cola_sample_cursor(binding source)
Definition cola_index.h:477
static binding bind(std::shared_ptr< Target const > target)
Definition cola_index.h:472
decltype(std::declval< Target const & >().view()) view_type
Definition cola_index.h:463
std::array< borrowed_cursor, 2 > borrowed_
Definition cola_index.h:469
std::optional< bit_comparison > advance_comparison()
Definition cola_index.h:436
P policy_type
Definition cola_index.h:421
std::uint64_t count_
Definition cola_index.h:471
decltype(std::declval< typename view_type::native_view >().cursor()) native_cursor
Definition cola_index.h:464
std::optional< bit_comparison > advance_impl()
Definition cola_index.h:439
native_cursor native_
Definition cola_index.h:468
std::shared_ptr< Target const > target_
Definition cola_index.h:467
cola_detail::frontier_selection choose() const
Definition cola_index.h:481
decltype(std::declval< typename view_type::borrowed_view >().cursor()) borrowed_cursor
Definition cola_index.h:465
bool done() const noexcept
Definition cola_index.h:423
cola_sample_view< P > peek() const &
Definition cola_index.h:425
std::shared_ptr< Target const > target() const noexcept
Definition cola_index.h:424
Definition cola_index.h:302
bit_view key
Definition cola_index.h:303
typename Native::stream_family type
Definition cola_index.h:107
Definition cola_index.h:105
Definition cola_index.h:89
std::array< std::optional< profile_blob_borrowed_predecessor< P > >, 2 > predecessors
Definition cola_index.h:91
std::optional< profile_blob_native_match< P > > native
Definition cola_index.h:90
Definition cola_index.h:85
std::array< std::uint64_t, 2 > borrowed_first
Definition cola_index.h:87
std::uint64_t native_last
Definition cola_index.h:86
std::array< std::uint64_t, 2 > borrowed_last
Definition cola_index.h:87
std::uint64_t native_first
Definition cola_index.h:86
Definition profile_blob.h:52
profile_query_context< P > comparison
Definition profile_blob.h:57
std::uint64_t target_ordinal
Definition profile_blob.h:55
Definition profile_blob.h:45
Definition profile.h:1280
Definition profile.h:458
std::uint64_t ordinal
Definition profile.h:459
Definition profile.h:452
Definition native_writer.h:158
void append(bit_view key, bit_view value, std::optional< std::uint64_t > retained_limit_bits={})
Definition native_writer.h:181
profile_array< P > finish()
Definition native_writer.h:208
Definition profile.h:328
int order() const noexcept
Definition profile.h:352
profile_query_context predecessor(std::uint64_t lcp_bits) const
Definition profile.h:437
Definition cola_index.h:99
Definition rank_groups.h:241
Definition word_view.h:31