-
-
Notifications
You must be signed in to change notification settings - Fork 184
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d383f05
commit f826285
Showing
16 changed files
with
948 additions
and
3,634 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,103 +1,254 @@ | ||
#pragma once | ||
|
||
#include <immer/extra/persist/champ/pool.hpp> | ||
#include <immer/extra/persist/errors.hpp> | ||
#include <immer/extra/persist/node_ptr.hpp> | ||
|
||
#include <immer/flex_vector.hpp> | ||
|
||
#include <boost/hana/functional/id.hpp> | ||
#include <boost/range/adaptor/indexed.hpp> | ||
|
||
#include <spdlog/spdlog.h> | ||
|
||
namespace immer::persist { | ||
namespace champ { | ||
|
||
template <class T, immer::detail::hamts::bits_t B> | ||
struct output_pool_builder | ||
class children_count_corrupted_exception : public pool_exception | ||
{ | ||
nodes_save<T, B> pool; | ||
public: | ||
children_count_corrupted_exception(node_id id, | ||
std::uint64_t nodemap, | ||
std::size_t expected_count, | ||
std::size_t real_count) | ||
: pool_exception{fmt::format( | ||
"Loaded container is corrupted. Inner " | ||
"node ID {} has nodemap {} which means it should have {} " | ||
"children but it has {}", | ||
id, | ||
nodemap, | ||
expected_count, | ||
real_count)} | ||
{ | ||
} | ||
}; | ||
|
||
void visit_inner(const auto* node, auto depth) | ||
class data_count_corrupted_exception : public pool_exception | ||
{ | ||
public: | ||
data_count_corrupted_exception(node_id id, | ||
std::uint64_t datamap, | ||
std::size_t expected_count, | ||
std::size_t real_count) | ||
: pool_exception{fmt::format( | ||
"Loaded container is corrupted. Inner " | ||
"node ID {} has datamap {} which means it should contain {} " | ||
"values but it has {}", | ||
id, | ||
datamap, | ||
expected_count, | ||
real_count)} | ||
{ | ||
auto id = get_node_id(node); | ||
if (pool.inners.count(id)) { | ||
return; | ||
} | ||
} | ||
}; | ||
|
||
auto node_info = inner_node_save<T, B>{ | ||
.nodemap = node->nodemap(), | ||
.datamap = node->datamap(), | ||
}; | ||
template <class T, | ||
typename Hash, | ||
typename Equal, | ||
typename MemoryPolicy, | ||
immer::detail::hamts::bits_t B, | ||
typename NodesLoad = nodes_load<T, B>, | ||
typename TransformF = boost::hana::id_t> | ||
class nodes_loader | ||
{ | ||
public: | ||
using champ_t = | ||
immer::detail::hamts::champ<T, Hash, Equal, MemoryPolicy, B>; | ||
using node_t = typename champ_t::node_t; | ||
using node_ptr = immer::persist::node_ptr<node_t>; | ||
|
||
using values_t = immer::flex_vector<immer::array<T>>; | ||
|
||
explicit nodes_loader(NodesLoad pool) | ||
requires std::is_same_v<TransformF, boost::hana::id_t> | ||
: pool_{std::move(pool)} | ||
{ | ||
} | ||
|
||
explicit nodes_loader(NodesLoad pool, TransformF transform) | ||
: pool_{std::move(pool)} | ||
, transform_{std::move(transform)} | ||
{ | ||
} | ||
|
||
if (node->datamap()) { | ||
node_info.values = {node->values(), | ||
node->values() + node->data_count()}; | ||
std::pair<node_ptr, values_t> load_collision(node_id id) | ||
{ | ||
if (auto* p = collisions_.find(id)) { | ||
return *p; | ||
} | ||
if (node->nodemap()) { | ||
auto fst = node->children(); | ||
auto lst = fst + node->children_count(); | ||
for (; fst != lst; ++fst) { | ||
node_info.children = | ||
std::move(node_info.children).push_back(get_node_id(*fst)); | ||
visit(*fst, depth + 1); | ||
} | ||
|
||
if (id.value >= pool_.size()) { | ||
throw invalid_node_id{id}; | ||
} | ||
|
||
pool.inners = std::move(pool.inners).set(id, node_info); | ||
const auto& node_info = pool_[id.value]; | ||
const auto values = get_values(node_info.values.data); | ||
|
||
const auto n = values.size(); | ||
auto node = node_ptr{node_t::make_collision_n(n), | ||
[](auto* ptr) { node_t::delete_collision(ptr); }}; | ||
immer::detail::uninitialized_copy( | ||
values.begin(), values.end(), node.get()->collisions()); | ||
auto result = std::make_pair(std::move(node), values_t{values}); | ||
collisions_ = std::move(collisions_).set(id, result); | ||
return result; | ||
} | ||
|
||
void visit_collision(const auto* node) | ||
std::pair<node_ptr, values_t> load_inner(node_id id) | ||
{ | ||
auto id = get_node_id(node); | ||
if (pool.inners.count(id)) { | ||
return; | ||
if (auto* p = inners_.find(id)) { | ||
return *p; | ||
} | ||
|
||
pool.inners = std::move(pool.inners) | ||
.set(id, | ||
inner_node_save<T, B>{ | ||
.values = {node->collisions(), | ||
node->collisions() + | ||
node->collision_count()}, | ||
.collisions = true, | ||
}); | ||
if (id.value >= pool_.size()) { | ||
throw invalid_node_id{id}; | ||
} | ||
|
||
const auto& node_info = pool_[id.value]; | ||
|
||
const auto children_count = node_info.children.size(); | ||
const auto values_count = node_info.values.data.size(); | ||
|
||
// Loading validation | ||
{ | ||
const auto expected_count = | ||
immer::detail::hamts::popcount(node_info.nodemap); | ||
if (expected_count != children_count) { | ||
throw children_count_corrupted_exception{ | ||
id, node_info.nodemap, expected_count, children_count}; | ||
} | ||
} | ||
|
||
{ | ||
const auto expected_count = | ||
immer::detail::hamts::popcount(node_info.datamap); | ||
if (expected_count != values_count) { | ||
throw data_count_corrupted_exception{ | ||
id, node_info.datamap, expected_count, values_count}; | ||
} | ||
} | ||
|
||
const auto node_values = get_values(node_info.values.data); | ||
|
||
auto values = values_t{}; | ||
|
||
// Load children | ||
const auto children = [&values, &node_info, this] { | ||
auto [children_ptrs, children_values] = | ||
load_children(node_info.children); | ||
|
||
if (!children_values.empty()) { | ||
values = std::move(values) + children_values; | ||
} | ||
|
||
/** | ||
* NOTE: Be careful with release_full and exceptions, nodes will not | ||
* be freed automatically. | ||
*/ | ||
auto result = immer::vector<ptr_with_deleter<node_t>>{}; | ||
for (auto& child : children_ptrs) { | ||
result = std::move(result).push_back( | ||
std::move(child).release_full()); | ||
} | ||
return result; | ||
}(); | ||
const auto delete_children = [children]() { | ||
for (const auto& ptr : children) { | ||
ptr.dec(); | ||
} | ||
}; | ||
|
||
auto inner = | ||
node_ptr{node_t::make_inner_n(children_count, values_count), | ||
[delete_children](auto* ptr) { | ||
node_t::delete_inner(ptr); | ||
delete_children(); | ||
}}; | ||
inner.get()->impl.d.data.inner.nodemap = node_info.nodemap; | ||
inner.get()->impl.d.data.inner.datamap = node_info.datamap; | ||
|
||
// Values | ||
if (values_count) { | ||
immer::detail::uninitialized_copy( | ||
node_values.begin(), node_values.end(), inner.get()->values()); | ||
values = std::move(values).push_back(node_values); | ||
} | ||
|
||
// Set children | ||
for (const auto& [index, child_ptr] : | ||
boost::adaptors::index(children)) { | ||
inner.get()->children()[index] = child_ptr.ptr; | ||
} | ||
|
||
inners_ = std::move(inners_).set(id, std::make_pair(inner, values)); | ||
return {std::move(inner), std::move(values)}; | ||
} | ||
|
||
void visit(const auto* node, immer::detail::hamts::count_t depth) | ||
std::pair<node_ptr, values_t> load_some_node(node_id id) | ||
{ | ||
using immer::detail::hamts::max_depth; | ||
if (id.value >= pool_.size()) { | ||
throw invalid_node_id{id}; | ||
} | ||
|
||
if (depth < max_depth<B>) { | ||
visit_inner(node, depth); | ||
if (pool_[id.value].collisions) { | ||
return load_collision(id); | ||
} else { | ||
visit_collision(node); | ||
return load_inner(id); | ||
} | ||
} | ||
|
||
node_id get_node_id(auto* ptr) | ||
std::pair<std::vector<node_ptr>, values_t> | ||
load_children(const immer::vector<node_id>& children_ids) | ||
{ | ||
auto [pool2, id] = | ||
immer::persist::champ::get_node_id(std::move(pool), ptr); | ||
pool = std::move(pool2); | ||
return id; | ||
} | ||
}; | ||
auto children = std::vector<node_ptr>{}; | ||
auto values = values_t{}; | ||
for (const auto& child_node_id : children_ids) { | ||
auto [child, child_values] = load_some_node(child_node_id); | ||
if (!child) { | ||
throw pool_exception{ | ||
fmt::format("Failed to load node ID {}", child_node_id)}; | ||
} | ||
|
||
template <typename T, | ||
typename Hash, | ||
typename Equal, | ||
typename MemoryPolicy, | ||
immer::detail::hamts::bits_t B, | ||
class Pool> | ||
auto save_nodes( | ||
const immer::detail::hamts::champ<T, Hash, Equal, MemoryPolicy, B>& champ, | ||
Pool pool) | ||
{ | ||
using champ_t = std::decay_t<decltype(champ)>; | ||
using node_t = typename champ_t::node_t; | ||
if (!child_values.empty()) { | ||
values = std::move(values) + child_values; | ||
} | ||
|
||
children.push_back(std::move(child)); | ||
} | ||
return {std::move(children), std::move(values)}; | ||
} | ||
|
||
auto save = output_pool_builder<typename node_t::value_t, B>{ | ||
.pool = std::move(pool), | ||
}; | ||
save.visit(champ.root, 0); | ||
private: | ||
immer::array<T> get_values(const auto& array) const | ||
{ | ||
if constexpr (std::is_same_v<TransformF, boost::hana::id_t>) { | ||
return array; | ||
} else { | ||
auto transformed_values = std::vector<T>{}; | ||
for (const auto& item : array) { | ||
transformed_values.push_back(transform_(item)); | ||
} | ||
return immer::array<T>{transformed_values.begin(), | ||
transformed_values.end()}; | ||
} | ||
} | ||
|
||
return std::move(save.pool); | ||
} | ||
private: | ||
const NodesLoad pool_; | ||
const TransformF transform_; | ||
immer::map<node_id, std::pair<node_ptr, values_t>> collisions_; | ||
immer::map<node_id, std::pair<node_ptr, values_t>> inners_; | ||
}; | ||
|
||
} // namespace champ | ||
} // namespace immer::persist |
Oops, something went wrong.