New adaptation policy
This commit is contained in:
@@ -231,6 +231,31 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template <class Predicate>
|
||||
[[nodiscard]] std::size_t find_if(Predicate&& predicate) const {
|
||||
std::size_t logical = 0;
|
||||
for (const auto& leaf : leaves_) {
|
||||
for (std::size_t i = 0; i < leaf->values.size(); ++i, ++logical) {
|
||||
if (predicate(leaf->values[i])) {
|
||||
return logical;
|
||||
}
|
||||
}
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
template <class Predicate, class Function>
|
||||
void for_each_match(Predicate&& predicate, Function&& function) const {
|
||||
std::size_t logical = 0;
|
||||
for (const auto& leaf : leaves_) {
|
||||
for (std::size_t i = 0; i < leaf->values.size(); ++i, ++logical) {
|
||||
if (predicate(leaf->values[i])) {
|
||||
function(logical, leaf->values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
void for_each_with_location(Function&& function) const {
|
||||
for (const auto& leaf : leaves_) {
|
||||
@@ -262,21 +287,22 @@ public:
|
||||
if (source.empty()) {
|
||||
return result;
|
||||
}
|
||||
const auto target_occupancy = bulk_target_occupancy(
|
||||
result.config_.leaf_capacity);
|
||||
const auto count = (source.size() + target_occupancy - 1)
|
||||
/ target_occupancy;
|
||||
result.leaves_.reserve(count);
|
||||
// Allocate the complete destination, including its directory, before
|
||||
// moving the first value. For nothrow-move T the following loop and
|
||||
// the final TieredStorage move cannot fail, so an allocation failure
|
||||
// leaves source completely untouched.
|
||||
const auto target_occupancy = result.prepare_bulk_destination(source.size());
|
||||
std::size_t leaf_index = 0;
|
||||
for (auto& value : source) {
|
||||
if (result.leaves_.empty()
|
||||
|| result.leaves_.back()->values.size() == target_occupancy) {
|
||||
result.leaves_.push_back(result.make_leaf());
|
||||
}
|
||||
result.leaves_.back()->values.push_back(std::move(value));
|
||||
auto& leaf = *result.leaves_[leaf_index];
|
||||
leaf.values.push_back(std::move(value));
|
||||
++result.size_;
|
||||
if (leaf.values.size() == target_occupancy
|
||||
&& leaf_index + 1 < result.leaves_.size()) {
|
||||
++leaf_index;
|
||||
}
|
||||
}
|
||||
result.rebuild_positions();
|
||||
result.rebuild_directory();
|
||||
assert(result.directory_.total() == result.size_);
|
||||
for (std::size_t leaf = 0; leaf < result.leaves_.size(); ++leaf) {
|
||||
result.refresh_leaf(leaf, relocate);
|
||||
}
|
||||
@@ -335,6 +361,31 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
// Rebuild a tiered layout without an intermediate vector. All leaves and
|
||||
// directory arrays are allocated first; consequently this operation has a
|
||||
// strong allocation-failure guarantee when T is nothrow-move-constructible.
|
||||
static TieredStorage reconfigured_move(TieredStorage& source,
|
||||
TieredConfig config) {
|
||||
TieredStorage result(config);
|
||||
if (source.empty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto target_occupancy = result.prepare_bulk_destination(source.size());
|
||||
std::size_t leaf_index = 0;
|
||||
source.for_each([&](T& value) {
|
||||
auto& leaf = *result.leaves_[leaf_index];
|
||||
leaf.values.push_back(std::move(value));
|
||||
++result.size_;
|
||||
if (leaf.values.size() == target_occupancy
|
||||
&& leaf_index + 1 < result.leaves_.size()) {
|
||||
++leaf_index;
|
||||
}
|
||||
});
|
||||
assert(result.directory_.total() == result.size_);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t logical_index(leaf_id_type leaf_id,
|
||||
std::size_t local) const {
|
||||
if (leaf_id >= leaf_positions_.size()) {
|
||||
@@ -387,6 +438,31 @@ private:
|
||||
return std::max<std::size_t>(1, capacity - capacity / 8);
|
||||
}
|
||||
|
||||
// Construct the entire shape needed by a bulk load while source elements
|
||||
// are still untouched. RingBlock allocates all of its optional slots in
|
||||
// its constructor, so inserting into these leaves does not allocate.
|
||||
[[nodiscard]] std::size_t prepare_bulk_destination(std::size_t element_count) {
|
||||
assert(element_count != 0);
|
||||
const auto target_occupancy = bulk_target_occupancy(config_.leaf_capacity);
|
||||
const auto count = 1 + (element_count - 1) / target_occupancy;
|
||||
|
||||
leaves_.reserve(count);
|
||||
leaf_positions_.reserve(count);
|
||||
std::vector<std::size_t> expected_sizes;
|
||||
expected_sizes.reserve(count);
|
||||
|
||||
auto remaining = element_count;
|
||||
for (std::size_t i = 0; i < count; ++i) {
|
||||
leaves_.push_back(make_leaf());
|
||||
const auto leaf_size = std::min(remaining, target_occupancy);
|
||||
expected_sizes.push_back(leaf_size);
|
||||
remaining -= leaf_size;
|
||||
}
|
||||
rebuild_positions();
|
||||
directory_.rebuild(expected_sizes);
|
||||
return target_occupancy;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Leaf> make_leaf() {
|
||||
if (next_leaf_id_ == std::numeric_limits<leaf_id_type>::max()) {
|
||||
throw std::length_error("tiered leaf id space exhausted");
|
||||
|
||||
Reference in New Issue
Block a user