diff --git a/immer/detail/hamts/champ_iterator.hpp b/immer/detail/hamts/champ_iterator.hpp index 373198bb..594ad750 100644 --- a/immer/detail/hamts/champ_iterator.hpp +++ b/immer/detail/hamts/champ_iterator.hpp @@ -15,6 +15,32 @@ namespace immer { namespace detail { namespace hamts { +template +struct relocation_info_t +{ + void reset() {} + void increment() {} + void step_right(count_t) {} + void step_down(count_t) {} +}; + +template +struct relocation_info_t +{ + count_t cur_off_; + std::uint8_t path_off_[max_depth] = { + 0, + }; + + void reset() { cur_off_ = 0; } + + void increment() { cur_off_++; } + + void step_right(count_t depth) { ++path_off_[depth - 1]; } + + void step_down(count_t depth) { path_off_[depth - 1] = 0; } +}; + template struct champ_iterator : iterator_facade, @@ -32,6 +58,7 @@ struct champ_iterator champ_iterator(const tree_t& v) : depth_{0} + , relocation_info_{} { if (v.root->datamap()) { cur_ = v.root->values(); @@ -47,6 +74,7 @@ struct champ_iterator : cur_{nullptr} , end_{nullptr} , depth_{0} + , relocation_info_{} { path_[0] = &v.root; } @@ -55,10 +83,13 @@ struct champ_iterator : cur_{other.cur_} , end_{other.end_} , depth_{other.depth_} + , relocation_info_{other.relocation_info_} { std::copy(other.path_, other.path_ + depth_ + 1, path_); } + auto relocation_info() const { return relocation_info_; } + private: friend iterator_core_access; @@ -68,10 +99,12 @@ struct champ_iterator node_t* const* path_[max_depth + 1] = { 0, }; + relocation_info_t relocation_info_; void increment() { ++cur_; + relocation_info_.increment(); ensure_valid_(); } @@ -83,16 +116,19 @@ struct champ_iterator if (parent->nodemap()) { ++depth_; path_[depth_] = parent->children(); - auto child = *path_[depth_]; + relocation_info_.step_down(depth_); + auto child = *path_[depth_]; assert(child); if (depth_ < max_depth) { if (child->datamap()) { cur_ = child->values(); end_ = cur_ + child->data_count(); + relocation_info_.reset(); } } else { cur_ = child->collisions(); end_ = cur_ + child->collision_count(); + relocation_info_.reset(); } return true; } @@ -108,16 +144,19 @@ struct champ_iterator auto next = path_[depth_] + 1; if (next < last) { path_[depth_] = next; - auto child = *path_[depth_]; + relocation_info_.step_right(depth_); + auto child = *path_[depth_]; assert(child); if (depth_ < max_depth) { if (child->datamap()) { cur_ = child->values(); end_ = cur_ + child->data_count(); + relocation_info_.reset(); } } else { cur_ = child->collisions(); end_ = cur_ + child->collision_count(); + relocation_info_.reset(); } return true; } @@ -136,6 +175,7 @@ struct champ_iterator // end of sequence assert(depth_ == 0); cur_ = end_ = nullptr; + relocation_info_.reset(); return; } } diff --git a/immer/memory_policy.hpp b/immer/memory_policy.hpp index d8556b94..2a26f089 100644 --- a/immer/memory_policy.hpp +++ b/immer/memory_policy.hpp @@ -90,7 +90,8 @@ template , bool UseTransientRValues = - get_use_transient_rvalues_v> + get_use_transient_rvalues_v, + bool TrackBaseOffsetOfPointers = false> struct memory_policy { using heap = HeapPolicy; @@ -103,6 +104,9 @@ struct memory_policy static constexpr bool use_transient_rvalues = UseTransientRValues; + static constexpr bool track_base_offset_of_pointers = + TrackBaseOffsetOfPointers; + using transience_t = typename transience::template apply::type; }; diff --git a/test/set/relocation.cpp b/test/set/relocation.cpp new file mode 100644 index 00000000..15caa113 --- /dev/null +++ b/test/set/relocation.cpp @@ -0,0 +1,53 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#include +#include + +using relocation_memory = immer::memory_policy; + +template , + typename Eq = std::equal_to> +using test_set_t = immer::set; + +TEST_CASE("relocation info") +{ + auto v = test_set_t{}; + for (auto i = 0u; i < 512u; ++i) + v = v.insert(i); + auto iter = v.begin(); + test_set_t::iterator::node_t *node = v.impl().root, + *parent = nullptr; + unsigned depth = 0; + + while (node->nodemap()) { + parent = node; + node = node->children()[0]; + ++depth; + } + CHECK(depth >= 2); + for (auto i = 0u; i < parent->children_count(); ++i) { + for (auto j = 0u; j < node->data_count(); ++j) { + CHECK(iter.relocation_info().cur_off_ == j); + CHECK(iter.relocation_info().path_off_[depth] == 0); + CHECK(iter.relocation_info().path_off_[depth - 1] == i); + CHECK(iter.relocation_info().path_off_[depth - 2] == 0); + ++iter; + } + if (i + 1 < parent->children_count()) { + node = parent->children()[i + 1]; + } + } +}