Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify and refactor free blocks tree iterator #63

Merged
merged 1 commit into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 15 additions & 37 deletions src/eptree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

#include "eptree.h"

EPTreeConstIterator& EPTreeConstIterator::operator++() {
EPTreeIterator& EPTreeIterator::operator++() {
assert(!is_end());
auto rnode = nodes_.rbegin();
while ((++rnode->iterator).is_end()) {
Expand All @@ -19,53 +19,31 @@ EPTreeConstIterator& EPTreeConstIterator::operator++() {
return *this; // end
}
}
uint32_t node_block_number = rnode->iterator->value;
uint32_t node_block_number = (*rnode->iterator).value;
for (auto node = rnode.base(); node != nodes_.end(); ++node) {
*node = {allocator_->LoadAllocatorBlock(node_block_number)};
node->iterator = node->node->begin();
node_block_number = node->iterator->value;
node_block_number = (*node->iterator).value;
}
return *this;
}

EPTreeConstIterator& EPTreeConstIterator::operator--() {
EPTreeIterator& EPTreeIterator::operator--() {
assert(!is_begin());
auto rnode = nodes_.rbegin();
for (; rnode->iterator.is_begin(); rnode++) {
if (rnode == nodes_.rend())
return *this; // begin
}
uint32_t node_block_number = (--rnode->iterator)->value;
uint32_t node_block_number = (*--rnode->iterator).value;
for (auto node = rnode.base(); node != nodes_.end(); ++node) {
*node = {allocator_->LoadAllocatorBlock(node_block_number)};
node->iterator = node->node->end();
node_block_number = (--node->iterator)->value;
node_block_number = (*--node->iterator).value;
}
return *this;
}

EPTreeConstIterator EPTreeConstIterator::operator++(int) {
EPTreeConstIterator tmp(*this);
++(*this);
return tmp;
}

EPTreeConstIterator EPTreeConstIterator::operator--(int) {
EPTreeConstIterator tmp(*this);
--(*this);
return tmp;
}

EPTreeIterator& EPTreeIterator::operator++() {
base::operator++();
return *this;
}

EPTreeIterator& EPTreeIterator::operator--() {
base::operator--();
return *this;
}

EPTreeIterator EPTreeIterator::operator++(int) {
EPTreeIterator tmp(*this);
++(*this);
Expand Down Expand Up @@ -96,7 +74,7 @@ bool EPTree::insert(const iterator::value_type& key_value) {
auto depth = node_level->tree_header()->depth.value();
// Where to split the level
auto split_point = node_level->middle();
key_type split_point_key = split_point->key;
key_type split_point_key = (*split_point).key;
// Alloc new right side tree
auto right_block_number = AllocBlockForTree(node_level->tree_header()->block_number.value(), allocated_extents);
RTree new_right(allocator_->LoadAllocatorBlock(right_block_number, /*new_block=*/true));
Expand Down Expand Up @@ -135,15 +113,15 @@ bool EPTree::insert(const iterator::value_type& key_value) {
return true;
}

bool EPTree::insert(const RTree::const_iterator& it_start, const RTree::const_iterator& it_end) {
bool EPTree::insert(const RTree::iterator& it_start, const RTree::iterator& it_end) {
for (const auto& val : std::ranges::subrange(it_start, it_end)) {
if (!insert(val))
return false;
}
return true;
}

void EPTree::erase(const const_iterator& pos, std::vector<FreeBlocksRangeInfo>& blocks_to_delete) {
void EPTree::erase(const iterator& pos, std::vector<FreeBlocksRangeInfo>& blocks_to_delete) {
// Erase from each node
for (auto& [node_level, node_it] : std::views::reverse(pos.nodes())) {
node_level->erase(node_it);
Expand Down Expand Up @@ -175,38 +153,38 @@ uint32_t EPTree::AllocBlockForTree(uint32_t near, std::vector<FreeBlocksExtentIn
return allocator_->FindSmallestFreeBlockExtent(near, allocated);
}

EPTree::iterator EPTree::begin_impl() const {
EPTree::iterator EPTree::begin() const {
std::vector<iterator::node_info> nodes;
nodes.reserve(tree_header()->depth.value());
assert(tree_header()->depth.value() >= 1);
for (int i = 0; i < tree_header()->depth.value(); i++) {
assert(i == 0 || !nodes.back().iterator.is_end());
iterator::node_info node{i == 0 ? block() : allocator_->LoadAllocatorBlock(nodes.back().iterator->value)};
iterator::node_info node{i == 0 ? block() : allocator_->LoadAllocatorBlock((*nodes.back().iterator).value)};
node.iterator = node.node->begin();
nodes.push_back(std::move(node));
}
return {allocator_, std::move(nodes)};
}

EPTree::iterator EPTree::end_impl() const {
EPTree::iterator EPTree::end() const {
std::vector<iterator::node_info> nodes;
nodes.reserve(tree_header()->depth.value());
assert(tree_header()->depth.value() >= 1);
for (int i = 0; i < tree_header()->depth.value(); i++) {
assert(i == 0 || !nodes.back().iterator.is_begin());
iterator::node_info node{i == 0 ? block() : allocator_->LoadAllocatorBlock((--nodes.back().iterator)->value)};
iterator::node_info node{i == 0 ? block() : allocator_->LoadAllocatorBlock((*--nodes.back().iterator).value)};
node.iterator = node.node->end();
nodes.push_back(std::move(node));
}
return {allocator_, std::move(nodes)};
}

EPTree::iterator EPTree::find_impl(key_type key, bool exact_match) const {
EPTree::iterator EPTree::find(key_type key, bool exact_match) const {
std::vector<iterator::node_info> nodes;
nodes.reserve(tree_header()->depth.value());
for (int i = 0; i < tree_header()->depth.value(); i++) {
assert(i == 0 || !nodes.back().iterator.is_end());
iterator::node_info node{i == 0 ? block() : allocator_->LoadAllocatorBlock((nodes.back().iterator)->value)};
iterator::node_info node{i == 0 ? block() : allocator_->LoadAllocatorBlock((*nodes.back().iterator).value)};
node.iterator = node.node->find(key, exact_match && i + 1 == tree_header()->depth.value());
nodes.push_back(std::move(node));
}
Expand Down
79 changes: 15 additions & 64 deletions src/eptree.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,29 @@
#include "free_blocks_allocator.h"
#include "rtree.h"

class EPTreeConstIterator {
class EPTreeIterator {
public:
using iterator_category = std::bidirectional_iterator_tag;
using difference_type = int;
using value_type = RTree::iterator::value_type;

using const_reference = RTree::iterator::const_reference;
using const_pointer = RTree::iterator::const_pointer;

using reference = RTree::const_iterator::reference;
using pointer = RTree::const_iterator::const_pointer;
using reference = RTree::iterator::reference;

using node_info = node_iterator_info<RTree>;

EPTreeConstIterator() = default;
EPTreeIterator() = default;

EPTreeConstIterator(FreeBlocksAllocator* allocator, std::vector<node_info> nodes)
EPTreeIterator(FreeBlocksAllocator* allocator, std::vector<node_info> nodes)
: allocator_(allocator), nodes_(std::move(nodes)) {}

reference operator*() const { return *nodes_.back().iterator; }
pointer operator->() const& { return nodes_.back().iterator.operator->(); }

EPTreeConstIterator& operator++();
EPTreeConstIterator& operator--();
EPTreeConstIterator operator++(int);
EPTreeConstIterator operator--(int);
EPTreeIterator& operator++();
EPTreeIterator& operator--();
EPTreeIterator operator++(int);
EPTreeIterator operator--(int);

bool operator==(const EPTreeConstIterator& other) const {
return nodes_.back().iterator == other.nodes_.back().iterator;
}
bool operator==(const EPTreeIterator& other) const { return nodes_.back().iterator == other.nodes_.back().iterator; }

std::vector<node_info>& nodes() { return nodes_; };
const std::vector<node_info>& nodes() const { return nodes_; };
Expand All @@ -58,67 +51,24 @@ class EPTreeConstIterator {

std::vector<node_info> nodes_;
};
static_assert(std::bidirectional_iterator<EPTreeConstIterator>);

class EPTreeIterator : public EPTreeConstIterator {
public:
using base = EPTreeConstIterator;

using iterator_category = std::bidirectional_iterator_tag;
using difference_type = base::difference_type;
using value_type = base::value_type;

using reference = RTree::iterator::reference;
using pointer = RTree::iterator::pointer;

using node_info = base::node_info;

EPTreeIterator() = default;

EPTreeIterator(FreeBlocksAllocator* allocator, std::vector<node_info> nodes) : base(allocator, std::move(nodes)) {}

reference operator*() const { return *nodes().back().iterator; }
pointer operator->() const& { return nodes().back().iterator.operator->(); }

EPTreeIterator& operator++();
EPTreeIterator& operator--();
EPTreeIterator operator++(int);
EPTreeIterator operator--(int);

bool operator==(const EPTreeIterator& other) const { return base::operator==(other); }
};
static_assert(std::bidirectional_iterator<EPTreeIterator>);

class EPTree : public EPTreeBlock {
public:
using iterator = EPTreeIterator;
using const_iterator = EPTreeConstIterator;
using reverse_iterator = TreeReverseIterator<iterator>;
using const_reverse_iterator = TreeReverseIterator<const_iterator>;

EPTree(FreeBlocksAllocator* allocator) : EPTreeBlock(allocator->root_block()), allocator_(allocator) {}

void Init(uint32_t block_number);

iterator begin() { return begin_impl(); }
iterator end() { return end_impl(); }
const_iterator begin() const { return begin_impl(); }
const_iterator end() const { return end_impl(); }

reverse_iterator rbegin() { return reverse_iterator{end()}; }
reverse_iterator rend() { return reverse_iterator{begin()}; }
const_reverse_iterator rbegin() const { return const_reverse_iterator{end()}; }
const_reverse_iterator rend() const { return const_reverse_iterator{begin()}; }

const_iterator cbegin() const { return begin(); }
const_iterator cend() const { return end(); }
iterator begin() const;
iterator end() const;

iterator find(key_type key, bool exact_match = true) { return find_impl(key, exact_match); }
const_iterator find(key_type key, bool exact_match = true) const { return find_impl(key, exact_match); }
iterator find(key_type key, bool exact_match = true) const;

bool insert(const iterator::value_type& key_value);
bool insert(const RTree::const_iterator& it_start, const RTree::const_iterator& it_end);
void erase(const const_iterator& pos, std::vector<FreeBlocksRangeInfo>& blocks_to_delete);
bool insert(const RTree::iterator& it_start, const RTree::iterator& it_end);
void erase(const iterator& pos, std::vector<FreeBlocksRangeInfo>& blocks_to_delete);
bool erase(key_type key, std::vector<FreeBlocksRangeInfo>& blocks_to_delete);

private:
Expand All @@ -130,3 +80,4 @@ class EPTree : public EPTreeBlock {

FreeBlocksAllocator* allocator_;
};
static_assert(std::ranges::bidirectional_range<EPTree>);
27 changes: 14 additions & 13 deletions src/free_blocks_allocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,10 @@ void FreeBlocksAllocator::AddFreeBlocksForSize(FreeBlocksRangeInfo range, size_t
// 1. It ends exactly at this range start.
// 2. This range start isn't aligned to one size up bucket block.
// 3. OR joining those ranges will still be smaller than one size up bucket
if (pos->end_block_number() == range_in_size.block_number &&
FreeBlocksExtentInfo before_pos = *pos;
if (before_pos.end_block_number() == range_in_size.block_number &&
(!is_aligned_pow2(range_in_size.block_number, next_size_pow2) ||
pos->blocks_count() + range_in_size.blocks_count < next_size_blocks_count)) {
before_pos.blocks_count + range_in_size.blocks_count < next_size_blocks_count)) {
join_before = *pos;
join_before_iter = pos;
range_in_size.block_number = join_before->block_number;
Expand All @@ -177,12 +178,12 @@ void FreeBlocksAllocator::AddFreeBlocksForSize(FreeBlocksRangeInfo range, size_t
// 1. It start exactly at this range end.
// 2. This range end isn't aligned to one size up bucket block.
// 3. OR joining those ranges will still be smaller than one size up bucket
if (pos->block_number() <= range_in_size.block_number) // We could be at begin() before
if (before_pos.block_number <= range_in_size.block_number) // We could be at begin() before
++pos;
assert(pos.is_end() || pos->block_number() > range_in_size.block_number);
if (!pos.is_end() && pos->block_number() == range_in_size.end_block_number() &&
assert(pos.is_end() || (*pos).block_number() > range_in_size.block_number);
if (!pos.is_end() && (*pos).block_number() == range_in_size.end_block_number() &&
(!is_aligned_pow2(range_in_size.end_block_number(), next_size_pow2) ||
pos->blocks_count() + range_in_size.blocks_count < next_size_blocks_count)) {
(*pos).blocks_count() + range_in_size.blocks_count < next_size_blocks_count)) {
FreeBlocksExtentInfo join_after = *pos;
range_in_size.blocks_count += join_after.blocks_count;
// We give up on the optimization that is done in the original logic (just update the join after block number to
Expand Down Expand Up @@ -211,7 +212,7 @@ void FreeBlocksAllocator::AddFreeBlocksForSize(FreeBlocksRangeInfo range, size_t
sub_range.blocks_count / size_blocks_count <= 0x10);
auto new_value = static_cast<nibble>(sub_range.blocks_count / size_blocks_count - 1);
if (join_before && sub_range.block_number == range_in_size.block_number) {
join_before_iter->value = new_value;
(*join_before_iter).value = new_value;
} else {
// Don't use pos to insert because:
// 1. Our find may go back so it isn't the exact location to insert it.
Expand Down Expand Up @@ -282,14 +283,14 @@ void FreeBlocksAllocator::RecreateEPTreeIfNeeded() {
// Already trivial
return;
}
auto last = eptree.rbegin();
if (last->key || last->value != 2)
auto last = eptree.end()--;
if ((*last).key || (*last).value != 2)
return;
uint32_t last_value = last->value;
uint32_t last_value = (*last).value;
assert(last_value == 2);
std::vector<FreeBlocksRangeInfo> blocks_to_delete;
// eptree is empty (aka have only initial FTreee), resize it to one eptree
auto nodes = last.base().nodes();
auto nodes = last.nodes();
for (auto& [node_level, node_it] : std::views::reverse(nodes)) {
if (node_level->header() == &eptree.tree_header()->current_tree) {
// this is the root, reinitialize it
Expand All @@ -308,13 +309,13 @@ bool FreeBlocksAllocator::IsRangeIsFree(FreeBlocksRangeInfo range) {
if (pos.is_end())
return false;
// Check intersection with the free range before us.
if (range.block_number >= pos->block_number() && range.block_number < pos->end_block_number())
if (range.block_number >= (*pos).block_number() && range.block_number < (*pos).end_block_number())
return true;
++pos;
if (pos.is_end())
return false;
// Check intersection with the free range after us.
return pos->block_number() >= range.block_number && pos->block_number() < range.end_block_number();
return (*pos).block_number() >= range.block_number && (*pos).block_number() < range.end_block_number();
}

std::optional<std::vector<uint32_t>> FreeBlocksAllocator::AllocBlocks(uint32_t chunks_count,
Expand Down
Loading
Loading