Files
Efim Beshmenev aea6e330e3 Realisation v1
2026-08-11 22:19:34 +03:00

207 lines
7.4 KiB
C++

#pragma once
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <limits>
#include <stdexcept>
#include <utility>
#include <vector>
namespace uc::detail {
// A compact directory over leaf sizes. Level zero describes leaves, every
// next level groups `fanout` children, and the requested maximum number of
// levels is respected. The top level uses binary search; descent only scans a
// single fanout-sized group. A Fenwick side index supplies O(log L) prefix
// sums needed by stable-ID -> logical-index resolution.
class HierarchicalSizeIndex {
public:
struct Location {
std::size_t leaf = 0;
std::size_t local = 0;
};
HierarchicalSizeIndex(std::size_t fanout = 16, std::size_t max_levels = 3)
: fanout_(std::max<std::size_t>(2, fanout)),
max_levels_(std::clamp<std::size_t>(max_levels, 1, 8)) {}
void configure(std::size_t fanout, std::size_t max_levels) {
const auto leaf_sizes = levels_.empty()
? std::vector<std::size_t>{} : levels_.front();
HierarchicalSizeIndex replacement(fanout, max_levels);
replacement.rebuild(leaf_sizes);
swap(replacement);
}
void rebuild(const std::vector<std::size_t>& leaf_sizes) {
// Build all allocation-owning state off to the side. A failed
// allocation must not leave a directory that no longer describes its
// storage.
std::vector<std::vector<std::size_t>> new_levels;
std::vector<std::size_t> new_top_prefix;
std::vector<std::size_t> new_fenwick(leaf_sizes.size() + 1, 0);
std::size_t new_total = 0;
if (leaf_sizes.empty()) {
levels_.swap(new_levels);
top_prefix_.swap(new_top_prefix);
fenwick_.swap(new_fenwick);
total_ = 0;
return;
}
new_levels.push_back(leaf_sizes);
// Keep the top directory at most fanout-sized. Binary search handles
// that top level, so building extra single-parent levels only adds a
// lookup and cannot improve asymptotic complexity.
while (new_levels.back().size() > fanout_
&& new_levels.size() < max_levels_) {
const auto& children = new_levels.back();
std::vector<std::size_t> parents;
parents.reserve((children.size() + fanout_ - 1) / fanout_);
for (std::size_t begin = 0; begin < children.size(); begin += fanout_) {
const auto end = std::min(children.size(), begin + fanout_);
std::size_t weight = 0;
for (auto i = begin; i < end; ++i) {
weight += children[i];
}
parents.push_back(weight);
}
new_levels.push_back(std::move(parents));
}
for (const auto weight : new_levels.back()) {
new_total += weight;
new_top_prefix.push_back(new_total);
}
for (std::size_t i = 0; i < leaf_sizes.size(); ++i) {
for (auto node = i + 1; node < new_fenwick.size();
node += node & (~node + 1)) {
new_fenwick[node] += leaf_sizes[i];
}
}
levels_.swap(new_levels);
top_prefix_.swap(new_top_prefix);
fenwick_.swap(new_fenwick);
total_ = new_total;
}
void swap(HierarchicalSizeIndex& other) noexcept {
using std::swap;
swap(fanout_, other.fanout_);
swap(max_levels_, other.max_levels_);
swap(total_, other.total_);
levels_.swap(other.levels_);
top_prefix_.swap(other.top_prefix_);
fenwick_.swap(other.fenwick_);
}
void update(std::size_t leaf, std::ptrdiff_t delta) {
if (levels_.empty() || leaf >= levels_.front().size()) {
throw std::out_of_range("directory leaf index out of range");
}
apply_delta(levels_[0][leaf], delta);
std::size_t node = leaf;
for (std::size_t level = 1; level < levels_.size(); ++level) {
node /= fanout_;
apply_delta(levels_[level][node], delta);
}
total_ = apply_delta_copy(total_, delta);
fenwick_add_signed(leaf, delta);
rebuild_top_prefix();
}
[[nodiscard]] Location locate(std::size_t logical_index) const {
if (logical_index >= total_ || levels_.empty()) {
throw std::out_of_range("logical index out of range");
}
const auto top_it = std::upper_bound(top_prefix_.begin(), top_prefix_.end(), logical_index);
std::size_t node = static_cast<std::size_t>(top_it - top_prefix_.begin());
std::size_t remaining = logical_index - (node == 0 ? 0 : top_prefix_[node - 1]);
for (std::size_t level = levels_.size() - 1; level > 0; --level) {
const auto& children = levels_[level - 1];
const auto begin = node * fanout_;
const auto end = std::min(children.size(), begin + fanout_);
auto child = begin;
for (; child < end; ++child) {
if (remaining < children[child]) {
break;
}
remaining -= children[child];
}
assert(child < end);
node = child;
}
return {node, remaining};
}
[[nodiscard]] std::size_t prefix_before(std::size_t leaf) const noexcept {
std::size_t sum = 0;
for (std::size_t i = leaf; i > 0; i -= i & (~i + 1)) {
sum += fenwick_[i];
}
return sum;
}
[[nodiscard]] std::size_t total() const noexcept { return total_; }
[[nodiscard]] std::size_t actual_levels() const noexcept { return levels_.size(); }
[[nodiscard]] std::size_t fanout() const noexcept { return fanout_; }
[[nodiscard]] std::size_t allocated_bytes() const noexcept {
std::size_t bytes = fenwick_.capacity() * sizeof(std::size_t)
+ top_prefix_.capacity() * sizeof(std::size_t);
for (const auto& level : levels_) {
bytes += level.capacity() * sizeof(std::size_t);
}
return bytes;
}
private:
static void apply_delta(std::size_t& value, std::ptrdiff_t delta) {
value = apply_delta_copy(value, delta);
}
static std::size_t apply_delta_copy(std::size_t value, std::ptrdiff_t delta) {
if (delta < 0) {
const auto magnitude = static_cast<std::size_t>(-delta);
assert(value >= magnitude);
return value - magnitude;
}
return value + static_cast<std::size_t>(delta);
}
void rebuild_top_prefix() {
top_prefix_.clear();
top_prefix_.reserve(levels_.back().size());
std::size_t prefix = 0;
for (const auto weight : levels_.back()) {
prefix += weight;
top_prefix_.push_back(prefix);
}
}
void fenwick_add(std::size_t leaf, std::size_t delta) noexcept {
for (auto i = leaf + 1; i < fenwick_.size(); i += i & (~i + 1)) {
fenwick_[i] += delta;
}
}
void fenwick_add_signed(std::size_t leaf, std::ptrdiff_t delta) noexcept {
for (auto i = leaf + 1; i < fenwick_.size(); i += i & (~i + 1)) {
fenwick_[i] = apply_delta_copy(fenwick_[i], delta);
}
}
std::size_t fanout_;
std::size_t max_levels_;
std::size_t total_ = 0;
std::vector<std::vector<std::size_t>> levels_;
std::vector<std::size_t> top_prefix_;
std::vector<std::size_t> fenwick_;
};
} // namespace uc::detail