Everett
Loading...
Searching...
No Matches
native_writer.h
Go to the documentation of this file.
1
13#pragma once
14
15#include <everett/profile.h>
16
17#include <array>
18#include <cstdint>
19#include <cstring>
20#include <optional>
21#include <stdexcept>
22#include <utility>
23#include <vector>
24
25namespace everett {
26 namespace profile_detail {
27 // Internal ordinary-FC framing shared by the checked public writer and
28 // the merge frontier. Retention and strict order are supplied by callers;
29 // no full key is stored here, only the preceding key's length.
30 template <class P> struct native_output {
31 using policy_type = P;
32 explicit native_output(std::optional<std::uint64_t> common_value_width)
34 if constexpr (P::fixed_width)
35 if (common_ != P::value_width)
36 error_detail::raise<std::invalid_argument>("native writer width disagrees with fixed policy");
37 if (common_) (void)multiply(*common_, P::bits_per_unit);
38 }
39 native_output(native_output const &) = delete;
41 native_output(native_output && other) noexcept
42 : data_(std::exchange(other.data_, {})), offsets_(std::move(other.offsets_)), common_(other.common_),
43 count_(std::exchange(other.count_, 0)), previous_units_(std::exchange(other.previous_units_, 0)),
44 finished_(std::exchange(other.finished_, true)) {}
45 native_output & operator=(native_output && other) noexcept {
46 if (this != &other) {
47 data_ = std::exchange(other.data_, {}); offsets_ = std::move(other.offsets_); common_ = other.common_;
48 count_ = std::exchange(other.count_, 0);
49 previous_units_ = std::exchange(other.previous_units_, 0);
50 finished_ = std::exchange(other.finished_, true);
51 }
52 return *this;
53 }
54 std::uint64_t size() const noexcept { return count_; }
55 bool finished() const noexcept { return finished_; }
56 bool failed() const noexcept { return false; }
57 std::optional<std::uint64_t> common_value_width() const noexcept { return common_; }
58 void require_active() const {
59 if (finished_) error_detail::raise<std::logic_error>("native profile writer is finished");
60 }
61
62 // The caller splits a representable full key at its exact retained
63 // prefix and proves strict order. Bounds and value framing remain checked.
64 // Input spans need only survive this call and must not alias private output.
65 void append(std::uint64_t retained, bit_view literal, bit_view value) {
67 if ((literal.size() & (P::bits_per_unit - 1)) || (value.size() & (P::bits_per_unit - 1)))
68 error_detail::raise<std::invalid_argument>("native writer record length disagrees with policy units");
69 auto value_units = value.size() >> P::unit_shift;
70 if (common_ && value_units != *common_)
71 error_detail::raise<std::invalid_argument>("native writer value disagrees with common width");
72 if (retained > previous_units_ || (count_ && literal.empty()))
73 error_detail::raise<std::invalid_argument>("native writer invalid known prefix");
74 auto literal_units = literal.size() >> P::unit_shift;
75 auto key_units = retained + literal_units;
76 auto next_count = add(count_, 1);
77 auto saved_bits = data_.bit_size;
78 auto saved_offsets = offsets_.size();
79 try {
80 auto block = count_ % P::codec_block_size == 0;
81 if (block) {
82 auto stride = multiply(count_, common_.value_or(0));
83 offsets_.push_back((data_.bit_size >> P::unit_shift) - stride);
84 }
85 if constexpr (P::unit == profile_unit::byte) {
86 // At most three ten-byte LEB128 counts. Admit the whole frame,
87 // grow once, and copy directly into its final byte positions.
88 std::array<std::byte, 30> control;
89 auto end = control.data();
90 auto count = [&](std::uint64_t n) {
91 while (n >= 128) { *end++ = std::byte((n & 127) | 128); n >>= 7; }
92 *end++ = std::byte(n);
93 };
94 count(block ? retained : previous_units_ - retained);
95 count(literal_units);
96 if (!common_) count(value_units);
97 auto control_bytes = std::uint64_t(end - control.data());
98 auto payload_bits = add(literal.size(), value.size());
99 resize(data_, add(saved_bits, add(control_bytes << 3, payload_bits)));
100 std::memcpy(data_.bytes.data() + (saved_bits >> 3), control.data(), static_cast<std::size_t>(control_bytes));
101 auto at = saved_bits + (control_bytes << 3);
102 copy_bits(data_.bytes.data(), at, literal);
103 copy_bits(data_.bytes.data(), at + literal.size(), value);
104 } else {
105 if (block) write_count<P>(data_, retained);
106 else write_backspace<P>(data_, previous_units_ - retained);
107 write_count<P>(data_, literal_units);
108 if (!common_) write_count<P>(data_, value_units);
111 }
112 } catch (...) {
113 resize(data_, saved_bits);
114 offsets_.resize(saved_offsets);
115 throw;
116 }
117 previous_units_ = key_units;
118 count_ = next_count;
119 }
120
123 profile_array<P> result;
124 auto extent = data_.bit_size >> P::unit_shift;
125 auto stride = multiply(count_, common_.value_or(0));
126 offsets_.push_back(extent - stride);
127 try {
128 result.offsets_ = elias_fano::build<typename P::architecture>(offsets_);
129 } catch (...) {
130 offsets_.pop_back();
131 throw;
132 }
135 result.metadata_.extent = extent;
137 result.bytes_ = std::move(data_.bytes);
138 data_.bit_size = 0;
139 offsets_ = {};
140 finished_ = true;
141 return result;
142 }
143
144 private:
146 std::vector<std::uint64_t> offsets_;
147 std::optional<std::uint64_t> common_;
148 std::uint64_t count_ = 0;
149 std::uint64_t previous_units_ = 0;
150 bool finished_ = false;
151 };
152 }
153
154 // Incremental ordinary-FC native output. A common value width, if any, is
155 // fixed before the first record. Otherwise every value carries its length.
156 // Keeps encoded output, one residual offset per physical block, and the last
157 // key; it does not stage a vector of full records. Output remains in memory.
158 template <class P> struct profile_native_writer {
159 using policy_type = P;
161
162 explicit profile_native_writer(std::optional<std::uint64_t> common_value_width = P::value_width)
167 : output_(std::move(other.output_)), previous_(std::exchange(other.previous_, {})) {}
169 if (this != &other) {
170 output_ = std::move(other.output_); previous_ = std::exchange(other.previous_, {});
171 }
172 return *this;
173 }
174
175 std::uint64_t size() const noexcept { return output_.size(); }
176 bool finished() const noexcept { return output_.finished(); }
177 std::optional<std::uint64_t> common_value_width() const noexcept { return output_.common_value_width(); }
178
179 // Keys must be strictly increasing. Input views need only survive this
180 // call. A rejected append leaves the preceding committed records intact.
181 void append(bit_view key, bit_view value, std::optional<std::uint64_t> retained_limit_bits = {}) {
182 output_.require_active();
183 if ((key.size() & (P::bits_per_unit - 1)) || (value.size() & (P::bits_per_unit - 1)))
184 error_detail::raise<std::invalid_argument>("native writer record length disagrees with policy units");
185 auto common = output_.common_value_width();
186 if (common && (value.size() >> P::unit_shift) != *common)
187 error_detail::raise<std::invalid_argument>("native writer value disagrees with common width");
188 auto comparison = compare_common_bits<typename P::architecture>(previous_.view(), key);
189 if (output_.size() && comparison.order >= 0)
190 error_detail::raise<std::invalid_argument>("native writer keys must be strictly increasing");
191 auto retained = std::min(comparison.common_bits, retained_limit_bits.value_or(comparison.common_bits)) >> P::unit_shift;
192 auto retained_bits = retained * P::bits_per_unit;
193 // Reserve before fallible output writes. Once the frame commits, updating
194 // the logical predecessor cannot allocate or leave a partial bit string.
195 auto next_bytes = profile_detail::byte_count(key.size());
196 if (next_bytes > previous_.bytes.max_size())
197 error_detail::raise<std::length_error>("profile bit string too large");
198 previous_.bytes.reserve(static_cast<std::size_t>(next_bytes));
199 auto literal = key.subview(retained_bits, key.size() - retained_bits);
200 output_.append(retained, literal, value);
202 profile_detail::copy_into(previous_, retained_bits, literal);
203 }
204 void append(profile_record const & record) { append(record.key.view(), record.value.view(), record.retained_limit_bits); }
205
206 // Finalizing Elias–Fano visits the staged block offsets. This operation
207 // is neither byte-budgeted nor a durable checkpoint.
209 auto result = output_.finish();
210 previous_ = {};
211 return result;
212 }
213
214 private:
217 };
218}
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_into(bit_string &target, std::uint64_t first, bit_view source)
Definition profile.h:474
void copy_bits(std::byte *target, std::uint64_t first, bit_view source) noexcept
Definition profile.h:123
void append(bit_string &target, bit_view source)
Definition profile.h:480
Definition active_engine.h:18
stream_role
Definition policy.h:25
Declares Everett's profile support.
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::uint64_t size() const noexcept
Definition profile.h:64
bool empty() const noexcept
Definition profile.h:65
Definition profile.h:1168
profile_metadata metadata_
Definition profile.h:1259
elias_fano offsets_
Definition profile.h:1258
std::vector< std::byte > bytes_
Definition profile.h:1256
Definition native_writer.h:30
std::vector< std::uint64_t > offsets_
Definition native_writer.h:146
void append(std::uint64_t retained, bit_view literal, bit_view value)
Definition native_writer.h:65
std::optional< std::uint64_t > common_
Definition native_writer.h:147
profile_array< P > finish()
Definition native_writer.h:121
std::uint64_t count_
Definition native_writer.h:148
bool failed() const noexcept
Definition native_writer.h:56
native_output & operator=(native_output &&other) noexcept
Definition native_writer.h:45
void require_active() const
Definition native_writer.h:58
native_output & operator=(native_output const &)=delete
native_output(native_output const &)=delete
std::uint64_t size() const noexcept
Definition native_writer.h:54
native_output(std::optional< std::uint64_t > common_value_width)
Definition native_writer.h:32
std::optional< std::uint64_t > common_value_width() const noexcept
Definition native_writer.h:57
native_output(native_output &&other) noexcept
Definition native_writer.h:41
P policy_type
Definition native_writer.h:31
bit_string data_
Definition native_writer.h:145
bool finished() const noexcept
Definition native_writer.h:55
bool finished_
Definition native_writer.h:150
std::uint64_t previous_units_
Definition native_writer.h:149
std::uint64_t extent
Definition profile.h:291
std::uint64_t terminal_key_units
Definition profile.h:289
std::uint64_t record_count
Definition profile.h:290
std::optional< std::uint64_t > common_value_width
Definition profile.h:286
Definition native_writer.h:158
bit_string previous_
Definition native_writer.h:216
profile_native_writer(profile_native_writer const &)=delete
std::optional< std::uint64_t > common_value_width() const noexcept
Definition native_writer.h:177
void append(profile_record const &record)
Definition native_writer.h:204
profile_native_writer & operator=(profile_native_writer const &)=delete
static constexpr stream_role role
Definition native_writer.h:160
void append(bit_view key, bit_view value, std::optional< std::uint64_t > retained_limit_bits={})
Definition native_writer.h:181
std::uint64_t size() const noexcept
Definition native_writer.h:175
profile_native_writer(profile_native_writer &&other) noexcept
Definition native_writer.h:166
profile_native_writer(std::optional< std::uint64_t > common_value_width=P::value_width)
Definition native_writer.h:162
P policy_type
Definition native_writer.h:159
bool finished() const noexcept
Definition native_writer.h:176
profile_detail::native_output< P > output_
Definition native_writer.h:215
profile_array< P > finish()
Definition native_writer.h:208
profile_native_writer & operator=(profile_native_writer &&other) noexcept
Definition native_writer.h:168
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