Everett
Loading...
Searching...
No Matches
profile_file_output.h
Go to the documentation of this file.
1
13#pragma once
14
16#include <everett/sections.h>
17
18#include <array>
19#include <bit>
20#include <cstdint>
21#include <limits>
22#include <optional>
23#include <span>
24#include <utility>
25#include <vector>
26
27namespace everett {
28 namespace profile_detail {
29 // Borrowed final sections. The caller retains these arrays, exact identities
30 // and their dependency pins through finish; no section is copied here.
31 template <class P> struct index_file_sections {
33 std::optional<blob_identity> const & target_id;
35 std::span<std::byte const> false_borrows;
36 std::span<std::uint64_t const> cut_lcps;
37 std::uint64_t virtual_count;
38 };
39
40 // Internal ordinary-FC sink. The caller supplies an exact retained prefix
41 // and proves ordered keys (strictly ordered for native records). Input spans
42 // are borrowed only during append.
43 // FC buffering is bounded; only one residual offset per block is retained.
44 template <class P, stream_role Role, class Ops = posix_object_ops> struct profile_file_output {
45 using policy_type = P;
46 static_assert(Role == stream_role::native || Role == stream_role::borrowed);
47 static constexpr bool borrowed = Role == stream_role::borrowed;
53 static constexpr std::size_t section_count = borrowed ? 9 : 5;
54 static constexpr std::optional<std::uint64_t> default_width = borrowed ?
55 std::optional<std::uint64_t>(0) : P::value_width;
56 profile_file_output(std::filesystem::path root, object_id id, object_attempt_id attempt,
57 std::optional<std::uint64_t> common = default_width)
59 stream_(std::move(root), std::move(id), std::move(attempt), kind, directory_bytes) {}
60 profile_file_output(std::filesystem::path root, object_id id, object_attempt_id attempt,
61 std::optional<std::uint64_t> common, Ops & ops)
63 stream_(std::move(root), std::move(id), std::move(attempt), kind, directory_bytes, ops) {}
68
69 std::uint64_t size() const noexcept { return count_; }
70 bool failed() const noexcept { return failed_ || stream_.failed(); }
71 bool finished() const noexcept { return stream_.finished(); }
72 std::optional<std::uint64_t> common_value_width() const noexcept { return common_; }
73 object_write_paths const & paths() const & noexcept { return stream_.paths(); }
74 object_write_paths const & paths() const && = delete;
75 void require_active() const {
76 if (failed() || finished())
77 error_detail::raise<std::logic_error>("profile file writer is not active");
78 }
79
80 void append(std::uint64_t retained, bit_view literal, bit_view value) {
82 if ((literal.size() & (P::bits_per_unit - 1)) || (value.size() & (P::bits_per_unit - 1)))
83 error_detail::raise<std::invalid_argument>("profile file record disagrees with policy units");
84 auto value_units = value.size() >> P::unit_shift;
85 if (common_ && value_units != *common_)
86 error_detail::raise<std::invalid_argument>("profile file value disagrees with common width");
87 if (retained > previous_units_ || (!borrowed && count_ && literal.empty()))
88 error_detail::raise<std::invalid_argument>("profile file invalid known prefix");
89 auto literal_units = literal.size() >> P::unit_shift;
90 auto key_units = add(retained, literal_units);
91 (void)multiply(key_units, P::bits_per_unit);
92 auto next_count = add(count_, 1);
93 control_.bytes.clear(); control_.bit_size = 0;
94 std::uint64_t zero_bits = 0;
95 auto block = count_ % P::codec_block_size == 0;
96 if (block) write_count<P>(control_, retained);
97 else if constexpr (P::unit == profile_unit::bit && P::backspace_code == bit_backspace_code::golomb) {
98 auto backspace = previous_units_ - retained;
99 auto bits = backspace_bits<P>(backspace);
100 if (bits <= 129) write_backspace<P>(control_, backspace);
101 else {
102 // The unary quotient can be arbitrarily longer than the key's
103 // new literal. Emit it separately, never grow the control scratch.
104 constexpr auto modulus = P::backspace_parameter;
105 constexpr auto width = unsigned(std::bit_width(modulus - 1));
106 constexpr auto cutoff = golomb_cutoff<P>();
107 zero_bits = backspace / modulus;
108 auto remainder = backspace % modulus;
109 auto remainder_width = width - unsigned(remainder < cutoff);
110 resize(control_, 1 + remainder_width);
111 std::uint64_t at = 0;
112 put_fixed(control_, at, 1, 1);
113 put_fixed(control_, at, remainder < cutoff ? remainder : remainder + cutoff, remainder_width);
114 }
115 } else write_backspace<P>(control_, previous_units_ - retained);
116 write_count<P>(control_, literal_units);
117 if (!common_) write_count<P>(control_, value_units);
118 auto frame_bits = add(add(zero_bits, control_.bit_size), add(literal.size(), value.size()));
119 auto next_bits = add(fc_bits_, frame_bits);
120 if (next_bits > std::numeric_limits<std::uint64_t>::max() - 7)
121 error_detail::raise<std::length_error>("profile file FC extent is too large");
122 // Allocate the next navigation entry before any output is emitted.
123 if (block) offsets_.push_back((fc_bits_ >> P::unit_shift) - multiply(count_, common_.value_or(0)));
124 try {
125 emit_zeroes(zero_bits);
126 emit(control_.view()); emit(literal); emit(value);
127 } catch (...) { failed_ = true; throw; }
128 fc_bits_ = next_bits; previous_units_ = key_units; count_ = next_count;
129 }
130
131 object_seal_receipt finish() requires (!borrowed) { return finish_impl(nullptr); }
133 return finish_impl(&index);
134 }
135
136 private:
139 if constexpr (borrowed) validate_index(*index);
140 auto extent = fc_bits_ >> P::unit_shift;
141 offsets_.push_back(extent - multiply(count_, common_.value_or(0)));
142 elias_fano offsets;
143 try { offsets = elias_fano::build<typename P::architecture>(offsets_); }
144 catch (...) { offsets_.pop_back(); throw; }
145 offsets_.pop_back();
146 std::array<std::byte, directory_bytes> directory{};
147 auto magic = borrowed ? "IX02" : "KV02";
148 for (unsigned i = 0; i != 4; ++i) directory[i] = std::byte(magic[i]);
150 file_detail::put(directory, 6, 2, section_count);
151 file_detail::put(directory, 8, 8, extent);
152 file_detail::put(directory, 16, 8, previous_units_);
153 file_detail::put(directory, 24, 8, offsets.universe);
154 file_detail::put(directory, 32, 1, offsets.low_width);
155 std::array<std::uint64_t, section_count> lengths{byte_count(fc_bits_), multiply(offsets.low.size(), 8),
156 multiply(offsets.high.size(), 8), multiply(offsets.samples.size(), 16), multiply(offsets.sparse.size(), 8)};
157 if constexpr (borrowed) {
158 file_detail::put(directory, 40, 8, index->virtual_count);
159 section_detail::put_id(directory, 48, index->native_id);
160 if (index->target_id) {
161 file_detail::put(directory, 33, 1, 1);
162 section_detail::put_id(directory, 64, index->target_id->native);
163 section_detail::put_id(directory, 80, index->target_id->index);
164 }
165 lengths[5] = multiply(index->interleave.classes.size(), 8);
166 lengths[6] = multiply(index->interleave.checkpoints.size(), 8);
167 lengths[7] = index->false_borrows.size();
168 lengths[8] = multiply(index->cut_lcps.size(), 8);
169 }
170 std::uint64_t end = directory.size();
171 for (std::size_t i = 0; i != lengths.size(); ++i) {
172 auto start = add(end, 7) & ~std::uint64_t{7};
173 end = add(start, lengths[i]);
174 file_detail::put(directory, descriptor_offset + (i << 4), 8, start);
175 file_detail::put(directory, descriptor_offset + (i << 4) + 8, 8, lengths[i]);
176 }
177 file_header<P> header{kind, multiply(end, 1u << (3 - P::unit_shift)), count_, common_};
179 if (file_detail::total_bytes<P>(header.extent) > std::uint64_t(std::numeric_limits<std::int64_t>::max()))
180 error_detail::raise<std::length_error>("profile file exceeds supported file offsets");
181 // EF allocation and complete metadata admission above remain retryable.
182 // Once final bytes are emitted, no retry may duplicate the directories.
183 try {
184 flush_tail();
185 align_stream(); emit_words(offsets.low);
186 align_stream(); emit_words(offsets.high);
188 align_stream(); emit_words(offsets.sparse);
189 if constexpr (borrowed) {
194 }
195 return stream_.finish(header, directory);
196 } catch (...) { failed_ = true; throw; }
197 }
198
199 static std::optional<std::uint64_t> checked_width(std::optional<std::uint64_t> common) {
200 if constexpr (borrowed) {
201 if (common != std::optional<std::uint64_t>(0))
202 error_detail::raise<std::invalid_argument>("borrowed file values must be empty");
203 } else if constexpr (P::fixed_width)
204 if (common != P::value_width)
205 error_detail::raise<std::invalid_argument>("profile file width disagrees with fixed policy");
206 if (common) (void)multiply(*common, P::bits_per_unit);
207 return common;
208 }
209 void validate_index(index_file_sections<P> const & index) const {
210 auto groups = index.virtual_count / P::group_size + (index.virtual_count % P::group_size != 0);
211 if (index.virtual_count < count_ || count_ > std::numeric_limits<std::uint64_t>::max() - 7 ||
212 index.false_borrows.size() != ((count_ + 7) >> 3) || index.cut_lcps.size() != groups ||
213 index.interleave.virtual_count != index.virtual_count || (!index.target_id && count_))
214 error_detail::raise<std::invalid_argument>("streamed index metadata shape mismatch");
215 if (index.interleave.view().count() != count_)
216 error_detail::raise<std::invalid_argument>("streamed index borrowed count mismatch");
217 if ((count_ & 7) && (std::to_integer<unsigned>(index.false_borrows.back()) >> (count_ & 7)))
218 error_detail::raise<std::invalid_argument>("streamed index flag padding is nonzero");
219 }
220
221 static bit_string control_buffer() { bit_string result; result.bytes.reserve(64); return result; }
222 static constexpr std::size_t buffer_bytes = 64 * 1024;
223 std::optional<std::uint64_t> common_;
225 std::array<std::byte, buffer_bytes> buffer_{};
226 std::vector<std::uint64_t> offsets_;
228 std::uint64_t buffered_bits_ = 0, fc_bits_ = 0, count_ = 0, previous_units_ = 0;
229 bool failed_ = false;
230
231 void emit(bit_view source) {
232 while (!source.empty()) {
233 if (((buffered_bits_ | source.offset()) & 7) == 0 && source.size() >= buffer_bytes * 8) {
234 flush_tail();
235 auto bytes = source.size() >> 3;
236 stream_.append(source.storage().subspan(static_cast<std::size_t>(source.offset() >> 3),
237 static_cast<std::size_t>(bytes)));
238 source = source.subview(bytes << 3, source.size() - (bytes << 3));
239 continue;
240 }
241 auto count = std::min<std::uint64_t>(source.size(), buffer_bytes * 8 - buffered_bits_);
242 copy_bits(buffer_.data(), buffered_bits_, source.prefix(count));
243 buffered_bits_ += count;
244 source = source.subview(count, source.size() - count);
246 }
247 }
248 void emit_zeroes(std::uint64_t bits) {
249 static constexpr std::array<std::byte, 4096> zero{};
250 while (bits) {
251 auto count = std::min<std::uint64_t>(bits, zero.size() * 8);
252 emit(bit_view(zero, count)); bits -= count;
253 }
254 }
255 void flush_tail() {
256 if (!buffered_bits_) return;
257 if (buffered_bits_ & 7)
258 buffer_[buffered_bits_ >> 3] &= std::byte(0xffu << (8 - (buffered_bits_ & 7)));
259 stream_.append(std::span(buffer_).first(static_cast<std::size_t>(byte_count(buffered_bits_))));
260 buffered_bits_ = 0;
261 }
263 static constexpr std::array<std::byte, 8> zero{};
264 auto gap = (0 - stream_.body_bytes()) & 7;
265 stream_.append(std::span(zero).first(static_cast<std::size_t>(gap)));
266 }
267 void emit_words(std::span<std::uint64_t const> words) {
268 if constexpr (std::endian::native == std::endian::little) stream_.append(std::as_bytes(words));
269 else {
270 while (!words.empty()) {
271 auto count = std::min(words.size(), buffer_bytes / 8);
272 for (std::size_t i = 0; i != count; ++i) file_detail::put(buffer_, i << 3, 8, words[i]);
273 stream_.append(std::span(buffer_).first(count << 3)); words = words.subspan(count);
274 }
275 }
276 }
277 void emit_samples(std::span<elias_fano_sample const> samples) {
278 static_assert(sizeof(elias_fano_sample) == 16 && offsetof(elias_fano_sample, first) == 0 &&
279 offsetof(elias_fano_sample, sparse) == 8);
280 if constexpr (std::endian::native == std::endian::little) stream_.append(std::as_bytes(samples));
281 else {
282 while (!samples.empty()) {
283 auto count = std::min(samples.size(), buffer_bytes / 16);
284 for (std::size_t i = 0; i != count; ++i) {
285 file_detail::put(buffer_, i << 4, 8, samples[i].first);
286 file_detail::put(buffer_, (i << 4) + 8, 8, samples[i].sparse);
287 }
288 stream_.append(std::span(buffer_).first(count << 4)); samples = samples.subspan(count);
289 }
290 }
291 }
292 };
293 }
294
295}
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
void put_fixed(bit_string &target, std::uint64_t &at, std::uint64_t value, unsigned width)
Definition profile.h:504
std::uint64_t add(std::uint64_t a, std::uint64_t b)
Definition profile.h:39
std::uint64_t multiply(std::uint64_t a, std::uint64_t b)
Definition profile.h:44
std::uint64_t byte_count(std::uint64_t bits) noexcept
Definition profile.h:50
void resize(bit_string &value, std::uint64_t bits)
Definition profile.h:465
void copy_bits(std::byte *target, std::uint64_t first, bit_view source) noexcept
Definition profile.h:123
constexpr std::size_t native_directory_bytes
Definition sections.h:42
constexpr std::size_t native_descriptor_offset
Definition sections.h:44
constexpr std::size_t index_descriptor_offset
Definition sections.h:45
void put_id(std::span< std::byte > bytes, std::size_t at, object_id const &id)
Definition sections.h:75
constexpr std::size_t index_directory_bytes
Definition sections.h:43
constexpr std::uint16_t version
Definition sections.h:41
Definition active_engine.h:18
file_kind
Definition object_path.h:25
Appends immutable object bodies before their final envelope is known.
Encodes portable blob sections and retains mmap-backed profile directories.
Definition profile.h:166
std::vector< std::byte > bytes
Definition profile.h:167
bit_view view() const &
Definition profile.h:178
std::uint64_t bit_size
Definition profile.h:168
Definition profile.h:56
std::span< std::byte const > storage() const noexcept
Definition profile.h:67
std::uint64_t size() const noexcept
Definition profile.h:64
std::uint64_t offset() const noexcept
Definition profile.h:66
bool empty() const noexcept
Definition profile.h:65
bit_view subview(std::uint64_t first, std::uint64_t count) const
Definition profile.h:73
bit_view prefix(std::uint64_t count) const
Definition profile.h:80
Definition elias_fano.h:195
Definition elias_fano.h:397
std::vector< std::uint64_t > sparse
Definition elias_fano.h:445
std::vector< std::uint64_t > high
Definition elias_fano.h:443
std::vector< std::uint64_t > low
Definition elias_fano.h:442
std::vector< elias_fano_sample > samples
Definition elias_fano.h:444
unsigned low_width
Definition elias_fano.h:448
std::uint64_t universe
Definition elias_fano.h:447
Definition file.h:34
Definition object_writer.h:39
Definition object_path.h:38
Definition object_writer.h:88
Definition object_stream.h:32
void append(std::span< std::byte const > bytes)
Definition object_stream.h:68
object_write_paths const & paths() const &noexcept
Definition object_stream.h:62
std::uint64_t body_bytes() const noexcept
Definition object_stream.h:55
bool failed() const noexcept
Definition object_stream.h:60
bool finished() const noexcept
Definition object_stream.h:61
object_seal_receipt finish(file_header< P > const &header)
Definition object_stream.h:91
Definition object_writer.h:53
Definition profile_file_output.h:31
std::span< std::byte const > false_borrows
Definition profile_file_output.h:35
object_id const & native_id
Definition profile_file_output.h:32
std::span< std::uint64_t const > cut_lcps
Definition profile_file_output.h:36
std::optional< blob_identity > const & target_id
Definition profile_file_output.h:33
rank_groups< P::group_size > const & interleave
Definition profile_file_output.h:34
std::uint64_t virtual_count
Definition profile_file_output.h:37
Definition profile_file_output.h:44
std::optional< std::uint64_t > common_value_width() const noexcept
Definition profile_file_output.h:72
object_seal_receipt finish_impl(index_file_sections< P > const *index)
Definition profile_file_output.h:137
std::uint64_t buffered_bits_
Definition profile_file_output.h:228
bool finished() const noexcept
Definition profile_file_output.h:71
object_write_paths const & paths() const &noexcept
Definition profile_file_output.h:73
static constexpr std::size_t buffer_bytes
Definition profile_file_output.h:222
void emit_zeroes(std::uint64_t bits)
Definition profile_file_output.h:248
static constexpr std::size_t descriptor_offset
Definition profile_file_output.h:51
profile_file_output(profile_file_output &&)=delete
std::array< std::byte, buffer_bytes > buffer_
Definition profile_file_output.h:225
bool failed_
Definition profile_file_output.h:229
void require_active() const
Definition profile_file_output.h:75
std::uint64_t size() const noexcept
Definition profile_file_output.h:69
std::vector< std::uint64_t > offsets_
Definition profile_file_output.h:226
static constexpr std::size_t directory_bytes
Definition profile_file_output.h:49
static bit_string control_buffer()
Definition profile_file_output.h:221
profile_file_output & operator=(profile_file_output &&)=delete
static constexpr std::size_t section_count
Definition profile_file_output.h:53
void emit_words(std::span< std::uint64_t const > words)
Definition profile_file_output.h:267
void flush_tail()
Definition profile_file_output.h:255
object_seal_receipt finish(index_file_sections< P > const &index)
Definition profile_file_output.h:132
std::uint64_t count_
Definition profile_file_output.h:228
void align_stream()
Definition profile_file_output.h:262
static constexpr file_kind kind
Definition profile_file_output.h:48
static constexpr std::optional< std::uint64_t > default_width
Definition profile_file_output.h:54
bit_string control_
Definition profile_file_output.h:224
std::uint64_t previous_units_
Definition profile_file_output.h:228
object_seal_receipt finish()
Definition profile_file_output.h:131
profile_file_output(std::filesystem::path root, object_id id, object_attempt_id attempt, std::optional< std::uint64_t > common=default_width)
Definition profile_file_output.h:56
object_write_paths const & paths() const &&=delete
static std::optional< std::uint64_t > checked_width(std::optional< std::uint64_t > common)
Definition profile_file_output.h:199
void emit_samples(std::span< elias_fano_sample const > samples)
Definition profile_file_output.h:277
bool failed() const noexcept
Definition profile_file_output.h:70
profile_file_output & operator=(profile_file_output const &)=delete
object_stream< P, Ops > stream_
Definition profile_file_output.h:227
profile_file_output(std::filesystem::path root, object_id id, object_attempt_id attempt, std::optional< std::uint64_t > common, Ops &ops)
Definition profile_file_output.h:60
std::optional< std::uint64_t > common_
Definition profile_file_output.h:223
static constexpr bool borrowed
Definition profile_file_output.h:47
std::uint64_t fc_bits_
Definition profile_file_output.h:228
void append(std::uint64_t retained, bit_view literal, bit_view value)
Definition profile_file_output.h:80
profile_file_output(profile_file_output const &)=delete
void emit(bit_view source)
Definition profile_file_output.h:231
P policy_type
Definition profile_file_output.h:45
void validate_index(index_file_sections< P > const &index) const
Definition profile_file_output.h:209
Definition rank_groups.h:241
rank_groups_view< K > view() const &
Definition rank_groups.h:271
std::uint64_t virtual_count
Definition rank_groups.h:276
std::vector< std::uint64_t > checkpoints
Definition rank_groups.h:275
std::vector< std::uint64_t > classes
Definition rank_groups.h:274