// // 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 // #pragma once #include #include #include #include #include #include #include #include namespace immer { namespace detail { namespace dvektor { constexpr auto fast_log2(std::size_t x) { return x == 0 ? 0 : sizeof(std::size_t) * 8 - 1 - __builtin_clzl(x); } template constexpr T branches = T{1} << B; template constexpr T mask = branches - 1; template constexpr auto max_depth = fast_log2(std::numeric_limits::max()) / B; template struct node; template using node_ptr = boost::intrusive_ptr>; template using leaf_node = std::array; template using inner_node = std::array, 1 << B>; template struct node : enable_intrusive_ptr, typename MP::refcount> , enable_optimized_heap_policy, typename MP::heap> { using leaf_node_t = leaf_node; using inner_node_t = inner_node; enum { leaf_kind, inner_kind } kind; union data_t { leaf_node_t leaf; inner_node_t inner; data_t(leaf_node_t n) : leaf(std::move(n)) {} data_t(inner_node_t n) : inner(std::move(n)) {} ~data_t() {} } data; ~node() { switch (kind) { case leaf_kind: data.leaf.~leaf_node_t(); break; case inner_kind: data.inner.~inner_node_t(); break; } } node(leaf_node n) : kind{leaf_kind} , data{std::move(n)} {} node(inner_node n) : kind{inner_kind} , data{std::move(n)} {} inner_node_t& inner() & { assert(kind == inner_kind); return data.inner; } const inner_node_t& inner() const& { assert(kind == inner_kind); return data.inner; } inner_node_t&& inner() && { assert(kind == inner_kind); return std::move(data.inner); } leaf_node_t& leaf() & { assert(kind == leaf_kind); return data.leaf; } const leaf_node_t& leaf() const& { assert(kind == leaf_kind); return data.leaf; } leaf_node_t&& leaf() && { assert(kind == leaf_kind); return std::move(data.leaf); } }; template auto make_node(Ts&&... xs) -> boost::intrusive_ptr> { return new node(std::forward(xs)...); } template struct ref { using inner_t = inner_node; using leaf_t = leaf_node; using node_t = node; using node_ptr_t = node_ptr; unsigned depth; std::array> display; template static auto make_node(Ts&&... xs) { return dvektor::make_node(std::forward(xs)...); } const T& get_elem(std::size_t index, std::size_t xr) const { auto display_idx = fast_log2(xr) / B; auto node = display[display_idx].get(); auto shift = display_idx * B; while (display_idx--) { node = node->inner()[(index >> shift) & mask].get(); shift -= B; } return node->leaf()[index & mask]; } node_ptr_t null_slot_and_copy_inner(node_ptr_t& node, std::size_t idx) { auto& n = node->inner(); auto x = node_ptr_t{}; x.swap(n[idx]); return copy_of_inner(x); } node_ptr_t null_slot_and_copy_leaf(node_ptr_t& node, std::size_t idx) { auto& n = node->inner(); auto x = node_ptr_t{}; x.swap(n[idx]); return copy_of_leaf(x); } node_ptr_t copy_of_inner(const node_ptr_t& n) { return make_node(n->inner()); } node_ptr_t copy_of_leaf(const node_ptr_t& n) { return make_node(n->leaf()); } void stabilize(std::size_t index) { auto shift = B; for (auto i = 1u; i < depth; ++i) { display[i] = copy_of_inner(display[i]); display[i]->inner()[(index >> shift) & mask] = display[i - 1]; shift += B; } } void goto_pos_writable_from_clean(std::size_t old_index, std::size_t index, std::size_t xr) { assert(depth); auto d = depth - 1; if (d == 0) { display[0] = copy_of_leaf(display[0]); } else { IMMER_UNREACHABLE; display[d] = copy_of_inner(display[d]); auto shift = B * d; while (--d) { display[d] = null_slot_and_copy_inner( display[d + 1], (index >> shift) & mask); shift -= B; } display[0] = null_slot_and_copy_leaf(display[1], (index >> B) & mask); } } void goto_pos_writable_from_dirty(std::size_t old_index, std::size_t new_index, std::size_t xr) { assert(depth); if (xr < (1 << B)) { display[0] = copy_of_leaf(display[0]); } else { auto display_idx = fast_log2(xr) / B; auto shift = B; for (auto i = 1u; i <= display_idx; ++i) { display[i] = copy_of_inner(display[i]); display[i]->inner()[(old_index >> shift) & mask] = display[i - 1]; shift += B; } for (auto i = display_idx - 1; i > 0; --i) { shift -= B; display[i] = null_slot_and_copy_inner( display[i + 1], (new_index >> shift) & mask); } display[0] = null_slot_and_copy_leaf(display[1], (new_index >> B) & mask); } } void goto_fresh_pos_writable_from_clean(std::size_t old_index, std::size_t new_index, std::size_t xr) { auto display_idx = fast_log2(xr) / B; if (display_idx > 0) { auto shift = display_idx * B; if (display_idx == depth) { display[display_idx] = make_node(inner_t{}); display[display_idx]->inner()[(old_index >> shift) & mask] = display[display_idx - 1]; ++depth; } while (--display_idx) { auto node = display[display_idx + 1] ->inner()[(new_index >> shift) & mask]; display[display_idx] = node ? std::move(node) : make_node(inner_t{}); } display[0] = make_node(leaf_t{}); } } void goto_fresh_pos_writable_from_dirty(std::size_t old_index, std::size_t new_index, std::size_t xr) { stabilize(old_index); goto_fresh_pos_writable_from_clean(old_index, new_index, xr); } void goto_next_block_start(std::size_t index, std::size_t xr) { auto display_idx = fast_log2(xr) / B; auto shift = display_idx * B; if (display_idx > 0) { display[display_idx - 1] = display[display_idx]->inner()[(index >> shift) & mask]; while (--display_idx) display[display_idx - 1] = display[display_idx]->inner()[0]; } } void goto_pos(std::size_t index, std::size_t xr) { auto display_idx = fast_log2(xr) / B; auto shift = display_idx * B; if (display_idx) { do { display[display_idx - 1] = display[display_idx]->inner()[(index >> shift) & mask]; shift -= B; } while (--display_idx); } } }; template struct impl { using inner_t = inner_node; using leaf_t = leaf_node; using node_t = node; using node_ptr_t = node_ptr; using ref_t = ref; std::size_t size; std::size_t focus; bool dirty; ref_t p; template static auto make_node(Ts&&... xs) { return dvektor::make_node(std::forward(xs)...); } void goto_pos_writable(std::size_t old_index, std::size_t new_index, std::size_t xr) { if (dirty) { p.goto_pos_writable_from_dirty(old_index, new_index, xr); } else { p.goto_pos_writable_from_clean(old_index, new_index, xr); dirty = true; } } void goto_fresh_pos_writable(std::size_t old_index, std::size_t new_index, std::size_t xr) { if (dirty) { p.goto_fresh_pos_writable_from_dirty(old_index, new_index, xr); } else { p.goto_fresh_pos_writable_from_clean(old_index, new_index, xr); dirty = true; } } impl push_back(T value) const { if (size) { auto block_index = size & ~mask; auto lo = size & mask; if (size != block_index) { auto s = impl{size + 1, block_index, dirty, p}; s.goto_pos_writable(focus, block_index, focus ^ block_index); s.p.display[0]->leaf()[lo] = std::move(value); return s; } else { auto s = impl{size + 1, block_index, dirty, p}; s.goto_fresh_pos_writable( focus, block_index, focus ^ block_index); s.p.display[0]->leaf()[lo] = std::move(value); return s; } } else { return impl{ 1, 0, false, {1, {{make_node(leaf_t{{std::move(value)}})}}}}; } } const T& get(std::size_t index) const { return p.get_elem(index, index ^ focus); } template impl update(std::size_t idx, FnT&& fn) const { auto s = impl{size, idx, dirty, p}; s.goto_pos_writable(focus, idx, focus ^ idx); auto& v = s.p.display[0]->leaf()[idx & mask]; v = fn(std::move(v)); return s; } impl assoc(std::size_t idx, T value) const { return update(idx, [&](auto&&) { return std::move(value); }); } }; template const impl empty = {0, 0, false, ref{1, {}}}; template struct iterator : boost::iterator_facade, T, boost::random_access_traversal_tag, const T&> { struct end_t {}; iterator() = default; iterator(const impl& v) : p_{v.p} , i_{0} , base_{0} { if (v.dirty) p_.stabilize(v.focus); p_.goto_pos(0, 0 ^ v.focus); curr_ = p_.display[0]->leaf().begin(); } iterator(const impl& v, end_t) : p_{v.p} , i_{v.size} , base_{(v.size - 1) & ~mask} { if (v.dirty) p_.stabilize(v.focus); p_.goto_pos(base_, base_ ^ v.focus); curr_ = p_.display[0]->leaf().begin() + (i_ - base_); } private: friend class boost::iterator_core_access; using leaf_iterator = typename leaf_node::const_iterator; ref p_; std::size_t i_; std::size_t base_; leaf_iterator curr_; void increment() { ++i_; if (i_ - base_ < branches) { ++curr_; } else { auto new_base = base_ + branches; p_.goto_next_block_start(new_base, base_ ^ new_base); base_ = new_base; curr_ = p_.display[0]->leaf().begin(); } } void decrement() { assert(i_ > 0); --i_; if (i_ >= base_) { --curr_; } else { auto new_base = base_ - branches; p_.goto_pos(new_base, base_ ^ new_base); base_ = new_base; curr_ = std::prev(p_.display[0]->leaf().end()); } } void advance(std::ptrdiff_t n) { i_ += n; if (i_ <= base_ && i_ - base_ < branches) { curr_ += n; } else { auto new_base = i_ & ~mask; p_.goto_pos(new_base, base_ ^ new_base); base_ = new_base; curr_ = p_.display[0]->leaf().begin() + (i_ - base_); } } bool equal(const iterator& other) const { return i_ == other.i_; } std::ptrdiff_t distance_to(const iterator& other) const { return other.i_ > i_ ? static_cast(other.i_ - i_) : -static_cast(i_ - other.i_); } const T& dereference() const { return *curr_; } }; } /* namespace dvektor */ } /* namespace detail */ } /* namespace immer */