Everett
Loading...
Searching...
No Matches
typed_world.h
Go to the documentation of this file.
1
12#pragma once
13
15#include <everett/fingerprint.h>
16#include <everett/sort_codec.h>
17#include <everett/session.h>
19
20#include <algorithm>
21#include <bit>
22#include <concepts>
23#include <memory>
24#include <string_view>
25
26namespace everett {
27 // A custom sort supplies state_type, initial(key), apply(key,state,arrow),
28 // compose(key,older,newer), present(key,state), hash_key(key), and
29 // hash_value(key,state). Its value codec encodes arrows, not necessarily
30 // states. replacement=true permits first-hit reads and put/erase helpers.
31 template <class S> struct sort_semantics : S {};
32
33 template <> struct sort_semantics<unsorted<std::optional<std::string>>> {
34 using state_type = std::optional<std::string>;
35 static constexpr bool replacement = true;
36 static state_type initial(std::string const &) { return std::nullopt; }
37 static state_type apply(std::string const &, state_type const &, state_type value) { return value; }
38 static state_type compose(std::string const &, state_type const &, state_type newer) { return newer; }
39 static state_type erase(std::string const &) { return std::nullopt; }
40 static bool present(std::string const &, state_type const & value) noexcept { return value.has_value(); }
41 static std::uint64_t hash_key(std::string const & key) noexcept { return u64_table_hash{}.key(key); }
42 static std::uint64_t hash_value(std::string const &, state_type const & value) noexcept {
43 return value ? u64_table_hash::mix(u64_table_hash{}.key(*value) ^ 0xd6e8feb86659fd93ULL) : 0;
44 }
45 };
46
47 namespace typed_detail {
48 template <class Leaves> struct default_sort { using type = void; };
49 template <class S> struct default_sort<registry_detail::sorts<S>> { using type = S; };
50 template <class P> using default_sort_t = typename default_sort<
52 template <class S> using key_t = typename sort_codec<S>::key_codec::value_type;
53 template <class S> using arrow_t = typename sort_codec<S>::value_codec::value_type;
54 template <class S> using state_t = typename sort_semantics<S>::state_type;
55 template <class S> constexpr bool replacement = [] {
56 if constexpr (requires { sort_semantics<S>::replacement; }) return bool(sort_semantics<S>::replacement);
57 else return false;
58 }();
59
60 template <class Leaves> struct all_replacements;
61 template <class... S> struct all_replacements<registry_detail::sorts<S...>>
62 : std::bool_constant<(replacement<S> && ...)> {};
63
64 // The byte profile already frames complete key and value extents. The
65 // built-in string sort can borrow those boundaries instead of escaping
66 // keys or putting a second length and bit-aligned tag inside the value.
67 template <class P, class S> inline constexpr bool byte_strings =
68 P::unit == profile_unit::byte && std::same_as<S, unsorted<std::optional<std::string>>>;
69 inline std::string byte_string(bit_view bits) {
70 if ((bits.offset() | bits.size()) & 7)
71 throw std::invalid_argument("unaligned byte string");
72 if (!bits.size()) return {};
73 return {reinterpret_cast<char const *>(bits.storage().data() + (bits.offset() >> 3)),
74 static_cast<std::size_t>(bits.size() >> 3)};
75 }
76 inline bool byte_string_present(bit_view encoded) {
77 if (((encoded.offset() | encoded.size()) & 7) || encoded.size() < 8)
78 throw std::invalid_argument("truncated or unaligned byte string value");
79 auto tag = std::to_integer<unsigned>(encoded.storage()[encoded.offset() >> 3]);
80 if (tag > 1 || (!tag && encoded.size() != 8))
81 throw std::invalid_argument("noncanonical byte string value");
82 return tag != 0;
83 }
84 template <class P, class S> key_t<S> read_key(sort_bit_reader & input) {
85 if constexpr (byte_strings<P, S>)
86 return byte_string(input.take_bits(input.remaining()));
88 }
89 template <class P, class S> bit_string key(key_t<S> const & value) {
90 bit_string result;
91 sort_bit_writer out(result);
92 write_sort_code<typename P::registry_type, S>(out);
93 if constexpr (byte_strings<P, S>) out.append(sort_codec_detail::string_bits(value));
95 if (result.bit_size & (P::bits_per_unit - 1))
96 throw std::invalid_argument("sort key is not aligned for its profile policy");
97 return result;
98 }
99 template <class P, class S> bit_string value(arrow_t<S> const & value) {
100 bit_string result;
101 sort_bit_writer out(result);
102 if constexpr (byte_strings<P, S>) {
103 result.bytes.reserve(value ? value->size() + 1 : 1);
104 out.write_bits(value ? 1 : 0, 8);
107 if constexpr (P::unit == profile_unit::byte)
108 if (auto tail = result.bit_size & 7) out.write_bits(0, unsigned(8 - tail));
109 if (P::value_width && (result.bit_size >> P::unit_shift) != *P::value_width)
110 throw std::invalid_argument("sort arrow differs from the policy's fixed value width");
111 return result;
112 }
113 template <class P, class S> arrow_t<S> value(bit_view encoded) {
114 if constexpr (byte_strings<P, S>) {
115 if (!byte_string_present(encoded)) return std::nullopt;
116 return byte_string(encoded.subview(8, encoded.size() - 8));
117 } else {
118 sort_bit_reader in(encoded);
119 auto result = sort_codec<S>::value_codec::read(in);
120 if constexpr (P::unit == profile_unit::byte) {
121 if (in.remaining() > 7 || (in.remaining() && in.read_bits(unsigned(in.remaining()))))
122 throw std::invalid_argument("noncanonical typed value padding");
123 } else if (!in.empty()) throw std::invalid_argument("trailing typed arrow bits");
124 return result;
125 }
126 }
127 template <class P, class F> decltype(auto) dispatch_key(bit_view encoded, F && action) {
128 sort_bit_reader in(encoded);
129 return dispatch_sort<typename P::registry_type>(in, [&]<class S>(std::type_identity<S> tag, auto & source) {
130 auto decoded = read_key<P, S>(source);
131 if (!source.empty()) throw std::invalid_argument("trailing typed key bits");
132 return std::invoke(std::forward<F>(action), tag, decoded);
133 });
134 }
135 template <class P> struct profile_key_transport {
136 template <class S> static bit_string encode(key_t<S> const & value) { return key<P, S>(value); }
137 template <class S> static bit_string prefix() { return sort_code<typename P::registry_type, S>(); }
138 template <class S> static key_t<S> decode(bit_view bits) {
139 sort_bit_reader input(bits);
140 auto result = read_key<P, S>(input);
141 if (!input.empty()) throw std::invalid_argument("trailing typed key bits");
142 return result;
143 }
144 template <class F> static decltype(auto) dispatch(bit_view bits, F && fn) {
145 return dispatch_key<P>(bits, std::forward<F>(fn));
146 }
147 };
148 template <class P, class Family, class = void> struct transport { using type = profile_key_transport<P>; };
149 template <class P, class Family> struct transport<P, Family, std::void_t<typename Family::key_transport>> {
150 using type = typename Family::key_transport;
151 };
152 template <class P, class Family> using transport_t = typename transport<P, Family>::type;
153 template <class P, class Transport> struct tombstone_predicate {
154 bool is_tombstone(bit_view encoded) const
155 requires std::same_as<default_sort_t<P>, unsorted<std::optional<std::string>>> {
156 // The optional-value tag is independent of the key and payload length.
157 if constexpr (P::unit == profile_unit::byte) return !byte_string_present(encoded);
158 else return !encoded.at(0);
159 }
160 bool is_tombstone(bit_view key, bit_view encoded) const {
161 return Transport::dispatch(key, [&]<class S>(std::type_identity<S>, auto const & decoded) {
162 if constexpr (replacement<S>) {
163 using semantics = sort_semantics<S>;
164 auto state = semantics::apply(decoded, semantics::initial(decoded), value<P, S>(encoded));
165 return !semantics::present(decoded, state);
166 } else return false;
167 });
168 }
169 };
170 template <class P, class Transport = profile_key_transport<P>>
172 // Only the binary overload: key-aware composition would reconstruct keys.
173 bit_view operator()(bit_view, bit_view newer) const { return newer; }
174 };
175 template <class P, class Transport = profile_key_transport<P>> struct compose : tombstone_predicate<P, Transport> {
177 return Transport::dispatch(key, [&]<class S>(std::type_identity<S>, auto const & decoded) {
178 auto before = value<P, S>(older), after = value<P, S>(newer);
179 return value<P, S>(sort_semantics<S>::compose(decoded, std::move(before), std::move(after)));
180 });
181 }
182 };
183 }
184
185 template <class A = wrapping_fingerprint_algebra> struct typed_world_metadata {
186 typename A::element signature = A::zero();
187 std::uint64_t live_count = 0;
188 std::string schema_id;
189
190 // Compact trusted checkpoint payload: signature, live count, then the
191 // nonempty schema ID. Runtime frontier intervals are stored separately.
192 std::vector<std::byte> encode() const requires std::same_as<typename A::element, std::uint64_t> {
193 if (schema_id.empty()) throw std::invalid_argument("empty typed schema identity");
194 std::vector<std::byte> result(16 + schema_id.size());
195 for (unsigned i = 0; i != 8; ++i) {
196 result[i] = std::byte(signature >> (i << 3));
197 result[8 + i] = std::byte(live_count >> (i << 3));
198 }
199 std::copy(std::as_bytes(std::span(schema_id)).begin(), std::as_bytes(std::span(schema_id)).end(), result.begin() + 16);
200 return result;
201 }
202 static typed_world_metadata decode(std::span<std::byte const> data)
203 requires std::same_as<typename A::element, std::uint64_t> {
204 if (data.size() <= 16) throw std::invalid_argument("truncated typed metadata");
206 for (unsigned i = 0; i != 8; ++i) {
207 result.signature |= std::uint64_t(std::to_integer<unsigned char>(data[i])) << (i << 3);
208 result.live_count |= std::uint64_t(std::to_integer<unsigned char>(data[8 + i])) << (i << 3);
209 }
210 result.schema_id.assign(reinterpret_cast<char const *>(data.data() + 16), data.size() - 16);
211 return result;
212 }
213 bool operator==(typed_world_metadata const &) const = default;
214 };
215
216 template <class P, class A, class Family = binary_runtime_family<P>> struct typed_contribution;
217 template <class P, class A, class Family = binary_runtime_family<P>> struct typed_batch;
218 template <class P, class A, std::uint64_t DepthLimit, class Family> struct typed_engine;
219 template <class P, class A, std::uint64_t DepthLimit, class Family> struct replacement_rebuild_engine;
220
221 template <class P = storage_policy<>, class A = wrapping_fingerprint_algebra,
222 class Family = binary_runtime_family<P>> struct typed_world {
223 using policy_type = P;
225 using runtime_family = Family;
227 using runtime_snapshot = typename Family::snapshot_type;
230 runtime_snapshot const & runtime() const & noexcept { return state_->runtime; }
231 runtime_snapshot const & runtime() const && = delete;
232 metadata_type const & metadata() const & noexcept { return state_->metadata; }
233 auto signature() const { return state_->metadata.signature; }
234 std::uint64_t live_count() const noexcept { return state_->metadata.live_count; }
235
236 // Metadata and the exact graph must have been admitted together. This
237 // checks schema identity and counts; it deliberately does not scan hashes.
238 static typed_world restore(runtime_snapshot data, metadata_type metadata, std::string_view expected_schema) {
239 if (expected_schema.empty() || metadata.schema_id != expected_schema || metadata.live_count > data.admissions())
240 throw std::invalid_argument("typed snapshot metadata or schema mismatch");
241 return {std::move(data), std::move(metadata)};
242 }
243 template <class S = typed_detail::default_sort_t<P>> typed_detail::state_t<S>
244 get(typed_detail::key_t<S> const & key) const {
245 return get_encoded<S>(key, key_transport::template encode<S>(key));
246 }
248 template <class S = typed_detail::default_sort_t<P>> contribution_type
249 change(typed_detail::key_t<S> const & key, typed_detail::arrow_t<S> const & arrow) const;
250 template <class S = typed_detail::default_sort_t<P>> contribution_type
251 put(typed_detail::key_t<S> const & key, typed_detail::state_t<S> const & value) const
252 requires typed_detail::replacement<S>;
253 template <class S = typed_detail::default_sort_t<P>> contribution_type
254 erase(typed_detail::key_t<S> const & key) const requires typed_detail::replacement<S>;
255 private:
256 template <class, class, class> friend struct typed_batch;
257 template <class, class, std::uint64_t, class> friend struct typed_engine;
258 template <class, class, std::uint64_t, class> friend struct replacement_rebuild_engine;
259 // A contribution already owns its encoded key. The logical key remains
260 // available for sort semantics, while this synchronous query shares the
261 // same implementation as get without encoding it again. Only typed_batch
262 // creates contribution records, through this same Family's transport;
263 // preflight still dispatches and validates the encoded key first. Custom views and
264 // all escaping cursor contexts still take an owning copy.
265 template <class S, class Query> typed_detail::state_t<S>
266 get_encoded(typed_detail::key_t<S> const & key, Query && encoded,
267 std::optional<std::uint64_t> * retained_limit_bits = nullptr) const {
268 static_assert(std::same_as<std::remove_cvref_t<Query>, bit_string>);
269 using semantics = sort_semantics<S>;
270 if (retained_limit_bits) retained_limit_bits->reset();
271 auto inspect = [&](auto const & native, std::uint64_t ordinal) {
272 if (retained_limit_bits) {
273 if constexpr (requires { native.encoded_at(ordinal).retained; })
274 *retained_limit_bits = native.encoded_at(ordinal).retained << P::unit_shift;
275 else *retained_limit_bits = 0; // A custom view can always emit the whole key.
276 }
277 };
278 if constexpr (typed_detail::replacement<S>) {
279 auto decode = [&](bit_view value) -> typed_detail::state_t<S> {
280 return semantics::apply(key, semantics::initial(key), typed_detail::value<P, S>(value));
281 };
282 if constexpr (requires { cola_detail::first_value(state_->runtime.query_root(), std::forward<Query>(encoded), decode); }) {
283 auto value = cola_detail::first_value(state_->runtime.query_root(), std::forward<Query>(encoded), decode, inspect);
284 return value ? std::move(*value) : semantics::initial(key);
285 }
286 }
287 auto cursor = [&] {
288 if constexpr (requires { state_->runtime.cursor_owned(bit_string(std::forward<Query>(encoded))); })
289 return state_->runtime.cursor_owned(bit_string(std::forward<Query>(encoded)));
290 else return state_->runtime.cursor(encoded.view());
291 }();
292 if constexpr (typed_detail::replacement<S>) {
293 while (!cursor.done()) {
294 cursor.step(1);
295 if (cursor.has_match()) {
296 auto match = cursor.take_match();
297 if (retained_limit_bits) {
298 if constexpr (requires { match.secondary; match.ordinal;
299 match.source->secondary_target()->view(); match.source->view(); }) {
300 if (match.secondary) inspect(match.source->secondary_target()->view(), match.ordinal);
301 else if constexpr (requires { match.source->view().native(); })
302 inspect(match.source->view().native(), match.ordinal);
303 else *retained_limit_bits = 0;
304 } else *retained_limit_bits = 0;
305 }
306 return semantics::apply(key, semantics::initial(key), typed_detail::value<P, S>(match.value.view()));
307 }
308 }
309 return semantics::initial(key);
310 } else {
311 std::vector<typed_detail::arrow_t<S>> arrows;
312 while (!cursor.done()) {
313 cursor.step(1);
314 while (cursor.has_match()) {
315 auto match = cursor.take_match();
316 arrows.push_back(typed_detail::value<P, S>(match.value.view()));
317 }
318 }
319 auto state = semantics::initial(key);
320 for (auto i = arrows.rbegin(); i != arrows.rend(); ++i) state = semantics::apply(key, std::move(state), *i);
321 return state;
322 }
323 }
328 // A snapshot retains one immutable head. Its schema and transitive file
329 // dependencies remain shared rather than copied into each reader.
330 std::shared_ptr<state const> state_;
332 : state_(std::make_shared<state const>(state{std::move(data), std::move(metadata)})) {}
333 };
334
335 template <class P, class A, class Family> struct typed_contribution {
336 std::optional<typed_world<P, A, Family>> const & base() const noexcept { return base_; }
337 std::span<profile_record const> records() const noexcept { return records_; }
338 private:
339 friend struct typed_batch<P, A, Family>;
340 template <class, class> friend struct typed_scan;
341 template <class, class, std::uint64_t, class> friend struct typed_engine;
342 template <class, class, std::uint64_t, class> friend struct replacement_rebuild_engine;
343 std::optional<typed_world<P, A, Family>> base_;
344 std::vector<profile_record> records_;
345 // Only a resolved scan can supply these observations. They are validated
346 // against the current native frontier before any mutation is admitted.
347 // The immutable base owns every byte referenced by these borrowed values.
348 std::optional<std::vector<bit_view>> observed_;
349 typed_contribution(std::optional<typed_world<P, A, Family>> base, std::vector<profile_record> records)
350 : base_(std::move(base)), records_(std::move(records)) {}
351 };
352
353 template <class P, class A, class Family> struct typed_batch {
354 typed_batch() = default;
355 explicit typed_batch(typed_world<P, A, Family> base) : base_(std::move(base)) {}
356 template <class S = typed_detail::default_sort_t<P>> typed_batch &
358 records_.push_back({typed_detail::transport_t<P, Family>::template encode<S>(key), typed_detail::value<P, S>(arrow)});
359 return *this;
360 }
361 template <class S = typed_detail::default_sort_t<P>> typed_batch &
363 requires typed_detail::replacement<S> { return change<S>(key, value); }
364 template <class S = typed_detail::default_sort_t<P>> typed_batch &
365 erase(typed_detail::key_t<S> const & key) requires typed_detail::replacement<S> {
366 auto encoded = typed_detail::transport_t<P, Family>::template encode<S>(key);
367 std::optional<std::uint64_t> retained;
368 if (base_ && !sort_semantics<S>::present(key, base_->template get_encoded<S>(key, encoded, &retained)))
369 throw std::invalid_argument("deleting absent typed key");
370 records_.push_back({std::move(encoded), typed_detail::value<P, S>(sort_semantics<S>::erase(key)), retained});
371 return *this;
372 }
374 std::sort(records_.begin(), records_.end(), [](auto const & a, auto const & b) {
375 return compare_bits<typename P::architecture>(a.key.view(), b.key.view()) < 0;
376 });
377 for (std::size_t i = 1; i < records_.size(); ++i)
378 if (compare_bits<typename P::architecture>(records_[i - 1].key.view(), records_[i].key.view()) == 0)
379 throw std::invalid_argument("duplicate key in typed batch");
380 return {std::move(base_), std::move(records_)};
381 }
382 private:
383 std::optional<typed_world<P, A, Family>> base_;
384 std::vector<profile_record> records_;
385 };
386
387 template <class P, class A, class Family> auto typed_world<P, A, Family>::batch() const -> batch_type { return batch_type(*this); }
388 template <class P, class A, class Family> template <class S>
390 auto result = batch(); result.template change<S>(key, arrow); return std::move(result).finish();
391 }
392 template <class P, class A, class Family> template <class S>
394 requires typed_detail::replacement<S> { return change<S>(key, value); }
395 template <class P, class A, class Family> template <class S>
397 requires typed_detail::replacement<S> {
398 auto result = batch(); result.template erase<S>(key); return std::move(result).finish();
399 }
400
401 // DepthLimit bounds the main-chain nodes of every imported or published
402 // root, including routing ancestors. It is enforced support, not an inferred
403 // COLA theorem, and bounds the allowance for ready singleton admissions.
404 template <class P = storage_policy<>, class A = wrapping_fingerprint_algebra, std::uint64_t DepthLimit = 256,
405 class Family = binary_runtime_family<P>>
407 using policy_type = P;
411 using runtime_family = Family;
416 using runtime_type = typename Family::template runtime_type<compose_type>;
417 static constexpr bool charged_service = requires { runtime_type::service_budget(std::uint64_t{}); };
418 static constexpr std::uint64_t ready_admission_allowance = [] {
419 if constexpr (charged_service) return 2 * runtime_type::local_charge_bound + 16 * DepthLimit + 512;
420 else return 2 * P::group_size + 128 + DepthLimit + 32;
421 }();
422 // Reservation cannot inspect a concurrently changing executor. Reserve a
423 // conservative 64-level service ceiling; actual service uses current h.
424 static constexpr std::uint64_t admission_allowance = [] {
425 if constexpr (charged_service)
426 return ready_admission_allowance + runtime_type::local_charge_bound * 8 * (runtime_type::maximum_levels + 2);
427 else return ready_admission_allowance;
428 }();
429 static_assert(DepthLimit && DepthLimit < (std::uint64_t{1} << 32) && P::group_size < (std::uint64_t{1} << 32));
430
431 explicit typed_engine(std::string schema_id = default_schema())
432 : runtime_(), current_(checked_snapshot(runtime_.snapshot()), metadata_type{A::zero(), 0, checked_schema(std::move(schema_id))}) {}
433 static typed_engine from_snapshot(world_type state) { return typed_engine(std::move(state)); }
434 template <class Storage> static typed_engine from_snapshot(world_type state, Storage storage)
435 requires requires { runtime_type::from_snapshot(state.runtime(), std::move(storage)); } {
436 return typed_engine(std::move(state), std::move(storage));
437 }
438 // The caller supplies an equivalent admitted layout. Metadata is a cheap
439 // consistency check, not a cryptographic proof of equivalent contents.
440 void rebase(world_type state) {
442 if (pending() || state.metadata() != current_.metadata() || state.runtime().admissions() != current_.runtime().admissions())
443 throw std::invalid_argument("typed rebase requires a settled equivalent snapshot");
444 require_depth(state.runtime());
445 auto replacement = [&] {
446 if constexpr (requires { runtime_.storage(); }) return runtime_type::from_snapshot(state.runtime(), runtime_.storage());
447 else return runtime_type::from_snapshot(state.runtime());
448 }();
449 runtime_ = std::move(replacement); current_ = std::move(state);
450 }
451 world_type snapshot() const { return current_; }
452 // A rebuild shares the concrete execution context, not a borrowed backend pointer.
453 auto storage() const requires requires (runtime_type const & value) { value.storage(); } { return runtime_.storage(); }
454 bool pending() const noexcept { return runtime_.pending(); }
455 bool failed() const noexcept { return failed_ || runtime_.failed(); }
456 void poison() noexcept {
457 failed_ = true;
458 if constexpr (requires { { runtime_.poison() } noexcept; }) runtime_.poison();
459 }
460 bool admission_ready() const noexcept { return runtime_.admission_ready(); }
461 auto work() const { return runtime_.work(); }
462 static typed_batch<P, A, Family> batch() { return {}; }
463 template <class S = typed_detail::default_sort_t<P>> static contribution_type
465 auto result = batch(); result.template change<S>(key, arrow); return std::move(result).finish();
466 }
467 template <class S = typed_detail::default_sort_t<P>> static contribution_type
469 requires typed_detail::replacement<S> { return change<S>(key, value); }
470 template <class S = typed_detail::default_sort_t<P>> static contribution_type
471 erase(typed_detail::key_t<S> const & key) requires typed_detail::replacement<S> {
472 auto result = batch(); result.template erase<S>(key); return std::move(result).finish();
473 }
474 static std::uint64_t reservation_work(std::uint64_t records) {
476 }
478 session_reservation result{reservation_work(input.records().size()), 0};
479 for (auto const & record : input.records())
480 result.bytes = profile_detail::add(result.bytes, profile_detail::add(record.key.bytes.size(), record.value.bytes.size()));
481 if (input.observed_) result.bytes = profile_detail::add(result.bytes,
482 profile_detail::multiply(input.observed_->size(), sizeof(bit_view)));
483 return result;
484 }
485 std::optional<world_type> advance(std::uint64_t budget) {
487 try {
488 auto updated = runtime_.advance(budget);
489 if (updated.same_layout(current_.runtime())) return std::nullopt;
490 require_depth(updated);
491 current_ = world_type(std::move(updated), current_.metadata());
492 return current_;
493 } catch (...) { poison(); throw; }
494 }
497 auto metadata = prepare(input);
498 if (input.records().empty()) return current_;
499 try {
500 if (initialize(input, metadata)) return current_;
501 complete(input.records(), std::move(metadata));
502 return current_;
503 } catch (...) { poison(); throw; }
504 }
505 private:
506 template <class, class, std::uint64_t, class> friend struct replacement_rebuild_engine;
507 // The rebuild wrapper shares this complete preflight before installing a
508 // pristine batch; neither caller can publish metadata ahead of execution.
509 template <class S> static void delta(metadata_type & metadata, typed_detail::key_t<S> const & key,
510 typed_detail::state_t<S> const & old, typed_detail::state_t<S> const & next) {
511 using semantics = sort_semantics<S>;
512 bool was = semantics::present(key, old), now = semantics::present(key, next);
513 auto before_hash = was ? A::lift(semantics::hash_value(key, old)) : A::zero();
514 auto after_hash = now ? A::lift(semantics::hash_value(key, next)) : A::zero();
515 metadata.signature = A::add(metadata.signature,
516 A::multiply(A::lift(semantics::hash_key(key)), A::subtract(after_hash, before_hash)));
517 if (now && !was) metadata.live_count = profile_detail::add(metadata.live_count, 1);
518 if (was && !now) {
519 if (!metadata.live_count) throw std::invalid_argument("inconsistent typed live count");
520 --metadata.live_count;
521 }
522 }
523 template <class F> void visit_changes(contribution_type & input, F && visit) const {
524 if (input.base() && input.base()->metadata().schema_id != current_.metadata().schema_id)
525 throw std::invalid_argument("typed contribution uses another schema");
526 std::optional<typed_detail::native_sweep<world_type>> sweep;
527 bool same_layout = input.observed_ && input.base_ && input.base_->runtime().same_layout(current_.runtime());
528 if (input.observed_ && !same_layout && !input.records_.empty())
529 sweep.emplace(current_, input.records_.front().key.view());
530 std::size_t ordinal = 0;
531 // Validate the complete contribution before changing the executor. A
532 // range contribution reuses one frontier, including across absent rows.
533 for (auto & record : input.records_) {
534 key_transport::dispatch(record.key.view(), [&]<class S>(std::type_identity<S> tag, auto const & key) {
535 using semantics = sort_semantics<S>;
536 auto arrow = typed_detail::value<P, S>(record.value.view());
537 std::optional<typed_detail::state_t<S>> replacement_next;
538 if constexpr (typed_detail::replacement<S>)
539 replacement_next.emplace(semantics::apply(key, semantics::initial(key), std::move(arrow)));
540 std::optional<std::uint64_t> retained;
541 auto old = [&] {
542 if (same_layout) {
543 retained = record.retained_limit_bits;
544 return semantics::apply(key, semantics::initial(key),
545 typed_detail::value<P, S>((*input.observed_)[ordinal]));
546 }
547 if (sweep) {
548 if constexpr (typed_detail::replacement<S>) {
549 auto hit = sweep->replacement(record.key.view());
550 if (!hit) return semantics::initial(key);
551 retained = hit->retained_bits;
552 return semantics::apply(key, semantics::initial(key), typed_detail::value<P, S>(hit->value));
553 } else throw std::logic_error("range observation on a nonreplacement sort");
554 }
555 auto target = replacement_next && !semantics::present(key, *replacement_next) ? &retained : nullptr;
556 return current_.template get_encoded<S>(key, record.key, target);
557 }();
558 if (input.observed_ && !same_layout) {
559 auto expected = semantics::apply(key, semantics::initial(key),
560 typed_detail::value<P, S>((*input.observed_)[ordinal]));
561 if (old != expected) throw std::invalid_argument("stale typed range value");
562 } else if (!input.observed_ && input.base() && old != input.base()->template get_encoded<S>(key, record.key))
563 throw std::invalid_argument("stale typed key value");
564 auto next = [&] {
565 if constexpr (typed_detail::replacement<S>) return std::move(*replacement_next);
566 else return semantics::apply(key, old, std::move(arrow));
567 }();
568 if constexpr (typed_detail::replacement<S>) {
569 if (!semantics::present(key, old) && !semantics::present(key, next))
570 throw std::invalid_argument("deleting absent typed key");
571 // A concurrent equivalent layout may encode a different prefix.
572 if (!semantics::present(key, next) && retained) record.retained_limit_bits = record.retained_limit_bits
573 ? std::min(*record.retained_limit_bits, *retained) : *retained;
574 }
575 std::invoke(visit, tag, key, std::move(old), std::move(next), record);
576 });
577 ++ordinal;
578 }
579 }
581 auto metadata = current_.metadata();
582 visit_changes(input, [&]<class S>(std::type_identity<S>, auto const & key, auto const & old,
583 auto const & next, auto const &) { delta<S>(metadata, key, old, next); });
584 return metadata;
585 }
586 // The rebuilding owner has already checked this mutation against its
587 // immutable preflight frontier. Avoid repeating a point lookup for it.
588 template <class S> void contribute_validated(profile_record const & record,
589 typed_detail::key_t<S> const & key, typed_detail::state_t<S> const & old,
590 typed_detail::state_t<S> const & next) {
591 require_active();
592 auto metadata = current_.metadata();
593 delta<S>(metadata, key, old, next);
594 try { complete(std::span(&record, 1), std::move(metadata)); }
595 catch (...) { poison(); throw; }
596 }
597 // The caller has validated the complete batch. An initialized prefix and
598 // this ordinary paid tail remain private until the final metadata is ready.
599 void complete(std::span<profile_record const> records, metadata_type metadata) {
600 for (auto const & record : records) {
601 while (!runtime_.admission_ready()) runtime_.advance(std::max<std::uint64_t>(runtime_.next_service_cost(), 1));
602 auto ready = runtime_.snapshot();
603 if (ready.query_root().head()->depth() > DepthLimit || runtime_.admission_cost() > ready_admission_allowance)
604 throw std::length_error("typed runtime exceeds ready-admission depth allowance");
605 auto service = [&] {
606 if constexpr (charged_service) return runtime_type::service_budget(profile_detail::add(ready.admissions(), 1));
607 else return std::uint64_t{0};
608 }();
609 if (!runtime_.try_contribute(record, service)) throw std::logic_error("typed ready admission unexpectedly blocked");
610 }
611 auto state = [&]() -> typename world_type::runtime_snapshot {
612 if constexpr (requires { runtime_.checkpoint(); }) {
613 if (!records.empty()) return runtime_.checkpoint();
614 }
615 return runtime_.snapshot();
616 }();
617 require_depth(state);
618 current_ = world_type(std::move(state), std::move(metadata));
619 }
620 bool initialize(contribution_type const & input, metadata_type const & metadata) {
621 try {
622 if constexpr (requires { runtime_.try_initialize_sorted(input.records(), std::uint64_t{}, DepthLimit); }) {
623 auto count = input.records().size();
624 if (count >= 2 && !current_.runtime().admissions() && !runtime_.pending()) {
625 // Prefix and tail share the original linear reservation: M*A
626 // funds initialization, and each remaining record retains its A.
627 auto prefix = std::bit_floor(count);
628 if (runtime_.try_initialize_sorted(input.records().first(prefix), reservation_work(prefix), DepthLimit)) {
629 require_depth(runtime_.snapshot());
630 complete(input.records().subspan(prefix), metadata);
631 return true;
632 }
633 }
634 }
635 return false;
636 } catch (...) { poison(); throw; }
637 }
640 bool failed_ = false;
641 static void require_depth(typename world_type::runtime_snapshot const & state) {
642 if (state.query_root().head()->depth() > DepthLimit)
643 throw std::length_error("typed runtime exceeds publication depth allowance");
644 }
645 static auto checked_snapshot(typename world_type::runtime_snapshot state) {
646 require_depth(state);
647 return state;
648 }
649 explicit typed_engine(world_type state)
650 : runtime_([&] {
651 require_depth(state.runtime());
652 return runtime_type::from_snapshot(state.runtime());
653 }()), current_(std::move(state)) {}
654 template <class Storage> typed_engine(world_type state, Storage storage)
655 : runtime_([&] {
656 require_depth(state.runtime());
657 if constexpr (requires { storage.check_schema(state.metadata().schema_id); })
658 storage.check_schema(state.metadata().schema_id);
659 return runtime_type::from_snapshot(state.runtime(), std::move(storage));
660 }()), current_(std::move(state)) {}
661 static std::string default_schema() {
662 if constexpr (requires { Family::default_schema(); }) return Family::default_schema();
663 else if constexpr (std::same_as<typename P::registry_type, string_registry>)
664 return "everett.optional-string/code0/v1";
665 else if constexpr (std::same_as<typename P::registry_type, unsorted<std::optional<std::string>>>)
666 return "everett.optional-string/tagless/byte-profile-v2";
667 else return {};
668 }
669 static std::string checked_schema(std::string value) {
670 if (value.empty()) throw std::invalid_argument("typed engine requires a schema identity");
671 return value;
672 }
673 void require_active() const {
674 if (failed_) throw std::logic_error("failed typed engine");
675 }
676 };
677}
Executes charged encoded COLA carries behind immutable queryable snapshots.
Declares Everett's fingerprint support.
auto first_value(cola_query_root< P, Blob > const &root, Query &&query, Decode &&decode, Inspect inspect={}) -> std::optional< std::invoke_result_t< Decode &, bit_view > >
Definition cola_query.h:120
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
bit_view string_bits(std::string const &value)
Definition sort_codec.h:31
bool byte_string_present(bit_view encoded)
Definition typed_world.h:76
constexpr bool replacement
Definition typed_world.h:55
typename sort_codec< S >::value_codec::value_type arrow_t
Definition typed_world.h:53
decltype(auto) dispatch_key(bit_view encoded, F &&action)
Definition typed_world.h:127
std::string byte_string(bit_view bits)
Definition typed_world.h:69
bit_string value(arrow_t< S > const &value)
Definition typed_world.h:99
typename transport< P, Family >::type transport_t
Definition typed_world.h:152
typename sort_codec< S >::key_codec::value_type key_t
Definition typed_world.h:52
bit_string key(key_t< S > const &value)
Definition typed_world.h:89
typename default_sort< typename registry_detail::info< typename P::registry_type >::leaves >::type default_sort_t
Definition typed_world.h:51
constexpr bool byte_strings
Definition typed_world.h:67
typename sort_semantics< S >::state_type state_t
Definition typed_world.h:54
key_t< S > read_key(sort_bit_reader &input)
Definition typed_world.h:84
Definition active_engine.h:18
Advances an age-ordered native frontier without repeated point queries.
Serializes charged contributions and equivalent layout work behind immutable snapshots.
Encodes typed bit records with inherited sort prefixes and sort-owned grammars.
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
bool at(std::uint64_t i) const
Definition profile.h:68
std::uint64_t size() const noexcept
Definition profile.h:64
std::uint64_t offset() const noexcept
Definition profile.h:66
bit_view subview(std::uint64_t first, std::uint64_t count) const
Definition profile.h:73
Definition profile.h:294
Definition registry.h:122
Definition replacement_rebuild.h:129
Definition session.h:33
std::uint64_t bytes
Definition session.h:35
Definition sort_codec.h:56
std::uint64_t remaining() const noexcept
Definition sort_codec.h:59
bool empty() const noexcept
Definition sort_codec.h:60
std::uint64_t read_bits(unsigned width)
Definition sort_codec.h:61
bit_view take_bits(std::uint64_t count)
Definition sort_codec.h:81
Definition sort_codec.h:38
void append(bit_view bits)
Definition sort_codec.h:48
void write_bits(std::uint64_t value, unsigned width)
Definition sort_codec.h:41
Definition sort_codec.h:401
static state_type initial(std::string const &)
Definition typed_world.h:36
static state_type compose(std::string const &, state_type const &, state_type newer)
Definition typed_world.h:38
static bool present(std::string const &, state_type const &value) noexcept
Definition typed_world.h:40
static state_type apply(std::string const &, state_type const &, state_type value)
Definition typed_world.h:37
static std::uint64_t hash_key(std::string const &key) noexcept
Definition typed_world.h:41
static std::uint64_t hash_value(std::string const &, state_type const &value) noexcept
Definition typed_world.h:42
static state_type erase(std::string const &)
Definition typed_world.h:39
std::optional< std::string > state_type
Definition typed_world.h:34
Definition typed_world.h:31
Definition typed_world.h:353
std::optional< typed_world< P, A, Family > > base_
Definition typed_world.h:383
typed_batch & change(typed_detail::key_t< S > const &key, typed_detail::arrow_t< S > const &arrow)
Definition typed_world.h:357
typed_batch & erase(typed_detail::key_t< S > const &key)
Definition typed_world.h:365
typed_batch & put(typed_detail::key_t< S > const &key, typed_detail::state_t< S > const &value)
Definition typed_world.h:362
typed_batch(typed_world< P, A, Family > base)
Definition typed_world.h:355
std::vector< profile_record > records_
Definition typed_world.h:384
typed_contribution< P, A, Family > finish() &&
Definition typed_world.h:373
Definition typed_world.h:335
std::span< profile_record const > records() const noexcept
Definition typed_world.h:337
typed_contribution(std::optional< typed_world< P, A, Family > > base, std::vector< profile_record > records)
Definition typed_world.h:349
std::optional< std::vector< bit_view > > observed_
Definition typed_world.h:348
std::optional< typed_world< P, A, Family > > const & base() const noexcept
Definition typed_world.h:336
std::vector< profile_record > records_
Definition typed_world.h:344
std::optional< typed_world< P, A, Family > > base_
Definition typed_world.h:343
Definition typed_world.h:60
Definition typed_world.h:175
bit_string operator()(bit_view key, bit_view older, bit_view newer) const
Definition typed_world.h:176
Definition typed_world.h:48
void type
Definition typed_world.h:48
static bit_string prefix()
Definition typed_world.h:137
static decltype(auto) dispatch(bit_view bits, F &&fn)
Definition typed_world.h:144
static key_t< S > decode(bit_view bits)
Definition typed_world.h:138
static bit_string encode(key_t< S > const &value)
Definition typed_world.h:136
Definition typed_world.h:171
bit_view operator()(bit_view, bit_view newer) const
Definition typed_world.h:173
Definition typed_world.h:153
bool is_tombstone(bit_view encoded) const
Definition typed_world.h:154
bool is_tombstone(bit_view key, bit_view encoded) const
Definition typed_world.h:160
Definition typed_world.h:148
Definition typed_world.h:406
typed_engine(world_type state)
Definition typed_world.h:649
metadata_type prepare(contribution_type &input) const
Definition typed_world.h:580
bool initialize(contribution_type const &input, metadata_type const &metadata)
Definition typed_world.h:620
runtime_type runtime_
Definition typed_world.h:638
static typed_batch< P, A, Family > batch()
Definition typed_world.h:462
typed_engine(world_type state, Storage storage)
Definition typed_world.h:654
static contribution_type put(typed_detail::key_t< S > const &key, typed_detail::state_t< S > const &value)
Definition typed_world.h:468
static contribution_type change(typed_detail::key_t< S > const &key, typed_detail::arrow_t< S > const &arrow)
Definition typed_world.h:464
static typed_engine from_snapshot(world_type state)
Definition typed_world.h:433
void require_active() const
Definition typed_world.h:673
bool admission_ready() const noexcept
Definition typed_world.h:460
static contribution_type erase(typed_detail::key_t< S > const &key)
Definition typed_world.h:471
static constexpr std::uint64_t ready_admission_allowance
Definition typed_world.h:418
typed_engine(std::string schema_id=default_schema())
Definition typed_world.h:431
static session_reservation reservation(contribution_type const &input)
Definition typed_world.h:477
auto storage() const
Definition typed_world.h:453
static std::string default_schema()
Definition typed_world.h:661
world_type contribute(contribution_type input)
Definition typed_world.h:495
void contribute_validated(profile_record const &record, typed_detail::key_t< S > const &key, typed_detail::state_t< S > const &old, typed_detail::state_t< S > const &next)
Definition typed_world.h:588
bool failed() const noexcept
Definition typed_world.h:455
bool pending() const noexcept
Definition typed_world.h:454
Family runtime_family
Definition typed_world.h:411
static constexpr bool charged_service
Definition typed_world.h:417
typename Family::template runtime_type< compose_type > runtime_type
Definition typed_world.h:416
world_type current_
Definition typed_world.h:639
std::conditional_t< typed_detail::all_replacements< typename registry_detail::info< typename P::registry_type >::leaves >::value, typed_detail::replacement_compose< P, key_transport >, typed_detail::compose< P, key_transport > > compose_type
Definition typed_world.h:415
void rebase(world_type state)
Definition typed_world.h:440
static std::uint64_t reservation_work(std::uint64_t records)
Definition typed_world.h:474
auto work() const
Definition typed_world.h:461
void poison() noexcept
Definition typed_world.h:456
static constexpr std::uint64_t admission_allowance
Definition typed_world.h:424
void complete(std::span< profile_record const > records, metadata_type metadata)
Definition typed_world.h:599
static typed_engine from_snapshot(world_type state, Storage storage)
Definition typed_world.h:434
P policy_type
Definition typed_world.h:407
static std::string checked_schema(std::string value)
Definition typed_world.h:669
static void delta(metadata_type &metadata, typed_detail::key_t< S > const &key, typed_detail::state_t< S > const &old, typed_detail::state_t< S > const &next)
Definition typed_world.h:509
void visit_changes(contribution_type &input, F &&visit) const
Definition typed_world.h:523
world_type snapshot() const
Definition typed_world.h:451
std::optional< world_type > advance(std::uint64_t budget)
Definition typed_world.h:485
bool failed_
Definition typed_world.h:640
static void require_depth(typename world_type::runtime_snapshot const &state)
Definition typed_world.h:641
typed_world< P, A, Family > world_type
Definition typed_world.h:408
typed_contribution< P, A, Family > contribution_type
Definition typed_world.h:409
static auto checked_snapshot(typename world_type::runtime_snapshot state)
Definition typed_world.h:645
typed_detail::transport_t< P, Family > key_transport
Definition typed_world.h:412
Definition typed_scan.h:33
Definition typed_world.h:324
runtime_snapshot runtime
Definition typed_world.h:325
metadata_type metadata
Definition typed_world.h:326
Definition typed_world.h:185
std::string schema_id
Definition typed_world.h:188
std::uint64_t live_count
Definition typed_world.h:187
static typed_world_metadata decode(std::span< std::byte const > data)
Definition typed_world.h:202
std::vector< std::byte > encode() const
Definition typed_world.h:192
A::element signature
Definition typed_world.h:186
bool operator==(typed_world_metadata const &) const =default
Definition typed_world.h:222
Family runtime_family
Definition typed_world.h:225
typed_detail::state_t< S > get_encoded(typed_detail::key_t< S > const &key, Query &&encoded, std::optional< std::uint64_t > *retained_limit_bits=nullptr) const
Definition typed_world.h:266
contribution_type change(typed_detail::key_t< S > const &key, typed_detail::arrow_t< S > const &arrow) const
auto signature() const
Definition typed_world.h:233
metadata_type const & metadata() const &noexcept
Definition typed_world.h:232
batch_type batch() const
Definition typed_world.h:387
contribution_type erase(typed_detail::key_t< S > const &key) const
std::shared_ptr< state const > state_
Definition typed_world.h:330
static typed_world restore(runtime_snapshot data, metadata_type metadata, std::string_view expected_schema)
Definition typed_world.h:238
typed_world(runtime_snapshot data, metadata_type metadata)
Definition typed_world.h:331
P policy_type
Definition typed_world.h:223
typed_detail::state_t< S > get(typed_detail::key_t< S > const &key) const
Definition typed_world.h:244
std::uint64_t live_count() const noexcept
Definition typed_world.h:234
runtime_snapshot const & runtime() const &&=delete
typed_detail::transport_t< P, Family > key_transport
Definition typed_world.h:226
contribution_type put(typed_detail::key_t< S > const &key, typed_detail::state_t< S > const &value) const
typename Family::snapshot_type runtime_snapshot
Definition typed_world.h:227
runtime_snapshot const & runtime() const &noexcept
Definition typed_world.h:230
Definition fingerprint.h:33
std::uint64_t key(std::string_view text) const noexcept
Definition fingerprint.h:42
static constexpr std::uint64_t mix(std::uint64_t x) noexcept
Definition fingerprint.h:34
Definition registry.h:70