Everett
Loading...
Searching...
No Matches
sort_profile_file_writer.h
Go to the documentation of this file.
1
12#pragma once
13
16
17namespace everett {
18 namespace sort_profile_detail {
19 template <class P, class Ops, class Stream = object_stream<P, Ops>> struct file_bit_sink {
20 static constexpr std::size_t buffer_bytes = 64 * 1024;
21 explicit file_bit_sink(Stream & stream) : stream_(stream) {}
22 std::uint64_t position() const noexcept { return bits_; }
23 std::uint64_t buffered_bits() const noexcept { return buffered_; }
24 // The unfinished physical byte is high aligned. Its unused low bits
25 // need not be zero; bit_string::copy produces a canonical owning tail.
26 bit_view buffered_payload() const & noexcept { return {buffer_, buffered_}; }
27 bit_view buffered_payload() const && = delete;
28 // A metadata limit can force a spill between records. Do not pad a
29 // partial byte in the middle of the payload: keep its bits for append.
31 auto bytes = buffered_ >> 3;
32 if (!bytes) return;
33 stream_.append(std::span(buffer_).first(static_cast<std::size_t>(bytes)));
34 buffered_ &= 7;
35 if (buffered_) buffer_[0] = buffer_[bytes];
36 }
37 void append(bit_view source) {
38 auto next = profile_detail::add(bits_, source.size());
39 if (next > std::numeric_limits<std::uint64_t>::max() - 7)
40 throw std::length_error("sort file bit extent");
41 while (!source.empty()) {
42 if (((buffered_ | source.offset()) & 7) == 0 && source.size() >= buffer_bytes * 8) {
43 flush();
44 auto bytes = source.size() >> 3;
45 stream_.append(source.storage().subspan(static_cast<std::size_t>(source.offset() >> 3),
46 static_cast<std::size_t>(bytes)));
47 source = source.subview(bytes << 3, source.size() - (bytes << 3));
48 } else {
49 auto count = std::min<std::uint64_t>(source.size(), buffer_bytes * 8 - buffered_);
50 profile_detail::copy_bits(buffer_.data(), buffered_, source.prefix(count));
51 buffered_ += count; source = source.subview(count, source.size() - count);
52 if (buffered_ == buffer_bytes * 8) flush();
53 }
54 }
55 bits_ = next;
56 }
57 void write_bits(std::uint64_t value, unsigned width) {
58 if (width > 64 || (width < 64 && (value >> width))) throw std::invalid_argument("sort file bit field");
59 std::array<std::byte, 8> bytes{};
60 if (width) {
61 auto aligned = value << (64 - width);
62 for (unsigned i = 0; i != 8; ++i) bytes[i] = std::byte(aligned >> (56 - (i << 3)));
63 }
64 append(bit_view(bytes, width));
65 }
66 template <class Code> void write_count(std::uint64_t value) {
68 (void)profile_detail::add(position(), profile_detail::backspace_bits<code>(value));
69 if constexpr (code::backspace_code == bit_backspace_code::exponential_golomb) {
70 auto quotient = value >> code::backspace_parameter;
71 if (quotient == std::numeric_limits<std::uint64_t>::max()) {
72 zeroes(64); write_bits(1, 1); write_bits(0, 64);
73 } else {
74 auto prefix = quotient + 1; auto width = unsigned(std::bit_width(prefix));
75 zeroes(width - 1); write_bits(prefix, width);
76 }
77 if constexpr (code::backspace_parameter)
78 write_bits(value & ((std::uint64_t{1} << code::backspace_parameter) - 1), unsigned(code::backspace_parameter));
79 } else {
80 constexpr auto modulus = code::backspace_parameter;
81 constexpr auto width = unsigned(std::bit_width(modulus - 1));
82 constexpr auto cutoff = profile_detail::golomb_cutoff<code>();
83 zeroes(value / modulus); write_bits(1, 1);
84 auto remainder = value % modulus;
85 if (remainder < cutoff) write_bits(remainder, width - 1);
86 else write_bits(remainder + cutoff, width);
87 }
88 }
89 // Only the final payload call may flush an incomplete physical byte.
90 void finish_payload() { flush(); }
91 void align() {
92 static constexpr std::array<std::byte, 8> zero{};
93 stream_.append(std::span(zero).first(static_cast<std::size_t>((0 - stream_.body_bytes()) & 7)));
94 }
95 void words(std::span<std::uint64_t const> input) {
96 if constexpr (std::endian::native == std::endian::little) stream_.append(std::as_bytes(input));
97 else while (!input.empty()) {
98 auto count = std::min(input.size(), buffer_bytes / 8);
99 for (std::size_t i = 0; i != count; ++i) file_detail::put(buffer_, i << 3, 8, input[i]);
100 stream_.append(std::span(buffer_).first(count << 3)); input = input.subspan(count);
101 }
102 }
103 void samples(std::span<elias_fano_sample const> input) {
104 static_assert(sizeof(elias_fano_sample) == 16 && offsetof(elias_fano_sample, first) == 0 && offsetof(elias_fano_sample, sparse) == 8);
105 if constexpr (std::endian::native == std::endian::little) stream_.append(std::as_bytes(input));
106 else while (!input.empty()) {
107 auto count = std::min(input.size(), buffer_bytes / 16);
108 for (std::size_t i = 0; i != count; ++i) {
109 file_detail::put(buffer_, i << 4, 8, input[i].first);
110 file_detail::put(buffer_, (i << 4) + 8, 8, input[i].sparse);
111 }
112 stream_.append(std::span(buffer_).first(count << 4)); input = input.subspan(count);
113 }
114 }
115 private:
116 Stream & stream_;
117 std::array<std::byte, buffer_bytes> buffer_{};
118 std::uint64_t buffered_ = 0, bits_ = 0;
119 void zeroes(std::uint64_t count) {
120 static constexpr std::array<std::byte, 4096> zero{};
121 while (count) {
122 auto part = std::min<std::uint64_t>(count, zero.size() * 8); append(bit_view(zero, part)); count -= part;
123 }
124 }
125 void flush() {
126 if (!buffered_) return;
127 if (buffered_ & 7) buffer_[buffered_ >> 3] &= std::byte(0xffu << (8 - (buffered_ & 7)));
128 stream_.append(std::span(buffer_).first(static_cast<std::size_t>(profile_detail::byte_count(buffered_))));
129 buffered_ = 0;
130 }
131 };
132 // Framing is complete before either concrete writer emits the shared KV03 sections.
133 template <class P, class Selector, class Sink, class Stream>
134 object_seal_receipt seal(encoder<P, Selector> const & encoder, Sink & sink, Stream & stream) {
135 auto const & ef = encoder.offsets;
136 std::array<std::byte, 192> directory{};
137 for (unsigned i = 0; i != 4; ++i) directory[i] = std::byte("KV03"[i]);
138 file_detail::put(directory, 4, 2, 3); file_detail::put(directory, 6, 2, 8);
139 file_detail::put(directory, 8, 8, encoder.metadata.extent);
141 file_detail::put(directory, 24, 8, ef.universe);
142 file_detail::put(directory, 32, 8, encoder.dictionary.bit_size);
143 file_detail::put(directory, 40, 8, encoder.seeds.bit_size);
144 directory[48] = std::byte(ef.low_width);
145 std::array<std::uint64_t, 8> lengths{profile_detail::byte_count(sink.position()),
146 profile_detail::multiply(ef.low.size(), 8), profile_detail::multiply(ef.high.size(), 8),
147 profile_detail::multiply(ef.samples.size(), 16), profile_detail::multiply(ef.sparse.size(), 8),
149 std::uint64_t end = directory.size();
150 for (std::size_t i = 0; i != lengths.size(); ++i) {
151 auto start = (profile_detail::add(end, 7)) & ~std::uint64_t{7}; end = profile_detail::add(start, lengths[i]);
152 file_detail::put(directory, 64 + 16 * i, 8, start); file_detail::put(directory, 72 + 16 * i, 8, lengths[i]);
153 }
156 sink.finish_payload();
157 sink.align(); sink.words(ef.low);
158 sink.align(); sink.words(ef.high);
159 sink.align(); sink.samples(ef.samples);
160 sink.align(); sink.words(ef.sparse);
161 sink.align(); stream.append(encoder.dictionary.bytes);
162 sink.align(); sink.words(encoder.dictionary_offsets);
163 sink.align(); stream.append(encoder.seeds.bytes);
164 return stream.finish(header, directory);
165 }
166 }
167
168 // Values and literal spans are consumed synchronously. The payload uses one
169 // 64KiB buffer; sparse offsets and the shared selector dictionary stay in RAM.
170 // No partially written attempt is a durable checkpoint. Any exception during
171 // append_frame or finish poisons the attempt; surviving paths remain intact.
172 template <class P, class Selector = registry_selector<typename P::registry_type>, class Ops = posix_object_ops>
174 using policy_type = P;
176 sort_profile_file_writer(std::filesystem::path root, object_id id, object_attempt_id attempt)
177 : stream_(std::move(root), std::move(id), std::move(attempt), file_kind::native_blob, 192), sink_(stream_) {}
178 sort_profile_file_writer(std::filesystem::path root, object_id id, object_attempt_id attempt, Ops & ops)
179 : stream_(std::move(root), std::move(id), std::move(attempt), file_kind::native_blob, 192, ops), sink_(stream_) {}
184 std::uint64_t size() const noexcept { return encoder_.size(); }
185 bool failed() const noexcept { return failed_ || stream_.failed(); }
186 bool finished() const noexcept { return stream_.finished(); }
187 auto common_value_width() const noexcept { return encoder_.metadata.common_value_width; }
188 object_write_paths const & paths() const & noexcept { return stream_.paths(); }
189 object_write_paths const & paths() const && = delete;
190 void append_frame(sort_profile_frame const & frame, std::span<bit_view const> key,
191 std::uint64_t common, bit_view value) {
193 try { encoder_.append_frame(sink_, frame, key, common, value); frame_only_ = true; }
194 catch (...) { failed_ = true; throw; }
195 }
196 // Checked full logical keys plus already sort-encoded values. The value
197 // never gets another length or a materialized record-sized scratch buffer.
198 void append_encoded(bit_view key, bit_view value, std::optional<std::uint64_t> retained_limit_bits = {}) {
200 if (frame_only_) throw std::logic_error("encoded append after trusted frame stream");
201 auto comparison = compare_common_bits<typename P::architecture>(previous_.view(), key);
202 if (size() && comparison.order >= 0) throw std::invalid_argument("sort file keys must be strictly ordered");
203 try {
204 sort_bit_reader input(key);
205 Selector::select(input, [&]<class S>(std::type_identity<S>, auto &) {
206 std::array<bit_view, 1> spans{key};
207 encoder_.template append<S>(sink_, key.prefix(input.position()), input.remaining(), spans, value, comparison.common_bits, retained_limit_bits);
208 });
210 } catch (...) { failed_ = true; throw; }
211 }
212 void append_encoded(profile_record const & record) { append_encoded(record.key.view(), record.value.view(), record.retained_limit_bits); }
215 try {
216 encoder_.finish(sink_.position());
217 return sort_profile_detail::seal<P>(encoder_, sink_, stream_);
218 } catch (...) { failed_ = true; throw; }
219 }
220 private:
225 bool failed_ = false, frame_only_ = false;
226 void require_active() const { if (failed() || finished()) throw std::logic_error("inactive sort file writer"); }
227 };
228}
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 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 copy_bits(std::byte *target, std::uint64_t first, bit_view source) noexcept
Definition profile.h:123
object_seal_receipt seal(encoder< P, Selector > const &encoder, Sink &sink, Stream &stream)
Definition sort_profile_file_writer.h:134
Definition active_engine.h:18
file_kind
Definition object_path.h:25
Appends immutable object bodies before their final envelope is known.
Navigates sort-owned bit records using shared sort seeds and sparse offsets.
Definition profile.h:166
std::vector< std::byte > bytes
Definition profile.h:167
bit_view view() const &
Definition profile.h:178
static bit_string copy(bit_view source)
Definition profile.h:181
std::uint64_t bit_size
Definition profile.h:168
Definition profile.h:56
std::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 file.h:34
Definition object_writer.h:39
Definition object_path.h:38
Definition object_writer.h:88
Definition object_stream.h:32
object_write_paths const & paths() const &noexcept
Definition object_stream.h:62
bool failed() const noexcept
Definition object_stream.h:60
bool finished() const noexcept
Definition object_stream.h:61
Definition object_writer.h:53
std::uint64_t extent
Definition profile.h:291
std::uint64_t terminal_key_units
Definition profile.h:289
std::optional< std::uint64_t > common_value_width
Definition profile.h:286
Definition profile.h:294
std::optional< std::uint64_t > retained_limit_bits
Definition profile.h:299
bit_string key
Definition profile.h:295
bit_string value
Definition profile.h:296
Definition sort_codec.h:56
Definition sort_profile.h:443
bit_string dictionary
Definition sort_profile.h:447
profile_metadata metadata
Definition sort_profile.h:446
std::vector< std::uint64_t > dictionary_offsets
Definition sort_profile.h:448
elias_fano offsets
Definition sort_profile.h:449
bit_string seeds
Definition sort_profile.h:447
std::uint64_t size() const noexcept
Definition sort_profile.h:450
Definition sort_profile_file_writer.h:19
void write_bits(std::uint64_t value, unsigned width)
Definition sort_profile_file_writer.h:57
void samples(std::span< elias_fano_sample const > input)
Definition sort_profile_file_writer.h:103
std::uint64_t position() const noexcept
Definition sort_profile_file_writer.h:22
std::uint64_t bits_
Definition sort_profile_file_writer.h:118
bit_view buffered_payload() const &&=delete
bit_view buffered_payload() const &noexcept
Definition sort_profile_file_writer.h:26
std::uint64_t buffered_bits() const noexcept
Definition sort_profile_file_writer.h:23
std::array< std::byte, buffer_bytes > buffer_
Definition sort_profile_file_writer.h:117
void write_count(std::uint64_t value)
Definition sort_profile_file_writer.h:66
void finish_payload()
Definition sort_profile_file_writer.h:90
void zeroes(std::uint64_t count)
Definition sort_profile_file_writer.h:119
void append(bit_view source)
Definition sort_profile_file_writer.h:37
void align()
Definition sort_profile_file_writer.h:91
std::uint64_t buffered_
Definition sort_profile_file_writer.h:118
file_bit_sink(Stream &stream)
Definition sort_profile_file_writer.h:21
void flush_complete()
Definition sort_profile_file_writer.h:30
Stream & stream_
Definition sort_profile_file_writer.h:116
static constexpr std::size_t buffer_bytes
Definition sort_profile_file_writer.h:20
void words(std::span< std::uint64_t const > input)
Definition sort_profile_file_writer.h:95
void flush()
Definition sort_profile_file_writer.h:125
Definition sort_profile_file_writer.h:173
void append_encoded(bit_view key, bit_view value, std::optional< std::uint64_t > retained_limit_bits={})
Definition sort_profile_file_writer.h:198
static constexpr std::size_t buffer_bytes
Definition sort_profile_file_writer.h:175
void append_frame(sort_profile_frame const &frame, std::span< bit_view const > key, std::uint64_t common, bit_view value)
Definition sort_profile_file_writer.h:190
object_write_paths const & paths() const &&=delete
void require_active() const
Definition sort_profile_file_writer.h:226
std::uint64_t size() const noexcept
Definition sort_profile_file_writer.h:184
auto common_value_width() const noexcept
Definition sort_profile_file_writer.h:187
sort_profile_file_writer(std::filesystem::path root, object_id id, object_attempt_id attempt, Ops &ops)
Definition sort_profile_file_writer.h:178
bit_string previous_
Definition sort_profile_file_writer.h:224
sort_profile_file_writer & operator=(sort_profile_file_writer &&)=delete
sort_profile_file_writer(std::filesystem::path root, object_id id, object_attempt_id attempt)
Definition sort_profile_file_writer.h:176
bool finished() const noexcept
Definition sort_profile_file_writer.h:186
sort_profile_file_writer & operator=(sort_profile_file_writer const &)=delete
object_seal_receipt finish()
Definition sort_profile_file_writer.h:213
sort_profile_detail::encoder< P, Selector > encoder_
Definition sort_profile_file_writer.h:223
sort_profile_file_writer(sort_profile_file_writer &&)=delete
void append_encoded(profile_record const &record)
Definition sort_profile_file_writer.h:212
sort_profile_file_writer(sort_profile_file_writer const &)=delete
object_write_paths const & paths() const &noexcept
Definition sort_profile_file_writer.h:188
bool frame_only_
Definition sort_profile_file_writer.h:225
object_stream< P, Ops > stream_
Definition sort_profile_file_writer.h:221
sort_profile_detail::file_bit_sink< P, Ops > sink_
Definition sort_profile_file_writer.h:222
bool failed() const noexcept
Definition sort_profile_file_writer.h:185
bool failed_
Definition sort_profile_file_writer.h:225
P policy_type
Definition sort_profile_file_writer.h:174
Definition sort_profile.h:163