#pragma once #include "universal_container/tiered_storage.hpp" #include #include #include #include #include #include #include #include #include namespace uc { enum class StorageMode : std::uint8_t { vector, tiered }; enum class ResidencyMode : std::uint8_t { automatic, forced_vector, forced_tiered }; // Deferred is the safe default: reads collect statistics, but a representation // change is performed only at a mutation boundary or by adapt_now(). Eager is // retained as an explicitly unsafe experiment for non-const reads. enum class ReadAdaptationMode : std::uint8_t { deferred, eager_nonconst }; enum class OperationKind : std::uint8_t { random_read, sequential_read, append, insert, erase, set }; struct OperationSample { OperationKind kind = OperationKind::random_read; std::size_t size = 0; std::size_t position = 0; std::size_t count = 1; std::size_t element_bytes = 4; }; struct AdaptationDecision { StorageMode target = StorageMode::vector; TieredConfig tiered_config{}; double expected_saving = 0.0; }; struct AdaptationConfig { // Selected by the paired aggressive/moderate/balanced benchmark: this // reacts early in a sustained edit phase but leaves a 128-edit negative // control in vector mode. std::size_t evaluation_interval = 256; std::size_t minimum_observations = 2048; std::size_t edit_evaluation_interval = 32; std::size_t minimum_edit_observations = 128; std::size_t minimum_tiered_size = 8 * 1024; // The forecast is deliberately bounded by both evidence accumulated in // the current residency and this hard ceiling. Treating the ceiling as a // guaranteed future phase length makes short edit bursts look much more // profitable than they really are. std::size_t forecast_operations = 128 * 1024; std::size_t minimum_forecast_operations = 256; double forecast_growth_factor = 8.0; // A sampled read carries `count = read_sample_rate`, so the statistical // weight is preserved while the hot operator[] path pays the full policy // update only once per cache-line-sized batch of accesses. std::size_t read_sample_rate = 256; // Structural samples retain their aggregate weight. Sampling every edit // made the replaceable policy itself a visible part of edit latency. std::size_t edit_sample_rate = 4; std::size_t minimum_residency_operations = 256; // Storage-mode hysteresis uses the edit share of the workload EWMA. The // wider entry threshold and narrower exit threshold form a Schmitt trigger: // a tiered sequence is not converted back to vector while edits are still // a material part of the current phase. double tiered_entry_edit_fraction = 0.05; double tiered_exit_edit_fraction = 0.01; std::size_t required_confirmation_windows = 2; // Shape changes are independent from storage-mode changes. They need a // longer residency and an edit-heavy phase because changing leaf capacity // is itself a full O(N) rebuild. std::size_t minimum_shape_residency_operations = 4 * 1024; double minimum_shape_edit_fraction = 0.10; std::size_t locality_radius_divisor = 50; std::size_t maximum_directory_levels = 4; double vector_to_tiered_safety = 1.50; double tiered_to_vector_safety = 2.00; double tiered_rebuild_safety = 2.50; // A leaf rebuild needs a material advantage over the incumbent, not merely // to win a noisy window. Directory-only changes still have their much // smaller rebuild cost, but use the same relative stability guard. double minimum_shape_improvement = 0.15; double ewma_alpha = 0.35; // The search space is data, not a hard-coded branch in the container. A // different policy/config can replace it without touching storage code. std::array tiered_leaf_candidates{64, 128, 256, 512, 1024}; // Calibratable cost units. The leaf movement term grows with B, while the // directory/fragmentation term grows with N/B. Their intersection is why // the selected block size changes with both N and edit locality. double vector_read = 1.0; double tiered_read_base = 12.0; double tiered_read_per_level = 2.0; double tiered_lookup_per_log_top = 1.20; double vector_scan_element = 0.70; double tiered_scan_element = 0.92; double tiered_scan_boundary = 12.0; double vector_append = 2.0; double tiered_append = 2.8; double vector_edit_fixed = 6.0; double tiered_edit_fixed = 30.0; double vector_move_unit = 0.025; double tiered_move_unit = 4.0; double tiered_directory_unit = 0.65; double localized_directory_multiplier = 0.15; double conversion_unit_per_element = 1.0; double directory_rebuild_unit_per_leaf = 8.0; }; struct AdaptationTelemetry { std::uint64_t observed_operations = 0; std::uint64_t sampled_reads = 0; std::uint64_t evaluations = 0; std::uint64_t vector_to_tiered = 0; std::uint64_t tiered_to_vector = 0; std::uint64_t tiered_rebuilds = 0; std::uint64_t tiered_leaf_rebuilds = 0; std::uint64_t tiered_directory_rebuilds = 0; std::size_t last_recommended_leaf = 0; std::size_t last_recommended_levels = 0; double last_vector_cost_per_operation = 0.0; double last_tiered_cost_per_operation = 0.0; double last_current_cost_per_operation = 0.0; double last_expected_saving = 0.0; double last_conversion_cost = 0.0; double last_local_edit_fraction = 0.0; double last_edit_fraction = 0.0; std::size_t last_forecast_operations = 0; std::size_t last_evidence_windows = 0; }; class CostModelPolicy { public: static constexpr std::size_t candidate_count = 5; explicit CostModelPolicy(AdaptationConfig config = {}, TieredConfig tiered = {}) : config_(normalize(config)), active_tiered_(normalize_tiered(tiered)), search_fanout_(active_tiered_.directory_fanout), search_max_levels_(config_.maximum_directory_levels) {} void set_tiered_config(TieredConfig tiered) noexcept { active_tiered_ = normalize_tiered(tiered); search_fanout_ = active_tiered_.directory_fanout; search_max_levels_ = config_.maximum_directory_levels; } void observe(const OperationSample& sample) noexcept { const auto count = static_cast(std::max(1, sample.count)); last_element_bytes_ = std::max(1, sample.element_bytes); last_observed_size_ = sample.size; operations_since_transition_ += sample.count; operations_since_shape_transition_ += sample.count; telemetry_.observed_operations += sample.count; if (sample.kind == OperationKind::random_read) { ++telemetry_.sampled_reads; } const bool edit = is_edit(sample.kind); // In vector mode, reads/appends cannot make tiered storage preferable. // Before the first relevant edit (or below the measured small-size // threshold), avoid evaluating all five counterfactual shapes in the // hot path. Once edits exist, sampled reads are evaluated normally so // that a short burst can be rejected rather than converted later. if (active_mode_ == StorageMode::vector && (sample.size < config_.minimum_tiered_size || (!edit && total_edit_weight_ == 0.0 && window_edit_weight_ == 0.0 && !decision_ready_ && !pending_decision_))) { return; } const bool localized = classify_edit_locality(sample); window_vector_cost_ += estimate_vector(sample); window_active_tiered_cost_ += estimate_tiered(sample, active_tiered_, localized); for (std::size_t i = 0; i < candidate_count; ++i) { window_candidate_costs_[i] += estimate_tiered( sample, candidate_config(i, sample.size), localized); } window_weight_ += count; if (edit) { window_edit_weight_ += count; total_edit_weight_ += count; if (localized) { window_local_edit_weight_ += count; } } if (window_weight_ >= static_cast(config_.evaluation_interval) || window_edit_weight_ >= static_cast( config_.edit_evaluation_interval)) { close_window(); } } [[nodiscard]] bool decision_ready() const noexcept { return decision_ready_; } [[nodiscard]] std::optional recommended_decision(StorageMode current, std::size_t size, TieredConfig current_config) noexcept { active_mode_ = current; if (!decision_ready_) { return std::nullopt; } const auto evidence_windows = closed_windows_since_decision_; decision_ready_ = false; closed_windows_since_decision_ = 0; ++telemetry_.evaluations; telemetry_.last_evidence_windows = evidence_windows; const auto evaluation = evaluate_decision(current, size, current_config); telemetry_.last_vector_cost_per_operation = evaluation.vector_cost; telemetry_.last_tiered_cost_per_operation = evaluation.best_tiered_cost; telemetry_.last_current_cost_per_operation = evaluation.current_cost; telemetry_.last_recommended_leaf = evaluation.best_config.leaf_capacity; telemetry_.last_recommended_levels = evaluation.best_config.directory_levels; telemetry_.last_conversion_cost = evaluation.selected_rebuild_cost; telemetry_.last_expected_saving = evaluation.decision ? evaluation.decision->expected_saving : 0.0; telemetry_.last_forecast_operations = evaluation.forecast_operations; telemetry_.last_edit_fraction = ewma_edit_fraction_; return confirm(evaluation.decision); } // Compatibility with simpler storage wrappers: this intentionally loses a // same-mode tiered shape recommendation. [[nodiscard]] std::optional recommended_mode(StorageMode current, std::size_t size) noexcept { const auto decision = recommended_decision(current, size, active_tiered_); return decision ? std::optional(decision->target) : std::nullopt; } void on_transition(StorageMode from, StorageMode to, TieredConfig active_config) noexcept { if (from == StorageMode::vector && to == StorageMode::tiered) { ++telemetry_.vector_to_tiered; } else if (from == StorageMode::tiered && to == StorageMode::vector) { ++telemetry_.tiered_to_vector; } else if (from == StorageMode::tiered && to == StorageMode::tiered) { ++telemetry_.tiered_rebuilds; if (normalize_tiered(active_config).leaf_capacity == active_tiered_.leaf_capacity) { ++telemetry_.tiered_directory_rebuilds; } else { ++telemetry_.tiered_leaf_rebuilds; } } const auto normalized_active = normalize_tiered(active_config); if (to == StorageMode::tiered) { synchronize_active_tiered_ewma(normalized_active); } active_tiered_ = normalized_active; active_mode_ = to; clear_transition_evidence(); } void on_transition(StorageMode from, StorageMode to) noexcept { on_transition(from, to, active_tiered_); } void reset() noexcept { const auto active = active_tiered_; const auto mode = active_mode_; const auto fanout = search_fanout_; const auto levels = search_max_levels_; *this = CostModelPolicy(config_, active); active_mode_ = mode; search_fanout_ = fanout; search_max_levels_ = levels; } [[nodiscard]] const AdaptationConfig& config() const noexcept { return config_; } [[nodiscard]] const AdaptationTelemetry& telemetry() const noexcept { return telemetry_; } private: static bool is_edit(OperationKind kind) noexcept { return kind == OperationKind::insert || kind == OperationKind::erase; } static TieredConfig normalize_tiered(TieredConfig config) noexcept { config.leaf_capacity = std::clamp(config.leaf_capacity, 4, 1U << 20U); config.directory_fanout = std::clamp( config.directory_fanout, 2, 1U << 16U); config.directory_levels = std::clamp(config.directory_levels, 1, 8); return config; } static AdaptationConfig normalize(AdaptationConfig config) noexcept { config.evaluation_interval = std::max(16, config.evaluation_interval); config.minimum_observations = std::max(config.evaluation_interval, config.minimum_observations); config.edit_evaluation_interval = std::max(8, config.edit_evaluation_interval); config.minimum_edit_observations = std::max( config.edit_evaluation_interval, config.minimum_edit_observations); config.forecast_operations = std::max(1, config.forecast_operations); config.minimum_forecast_operations = std::clamp( config.minimum_forecast_operations, 1, config.forecast_operations); config.forecast_growth_factor = std::max(1.0, config.forecast_growth_factor); config.read_sample_rate = std::max(1, config.read_sample_rate); config.edit_sample_rate = std::max(1, config.edit_sample_rate); config.tiered_entry_edit_fraction = std::clamp( config.tiered_entry_edit_fraction, 0.0, 1.0); config.tiered_exit_edit_fraction = std::clamp( config.tiered_exit_edit_fraction, 0.0, config.tiered_entry_edit_fraction); config.required_confirmation_windows = std::max( 1, config.required_confirmation_windows); config.minimum_shape_edit_fraction = std::clamp( config.minimum_shape_edit_fraction, 0.0, 1.0); config.locality_radius_divisor = std::max(1, config.locality_radius_divisor); config.maximum_directory_levels = std::clamp( config.maximum_directory_levels, 1, 8); config.minimum_shape_improvement = std::clamp( config.minimum_shape_improvement, 0.0, 1.0); config.ewma_alpha = std::clamp(config.ewma_alpha, 0.01, 1.0); for (auto& leaf : config.tiered_leaf_candidates) { leaf = std::clamp(leaf, 4, 1U << 20U); } std::sort(config.tiered_leaf_candidates.begin(), config.tiered_leaf_candidates.end()); return config; } [[nodiscard]] std::size_t actual_levels(std::size_t size, std::size_t leaf, std::size_t fanout, std::size_t maximum) const noexcept { auto nodes = (size + leaf - 1) / leaf; std::size_t levels = 1; while (nodes > fanout && levels < maximum) { nodes = (nodes + fanout - 1) / fanout; ++levels; } return levels; } [[nodiscard]] TieredConfig candidate_config(std::size_t index, std::size_t size) const noexcept { const auto leaf = config_.tiered_leaf_candidates[index]; return {leaf, search_fanout_, actual_levels(size, leaf, search_fanout_, search_max_levels_)}; } bool classify_edit_locality(const OperationSample& sample) noexcept { if (!is_edit(sample.kind)) { return false; } bool localized = false; if (has_last_edit_) { const auto distance = sample.position > last_edit_position_ ? sample.position - last_edit_position_ : last_edit_position_ - sample.position; const auto radius = std::max( 32, sample.size / config_.locality_radius_divisor); localized = distance <= radius; } last_edit_position_ = sample.position; has_last_edit_ = true; return localized; } [[nodiscard]] double estimate_vector(const OperationSample& sample) const noexcept { const auto count = static_cast(std::max(1, sample.count)); switch (sample.kind) { case OperationKind::random_read: return config_.vector_read * count; case OperationKind::sequential_read: return config_.vector_scan_element * count; case OperationKind::append: return config_.vector_append * count; case OperationKind::set: return config_.vector_read * 1.3 * count; case OperationKind::insert: case OperationKind::erase: { const auto width_scale = std::max(1.0, static_cast(sample.element_bytes) / sizeof(std::uint32_t)); const auto tail = sample.position < sample.size ? sample.size - sample.position : 0; return (config_.vector_edit_fixed + config_.vector_move_unit * width_scale * static_cast(tail)) * count; } } return count; } [[nodiscard]] double estimate_tiered(const OperationSample& sample, TieredConfig tiered, bool localized) const noexcept { tiered = normalize_tiered(tiered); const auto count = static_cast(std::max(1, sample.count)); const auto leaf_count = std::max( 1, (sample.size + tiered.leaf_capacity - 1) / tiered.leaf_capacity); const auto level_count = actual_levels( sample.size, tiered.leaf_capacity, tiered.directory_fanout, tiered.directory_levels); auto top_nodes = leaf_count; for (std::size_t level = 1; level < level_count; ++level) { top_nodes = (top_nodes + tiered.directory_fanout - 1) / tiered.directory_fanout; } const auto levels = static_cast(level_count); const auto top_log2 = top_nodes <= 1 ? 0.0 : static_cast( std::bit_width(top_nodes) - 1U); const auto lookup = config_.tiered_read_base + config_.tiered_read_per_level * levels + config_.tiered_lookup_per_log_top * top_log2; switch (sample.kind) { case OperationKind::random_read: return lookup * count; case OperationKind::sequential_read: return (config_.tiered_scan_element + config_.tiered_scan_boundary / static_cast(tiered.leaf_capacity)) * count; case OperationKind::append: return (config_.tiered_append + 8.0 / static_cast(tiered.leaf_capacity)) * count; case OperationKind::set: return lookup * 1.3 * count; case OperationKind::insert: case OperationKind::erase: { const auto width_scale = std::max(1.0, static_cast(sample.element_bytes) / sizeof(std::uint32_t)); const auto local = sample.position % tiered.leaf_capacity; const auto local_moves = std::min(local, tiered.leaf_capacity - local); const auto directory_multiplier = localized ? config_.localized_directory_multiplier : 1.0; const auto directory_work = config_.tiered_directory_unit * directory_multiplier * static_cast(leaf_count); return (config_.tiered_edit_fixed + config_.tiered_move_unit * width_scale * static_cast(local_moves) + directory_work + lookup) * count; } } return count; } struct DecisionEvaluation { std::optional decision; TieredConfig best_config{}; double vector_cost = 0.0; double best_tiered_cost = 0.0; double current_cost = 0.0; double selected_rebuild_cost = 0.0; std::size_t forecast_operations = 0; }; [[nodiscard]] DecisionEvaluation evaluate_decision( StorageMode current, std::size_t size, TieredConfig current_config) const noexcept { DecisionEvaluation evaluation; std::size_t best_index = 0; for (std::size_t i = 1; i < candidate_count; ++i) { if (ewma_candidate_costs_[i] < ewma_candidate_costs_[best_index]) { best_index = i; } } evaluation.best_config = candidate_config(best_index, size); evaluation.vector_cost = ewma_vector_cost_; evaluation.best_tiered_cost = ewma_candidate_costs_[best_index]; const auto current_tiered_cost = ewma_active_tiered_cost_; evaluation.current_cost = current == StorageMode::vector ? evaluation.vector_cost : current_tiered_cost; const auto width_scale = std::max(1.0, static_cast(last_element_bytes_) / sizeof(std::uint32_t)); const auto conversion_cost = static_cast(size) * config_.conversion_unit_per_element * width_scale; evaluation.selected_rebuild_cost = conversion_cost; const auto evidence_forecast = static_cast(std::ceil( total_weight_ * config_.forecast_growth_factor)); evaluation.forecast_operations = std::clamp( evidence_forecast, config_.minimum_forecast_operations, config_.forecast_operations); const bool enough_evidence = total_weight_ >= static_cast(config_.minimum_observations) || total_edit_weight_ >= static_cast(config_.minimum_edit_observations); if (!initialized_ || !enough_evidence || operations_since_transition_ < config_.minimum_residency_operations) { return evaluation; } const auto forecast = static_cast(evaluation.forecast_operations); double best_net_saving = 0.0; if (current == StorageMode::vector) { if (size >= config_.minimum_tiered_size && ewma_edit_fraction_ >= config_.tiered_entry_edit_fraction && evaluation.best_tiered_cost < evaluation.vector_cost) { const auto gross = (evaluation.vector_cost - evaluation.best_tiered_cost) * forecast; const auto threshold = conversion_cost * config_.vector_to_tiered_safety; if (gross > threshold) { evaluation.decision = AdaptationDecision{ StorageMode::tiered, evaluation.best_config, gross}; best_net_saving = gross - threshold; } } return evaluation; } // A tiered sequence returns to vector only after the workload has // crossed the low (read-dominant) side of the phase hysteresis band. if (ewma_edit_fraction_ <= config_.tiered_exit_edit_fraction && evaluation.vector_cost < current_tiered_cost) { const auto gross = (current_tiered_cost - evaluation.vector_cost) * forecast; const auto threshold = conversion_cost * config_.tiered_to_vector_safety; if (gross > threshold) { evaluation.decision = AdaptationDecision{ StorageMode::vector, normalize_tiered(current_config), gross}; best_net_saving = gross - threshold; } } const auto normalized_current = normalize_tiered(current_config); const bool shape_differs = evaluation.best_config != normalized_current; const bool directory_only = evaluation.best_config.leaf_capacity == normalized_current.leaf_capacity; const auto shape_rebuild_cost = directory_only ? static_cast((size + evaluation.best_config.leaf_capacity - 1) / evaluation.best_config.leaf_capacity) * config_.directory_rebuild_unit_per_leaf : conversion_cost; const auto relative_improvement = current_tiered_cost > 0.0 ? (current_tiered_cost - evaluation.best_tiered_cost) / current_tiered_cost : 0.0; if (operations_since_shape_transition_ >= config_.minimum_shape_residency_operations && ewma_edit_fraction_ >= config_.minimum_shape_edit_fraction && shape_differs && evaluation.best_tiered_cost < current_tiered_cost && relative_improvement >= config_.minimum_shape_improvement) { const auto gross = (current_tiered_cost - evaluation.best_tiered_cost) * forecast; const auto threshold = shape_rebuild_cost * config_.tiered_rebuild_safety; const auto net = gross - threshold; if (gross > threshold && net > best_net_saving) { evaluation.decision = AdaptationDecision{ StorageMode::tiered, evaluation.best_config, gross}; evaluation.selected_rebuild_cost = shape_rebuild_cost; } } return evaluation; } void close_window() noexcept { const auto inverse_weight = 1.0 / window_weight_; const auto vector_per_operation = window_vector_cost_ * inverse_weight; const auto active_per_operation = window_active_tiered_cost_ * inverse_weight; const auto edit_fraction = window_edit_weight_ * inverse_weight; std::array candidates{}; for (std::size_t i = 0; i < candidate_count; ++i) { candidates[i] = window_candidate_costs_[i] * inverse_weight; } if (!initialized_) { ewma_vector_cost_ = vector_per_operation; ewma_active_tiered_cost_ = active_per_operation; ewma_candidate_costs_ = candidates; ewma_edit_fraction_ = edit_fraction; initialized_ = true; } else { const auto alpha = config_.ewma_alpha; ewma_vector_cost_ = alpha * vector_per_operation + (1.0 - alpha) * ewma_vector_cost_; ewma_active_tiered_cost_ = alpha * active_per_operation + (1.0 - alpha) * ewma_active_tiered_cost_; for (std::size_t i = 0; i < candidate_count; ++i) { ewma_candidate_costs_[i] = alpha * candidates[i] + (1.0 - alpha) * ewma_candidate_costs_[i]; } ewma_edit_fraction_ = alpha * edit_fraction + (1.0 - alpha) * ewma_edit_fraction_; } if (window_edit_weight_ > 0.0) { telemetry_.last_local_edit_fraction = window_local_edit_weight_ / window_edit_weight_; } telemetry_.last_edit_fraction = ewma_edit_fraction_; total_weight_ += window_weight_; window_vector_cost_ = 0.0; window_active_tiered_cost_ = 0.0; window_candidate_costs_.fill(0.0); window_weight_ = 0.0; window_edit_weight_ = 0.0; window_local_edit_weight_ = 0.0; update_confirmation(evaluate_decision( active_mode_, last_observed_size_, active_tiered_).decision); decision_ready_ = true; if (closed_windows_since_decision_ != std::numeric_limits::max()) { ++closed_windows_since_decision_; } } static bool same_decision(const AdaptationDecision& left, const AdaptationDecision& right) noexcept { return left.target == right.target && left.tiered_config == right.tiered_config; } void update_confirmation( const std::optional& decision) noexcept { if (!decision) { pending_decision_.reset(); confirmation_count_ = 0; return; } if (pending_decision_ && same_decision(*pending_decision_, *decision)) { confirmation_count_ = std::min( config_.required_confirmation_windows, confirmation_count_ + 1); pending_decision_ = decision; } else { pending_decision_ = decision; confirmation_count_ = 1; } } [[nodiscard]] std::optional confirm(const std::optional& decision) noexcept { if (!decision || !pending_decision_ || !same_decision(*pending_decision_, *decision) || confirmation_count_ < config_.required_confirmation_windows) { return std::nullopt; } auto confirmed = decision; pending_decision_.reset(); confirmation_count_ = 0; return confirmed; } void synchronize_active_tiered_ewma(TieredConfig active) noexcept { if (!initialized_) { return; } for (std::size_t i = 0; i < candidate_count; ++i) { if (config_.tiered_leaf_candidates[i] == active.leaf_capacity) { ewma_active_tiered_cost_ = ewma_candidate_costs_[i]; return; } } } // A committed transition starts a new payback/residency epoch, but the // workload signal is deliberately retained. Throwing the EWMA away made // the policy relearn the same stationary phase from noisy short windows and // allowed vector<->tiered and leaf-shape ping-pong. void clear_transition_evidence() noexcept { operations_since_transition_ = 0; operations_since_shape_transition_ = 0; total_weight_ = 0.0; total_edit_weight_ = 0.0; window_weight_ = 0.0; window_vector_cost_ = 0.0; window_active_tiered_cost_ = 0.0; window_candidate_costs_.fill(0.0); window_edit_weight_ = 0.0; window_local_edit_weight_ = 0.0; pending_decision_.reset(); confirmation_count_ = 0; has_last_edit_ = false; decision_ready_ = false; closed_windows_since_decision_ = 0; } AdaptationConfig config_; TieredConfig active_tiered_; StorageMode active_mode_ = StorageMode::vector; std::size_t search_fanout_ = 64; std::size_t search_max_levels_ = 4; AdaptationTelemetry telemetry_; std::array window_candidate_costs_{}; std::array ewma_candidate_costs_{}; double window_vector_cost_ = 0.0; double window_active_tiered_cost_ = 0.0; double window_weight_ = 0.0; double window_edit_weight_ = 0.0; double window_local_edit_weight_ = 0.0; double total_weight_ = 0.0; double total_edit_weight_ = 0.0; double ewma_vector_cost_ = 0.0; double ewma_active_tiered_cost_ = 0.0; double ewma_edit_fraction_ = 0.0; std::size_t operations_since_transition_ = 0; std::size_t operations_since_shape_transition_ = 0; std::size_t last_edit_position_ = 0; std::size_t last_observed_size_ = 0; std::size_t last_element_bytes_ = sizeof(std::uint32_t); std::size_t confirmation_count_ = 0; std::optional pending_decision_; bool has_last_edit_ = false; bool initialized_ = false; bool decision_ready_ = false; std::size_t closed_windows_since_decision_ = 0; }; } // namespace uc