Everett
Loading...
Searching...
No Matches
profile_blob.h
Go to the documentation of this file.
1
13#pragma once
14
16
17#include <everett/profile.h>
19#include <everett/rank_groups.h>
20#include <everett/word_view.h>
21
22#include <algorithm>
23#include <cstddef>
24#include <cstdint>
25#include <limits>
26#include <memory>
27#include <optional>
28#include <span>
29#include <stdexcept>
30#include <utility>
31#include <vector>
32
33namespace everett {
34 template <class P, class Native = profile_array<P>, class Output = profile_detail::index_output<P>>
35 struct index_builder;
36
38 std::uint64_t native_first = 0;
39 std::uint64_t native_last = 0;
40 std::uint64_t borrowed_first = 0;
41 std::uint64_t borrowed_last = 0;
42 };
43
44 template <class P>
46 using policy_type = P;
47 std::uint64_t ordinal = 0;
49 };
50
51 template <class P>
53 using policy_type = P;
54 std::uint64_t ordinal = 0;
55 std::uint64_t target_ordinal = 0;
56 bool false_borrow = false;
58 };
59
60 template <class P>
62 using policy_type = P;
63 std::optional<profile_blob_native_match<P>> native;
64 std::optional<profile_blob_borrowed_predecessor<P>> borrowed_predecessor;
65 };
66
67 // A borrowed query view over one native/index pair. The backing sections
68 // must remain immutable and outlive the view. Construction checks shapes;
69 // query-time projection validates the selected ranks before subtraction.
70 // Exact sample keys, cut LCPs and dependency identities require admission
71 // validation by the owner; this view does not scan their contents.
72 template <class P>
74 using policy_type = P;
75 static constexpr std::uint64_t group_size = P::group_size;
78
80 rank_groups_view<group_size> interleave, std::span<std::byte const> false_borrows,
81 word_view cut_lcps, std::uint64_t virtual_count)
83 false_borrows_(false_borrows), cut_lcps_(cut_lcps), virtual_count_(virtual_count) {
84 if (borrowed.size() > std::numeric_limits<std::uint64_t>::max() - 7 ||
85 native.size() > std::numeric_limits<std::uint64_t>::max() - borrowed.size() ||
86 native.size() + borrowed.size() != virtual_count ||
87 interleave.size() != virtual_count ||
88 false_borrows.size() != ((borrowed.size() + 7) >> 3) ||
90 error_detail::raise<std::invalid_argument>("profile blob section shape mismatch");
91 }
92
93 native_view const & native() const noexcept { return native_; }
94 borrowed_view const & borrowed() const noexcept { return borrowed_; }
95 rank_groups_view<group_size> const & interleave() const noexcept { return interleave_; }
96 std::span<std::byte const> false_borrow_bits() const noexcept { return false_borrows_; }
97 word_view cut_lcps() const noexcept { return cut_lcps_; }
98 std::uint64_t virtual_size() const noexcept { return virtual_count_; }
99 std::uint64_t group_count() const noexcept {
101 }
102
103 bool false_borrow(std::uint64_t ordinal) const {
104 if (ordinal >= borrowed_.size()) error_detail::raise<std::out_of_range>("profile blob borrowed ordinal");
105 return (std::to_integer<unsigned>(false_borrows_[ordinal >> 3]) >> (ordinal & 7)) & 1;
106 }
107
108 profile_blob_window project(std::uint64_t group) const {
109 if (group >= group_count()) error_detail::raise<std::out_of_range>("profile blob virtual group");
110 auto first = group * group_size;
111 auto last = first + std::min<std::uint64_t>(group_size, virtual_count_ - first);
112 auto const & ranks = interleave_;
113 auto a = ranks.template rank<typename P::architecture>(group);
114 auto population = ranks.class_at(group);
115 if (a > first || a > borrowed_.size() || population > last - first ||
116 population > borrowed_.size() - a)
117 error_detail::raise<std::invalid_argument>("invalid profile blob rank projection");
118 auto b = a + population;
119 if (last - b > native_.size())
120 error_detail::raise<std::invalid_argument>("invalid profile blob native projection");
121 return {first - a, last - b, a, b};
122 }
123
124 // lower compares the exact sampled boundary to its owned query. At group
125 // zero an empty-key context suffices. The query must not precede lower.
127 std::uint64_t group, profile_query_context<P> const & lower,
128 profile_comparison_work * native_work = nullptr,
129 profile_comparison_work * borrowed_work = nullptr) const {
130 if (lower.order() > 0) error_detail::raise<std::invalid_argument>("query precedes its routed boundary");
131 auto window = project(group);
133 native_.compare_window(window.native_first, window.native_last, lower,
135 auto order = item.comparison.order();
136 if (!order) result.native = profile_blob_native_match<P>{item.ordinal, bit_string::copy(item.value)};
137 return order < 0;
138 }, native_work);
139
140 auto remember = [&](std::uint64_t ordinal, profile_query_context<P> const & comparison) {
141 auto is_false = false_borrow(ordinal);
142 result.borrowed_predecessor = profile_blob_borrowed_predecessor<P>{
143 ordinal, checked_target_ordinal(ordinal), is_false, comparison};
144 if (!comparison.order() && is_false && !result.native) {
145 if (!window.native_first) error_detail::raise<std::invalid_argument>("false borrow has no native predecessor");
146 auto native_ordinal = window.native_first - 1;
147 result.native = profile_blob_native_match<P>{
148 native_ordinal, bit_string::copy(native_.encoded_at(native_ordinal).value)};
149 }
150 };
151 borrowed_.compare_window(window.borrowed_first, window.borrowed_last, lower,
152 [&](profile_comparison_item<P> item) {
153 if (item.comparison.order() > 0) return false;
154 remember(item.ordinal, item.comparison);
155 return true;
156 }, borrowed_work);
157 if (!result.borrowed_predecessor && window.borrowed_first) {
158 // Ordered cut LCPs recover equality and direction without fetching the
159 // preceding record's length or touching its physical block.
160 auto comparison = lower.predecessor(cut_lcps_[group]);
161 remember(window.borrowed_first - 1, comparison);
162 }
163 return result;
164 }
165
166
167 private:
171 std::span<std::byte const> false_borrows_;
173 std::uint64_t virtual_count_;
174
175 static std::uint64_t checked_target_ordinal(std::uint64_t ordinal) {
176 if (ordinal > std::numeric_limits<std::uint64_t>::max() / group_size) {
177 error_detail::raise<std::overflow_error>("borrowed target ordinal overflows");
178 }
179 return ordinal * group_size;
180 }
181 };
182
183 // Immutable native/index pair. P fixes key units and native value layout;
184 // the borrowed role keeps P while contributing zero-width value slots.
185 // Group ranks and target ordinals count entries, independently of P's units.
186 template <class P>
188 using policy_type = P;
189 static constexpr std::uint64_t group_size = P::group_size;
192
194 std::span<profile_record const> native,
195 std::span<bit_string const> borrowed = {}) {
196 check_count(native.size(), borrowed.size());
197 for (std::size_t i = 1; i < native.size(); ++i) {
198 if (compare_bits<typename P::architecture>(native[i - 1].key.view(), native[i].key.view()) >= 0) {
199 error_detail::raise<std::invalid_argument>("profile blob native keys must be strictly sorted");
200 }
201 }
202 profile_blob result;
203 result.native_ = std::make_shared<native_array const>(
204 native_array::build(native));
205 build_index(result, native, borrowed);
206 return result;
207 }
208
209 // Adopt a trusted ordinary-FC native array without decoding or rebuilding
210 // it. Keys must be unique. The all-native index needs only zero rank/cut
211 // directories; profile_native_writer establishes the content precondition.
213 (void)native.view();
214 profile_blob result;
215 result.virtual_count_ = native.size();
216 auto groups = result.group_count();
217 if (groups > result.cut_lcps_.max_size()) error_detail::raise<std::length_error>("native index is too large");
218 result.cut_lcps_.resize(static_cast<std::size_t>(groups), 0);
220 result.native_ = std::make_shared<native_array const>(std::move(native));
221 return result;
222 }
223
224 // Reindexing retains the exact ordinary-FC native allocation and its
225 // independent physical directory. Only index-local navigation is rebuilt.
226 profile_blob reindex(std::span<bit_string const> borrowed) const {
227 check_count(native_->size(), borrowed.size());
228 std::vector<profile_record> native_records;
229 native_records.reserve(static_cast<std::size_t>(native_->size()));
230 native_->view().visit_all([&](profile_item<P> item) {
231 native_records.push_back({bit_string::copy(item.key.prefix), {}});
232 return true;
233 });
234 profile_blob result;
235 result.native_ = native_;
236 build_index(result, native_records, borrowed);
237 return result;
238 }
239
240 native_array const & native() const noexcept { return *native_; }
241 borrowed_array const & borrowed() const noexcept { return borrowed_; }
242 rank_groups<group_size> const & interleave() const noexcept { return interleave_; }
243 std::span<std::byte const> false_borrow_bits() const noexcept { return false_borrows_; }
244 std::uint64_t virtual_size() const noexcept { return virtual_count_; }
245 std::span<std::uint64_t const> cut_lcps() const noexcept { return cut_lcps_; }
246 // Incremental construction binds the exact downstream pair. Batch
247 // build/reindex accept unbound sample spans and leave this empty.
248 std::shared_ptr<profile_blob const> target() const noexcept { return target_; }
249 std::uint64_t group_count() const noexcept {
250 return virtual_count_ / group_size + (virtual_count_ % group_size != 0);
251 }
252
254 return {native_->view(), borrowed_.view(), interleave_.view(), false_borrows_,
255 word_view(std::span<std::uint64_t const>(cut_lcps_)), virtual_count_};
256 }
257 profile_blob_view<P> view() const && = delete;
258
259 bool false_borrow(std::uint64_t ordinal) const { return view().false_borrow(ordinal); }
260 profile_blob_window project(std::uint64_t group) const { return view().project(group); }
262 std::uint64_t group, profile_query_context<P> const & lower,
263 profile_comparison_work * native_work = nullptr,
264 profile_comparison_work * borrowed_work = nullptr) const {
265 return view().search_window(group, lower, native_work, borrowed_work);
266 }
267
268 private:
269 friend struct index_builder<P, native_array>;
270
271 std::shared_ptr<native_array const> native_ =
272 std::make_shared<native_array const>(native_array::build({}));
273 borrowed_array borrowed_ = borrowed_array::build({});
275 std::vector<std::byte> false_borrows_;
276 std::uint64_t virtual_count_ = 0;
277 std::vector<std::uint64_t> cut_lcps_;
278 std::shared_ptr<profile_blob const> target_;
279
280 static void check_count(std::uint64_t native, std::uint64_t borrowed) {
281 if (borrowed > std::numeric_limits<std::uint64_t>::max() - 7 ||
282 native > std::numeric_limits<std::uint64_t>::max() - borrowed) {
283 error_detail::raise<std::length_error>("profile blob virtual count overflows");
284 }
285 }
286
287
288 static void build_index(profile_blob & result, std::span<profile_record const> native,
289 std::span<bit_string const> borrowed) {
290 std::vector<profile_record> borrowed_records;
291 borrowed_records.reserve(borrowed.size());
292 result.false_borrows_.resize((borrowed.size() + 7) >> 3);
293 std::size_t native_at = 0;
294 for (std::size_t i = 0; i != borrowed.size(); ++i) {
295 if (i && compare_bits<typename P::architecture>(borrowed[i - 1].view(), borrowed[i].view()) > 0) {
296 error_detail::raise<std::invalid_argument>("profile blob borrowed keys must be sorted");
297 }
298 while (native_at != native.size() &&
299 compare_bits<typename P::architecture>(native[native_at].key.view(), borrowed[i].view()) < 0) {
300 ++native_at;
301 }
302 if (native_at != native.size() &&
303 compare_bits<typename P::architecture>(native[native_at].key.view(), borrowed[i].view()) == 0) {
304 result.false_borrows_[i >> 3] |= static_cast<std::byte>(1u << (i & 7));
305 }
306 borrowed_records.push_back({borrowed[i], {}});
307 }
308 auto count = std::uint64_t(native.size()) + borrowed.size();
309 std::vector<std::uint64_t> classes(static_cast<std::size_t>(count / group_size + (count % group_size != 0)));
310 std::size_t a = 0;
311 std::size_t s = 0;
312 for (std::uint64_t i = 0; i != count; ++i) {
313 // Native precedes every borrowed occurrence of an equal key.
314 auto take_borrowed = s != borrowed.size() &&
315 (a == native.size() || compare_bits<typename P::architecture>(borrowed[s].view(), native[a].key.view()) < 0);
316 if (i % group_size == 0) {
317 auto const & boundary = take_borrowed ? borrowed[s] : native[a].key;
318 result.cut_lcps_.push_back(s ? compare_common_bits<typename P::architecture>(borrowed[s - 1].view(), boundary.view()).common_bits : 0);
319 }
320 if (take_borrowed) {
321 ++classes[static_cast<std::size_t>(i / group_size)];
322 ++s;
323 } else {
324 ++a;
325 }
326 }
327 result.borrowed_ = borrowed_array::build(borrowed_records);
328 result.interleave_ = rank_groups<group_size>::build(classes, count);
329 result.virtual_count_ = count;
330 }
331 };
332}
Outlines exceptional check failures while preserving their types and messages.
Definition active_engine.h:18
Declares Everett's profile support.
Declares Everett's owning fractional-index artifact.
Declares Everett's rank groups support.
Definition profile.h:166
bit_view view() const &
Definition profile.h:178
static bit_string copy(bit_view source)
Definition profile.h:181
Definition index_builder.h:47
Definition profile.h:1168
Definition profile_blob.h:52
P policy_type
Definition profile_blob.h:53
std::uint64_t ordinal
Definition profile_blob.h:54
profile_query_context< P > comparison
Definition profile_blob.h:57
bool false_borrow
Definition profile_blob.h:56
std::uint64_t target_ordinal
Definition profile_blob.h:55
Definition profile_blob.h:45
std::uint64_t ordinal
Definition profile_blob.h:47
P policy_type
Definition profile_blob.h:46
bit_string value
Definition profile_blob.h:48
Definition profile_blob.h:73
word_view cut_lcps_
Definition profile_blob.h:172
std::uint64_t virtual_size() const noexcept
Definition profile_blob.h:98
native_view const & native() const noexcept
Definition profile_blob.h:93
static std::uint64_t checked_target_ordinal(std::uint64_t ordinal)
Definition profile_blob.h:175
word_view cut_lcps() const noexcept
Definition profile_blob.h:97
P policy_type
Definition profile_blob.h:74
profile_blob_window project(std::uint64_t group) const
Definition profile_blob.h:108
borrowed_view borrowed_
Definition profile_blob.h:169
profile_blob_view(native_view native, borrowed_view borrowed, rank_groups_view< group_size > interleave, std::span< std::byte const > false_borrows, word_view cut_lcps, std::uint64_t virtual_count)
Definition profile_blob.h:79
std::span< std::byte const > false_borrow_bits() const noexcept
Definition profile_blob.h:96
rank_groups_view< group_size > interleave_
Definition profile_blob.h:170
bool false_borrow(std::uint64_t ordinal) const
Definition profile_blob.h:103
rank_groups_view< group_size > const & interleave() const noexcept
Definition profile_blob.h:95
std::uint64_t group_count() const noexcept
Definition profile_blob.h:99
std::uint64_t virtual_count_
Definition profile_blob.h:173
borrowed_view const & borrowed() const noexcept
Definition profile_blob.h:94
profile_blob_window_result< P > search_window(std::uint64_t group, profile_query_context< P > const &lower, profile_comparison_work *native_work=nullptr, profile_comparison_work *borrowed_work=nullptr) const
Definition profile_blob.h:126
static constexpr std::uint64_t group_size
Definition profile_blob.h:75
std::span< std::byte const > false_borrows_
Definition profile_blob.h:171
native_view native_
Definition profile_blob.h:168
Definition profile_blob.h:61
std::optional< profile_blob_borrowed_predecessor< P > > borrowed_predecessor
Definition profile_blob.h:64
P policy_type
Definition profile_blob.h:62
std::optional< profile_blob_native_match< P > > native
Definition profile_blob.h:63
Definition profile_blob.h:37
std::uint64_t native_first
Definition profile_blob.h:38
std::uint64_t native_last
Definition profile_blob.h:39
std::uint64_t borrowed_first
Definition profile_blob.h:40
std::uint64_t borrowed_last
Definition profile_blob.h:41
Definition profile_blob.h:187
std::shared_ptr< profile_blob const > target() const noexcept
Definition profile_blob.h:248
std::vector< std::byte > false_borrows_
Definition profile_blob.h:275
std::uint64_t virtual_size() const noexcept
Definition profile_blob.h:244
borrowed_array const & borrowed() const noexcept
Definition profile_blob.h:241
native_array const & native() const noexcept
Definition profile_blob.h:240
profile_blob reindex(std::span< bit_string const > borrowed) const
Definition profile_blob.h:226
std::uint64_t virtual_count_
Definition profile_blob.h:276
std::vector< std::uint64_t > cut_lcps_
Definition profile_blob.h:277
borrowed_array borrowed_
Definition profile_blob.h:273
profile_blob_view< P > view() const &&=delete
static void build_index(profile_blob &result, std::span< profile_record const > native, std::span< bit_string const > borrowed)
Definition profile_blob.h:288
profile_blob_window project(std::uint64_t group) const
Definition profile_blob.h:260
profile_blob_view< P > view() const &
Definition profile_blob.h:253
static profile_blob build(std::span< profile_record const > native, std::span< bit_string const > borrowed={})
Definition profile_blob.h:193
rank_groups< group_size > interleave_
Definition profile_blob.h:274
std::span< std::uint64_t const > cut_lcps() const noexcept
Definition profile_blob.h:245
P policy_type
Definition profile_blob.h:188
static void check_count(std::uint64_t native, std::uint64_t borrowed)
Definition profile_blob.h:280
static profile_blob adopt_native(native_array native)
Definition profile_blob.h:212
std::shared_ptr< native_array const > native_
Definition profile_blob.h:271
std::uint64_t group_count() const noexcept
Definition profile_blob.h:249
std::shared_ptr< profile_blob const > target_
Definition profile_blob.h:278
std::span< std::byte const > false_borrow_bits() const noexcept
Definition profile_blob.h:243
rank_groups< group_size > const & interleave() const noexcept
Definition profile_blob.h:242
Definition profile.h:458
Definition profile.h:452
bit_view value
Definition profile.h:313
Definition profile.h:302
profile_anchor< P > key
Definition profile.h:304
Definition profile.h:328
int order() const noexcept
Definition profile.h:352
void compare_window(std::uint64_t first, std::uint64_t last, profile_query_context< P > context, F &&callback, profile_comparison_work *work=nullptr, std::uint64_t *predecessor=nullptr) const
Definition profile.h:781
std::uint64_t size() const noexcept
Definition profile.h:748
profile_encoded_record encoded_at(std::uint64_t ordinal) const
Definition profile.h:758
Definition rank_groups.h:127
std::uint64_t size() const noexcept
Definition rank_groups.h:151
Definition rank_groups.h:241
Definition word_view.h:31
std::size_t size() const noexcept
Definition word_view.h:41
Borrows native or little-endian directory words and select samples.