Everett
Loading...
Searching...
No Matches
sections.h
Go to the documentation of this file.
1
13#pragma once
14
17#include <everett/word_view.h>
18
19#include <algorithm>
20#include <array>
21#include <bit>
22#include <cstddef>
23#include <cstdint>
24#include <limits>
25#include <memory>
26#include <optional>
27#include <span>
28#include <stdexcept>
29#include <string>
30#include <utility>
31#include <vector>
32
33namespace everett {
37 bool operator==(blob_identity const &) const = default;
38 };
39
40 namespace section_detail {
41 inline constexpr std::uint16_t version = 2;
42 inline constexpr std::size_t native_directory_bytes = 128;
43 inline constexpr std::size_t index_directory_bytes = 256;
44 inline constexpr std::size_t native_descriptor_offset = 48;
45 inline constexpr std::size_t index_descriptor_offset = 112;
46 inline constexpr std::size_t descriptor_bytes = 16;
47 inline constexpr std::size_t fc = 0, low = 1, high = 2, samples = 3, sparse = 4;
48 inline constexpr std::size_t classes = 5, checkpoints = 6, false_borrows = 7, cut_lcps = 8;
49
50 struct descriptor {
51 std::uint64_t offset = 0;
52 std::uint64_t length = 0;
53 bool operator==(descriptor const &) const = default;
54 };
55 struct directory {
56 std::array<descriptor, 9> sections{};
57 std::size_t count = 0;
58 std::size_t bytes = 0;
59 std::uint64_t extent = 0;
60 std::uint64_t terminal_key_units = 0;
61 std::uint64_t universe = 0;
62 unsigned low_width = 0;
63 std::uint64_t virtual_count = 0;
64 std::optional<object_id> native_id;
65 std::optional<blob_identity> target_id;
66 };
67
68 inline std::uint64_t align(std::uint64_t value) noexcept {
69 return (value + 7) & ~std::uint64_t{7};
70 }
71 inline void zero(std::span<std::byte const> bytes, char const * message) {
72 for (auto byte : bytes) if (byte != std::byte{0}) throw std::invalid_argument(message);
73 }
74 inline unsigned hex_value(char c) noexcept { return unsigned(c <= '9' ? c - '0' : c - 'a' + 10); }
75 inline void put_id(std::span<std::byte> bytes, std::size_t at, object_id const & id) {
76 auto const & text = id.hex();
77 if (text.size() != 32) throw std::invalid_argument("invalid Everett section object identity");
78 if (at > bytes.size() || 16 > bytes.size() - at)
79 throw std::out_of_range("Everett section identity position");
80 for (std::size_t i = 0; i != 16; ++i)
81 bytes[at + i] = std::byte((hex_value(text[2 * i]) << 4) | hex_value(text[2 * i + 1]));
82 }
83 inline object_id get_id(std::span<std::byte const> bytes, std::size_t at) {
84 constexpr char hex[] = "0123456789abcdef";
85 std::string text(32, '0');
86 for (std::size_t i = 0; i != 16; ++i) {
87 auto byte = std::to_integer<unsigned>(bytes[at + i]);
88 text[2 * i] = hex[byte >> 4];
89 text[2 * i + 1] = hex[byte & 15];
90 }
91 return object_id(std::move(text));
92 }
93 inline std::span<std::byte const> section(std::span<std::byte const> body,
94 directory const & layout, std::size_t slot) {
95 if (slot >= layout.count) throw std::out_of_range("Everett section slot");
96 auto const & part = layout.sections[slot];
97 if (part.offset > body.size() || part.length > body.size() - part.offset)
98 throw std::invalid_argument("Everett section exceeds body");
99 return body.subspan(static_cast<std::size_t>(part.offset), static_cast<std::size_t>(part.length));
100 }
101
102 template <class P> directory parse(file_header<P> const & header, std::span<std::byte const> body) {
103 directory result;
104 bool index = header.kind == file_kind::fractional_index;
105 result.count = index ? 9 : 5;
107 auto descriptors = index ? index_descriptor_offset : native_descriptor_offset;
108 if (body.size() > std::numeric_limits<std::uint64_t>::max() - 7)
109 throw std::invalid_argument("Everett section container extent overflows");
110 if (body.size() < result.bytes) throw std::invalid_argument("truncated Everett section directory");
111 if (header.extent != profile_detail::multiply(body.size(), 1u << (3 - P::unit_shift)))
112 throw std::invalid_argument("Everett section container must occupy complete bytes");
113 auto magic = index ? "IX02" : "KV02";
114 for (unsigned i = 0; i != 4; ++i)
115 if (std::to_integer<unsigned>(body[i]) != unsigned(magic[i]))
116 throw std::invalid_argument("unexpected Everett section magic");
117 if (file_detail::get(body, 4, 2) != version || file_detail::get(body, 6, 2) != result.count)
118 throw std::invalid_argument("unsupported Everett section directory");
119 result.extent = file_detail::get(body, 8, 8);
120 result.terminal_key_units = file_detail::get(body, 16, 8);
121 result.universe = file_detail::get(body, 24, 8);
122 result.low_width = unsigned(file_detail::get(body, 32, 1));
123 if (index) {
124 auto target = file_detail::get(body, 33, 1);
125 if (target > 1) throw std::invalid_argument("invalid Everett target flag");
126 zero(body.subspan(34, 6), "nonzero Everett directory reserved bytes");
127 zero(body.subspan(96, 16), "nonzero Everett directory reserved bytes");
128 result.virtual_count = file_detail::get(body, 40, 8);
129 if (result.virtual_count < header.record_count)
130 throw std::invalid_argument("borrowed records exceed virtual count");
131 result.native_id = get_id(body, 48);
132 if (target) result.target_id = blob_identity{get_id(body, 64), get_id(body, 80)};
133 else zero(body.subspan(64, 32), "absent Everett target must encode zero");
134 } else zero(body.subspan(33, 15), "nonzero Everett directory reserved bytes");
135 std::uint64_t end = result.bytes;
136 for (std::size_t i = 0; i != result.count; ++i) {
137 auto at = descriptors + i * descriptor_bytes;
138 descriptor part{file_detail::get(body, at, 8), file_detail::get(body, at + 8, 8)};
139 if (part.offset != align(end) || part.offset > body.size() || part.length > body.size() - part.offset)
140 throw std::invalid_argument("noncanonical Everett section range");
141 end = part.offset + part.length;
142 result.sections[i] = part;
143 }
144 if (end != body.size()) throw std::invalid_argument("trailing Everett section bytes");
145 return result;
146 }
147 inline void validate_gaps(std::span<std::byte const> body, directory const & layout) {
148 std::size_t end = layout.bytes;
149 for (std::size_t i = 0; i != layout.count; ++i) {
150 auto const & part = layout.sections[i];
151 zero(body.subspan(end, static_cast<std::size_t>(part.offset) - end), "nonzero Everett section alignment padding");
152 end = static_cast<std::size_t>(part.offset + part.length);
153 }
154 }
155 template <class P, stream_role Role> profile_view<P, Role> profile(
156 file_header<P> const & header, std::span<std::byte const> body, directory const & layout) {
157 auto metadata = profile_detail::initial_metadata<P, Role>();
158 metadata.record_count = header.record_count;
159 metadata.extent = layout.extent;
160 metadata.terminal_key_units = layout.terminal_key_units;
161 metadata.common_value_width = header.common_value_width;
162 metadata.policy_fixed_values = header.policy_value_width.has_value();
163 metadata.policy_value_width = header.policy_value_width.value_or(0);
164 auto part = [&](std::size_t slot) { return section(body, layout, slot); };
165 elias_fano_view offsets{
168 profile_detail::add(header.record_count / P::codec_block_size +
169 (header.record_count % P::codec_block_size != 0), 1), layout.universe, layout.low_width};
170 auto result = profile_view<P, Role>::from_sections(part(fc), offsets, metadata);
171 result.validate_offset_metadata();
172 return result;
173 }
174 inline void equal_words(word_view actual, std::span<std::uint64_t const> expected, char const * message) {
175 if (actual.size() != expected.size()) throw std::invalid_argument(message);
176 for (std::size_t i = 0; i != expected.size(); ++i)
177 if (actual[i] != expected[i]) throw std::invalid_argument(message);
178 }
179 template <class P, stream_role Role> void scan_profile(profile_view<P, Role> const & view) {
180 view.validate_contents();
181 auto const & metadata = view.metadata();
182 auto offsets = view.group_offsets();
183 std::vector<std::uint64_t> residuals;
184 auto groups = view.block_count();
185 if (groups == std::numeric_limits<std::uint64_t>::max() || groups + 1 > residuals.max_size())
186 throw std::length_error("Everett scan offset count");
187 residuals.reserve(static_cast<std::size_t>(groups + 1));
188 bit_view data(view.bytes(), profile_detail::multiply(metadata.extent, P::bits_per_unit));
189 std::uint64_t at = 0, previous_units = 0;
190 bit_string previous;
191 for (std::uint64_t ordinal = 0; ordinal != metadata.record_count; ++ordinal) {
192 std::uint64_t retained;
193 if (ordinal % P::codec_block_size == 0) {
194 auto stride = profile_detail::multiply(ordinal, metadata.common_value_width.value_or(0));
195 if (stride > at) throw std::invalid_argument("Everett fixed values exceed physical position");
196 residuals.push_back(at - stride);
197 retained = profile_detail::read_count<P>(data, at);
198 if (retained > previous_units)
199 throw std::invalid_argument("Everett profile absolute prefix exceeds predecessor");
200 } else {
201 auto backspace = profile_detail::read_backspace<P>(data, at);
202 if (backspace > previous_units) throw std::invalid_argument("Everett profile backspace exceeds predecessor");
203 retained = previous_units - backspace;
204 }
205 auto suffix = profile_detail::read_count<P>(data, at);
206 auto values = metadata.common_value_width ? *metadata.common_value_width : profile_detail::read_count<P>(data, at);
207 auto length = profile_detail::add(retained, suffix);
208 auto start_bits = profile_detail::multiply(at, P::bits_per_unit);
209 auto suffix_bits = profile_detail::multiply(suffix, P::bits_per_unit);
210 auto value_bits = profile_detail::multiply(values, P::bits_per_unit);
211 if (start_bits > data.size() || suffix_bits > data.size() - start_bits ||
212 value_bits > data.size() - start_bits - suffix_bits)
213 throw std::invalid_argument("truncated Everett profile record");
214 auto retained_bits = profile_detail::multiply(retained, P::bits_per_unit);
215 // The retained prefix is already equal. Ordinary FC differs at the
216 // first new unit, so keep that constant-time check. A conservative
217 // prefix may repeat equal units; compare only the explicit tails.
218 if (retained < previous_units) {
219 if (!suffix) throw std::invalid_argument("Everett profile key precedes its predecessor");
220 auto before = profile_detail::load_bits(previous.view(), retained_bits, P::bits_per_unit);
221 auto after = profile_detail::load_bits(data, start_bits, P::bits_per_unit);
222 if (after < before)
223 throw std::invalid_argument("Everett profile keys are not sorted");
224 if (after == before) {
225 auto order = compare_bits<typename P::architecture>(previous.view().subview(retained_bits + P::bits_per_unit,
226 previous.view().size() - retained_bits - P::bits_per_unit),
227 data.subview(start_bits + P::bits_per_unit, suffix_bits - P::bits_per_unit));
228 if (order > 0) throw std::invalid_argument("Everett profile keys are not sorted");
229 if (order == 0 && Role == stream_role::native)
230 throw std::invalid_argument("Everett native profile keys must be unique");
231 }
232 } else if (ordinal && !suffix && Role == stream_role::native)
233 throw std::invalid_argument("Everett native profile keys must be unique");
234 profile_detail::resize(previous, retained_bits);
235 profile_detail::append(previous, data.subview(start_bits, suffix_bits));
236 at = profile_detail::add(at, profile_detail::add(suffix, values));
237 previous_units = length;
238 }
239 if (at != metadata.extent || previous_units != metadata.terminal_key_units)
240 throw std::invalid_argument("Everett terminal profile extent or length mismatch");
241 auto stride = profile_detail::multiply(metadata.record_count, metadata.common_value_width.value_or(0));
242 if (stride > at) throw std::invalid_argument("Everett fixed payload exceeds extent");
243 residuals.push_back(at - stride);
244 auto expected = elias_fano::build<typename P::architecture>(residuals);
245 if (expected.universe != offsets.universe() || expected.low_width != offsets.low_width())
246 throw std::invalid_argument("noncanonical Everett Elias-Fano parameters");
247 equal_words(offsets.low_words(), expected.low, "Everett Elias-Fano low words mismatch");
248 equal_words(offsets.high_words(), expected.high, "Everett Elias-Fano high words mismatch");
249 equal_words(offsets.sparse_words(), expected.sparse, "Everett Elias-Fano sparse words mismatch");
250 auto actual_samples = offsets.samples();
251 if (actual_samples.size() != expected.samples.size()) throw std::invalid_argument("Everett Elias-Fano sample count mismatch");
252 for (std::size_t i = 0; i != expected.samples.size(); ++i) {
253 auto a = actual_samples[i];
254 auto b = expected.samples[i];
255 if (a.first != b.first || a.sparse != b.sparse) throw std::invalid_argument("Everett Elias-Fano sample mismatch");
256 }
257 }
258 }
259
260 // Owns only the small directory and any endian-conversion buffers. Source
261 // FC arrays are borrowed and must remain immutable and alive through
262 // every use. Encoding does not scan them to certify that precondition.
263 // Move-only because copied section spans could refer to conversion buffers.
264 template <class P> struct encoded_sections {
265 using policy_type = P;
269 : header_(std::move(other.header_)), directory_(other.directory_), zero_{},
270 directory_size_(std::exchange(other.directory_size_, 0)), sections_(std::move(other.sections_)),
271 converted_(std::move(other.converted_)) {}
273 if (this != &other) {
274 header_ = std::move(other.header_); directory_ = other.directory_;
275 directory_size_ = std::exchange(other.directory_size_, 0);
276 sections_ = std::move(other.sections_); converted_ = std::move(other.converted_);
277 }
278 return *this;
279 }
280 file_header<P> const & header() const & { require_active(); return header_; }
281 file_header<P> const & header() const && = delete;
282 // Returned spans borrow this encoder as well as its source arrays, and
283 // expire on encoder movement or destruction.
284 std::vector<std::span<std::byte const>> chunks() const & {
286 std::vector<std::span<std::byte const>> result;
287 result.reserve(2 * sections_.size() + 1);
288 result.push_back(std::span(directory_).first(directory_size_));
289 std::uint64_t end = directory_size_;
290 for (auto section : sections_) {
291 auto gap = section_detail::align(end) - end;
292 if (gap) result.push_back(std::span(zero_).first(static_cast<std::size_t>(gap)));
293 result.push_back(section);
294 end += gap + section.size();
295 }
296 return result;
297 }
298 std::vector<std::span<std::byte const>> chunks() const && = delete;
299 std::vector<std::byte> materialize() const {
301 std::vector<std::byte> body;
302 auto size = file_detail::body_bytes<P>(header_.extent);
303 if (size > body.max_size()) throw std::length_error("Everett section body too large");
304 body.reserve(static_cast<std::size_t>(size));
305 for (auto chunk : chunks()) body.insert(body.end(), chunk.begin(), chunk.end());
306 return encode_file(header_, body);
307 }
308 object_seal_receipt seal(std::filesystem::path const & root, object_id const & id,
309 object_attempt_id const & attempt) const {
310 auto parts = chunks();
311 return object_writer<P>::seal(root, id, attempt, header_, parts);
312 }
313
314 private:
316 template <class Q> friend encoded_sections<Q> encode_index_sections(profile_blob<Q> const &, object_id const &,
317 std::optional<blob_identity>);
318 template <class Q> friend encoded_sections<Q> encode_index_sections(profile_index<Q> const &, object_id const &,
319 std::optional<blob_identity>);
320 encoded_sections() = default;
321 void require_active() const {
322 if (!directory_size_) throw std::logic_error("Everett section encoding has no source");
323 }
325 std::array<std::byte, section_detail::index_directory_bytes> directory_{};
326 std::array<std::byte, 8> zero_{};
327 std::size_t directory_size_ = 0;
328 std::vector<std::span<std::byte const>> sections_;
329 std::vector<std::vector<std::byte>> converted_;
330
331 void words(std::span<std::uint64_t const> values) {
332 if constexpr (std::endian::native == std::endian::little) sections_.push_back(std::as_bytes(values));
333 else {
334 auto & bytes = converted_.emplace_back(values.size() << 3);
335 for (std::size_t i = 0; i != values.size(); ++i) file_detail::put(bytes, (i << 3), 8, values[i]);
336 sections_.push_back(bytes);
337 }
338 }
339 void samples(std::span<elias_fano_sample const> values) {
340 static_assert(sizeof(elias_fano_sample) == 16 && offsetof(elias_fano_sample, first) == 0 &&
341 offsetof(elias_fano_sample, sparse) == 8);
342 if constexpr (std::endian::native == std::endian::little) sections_.push_back(std::as_bytes(values));
343 else {
344 auto & bytes = converted_.emplace_back(values.size() << 4);
345 for (std::size_t i = 0; i != values.size(); ++i) {
346 file_detail::put(bytes, (i << 4), 8, values[i].first);
347 file_detail::put(bytes, (i << 4) + 8, 8, values[i].sparse);
348 }
349 sections_.push_back(bytes);
350 }
351 }
352 template <stream_role Role> void profile(profile_array<P, Role> const & array) {
353 sections_.push_back(array.bytes());
354 auto const & offsets = array.group_offsets();
355 words(offsets.low); words(offsets.high); samples(offsets.samples); words(offsets.sparse);
356 auto const & metadata = array.metadata();
357 header_.record_count = metadata.record_count;
358 header_.common_value_width = metadata.common_value_width;
359 header_.policy_value_width = metadata.policy_fixed_values
360 ? std::optional(metadata.policy_value_width) : std::nullopt;
361 file_detail::put(directory_, 8, 8, metadata.extent);
362 file_detail::put(directory_, 16, 8, metadata.terminal_key_units);
363 file_detail::put(directory_, 24, 8, offsets.universe);
364 file_detail::put(directory_, 32, 1, offsets.low_width);
365 }
366 template <class Index> void index(Index const & source, object_id const & native_id,
367 std::optional<blob_identity> const & exact_target) {
368 profile(source.borrowed());
369 words(source.interleave().classes);
370 words(source.interleave().checkpoints);
371 sections_.push_back(source.false_borrow_bits());
372 words(source.cut_lcps());
373 file_detail::put(directory_, 40, 8, source.virtual_size());
374 section_detail::put_id(directory_, 48, native_id);
375 if (exact_target) {
376 file_detail::put(directory_, 33, 1, 1);
377 section_detail::put_id(directory_, 64, exact_target->native);
378 section_detail::put_id(directory_, 80, exact_target->index);
379 }
381 }
382 void finish(file_kind kind) {
383 header_.kind = kind;
384 bool index = kind == file_kind::fractional_index;
387 auto magic = index ? "IX02" : "KV02";
388 for (unsigned i = 0; i != 4; ++i) directory_[i] = std::byte(magic[i]);
391 std::uint64_t end = directory_size_;
392 for (std::size_t i = 0; i != sections_.size(); ++i) {
393 if (end > std::numeric_limits<std::uint64_t>::max() - 7)
394 throw std::overflow_error("Everett section alignment overflows");
395 auto start = section_detail::align(end);
396 end = profile_detail::add(start, sections_[i].size());
397 file_detail::put(directory_, descriptors + (i << 4), 8, start);
398 file_detail::put(directory_, descriptors + (i << 4) + 8, 8, sections_[i].size());
399 }
400 header_.extent = profile_detail::multiply(end, 1u << (3 - P::unit_shift));
402 }
403 };
404
406 encoded_sections<P> result;
407 result.profile(native);
409 return result;
410 }
412
414 object_id const & native_id, std::optional<blob_identity> exact_target = std::nullopt) {
415 if ((!exact_target && (pair.target() || pair.borrowed().size())) ||
416 (pair.target() && pair.borrowed().size() != pair.target()->group_count()))
417 throw std::invalid_argument("Everett index encoding needs its exact bound target");
418 encoded_sections<P> result;
419 result.index(pair, native_id, exact_target);
420 return result;
421 }
423 std::optional<blob_identity> = std::nullopt) = delete;
424
425 // Serialize a newly built index against an unchanged native file. Identities
426 // are caller-authenticated declarations, checked against the pinned objects
427 // when binding/adopting the resulting pair.
429 object_id const & native_id, std::optional<blob_identity> exact_target = std::nullopt) {
430 if (!exact_target && index.borrowed().size())
431 throw std::invalid_argument("Everett index encoding needs its exact bound target");
432 encoded_sections<P> result;
433 result.index(index, native_id, exact_target);
434 return result;
435 }
437 std::optional<blob_identity> = std::nullopt) = delete;
438
439 // Typed construction reads the envelope and fixed section directory only.
440 // It retains the mapping; view/section spans borrow that owner's lifetime.
441 // Semantic validation and the whole-body CRC are explicit scan operations.
442 template <class P, stream_role Role> struct mapped_profile {
443 using policy_type = P;
444 static mapped_profile open(file<P> source) {
445 auto header = source.header();
447 if (header.kind != kind) throw std::invalid_argument("Everett mapped profile kind mismatch");
448 auto body = source.body();
449 auto layout = section_detail::parse(header, body.bytes());
450 auto view = section_detail::profile<P, Role>(header, body.bytes(), layout);
451 return {std::move(source), std::move(body), std::move(layout), std::move(view)};
452 }
453 static mapped_profile open(std::filesystem::path const & path) { return open(file<P>::open(path)); }
455 return open(file<P>::from_slice(std::move(source), mode));
456 }
457 bool active() const noexcept { return !body_.empty(); }
459 profile_view<P, Role> view() const && = delete;
460 std::uint64_t size() const { require_active(); return view_.size(); }
462 section_detail::directory const & layout() const && = delete;
463 std::span<std::byte const> section(std::size_t slot) const & {
465 }
466 std::span<std::byte const> section(std::size_t) const && = delete;
473 private:
474 void require_active() const {
475 if (!active()) throw std::logic_error("Everett mapped profile has no mapping");
476 }
478 : source_(std::move(source)), body_(std::move(body)), layout_(std::move(layout)), view_(std::move(view)) {}
483 };
485
486 template <class P> struct mapped_index {
487 using policy_type = P;
488 static mapped_index open(file<P> source) {
489 auto profile = mapped_profile<P, stream_role::borrowed>::open(std::move(source));
490 auto words = [&](std::size_t slot) { return word_view::little_endian(profile.section(slot)); };
491 auto const & layout = profile.layout();
494 auto cuts = words(section_detail::cut_lcps);
495 auto groups = layout.virtual_count / P::group_size + (layout.virtual_count % P::group_size != 0);
496 if (profile.size() > std::numeric_limits<std::uint64_t>::max() - 7 ||
497 cuts.size() != groups || profile.section(section_detail::false_borrows).size() !=
498 ((profile.size() + 7) >> 3))
499 throw std::invalid_argument("Everett index navigation shape mismatch");
500 return {std::move(profile), std::move(ranks), cuts};
501 }
502 static mapped_index open(std::filesystem::path const & path) { return open(file<P>::open(path)); }
504 return open(file<P>::from_slice(std::move(source), mode));
505 }
508 rank_groups_view<P::group_size> interleave() const & {
509 require_active(); return ranks_;
510 }
512 std::span<std::byte const> false_borrow_bits() const & { return profile_.section(section_detail::false_borrows); }
513 std::span<std::byte const> false_borrow_bits() const && = delete;
514 word_view cut_lcps() const & { require_active(); return cuts_; }
515 word_view cut_lcps() const && = delete;
516 object_id const & native_id() const & { return *profile_.layout().native_id; }
517 object_id const & native_id() const && = delete;
518 std::optional<blob_identity> const & target_id() const & { return profile_.layout().target_id; }
519 std::optional<blob_identity> const & target_id() const && = delete;
520 std::uint64_t virtual_size() const { return profile_.layout().virtual_count; }
521 section_detail::directory const & layout() const & { return profile_.layout(); }
522 section_detail::directory const & layout() const && = delete;
523 std::span<std::byte const> section(std::size_t slot) const & { return profile_.section(slot); }
524 std::span<std::byte const> section(std::size_t) const && = delete;
525 void scan() const {
526 profile_.scan();
527 std::vector<std::uint64_t> classes;
528 auto groups = ranks_.group_count();
529 if (groups > classes.max_size()) throw std::length_error("Everett rank scan size");
530 classes.reserve(static_cast<std::size_t>(groups));
531 for (std::uint64_t group = 0; group != groups; ++group) classes.push_back(ranks_.class_at(group));
532 auto expected = rank_groups<P::group_size>::build(classes, virtual_size());
533 if (expected.view().count() != borrowed().size()) throw std::invalid_argument("Everett rank total mismatch");
534 section_detail::equal_words(ranks_.class_words(), expected.classes, "Everett rank class words mismatch");
535 section_detail::equal_words(ranks_.checkpoint_words(), expected.checkpoints, "Everett rank checkpoint mismatch");
536 auto flags = false_borrow_bits();
537 if (auto tail = (borrowed().size() & 7))
538 if ((std::to_integer<unsigned>(flags.back()) >> tail) != 0)
539 throw std::invalid_argument("nonzero Everett false-borrow padding");
540 }
541 private:
542 void require_active() const {
543 if (!profile_.active()) throw std::logic_error("Everett mapped index has no mapping");
544 }
546 : profile_(std::move(profile)), ranks_(std::move(ranks)), cuts_(cuts) {}
550 };
551}
void put(std::span< std::byte > bytes, std::size_t at, unsigned width, std::uint64_t value) noexcept
Definition file.h:70
void validate_metadata(file_header< P > const &header)
Definition file.h:93
std::uint64_t get(std::span< std::byte const > bytes, std::size_t at, unsigned width) noexcept
Definition file.h:65
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
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
object_id get_id(std::span< std::byte const > bytes, std::size_t at)
Definition sections.h:83
std::uint64_t align(std::uint64_t value) noexcept
Definition sections.h:68
constexpr std::size_t native_directory_bytes
Definition sections.h:42
constexpr std::size_t low
Definition sections.h:47
constexpr std::size_t native_descriptor_offset
Definition sections.h:44
void scan_profile(profile_view< P, Role > const &view)
Definition sections.h:179
void validate_gaps(std::span< std::byte const > body, directory const &layout)
Definition sections.h:147
constexpr std::size_t checkpoints
Definition sections.h:48
constexpr std::size_t index_descriptor_offset
Definition sections.h:45
constexpr std::size_t descriptor_bytes
Definition sections.h:46
void put_id(std::span< std::byte > bytes, std::size_t at, object_id const &id)
Definition sections.h:75
constexpr std::size_t samples
Definition sections.h:47
constexpr std::size_t index_directory_bytes
Definition sections.h:43
constexpr std::size_t sparse
Definition sections.h:47
constexpr std::size_t high
Definition sections.h:47
constexpr std::size_t fc
Definition sections.h:47
void equal_words(word_view actual, std::span< std::uint64_t const > expected, char const *message)
Definition sections.h:174
constexpr std::uint16_t version
Definition sections.h:41
constexpr std::size_t classes
Definition sections.h:48
directory parse(file_header< P > const &header, std::span< std::byte const > body)
Definition sections.h:102
constexpr std::size_t cut_lcps
Definition sections.h:48
constexpr std::size_t false_borrows
Definition sections.h:48
void zero(std::span< std::byte const > bytes, char const *message)
Definition sections.h:71
std::span< std::byte const > section(std::span< std::byte const > body, directory const &layout, std::size_t slot)
Definition sections.h:93
profile_view< P, Role > profile(file_header< P > const &header, std::span< std::byte const > body, directory const &layout)
Definition sections.h:155
unsigned hex_value(char c) noexcept
Definition sections.h:74
Definition active_engine.h:18
encoded_sections< P > encode_index_sections(profile_blob< P > const &pair, object_id const &native_id, std::optional< blob_identity > exact_target=std::nullopt)
Definition sections.h:413
file_open_mode
Definition file.h:32
std::vector< std::byte > encode_file(file_header< P > const &header, std::span< std::byte const > body)
Definition file.h:221
encoded_sections< P > encode_native_sections(profile_array< P, stream_role::native > const &native)
Definition sections.h:405
file_kind
Definition object_path.h:25
Streams and seals immutable object files without catalog publication.
Declares Everett's profile blob support.
Definition profile.h:166
bit_view view() const &
Definition profile.h:178
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
Definition sections.h:34
bool operator==(blob_identity const &) const =default
object_id index
Definition sections.h:36
object_id native
Definition sections.h:35
Definition elias_fano.h:195
Definition elias_fano.h:212
Definition sections.h:264
encoded_sections & operator=(encoded_sections const &)=delete
encoded_sections(encoded_sections const &)=delete
std::vector< std::byte > materialize() const
Definition sections.h:299
std::vector< std::span< std::byte const > > sections_
Definition sections.h:328
void samples(std::span< elias_fano_sample const > values)
Definition sections.h:339
P policy_type
Definition sections.h:265
void require_active() const
Definition sections.h:321
friend encoded_sections< Q > encode_native_sections(profile_array< Q, stream_role::native > const &)
file_header< P > const & header() const &&=delete
encoded_sections(encoded_sections &&other) noexcept
Definition sections.h:268
void finish(file_kind kind)
Definition sections.h:382
friend encoded_sections< Q > encode_index_sections(profile_index< Q > const &, object_id const &, std::optional< blob_identity >)
void profile(profile_array< P, Role > const &array)
Definition sections.h:352
file_header< P > header_
Definition sections.h:324
std::vector< std::vector< std::byte > > converted_
Definition sections.h:329
object_seal_receipt seal(std::filesystem::path const &root, object_id const &id, object_attempt_id const &attempt) const
Definition sections.h:308
friend encoded_sections< Q > encode_index_sections(profile_blob< Q > const &, object_id const &, std::optional< blob_identity >)
std::array< std::byte, section_detail::index_directory_bytes > directory_
Definition sections.h:325
std::size_t directory_size_
Definition sections.h:327
std::vector< std::span< std::byte const > > chunks() const &&=delete
file_header< P > const & header() const &
Definition sections.h:280
std::vector< std::span< std::byte const > > chunks() const &
Definition sections.h:284
void words(std::span< std::uint64_t const > values)
Definition sections.h:331
void index(Index const &source, object_id const &native_id, std::optional< blob_identity > const &exact_target)
Definition sections.h:366
encoded_sections & operator=(encoded_sections &&other) noexcept
Definition sections.h:272
std::array< std::byte, 8 > zero_
Definition sections.h:326
Definition file.h:34
std::uint64_t record_count
Definition file.h:38
std::optional< std::uint64_t > common_value_width
Definition file.h:39
file_kind kind
Definition file.h:36
std::uint64_t extent
Definition file.h:37
std::optional< std::uint64_t > policy_value_width
Definition file.h:42
Definition file.h:242
file_header< P > header() const
Definition file.h:268
mapped_slice body() const
Definition file.h:269
Definition sections.h:486
static mapped_index open(std::filesystem::path const &path)
Definition sections.h:502
word_view cut_lcps() const &
Definition sections.h:514
rank_groups_view< P::group_size > ranks_
Definition sections.h:548
object_id const & native_id() const &
Definition sections.h:516
mapped_index(mapped_profile< P, stream_role::borrowed > profile, rank_groups_view< P::group_size > ranks, word_view cuts)
Definition sections.h:545
std::span< std::byte const > section(std::size_t slot) const &
Definition sections.h:523
rank_groups_view< P::group_size > interleave() const &
Definition sections.h:508
word_view cut_lcps() const &&=delete
rank_groups_view< P::group_size > interleave() const &&=delete
std::optional< blob_identity > const & target_id() const &&=delete
std::span< std::byte const > false_borrow_bits() const &&=delete
void scan() const
Definition sections.h:525
std::span< std::byte const > false_borrow_bits() const &
Definition sections.h:512
static mapped_index open(file< P > source)
Definition sections.h:488
static mapped_index from_slice(mapped_slice source, file_open_mode mode=file_open_mode::checked)
Definition sections.h:503
object_id const & native_id() const &&=delete
std::optional< blob_identity > const & target_id() const &
Definition sections.h:518
word_view cuts_
Definition sections.h:549
section_detail::directory const & layout() const &&=delete
profile_view< P, stream_role::borrowed > borrowed() const &
Definition sections.h:506
std::uint64_t virtual_size() const
Definition sections.h:520
P policy_type
Definition sections.h:487
profile_view< P, stream_role::borrowed > borrowed() const &&=delete
void require_active() const
Definition sections.h:542
mapped_profile< P, stream_role::borrowed > profile_
Definition sections.h:547
std::span< std::byte const > section(std::size_t) const &&=delete
section_detail::directory const & layout() const &
Definition sections.h:521
Definition sections.h:442
void scan() const
Definition sections.h:467
mapped_slice body_
Definition sections.h:480
section_detail::directory const & layout() const &
Definition sections.h:461
static mapped_profile from_slice(mapped_slice source, file_open_mode mode=file_open_mode::checked)
Definition sections.h:454
std::uint64_t size() const
Definition sections.h:460
profile_view< P, Role > view_
Definition sections.h:482
section_detail::directory layout_
Definition sections.h:481
profile_view< P, Role > view() const &&=delete
bool active() const noexcept
Definition sections.h:457
void require_active() const
Definition sections.h:474
section_detail::directory const & layout() const &&=delete
std::span< std::byte const > section(std::size_t) const &&=delete
static mapped_profile open(std::filesystem::path const &path)
Definition sections.h:453
mapped_profile(file< P > source, mapped_slice body, section_detail::directory layout, profile_view< P, Role > view)
Definition sections.h:477
P policy_type
Definition sections.h:443
std::span< std::byte const > section(std::size_t slot) const &
Definition sections.h:463
file< P > source_
Definition sections.h:479
static mapped_profile open(file< P > source)
Definition sections.h:444
profile_view< P, Role > view() const &
Definition sections.h:458
Definition mapped_file.h:81
std::span< std::byte const > bytes() const &noexcept
Definition mapped_file.h:99
bool empty() const noexcept
Definition mapped_file.h:97
Definition object_writer.h:39
Definition object_path.h:38
Definition object_writer.h:88
static object_seal_receipt seal(std::filesystem::path const &root, object_id const &id, object_attempt_id const &attempt, file_header< P > const &header, std::span< std::span< std::byte const > const > chunks, Ops &ops)
Definition object_writer.h:164
Definition profile.h:1168
elias_fano const & group_offsets() const noexcept
Definition profile.h:1243
std::uint64_t size() const noexcept
Definition profile.h:1240
profile_metadata const & metadata() const noexcept
Definition profile.h:1242
std::span< std::byte const > bytes() const noexcept
Definition profile.h:1241
Definition profile_blob.h:187
Definition profile_index.h:39
borrowed_array const & borrowed() const &noexcept
Definition profile_index.h:55
std::uint64_t record_count
Definition profile.h:290
Definition profile.h:695
std::span< std::byte const > bytes() const noexcept
Definition profile.h:749
elias_fano_view group_offsets() const noexcept
Definition profile.h:751
void validate_contents() const
Definition profile.h:716
std::uint64_t block_count() const noexcept
Definition profile.h:734
static profile_view from_sections(std::span< std::byte const > bytes, elias_fano_view offsets, profile_metadata metadata)
Definition profile.h:708
std::uint64_t size() const noexcept
Definition profile.h:748
profile_metadata const & metadata() const noexcept
Definition profile.h:750
Definition rank_groups.h:127
word_view checkpoint_words() const noexcept
Definition rank_groups.h:166
std::uint64_t group_count() const noexcept
Definition rank_groups.h:162
std::uint64_t class_at(std::uint64_t group) const
Definition rank_groups.h:167
word_view class_words() const noexcept
Definition rank_groups.h:165
static rank_groups build(std::span< std::uint64_t const > source, std::uint64_t count)
Definition rank_groups.h:247
static sample_view little_endian(std::span< std::byte const > bytes)
Definition word_view.h:92
Definition sections.h:50
bool operator==(descriptor const &) const =default
std::uint64_t offset
Definition sections.h:51
std::uint64_t length
Definition sections.h:52
Definition sections.h:55
std::optional< object_id > native_id
Definition sections.h:64
std::optional< blob_identity > target_id
Definition sections.h:65
std::size_t count
Definition sections.h:57
std::uint64_t extent
Definition sections.h:59
std::uint64_t virtual_count
Definition sections.h:63
std::uint64_t terminal_key_units
Definition sections.h:60
unsigned low_width
Definition sections.h:62
std::size_t bytes
Definition sections.h:58
std::uint64_t universe
Definition sections.h:61
std::array< descriptor, 9 > sections
Definition sections.h:56
Definition word_view.h:31
static word_view little_endian(std::span< std::byte const > bytes)
Definition word_view.h:36
std::size_t size() const noexcept
Definition word_view.h:41
Borrows native or little-endian directory words and select samples.