diff options
Diffstat (limited to 'third_party/immer/immer')
57 files changed, 0 insertions, 15927 deletions
diff --git a/third_party/immer/immer/algorithm.hpp b/third_party/immer/immer/algorithm.hpp deleted file mode 100644 index ecdc417e2b07..000000000000 --- a/third_party/immer/immer/algorithm.hpp +++ /dev/null @@ -1,212 +0,0 @@ -// -// 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 <algorithm> -#include <numeric> -#include <type_traits> - -namespace immer { - -/** - * @defgroup algorithm - * @{ - */ - -/*@{*/ -// Right now these algorithms dispatch directly to the vector -// implementations unconditionally. This will be changed in the -// future to support other kinds of containers. - -/*! - * Apply operation `fn` for every contiguous *chunk* of data in the - * range sequentially. Each time, `Fn` is passed two `value_type` - * pointers describing a range over a part of the vector. This allows - * iterating over the elements in the most efficient way. - * - * @rst - * - * .. tip:: This is a low level method. Most of the time, :doc:`other - * wrapper algorithms <algorithms>` should be used instead. - * - * @endrst - */ -template <typename Range, typename Fn> -void for_each_chunk(const Range& r, Fn&& fn) -{ - r.impl().for_each_chunk(std::forward<Fn>(fn)); -} - -template <typename Iterator, typename Fn> -void for_each_chunk(const Iterator& first, const Iterator& last, Fn&& fn) -{ - assert(&first.impl() == &last.impl()); - first.impl().for_each_chunk( - first.index(), last.index(), std::forward<Fn>(fn)); -} - -template <typename T, typename Fn> -void for_each_chunk(const T* first, const T* last, Fn&& fn) -{ - std::forward<Fn>(fn)(first, last); -} - -/*! - * Apply operation `fn` for every contiguous *chunk* of data in the - * range sequentially, until `fn` returns `false`. Each time, `Fn` is - * passed two `value_type` pointers describing a range over a part of - * the vector. This allows iterating over the elements in the most - * efficient way. - * - * @rst - * - * .. tip:: This is a low level method. Most of the time, :doc:`other - * wrapper algorithms <algorithms>` should be used instead. - * - * @endrst - */ -template <typename Range, typename Fn> -bool for_each_chunk_p(const Range& r, Fn&& fn) -{ - return r.impl().for_each_chunk_p(std::forward<Fn>(fn)); -} - -template <typename Iterator, typename Fn> -bool for_each_chunk_p(const Iterator& first, const Iterator& last, Fn&& fn) -{ - assert(&first.impl() == &last.impl()); - return first.impl().for_each_chunk_p( - first.index(), last.index(), std::forward<Fn>(fn)); -} - -template <typename T, typename Fn> -bool for_each_chunk_p(const T* first, const T* last, Fn&& fn) -{ - return std::forward<Fn>(fn)(first, last); -} - -/*! - * Equivalent of `std::accumulate` applied to the range `r`. - */ -template <typename Range, typename T> -T accumulate(Range&& r, T init) -{ - for_each_chunk(r, [&](auto first, auto last) { - init = std::accumulate(first, last, init); - }); - return init; -} - -template <typename Range, typename T, typename Fn> -T accumulate(Range&& r, T init, Fn fn) -{ - for_each_chunk(r, [&](auto first, auto last) { - init = std::accumulate(first, last, init, fn); - }); - return init; -} - -/*! - * Equivalent of `std::accumulate` applied to the range @f$ [first, - * last) @f$. - */ -template <typename Iterator, typename T> -T accumulate(Iterator first, Iterator last, T init) -{ - for_each_chunk(first, last, [&](auto first, auto last) { - init = std::accumulate(first, last, init); - }); - return init; -} - -template <typename Iterator, typename T, typename Fn> -T accumulate(Iterator first, Iterator last, T init, Fn fn) -{ - for_each_chunk(first, last, [&](auto first, auto last) { - init = std::accumulate(first, last, init, fn); - }); - return init; -} - -/*! - * Equivalent of `std::for_each` applied to the range `r`. - */ -template <typename Range, typename Fn> -Fn&& for_each(Range&& r, Fn&& fn) -{ - for_each_chunk(r, [&](auto first, auto last) { - for (; first != last; ++first) - fn(*first); - }); - return std::forward<Fn>(fn); -} - -/*! - * Equivalent of `std::for_each` applied to the range @f$ [first, - * last) @f$. - */ -template <typename Iterator, typename Fn> -Fn&& for_each(Iterator first, Iterator last, Fn&& fn) -{ - for_each_chunk(first, last, [&](auto first, auto last) { - for (; first != last; ++first) - fn(*first); - }); - return std::forward<Fn>(fn); -} - -/*! - * Equivalent of `std::copy` applied to the range `r`. - */ -template <typename Range, typename OutIter> -OutIter copy(Range&& r, OutIter out) -{ - for_each_chunk( - r, [&](auto first, auto last) { out = std::copy(first, last, out); }); - return out; -} - -/*! - * Equivalent of `std::copy` applied to the range @f$ [first, - * last) @f$. - */ -template <typename InIter, typename OutIter> -OutIter copy(InIter first, InIter last, OutIter out) -{ - for_each_chunk(first, last, [&](auto first, auto last) { - out = std::copy(first, last, out); - }); - return out; -} - -/*! - * Equivalent of `std::all_of` applied to the range `r`. - */ -template <typename Range, typename Pred> -bool all_of(Range&& r, Pred p) -{ - return for_each_chunk_p( - r, [&](auto first, auto last) { return std::all_of(first, last, p); }); -} - -/*! - * Equivalent of `std::all_of` applied to the range @f$ [first, last) - * @f$. - */ -template <typename Iter, typename Pred> -bool all_of(Iter first, Iter last, Pred p) -{ - return for_each_chunk_p(first, last, [&](auto first, auto last) { - return std::all_of(first, last, p); - }); -} - -/** @} */ // group: algorithm - -} // namespace immer diff --git a/third_party/immer/immer/array.hpp b/third_party/immer/immer/array.hpp deleted file mode 100644 index 24c8f84c8bbf..000000000000 --- a/third_party/immer/immer/array.hpp +++ /dev/null @@ -1,364 +0,0 @@ -// -// 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 <immer/detail/arrays/with_capacity.hpp> -#include <immer/memory_policy.hpp> - -namespace immer { - -template <typename T, typename MemoryPolicy> -class array_transient; - -/*! - * Immutable container that stores a sequence of elements in - * contiguous memory. - * - * @tparam T The type of the values to be stored in the container. - * - * @rst - * - * It supports the most efficient iteration and random access, - * equivalent to a ``std::vector`` or ``std::array``, but all - * manipulations are :math:`O(size)`. - * - * .. tip:: Don't be fooled by the bad complexity of this data - * structure. It is a great choice for short sequence or when it - * is seldom or never changed. This depends on the ``sizeof(T)`` - * and the expensiveness of its ``T``'s copy constructor, in case - * of doubt, measure. For basic types, using an `array` when - * :math:`n < 100` is a good heuristic. - * - * @endrst - */ -template <typename T, typename MemoryPolicy = default_memory_policy> -class array -{ - using impl_t = - std::conditional_t<MemoryPolicy::use_transient_rvalues, - detail::arrays::with_capacity<T, MemoryPolicy>, - detail::arrays::no_capacity<T, MemoryPolicy>>; - - using move_t = - std::integral_constant<bool, MemoryPolicy::use_transient_rvalues>; - -public: - using value_type = T; - using reference = const T&; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using const_reference = const T&; - - using iterator = const T*; - using const_iterator = iterator; - using reverse_iterator = std::reverse_iterator<iterator>; - - using memory_policy = MemoryPolicy; - using transient_type = array_transient<T, MemoryPolicy>; - - /*! - * Default constructor. It creates an array of `size() == 0`. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - array() = default; - - /*! - * Constructs an array containing the elements in `values`. - */ - array(std::initializer_list<T> values) - : impl_{impl_t::from_initializer_list(values)} - {} - - /*! - * Constructs a array containing the elements in the range - * defined by the forward iterator `first` and range sentinel `last`. - */ - template <typename Iter, - typename Sent, - std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent> && - detail::is_forward_iterator_v<Iter>, - bool> = true> - array(Iter first, Sent last) - : impl_{impl_t::from_range(first, last)} - {} - - /*! - * Constructs a array containing the element `val` repeated `n` - * times. - */ - array(size_type n, T v = {}) - : impl_{impl_t::from_fill(n, v)} - {} - - /*! - * Returns an iterator pointing at the first element of the - * collection. It does not allocate memory and its complexity is - * @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator begin() const { return impl_.data(); } - - /*! - * Returns an iterator pointing just after the last element of the - * collection. It does not allocate and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator end() const { return impl_.data() + impl_.size; } - - /*! - * Returns an iterator that traverses the collection backwards, - * pointing at the first element of the reversed collection. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD reverse_iterator rbegin() const - { - return reverse_iterator{end()}; - } - - /*! - * Returns an iterator that traverses the collection backwards, - * pointing after the last element of the reversed collection. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD reverse_iterator rend() const - { - return reverse_iterator{begin()}; - } - - /*! - * Returns the number of elements in the container. It does - * not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD std::size_t size() const { return impl_.size; } - - /*! - * Returns `true` if there are no elements in the container. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD bool empty() const { return impl_.size == 0; } - - /*! - * Access the raw data. - */ - IMMER_NODISCARD const T* data() const { return impl_.data(); } - - /*! - * Access the last element. - */ - IMMER_NODISCARD const T& back() const { return data()[size() - 1]; } - - /*! - * Access the first element. - */ - IMMER_NODISCARD const T& front() const { return data()[0]; } - - /*! - * Returns a `const` reference to the element at position `index`. - * It is undefined when @f$ 0 index \geq size() @f$. It does not - * allocate memory and its complexity is *effectively* @f$ O(1) - * @f$. - */ - IMMER_NODISCARD reference operator[](size_type index) const - { - return impl_.get(index); - } - - /*! - * Returns a `const` reference to the element at position - * `index`. It throws an `std::out_of_range` exception when @f$ - * index \geq size() @f$. It does not allocate memory and its - * complexity is *effectively* @f$ O(1) @f$. - */ - reference at(size_type index) const { return impl_.get_check(index); } - - /*! - * Returns whether the vectors are equal. - */ - IMMER_NODISCARD bool operator==(const array& other) const - { - return impl_.equals(other.impl_); - } - IMMER_NODISCARD bool operator!=(const array& other) const - { - return !(*this == other); - } - - /*! - * Returns an array with `value` inserted at the end. It may - * allocate memory and its complexity is @f$ O(size) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/array/array.cpp - * :language: c++ - * :dedent: 8 - * :start-after: push-back/start - * :end-before: push-back/end - * - * @endrst - */ - IMMER_NODISCARD array push_back(value_type value) const& - { - return impl_.push_back(std::move(value)); - } - - IMMER_NODISCARD decltype(auto) push_back(value_type value) && - { - return push_back_move(move_t{}, std::move(value)); - } - - /*! - * Returns an array containing value `value` at position `idx`. - * Undefined for `index >= size()`. - * It may allocate memory and its complexity is @f$ O(size) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/array/array.cpp - * :language: c++ - * :dedent: 8 - * :start-after: set/start - * :end-before: set/end - * - * @endrst - */ - IMMER_NODISCARD array set(std::size_t index, value_type value) const& - { - return impl_.assoc(index, std::move(value)); - } - - IMMER_NODISCARD decltype(auto) set(size_type index, value_type value) && - { - return set_move(move_t{}, index, std::move(value)); - } - - /*! - * Returns an array containing the result of the expression - * `fn((*this)[idx])` at position `idx`. - * Undefined for `index >= size()`. - * It may allocate memory and its complexity is @f$ O(size) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/array/array.cpp - * :language: c++ - * :dedent: 8 - * :start-after: update/start - * :end-before: update/end - * - * @endrst - */ - template <typename FnT> - IMMER_NODISCARD array update(std::size_t index, FnT&& fn) const& - { - return impl_.update(index, std::forward<FnT>(fn)); - } - - template <typename FnT> - IMMER_NODISCARD decltype(auto) update(size_type index, FnT&& fn) && - { - return update_move(move_t{}, index, std::forward<FnT>(fn)); - } - - /*! - * Returns a array containing only the first `min(elems, size())` - * elements. It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/array/array.cpp - * :language: c++ - * :dedent: 8 - * :start-after: take/start - * :end-before: take/end - * - * @endrst - */ - IMMER_NODISCARD array take(size_type elems) const& - { - return impl_.take(elems); - } - - IMMER_NODISCARD decltype(auto) take(size_type elems) && - { - return take_move(move_t{}, elems); - } - - /*! - * Returns an @a transient form of this container, an - * `immer::array_transient`. - */ - IMMER_NODISCARD transient_type transient() const& - { - return transient_type{impl_}; - } - IMMER_NODISCARD transient_type transient() && - { - return transient_type{std::move(impl_)}; - } - - // Semi-private - const impl_t& impl() const { return impl_; } - -private: - friend transient_type; - - array(impl_t impl) - : impl_(std::move(impl)) - {} - - array&& push_back_move(std::true_type, value_type value) - { - impl_.push_back_mut({}, std::move(value)); - return std::move(*this); - } - array push_back_move(std::false_type, value_type value) - { - return impl_.push_back(std::move(value)); - } - - array&& set_move(std::true_type, size_type index, value_type value) - { - impl_.assoc_mut({}, index, std::move(value)); - return std::move(*this); - } - array set_move(std::false_type, size_type index, value_type value) - { - return impl_.assoc(index, std::move(value)); - } - - template <typename Fn> - array&& update_move(std::true_type, size_type index, Fn&& fn) - { - impl_.update_mut({}, index, std::forward<Fn>(fn)); - return std::move(*this); - } - template <typename Fn> - array update_move(std::false_type, size_type index, Fn&& fn) - { - return impl_.update(index, std::forward<Fn>(fn)); - } - - array&& take_move(std::true_type, size_type elems) - { - impl_.take_mut({}, elems); - return std::move(*this); - } - array take_move(std::false_type, size_type elems) - { - return impl_.take(elems); - } - - impl_t impl_ = impl_t::empty(); -}; - -} /* namespace immer */ diff --git a/third_party/immer/immer/array_transient.hpp b/third_party/immer/immer/array_transient.hpp deleted file mode 100644 index bc2d1a5b7b2c..000000000000 --- a/third_party/immer/immer/array_transient.hpp +++ /dev/null @@ -1,202 +0,0 @@ -// -// 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 <immer/detail/arrays/with_capacity.hpp> -#include <immer/memory_policy.hpp> - -namespace immer { - -template <typename T, typename MemoryPolicy> -class array; - -/*! - * Mutable version of `immer::array`. - * - * @rst - * - * Refer to :doc:`transients` to learn more about when and how to use - * the mutable versions of immutable containers. - * - * @endrst - */ -template <typename T, typename MemoryPolicy = default_memory_policy> -class array_transient : MemoryPolicy::transience_t::owner -{ - using impl_t = detail::arrays::with_capacity<T, MemoryPolicy>; - using impl_no_capacity_t = detail::arrays::no_capacity<T, MemoryPolicy>; - using owner_t = typename MemoryPolicy::transience_t::owner; - -public: - using value_type = T; - using reference = const T&; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using const_reference = const T&; - - using iterator = const T*; - using const_iterator = iterator; - using reverse_iterator = std::reverse_iterator<iterator>; - - using memory_policy = MemoryPolicy; - using persistent_type = array<T, MemoryPolicy>; - - /*! - * Default constructor. It creates a mutable array of `size() == - * 0`. It does not allocate memory and its complexity is - * @f$ O(1) @f$. - */ - array_transient() = default; - - /*! - * Returns an iterator pointing at the first element of the - * collection. It does not allocate memory and its complexity is - * @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator begin() const { return impl_.data(); } - - /*! - * Returns an iterator pointing just after the last element of the - * collection. It does not allocate and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator end() const { return impl_.data() + impl_.size; } - - /*! - * Returns an iterator that traverses the collection backwards, - * pointing at the first element of the reversed collection. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD reverse_iterator rbegin() const - { - return reverse_iterator{end()}; - } - - /*! - * Returns an iterator that traverses the collection backwards, - * pointing after the last element of the reversed collection. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD reverse_iterator rend() const - { - return reverse_iterator{begin()}; - } - - /*! - * Returns the number of elements in the container. It does - * not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD std::size_t size() const { return impl_.size; } - - /*! - * Returns `true` if there are no elements in the container. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD bool empty() const { return impl_.size == 0; } - - /*! - * Access the raw data. - */ - IMMER_NODISCARD const T* data() const { return impl_.data(); } - - /*! - * Provide mutable access to the raw underlaying data. - */ - IMMER_NODISCARD T* data_mut() { return impl_.data_mut(*this); } - - /*! - * Access the last element. - */ - IMMER_NODISCARD const T& back() const { return data()[size() - 1]; } - - /*! - * Access the first element. - */ - IMMER_NODISCARD const T& front() const { return data()[0]; } - - /*! - * Returns a `const` reference to the element at position `index`. - * It is undefined when @f$ 0 index \geq size() @f$. It does not - * allocate memory and its complexity is *effectively* @f$ O(1) - * @f$. - */ - reference operator[](size_type index) const { return impl_.get(index); } - - /*! - * Returns a `const` reference to the element at position - * `index`. It throws an `std::out_of_range` exception when @f$ - * index \geq size() @f$. It does not allocate memory and its - * complexity is *effectively* @f$ O(1) @f$. - */ - reference at(size_type index) const { return impl_.get_check(index); } - - /*! - * Inserts `value` at the end. It may allocate memory and its - * complexity is *effectively* @f$ O(1) @f$. - */ - void push_back(value_type value) - { - impl_.push_back_mut(*this, std::move(value)); - } - - /*! - * Sets to the value `value` at position `idx`. - * Undefined for `index >= size()`. - * It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - */ - void set(size_type index, value_type value) - { - impl_.assoc_mut(*this, index, std::move(value)); - } - - /*! - * Updates the array to contain the result of the expression - * `fn((*this)[idx])` at position `idx`. - * Undefined for `0 >= size()`. - * It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - */ - template <typename FnT> - void update(size_type index, FnT&& fn) - { - impl_.update_mut(*this, index, std::forward<FnT>(fn)); - } - - /*! - * Resizes the array to only contain the first `min(elems, size())` - * elements. It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - */ - void take(size_type elems) { impl_.take_mut(*this, elems); } - - /*! - * Returns an @a immutable form of this container, an - * `immer::array`. - */ - IMMER_NODISCARD persistent_type persistent() & - { - this->owner_t::operator=(owner_t{}); - return persistent_type{impl_}; - } - IMMER_NODISCARD persistent_type persistent() && - { - return persistent_type{std::move(impl_)}; - } - -private: - friend persistent_type; - - array_transient(impl_t impl) - : impl_(std::move(impl)) - {} - - impl_t impl_ = impl_t::empty(); -}; - -} // namespace immer diff --git a/third_party/immer/immer/atom.hpp b/third_party/immer/immer/atom.hpp deleted file mode 100644 index f3ebb5aa1c0e..000000000000 --- a/third_party/immer/immer/atom.hpp +++ /dev/null @@ -1,254 +0,0 @@ -// -// 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 <immer/box.hpp> -#include <immer/refcount/no_refcount_policy.hpp> - -#include <atomic> -#include <type_traits> - -namespace immer { - -namespace detail { - -template <typename T, typename MemoryPolicy> -struct refcount_atom_impl -{ - using box_type = box<T, MemoryPolicy>; - using value_type = T; - using memory_policy = MemoryPolicy; - using spinlock_t = typename MemoryPolicy::refcount::spinlock_type; - using scoped_lock_t = typename spinlock_t::scoped_lock; - - refcount_atom_impl(const refcount_atom_impl&) = delete; - refcount_atom_impl(refcount_atom_impl&&) = delete; - refcount_atom_impl& operator=(const refcount_atom_impl&) = delete; - refcount_atom_impl& operator=(refcount_atom_impl&&) = delete; - - refcount_atom_impl(box_type b) - : impl_{std::move(b)} - {} - - box_type load() const - { - scoped_lock_t lock{lock_}; - return impl_; - } - - void store(box_type b) - { - scoped_lock_t lock{lock_}; - impl_ = std::move(b); - } - - box_type exchange(box_type b) - { - { - scoped_lock_t lock{lock_}; - swap(b, impl_); - } - return b; - } - - template <typename Fn> - box_type update(Fn&& fn) - { - while (true) { - auto oldv = load(); - auto newv = oldv.update(fn); - { - scoped_lock_t lock{lock_}; - if (oldv.impl_ == impl_.impl_) { - impl_ = newv; - return {newv}; - } - } - } - } - -private: - mutable spinlock_t lock_; - box_type impl_; -}; - -template <typename T, typename MemoryPolicy> -struct gc_atom_impl -{ - using box_type = box<T, MemoryPolicy>; - using value_type = T; - using memory_policy = MemoryPolicy; - - static_assert(std::is_same<typename MemoryPolicy::refcount, - no_refcount_policy>::value, - "gc_atom_impl can only be used when there is no refcount!"); - - gc_atom_impl(const gc_atom_impl&) = delete; - gc_atom_impl(gc_atom_impl&&) = delete; - gc_atom_impl& operator=(const gc_atom_impl&) = delete; - gc_atom_impl& operator=(gc_atom_impl&&) = delete; - - gc_atom_impl(box_type b) - : impl_{b.impl_} - {} - - box_type load() const { return {impl_.load()}; } - - void store(box_type b) { impl_.store(b.impl_); } - - box_type exchange(box_type b) { return {impl_.exchange(b.impl_)}; } - - template <typename Fn> - box_type update(Fn&& fn) - { - while (true) { - auto oldv = box_type{impl_.load()}; - auto newv = oldv.update(fn); - if (impl_.compare_exchange_weak(oldv.impl_, newv.impl_)) - return {newv}; - } - } - -private: - std::atomic<typename box_type::holder*> impl_; -}; - -} // namespace detail - -/*! - * Stores for boxed values of type `T` in a thread-safe manner. - * - * @see box - * - * @rst - * - * .. warning:: If memory policy used includes thread unsafe reference counting, - * no no thread safety is assumed, and the atom becomes thread unsafe too! - * - * .. note:: ``box<T>`` provides a value based box of type ``T``, this is, we - * can think about it as a value-based version of ``std::shared_ptr``. In a - * similar fashion, ``atom<T>`` is in spirit the value-based equivalent of - * C++20 ``std::atomic_shared_ptr``. However, the API does not follow - * ``std::atomic`` interface closely, since it attempts to be a higher level - * construction, most similar to Clojure's ``(atom)``. It is remarkable in - * particular that, since ``box<T>`` underlying object is immutable, using - * ``atom<T>`` is fully thread-safe in ways that ``std::atmic_shared_ptr`` is - * not. This is so because dereferencing the underlying pointer in a - * ``std::atomic_share_ptr`` may require further synchronization, in - * particular when invoking non-const methods. - * - * @endrst - */ -template <typename T, typename MemoryPolicy = default_memory_policy> -class atom -{ -public: - using box_type = box<T, MemoryPolicy>; - using value_type = T; - using memory_policy = MemoryPolicy; - - atom(const atom&) = delete; - atom(atom&&) = delete; - void operator=(const atom&) = delete; - void operator=(atom&&) = delete; - - /*! - * Constructs an atom holding a value `b`; - */ - atom(box_type v = {}) - : impl_{std::move(v)} - {} - - /*! - * Sets a new value in the atom. - */ - atom& operator=(box_type b) - { - impl_.store(std::move(b)); - return *this; - } - - /*! - * Reads the currently stored value in a thread-safe manner. - */ - operator box_type() const { return impl_.load(); } - - /*! - * Reads the currently stored value in a thread-safe manner. - */ - operator value_type() const { return *impl_.load(); } - - /*! - * Reads the currently stored value in a thread-safe manner. - */ - IMMER_NODISCARD box_type load() const { return impl_.load(); } - - /*! - * Stores a new value in a thread-safe manner. - */ - void store(box_type b) { impl_.store(std::move(b)); } - - /*! - * Stores a new value and returns the old value, in a thread-safe manner. - */ - IMMER_NODISCARD box_type exchange(box_type b) - { - return impl_.exchange(std::move(b)); - } - - /*! - * Stores the result of applying `fn` to the current value atomically and - * returns the new resulting value. - * - * @rst - * - * .. warning:: ``fn`` must be a pure function and have no side effects! The - * function might be evaluated multiple times when multiple threads - * content to update the value. - * - * @endrst - */ - template <typename Fn> - box_type update(Fn&& fn) - { - return impl_.update(std::forward<Fn>(fn)); - } - -private: - struct get_refcount_atom_impl - { - template <typename U, typename MP> - struct apply - { - using type = detail::refcount_atom_impl<U, MP>; - }; - }; - - struct get_gc_atom_impl - { - template <typename U, typename MP> - struct apply - { - using type = detail::gc_atom_impl<U, MP>; - }; - }; - - // If we are using "real" garbage collection (we assume this when we use - // `no_refcount_policy`), we just store the pointer in an atomic. If we use - // reference counting, we rely on the reference counting spinlock. - using impl_t = typename std::conditional_t< - std::is_same<typename MemoryPolicy::refcount, - no_refcount_policy>::value, - get_gc_atom_impl, - get_refcount_atom_impl>::template apply<T, MemoryPolicy>::type; - - impl_t impl_; -}; - -} // namespace immer diff --git a/third_party/immer/immer/box.hpp b/third_party/immer/immer/box.hpp deleted file mode 100644 index 0fd2961272ca..000000000000 --- a/third_party/immer/immer/box.hpp +++ /dev/null @@ -1,194 +0,0 @@ -// -// 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 <immer/detail/util.hpp> -#include <immer/memory_policy.hpp> - -namespace immer { - -namespace detail { - -template <typename U, typename MP> -struct gc_atom_impl; - -template <typename U, typename MP> -struct refcount_atom_impl; - -} // namespace detail - -/*! - * Immutable box for a single value of type `T`. - * - * The box is always copiable and movable. The `T` copy or move - * operations are never called. Since a box is immutable, copying or - * moving just copy the underlying pointers. - */ -template <typename T, typename MemoryPolicy = default_memory_policy> -class box -{ - friend struct detail::gc_atom_impl<T, MemoryPolicy>; - friend struct detail::refcount_atom_impl<T, MemoryPolicy>; - - struct holder : MemoryPolicy::refcount - { - T value; - - template <typename... Args> - holder(Args&&... args) - : value{std::forward<Args>(args)...} - {} - }; - - using heap = typename MemoryPolicy::heap::type; - - holder* impl_ = nullptr; - - box(holder* impl) - : impl_{impl} - {} - -public: - using value_type = T; - using memory_policy = MemoryPolicy; - - /*! - * Constructs a box holding `T{}`. - */ - box() - : impl_{detail::make<heap, holder>()} - {} - - /*! - * Constructs a box holding `T{arg}` - */ - template <typename Arg, - typename Enable = std::enable_if_t< - !std::is_same<box, std::decay_t<Arg>>::value && - std::is_constructible<T, Arg>::value>> - box(Arg&& arg) - : impl_{detail::make<heap, holder>(std::forward<Arg>(arg))} - {} - - /*! - * Constructs a box holding `T{arg1, arg2, args...}` - */ - template <typename Arg1, typename Arg2, typename... Args> - box(Arg1&& arg1, Arg2&& arg2, Args&&... args) - : impl_{detail::make<heap, holder>(std::forward<Arg1>(arg1), - std::forward<Arg2>(arg2), - std::forward<Args>(args)...)} - {} - - friend void swap(box& a, box& b) - { - using std::swap; - swap(a.impl_, b.impl_); - } - - box(box&& other) { swap(*this, other); } - box(const box& other) - : impl_(other.impl_) - { - impl_->inc(); - } - box& operator=(box&& other) - { - swap(*this, other); - return *this; - } - box& operator=(const box& other) - { - auto aux = other; - swap(*this, aux); - return *this; - } - ~box() - { - if (impl_ && impl_->dec()) { - impl_->~holder(); - heap::deallocate(sizeof(holder), impl_); - } - } - - /*! Query the current value. */ - IMMER_NODISCARD const T& get() const { return impl_->value; } - - /*! Conversion to the boxed type. */ - operator const T&() const { return get(); } - - /*! Access via dereference */ - const T& operator*() const { return get(); } - - /*! Access via pointer member access */ - const T* operator->() const { return &get(); } - - /*! Comparison. */ - IMMER_NODISCARD bool operator==(detail::exact_t<const box&> other) const - { - return impl_ == other.value.impl_ || get() == other.value.get(); - } - // Note that the `exact_t` disambiguates comparisons against `T{}` - // directly. In that case we want to use `operator T&` and - // compare directly. We definitely never want to convert a value - // to a box (which causes an allocation) just to compare it. - IMMER_NODISCARD bool operator!=(detail::exact_t<const box&> other) const - { - return !(*this == other.value); - } - IMMER_NODISCARD bool operator<(detail::exact_t<const box&> other) const - { - return get() < other.value.get(); - } - - /*! - * Returns a new box built by applying the `fn` to the underlying - * value. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/box/box.cpp - * :language: c++ - * :dedent: 8 - * :start-after: update/start - * :end-before: update/end - * - * @endrst - */ - template <typename Fn> - IMMER_NODISCARD box update(Fn&& fn) const& - { - return std::forward<Fn>(fn)(get()); - } - template <typename Fn> - IMMER_NODISCARD box&& update(Fn&& fn) && - { - if (impl_->unique()) - impl_->value = std::forward<Fn>(fn)(std::move(impl_->value)); - else - *this = std::forward<Fn>(fn)(impl_->value); - return std::move(*this); - } -}; - -} // namespace immer - -namespace std { - -template <typename T, typename MP> -struct hash<immer::box<T, MP>> -{ - std::size_t operator()(const immer::box<T, MP>& x) const - { - return std::hash<T>{}(*x); - } -}; - -} // namespace std diff --git a/third_party/immer/immer/config.hpp b/third_party/immer/immer/config.hpp deleted file mode 100644 index 581e905a4de3..000000000000 --- a/third_party/immer/immer/config.hpp +++ /dev/null @@ -1,93 +0,0 @@ -// -// 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 - -#if defined(__has_cpp_attribute) -#if __has_cpp_attribute(nodiscard) -#define IMMER_NODISCARD [[nodiscard]] -#endif -#else -#if _MSVC_LANG >= 201703L -#define IMMER_NODISCARD [[nodiscard]] -#endif -#endif - -#ifndef IMMER_NODISCARD -#define IMMER_NODISCARD -#endif - -#ifndef IMMER_TAGGED_NODE -#ifdef NDEBUG -#define IMMER_TAGGED_NODE 0 -#else -#define IMMER_TAGGED_NODE 1 -#endif -#endif - -#if IMMER_TAGGED_NODE -#define IMMER_ASSERT_TAGGED(assertion) assert(assertion) -#else -#define IMMER_ASSERT_TAGGED(assertion) -#endif - -#ifndef IMMER_DEBUG_TRACES -#define IMMER_DEBUG_TRACES 0 -#endif - -#ifndef IMMER_DEBUG_PRINT -#define IMMER_DEBUG_PRINT 0 -#endif - -#ifndef IMMER_DEBUG_DEEP_CHECK -#define IMMER_DEBUG_DEEP_CHECK 0 -#endif - -#if IMMER_DEBUG_TRACES || IMMER_DEBUG_PRINT -#include <iostream> -#include <prettyprint.hpp> -#endif - -#if IMMER_DEBUG_TRACES -#define IMMER_TRACE(...) std::cout << __VA_ARGS__ << std::endl -#else -#define IMMER_TRACE(...) -#endif -#define IMMER_TRACE_F(...) \ - IMMER_TRACE(__FILE__ << ":" << __LINE__ << ": " << __VA_ARGS__) -#define IMMER_TRACE_E(expr) IMMER_TRACE(" " << #expr << " = " << (expr)) - -#if defined(_MSC_VER) -#define IMMER_UNREACHABLE __assume(false) -#define IMMER_LIKELY(cond) cond -#define IMMER_UNLIKELY(cond) cond -#define IMMER_FORCEINLINE __forceinline -#define IMMER_PREFETCH(p) -#else -#define IMMER_UNREACHABLE __builtin_unreachable() -#define IMMER_LIKELY(cond) __builtin_expect(!!(cond), 1) -#define IMMER_UNLIKELY(cond) __builtin_expect(!!(cond), 0) -#define IMMER_FORCEINLINE inline __attribute__((always_inline)) -#define IMMER_PREFETCH(p) -// #define IMMER_PREFETCH(p) __builtin_prefetch(p) -#endif - -#define IMMER_DESCENT_DEEP 0 - -#ifdef NDEBUG -#define IMMER_ENABLE_DEBUG_SIZE_HEAP 0 -#else -#define IMMER_ENABLE_DEBUG_SIZE_HEAP 1 -#endif - -namespace immer { - -const auto default_bits = 5; -const auto default_free_list_size = 1 << 10; - -} // namespace immer diff --git a/third_party/immer/immer/detail/arrays/no_capacity.hpp b/third_party/immer/immer/detail/arrays/no_capacity.hpp deleted file mode 100644 index 9cb561e14bc1..000000000000 --- a/third_party/immer/immer/detail/arrays/no_capacity.hpp +++ /dev/null @@ -1,203 +0,0 @@ -// -// 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 <immer/algorithm.hpp> -#include <immer/detail/arrays/node.hpp> - -namespace immer { -namespace detail { -namespace arrays { - -template <typename T, typename MemoryPolicy> -struct no_capacity -{ - using node_t = node<T, MemoryPolicy>; - using edit_t = typename MemoryPolicy::transience_t::edit; - using size_t = std::size_t; - - node_t* ptr; - size_t size; - - static const no_capacity& empty() - { - static const no_capacity empty_{ - node_t::make_n(0), - 0, - }; - return empty_; - } - - no_capacity(node_t* p, size_t s) - : ptr{p} - , size{s} - {} - - no_capacity(const no_capacity& other) - : no_capacity{other.ptr, other.size} - { - inc(); - } - - no_capacity(no_capacity&& other) - : no_capacity{empty()} - { - swap(*this, other); - } - - no_capacity& operator=(const no_capacity& other) - { - auto next = other; - swap(*this, next); - return *this; - } - - no_capacity& operator=(no_capacity&& other) - { - swap(*this, other); - return *this; - } - - friend void swap(no_capacity& x, no_capacity& y) - { - using std::swap; - swap(x.ptr, y.ptr); - swap(x.size, y.size); - } - - ~no_capacity() { dec(); } - - void inc() - { - using immer::detail::get; - ptr->refs().inc(); - } - - void dec() - { - using immer::detail::get; - if (ptr->refs().dec()) - node_t::delete_n(ptr, size, size); - } - - T* data() { return ptr->data(); } - const T* data() const { return ptr->data(); } - - T* data_mut(edit_t e) - { - if (!ptr->can_mutate(e)) - ptr = node_t::copy_e(e, size, ptr, size); - return data(); - } - - template <typename Iter, - typename Sent, - std::enable_if_t<is_forward_iterator_v<Iter> && - compatible_sentinel_v<Iter, Sent>, - bool> = true> - static no_capacity from_range(Iter first, Sent last) - { - auto count = static_cast<size_t>(distance(first, last)); - if (count == 0) - return empty(); - else - return { - node_t::copy_n(count, first, last), - count, - }; - } - - static no_capacity from_fill(size_t n, T v) - { - return {node_t::fill_n(n, v), n}; - } - - template <typename U> - static no_capacity from_initializer_list(std::initializer_list<U> values) - { - using namespace std; - return from_range(begin(values), end(values)); - } - - template <typename Fn> - void for_each_chunk(Fn&& fn) const - { - std::forward<Fn>(fn)(data(), data() + size); - } - - template <typename Fn> - bool for_each_chunk_p(Fn&& fn) const - { - return std::forward<Fn>(fn)(data(), data() + size); - } - - const T& get(std::size_t index) const { return data()[index]; } - - const T& get_check(std::size_t index) const - { - if (index >= size) - throw std::out_of_range{"out of range"}; - return data()[index]; - } - - bool equals(const no_capacity& other) const - { - return ptr == other.ptr || - (size == other.size && - std::equal(data(), data() + size, other.data())); - } - - no_capacity push_back(T value) const - { - auto p = node_t::copy_n(size + 1, ptr, size); - try { - new (p->data() + size) T{std::move(value)}; - return {p, size + 1}; - } catch (...) { - node_t::delete_n(p, size, size + 1); - throw; - } - } - - no_capacity assoc(std::size_t idx, T value) const - { - auto p = node_t::copy_n(size, ptr, size); - try { - p->data()[idx] = std::move(value); - return {p, size}; - } catch (...) { - node_t::delete_n(p, size, size); - throw; - } - } - - template <typename Fn> - no_capacity update(std::size_t idx, Fn&& op) const - { - auto p = node_t::copy_n(size, ptr, size); - try { - auto& elem = p->data()[idx]; - elem = std::forward<Fn>(op)(std::move(elem)); - return {p, size}; - } catch (...) { - node_t::delete_n(p, size, size); - throw; - } - } - - no_capacity take(std::size_t sz) const - { - auto p = node_t::copy_n(sz, ptr, sz); - return {p, sz}; - } -}; - -} // namespace arrays -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/arrays/node.hpp b/third_party/immer/immer/detail/arrays/node.hpp deleted file mode 100644 index f96a63a9f4af..000000000000 --- a/third_party/immer/immer/detail/arrays/node.hpp +++ /dev/null @@ -1,127 +0,0 @@ -// -// 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 <immer/detail/combine_standard_layout.hpp> -#include <immer/detail/type_traits.hpp> -#include <immer/detail/util.hpp> - -#include <limits> - -namespace immer { -namespace detail { -namespace arrays { - -template <typename T, typename MemoryPolicy> -struct node -{ - using memory = MemoryPolicy; - using heap = typename MemoryPolicy::heap::type; - using transience = typename memory::transience_t; - using refs_t = typename memory::refcount; - using ownee_t = typename transience::ownee; - using node_t = node; - using edit_t = typename transience::edit; - - struct data_t - { - aligned_storage_for<T> buffer; - }; - - using impl_t = combine_standard_layout_t<data_t, refs_t, ownee_t>; - - impl_t impl; - - constexpr static std::size_t sizeof_n(size_t count) - { - return immer_offsetof(impl_t, d.buffer) + - sizeof(T) * (count == 0 ? 1 : count); - } - - refs_t& refs() const { return auto_const_cast(get<refs_t>(impl)); } - - const ownee_t& ownee() const { return get<ownee_t>(impl); } - ownee_t& ownee() { return get<ownee_t>(impl); } - - const T* data() const { return reinterpret_cast<const T*>(&impl.d.buffer); } - T* data() { return reinterpret_cast<T*>(&impl.d.buffer); } - - bool can_mutate(edit_t e) const - { - return refs().unique() || ownee().can_mutate(e); - } - - static void delete_n(node_t* p, size_t sz, size_t cap) - { - destroy_n(p->data(), sz); - heap::deallocate(sizeof_n(cap), p); - } - - static node_t* make_n(size_t n) - { - return new (heap::allocate(sizeof_n(n))) node_t{}; - } - - static node_t* make_e(edit_t e, size_t n) - { - auto p = make_n(n); - p->ownee() = e; - return p; - } - - static node_t* fill_n(size_t n, T v) - { - auto p = make_n(n); - try { - std::uninitialized_fill_n(p->data(), n, v); - return p; - } catch (...) { - heap::deallocate(sizeof_n(n), p); - throw; - } - } - - template <typename Iter, - typename Sent, - std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent>, - bool> = true> - static node_t* copy_n(size_t n, Iter first, Sent last) - { - auto p = make_n(n); - try { - uninitialized_copy(first, last, p->data()); - return p; - } catch (...) { - heap::deallocate(sizeof_n(n), p); - throw; - } - } - - static node_t* copy_n(size_t n, node_t* p, size_t count) - { - return copy_n(n, p->data(), p->data() + count); - } - - template <typename Iter> - static node_t* copy_e(edit_t e, size_t n, Iter first, Iter last) - { - auto p = copy_n(n, first, last); - p->ownee() = e; - return p; - } - - static node_t* copy_e(edit_t e, size_t n, node_t* p, size_t count) - { - return copy_e(e, n, p->data(), p->data() + count); - } -}; - -} // namespace arrays -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/arrays/with_capacity.hpp b/third_party/immer/immer/detail/arrays/with_capacity.hpp deleted file mode 100644 index 290809e4b6e5..000000000000 --- a/third_party/immer/immer/detail/arrays/with_capacity.hpp +++ /dev/null @@ -1,303 +0,0 @@ -// -// 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 <immer/detail/arrays/no_capacity.hpp> - -namespace immer { -namespace detail { -namespace arrays { - -template <typename T, typename MemoryPolicy> -struct with_capacity -{ - using no_capacity_t = no_capacity<T, MemoryPolicy>; - - using node_t = node<T, MemoryPolicy>; - using edit_t = typename MemoryPolicy::transience_t::edit; - using size_t = std::size_t; - - node_t* ptr; - size_t size; - size_t capacity; - - static const with_capacity& empty() - { - static const with_capacity empty_{node_t::make_n(1), 0, 1}; - return empty_; - } - - with_capacity(node_t* p, size_t s, size_t c) - : ptr{p} - , size{s} - , capacity{c} - {} - - with_capacity(const with_capacity& other) - : with_capacity{other.ptr, other.size, other.capacity} - { - inc(); - } - - with_capacity(const no_capacity_t& other) - : with_capacity{other.ptr, other.size, other.size} - { - inc(); - } - - with_capacity(with_capacity&& other) - : with_capacity{empty()} - { - swap(*this, other); - } - - with_capacity& operator=(const with_capacity& other) - { - auto next = other; - swap(*this, next); - return *this; - } - - with_capacity& operator=(with_capacity&& other) - { - swap(*this, other); - return *this; - } - - friend void swap(with_capacity& x, with_capacity& y) - { - using std::swap; - swap(x.ptr, y.ptr); - swap(x.size, y.size); - swap(x.capacity, y.capacity); - } - - ~with_capacity() { dec(); } - - void inc() - { - using immer::detail::get; - ptr->refs().inc(); - } - - void dec() - { - using immer::detail::get; - if (ptr->refs().dec()) - node_t::delete_n(ptr, size, capacity); - } - - const T* data() const { return ptr->data(); } - T* data() { return ptr->data(); } - - T* data_mut(edit_t e) - { - if (!ptr->can_mutate(e)) { - auto p = node_t::copy_e(e, capacity, ptr, size); - dec(); - ptr = p; - } - return data(); - } - - operator no_capacity_t() const - { - if (size == capacity) { - ptr->refs().inc(); - return {ptr, size}; - } else { - return {node_t::copy_n(size, ptr, size), size}; - } - } - - template <typename Iter, - typename Sent, - std::enable_if_t<is_forward_iterator_v<Iter> && - compatible_sentinel_v<Iter, Sent>, - bool> = true> - static with_capacity from_range(Iter first, Sent last) - { - auto count = static_cast<size_t>(distance(first, last)); - if (count == 0) - return empty(); - else - return {node_t::copy_n(count, first, last), count, count}; - } - - template <typename U> - static with_capacity from_initializer_list(std::initializer_list<U> values) - { - using namespace std; - return from_range(begin(values), end(values)); - } - - static with_capacity from_fill(size_t n, T v) - { - return {node_t::fill_n(n, v), n, n}; - } - - template <typename Fn> - void for_each_chunk(Fn&& fn) const - { - std::forward<Fn>(fn)(data(), data() + size); - } - - template <typename Fn> - bool for_each_chunk_p(Fn&& fn) const - { - return std::forward<Fn>(fn)(data(), data() + size); - } - - const T& get(std::size_t index) const { return data()[index]; } - - const T& get_check(std::size_t index) const - { - if (index >= size) - throw std::out_of_range{"out of range"}; - return data()[index]; - } - - bool equals(const with_capacity& other) const - { - return ptr == other.ptr || - (size == other.size && - std::equal(data(), data() + size, other.data())); - } - - static size_t recommend_up(size_t sz, size_t cap) - { - auto max = std::numeric_limits<size_t>::max(); - return sz <= cap ? cap - : cap >= max / 2 ? max - /* otherwise */ - : std::max(2 * cap, sz); - } - - static size_t recommend_down(size_t sz, size_t cap) - { - return sz == 0 ? 1 - : sz < cap / 2 ? sz * 2 : - /* otherwise */ cap; - } - - with_capacity push_back(T value) const - { - auto cap = recommend_up(size + 1, capacity); - auto p = node_t::copy_n(cap, ptr, size); - try { - new (p->data() + size) T{std::move(value)}; - return {p, size + 1, cap}; - } catch (...) { - node_t::delete_n(p, size, cap); - throw; - } - } - - void push_back_mut(edit_t e, T value) - { - if (ptr->can_mutate(e) && capacity > size) { - new (data() + size) T{std::move(value)}; - ++size; - } else { - auto cap = recommend_up(size + 1, capacity); - auto p = node_t::copy_e(e, cap, ptr, size); - try { - new (p->data() + size) T{std::move(value)}; - *this = {p, size + 1, cap}; - } catch (...) { - node_t::delete_n(p, size, cap); - throw; - } - } - } - - with_capacity assoc(std::size_t idx, T value) const - { - auto p = node_t::copy_n(capacity, ptr, size); - try { - p->data()[idx] = std::move(value); - return {p, size, capacity}; - } catch (...) { - node_t::delete_n(p, size, capacity); - throw; - } - } - - void assoc_mut(edit_t e, std::size_t idx, T value) - { - if (ptr->can_mutate(e)) { - data()[idx] = std::move(value); - } else { - auto p = node_t::copy_n(capacity, ptr, size); - try { - p->data()[idx] = std::move(value); - *this = {p, size, capacity}; - } catch (...) { - node_t::delete_n(p, size, capacity); - throw; - } - } - } - - template <typename Fn> - with_capacity update(std::size_t idx, Fn&& op) const - { - auto p = node_t::copy_n(capacity, ptr, size); - try { - auto& elem = p->data()[idx]; - elem = std::forward<Fn>(op)(std::move(elem)); - return {p, size, capacity}; - } catch (...) { - node_t::delete_n(p, size, capacity); - throw; - } - } - - template <typename Fn> - void update_mut(edit_t e, std::size_t idx, Fn&& op) - { - if (ptr->can_mutate(e)) { - auto& elem = data()[idx]; - elem = std::forward<Fn>(op)(std::move(elem)); - } else { - auto p = node_t::copy_e(e, capacity, ptr, size); - try { - auto& elem = p->data()[idx]; - elem = std::forward<Fn>(op)(std::move(elem)); - *this = {p, size, capacity}; - } catch (...) { - node_t::delete_n(p, size, capacity); - throw; - } - } - } - - with_capacity take(std::size_t sz) const - { - auto cap = recommend_down(sz, capacity); - auto p = node_t::copy_n(cap, ptr, sz); - return {p, sz, cap}; - } - - void take_mut(edit_t e, std::size_t sz) - { - if (ptr->can_mutate(e)) { - destroy_n(data() + size, size - sz); - size = sz; - } else { - auto cap = recommend_down(sz, capacity); - auto p = node_t::copy_e(e, cap, ptr, sz); - *this = {p, sz, cap}; - } - } -}; - -} // namespace arrays -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/combine_standard_layout.hpp b/third_party/immer/immer/detail/combine_standard_layout.hpp deleted file mode 100644 index 55c69bc91682..000000000000 --- a/third_party/immer/immer/detail/combine_standard_layout.hpp +++ /dev/null @@ -1,235 +0,0 @@ -// -// 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 <type_traits> - -#if defined(__GNUC__) && __GNUC__ == 7 && __GNUC_MINOR__ == 1 -#define IMMER_BROKEN_STANDARD_LAYOUT_DETECTION 1 -#define immer_offsetof(st, m) ((std::size_t) & (((st*) 0)->m)) -#else -#define IMMER_BROKEN_STANDARD_LAYOUT_DETECTION 0 -#define immer_offsetof offsetof -#endif - -namespace immer { -namespace detail { - -// -// Metafunction that returns a standard layout struct that combines -// all the standard layout types in `Ts...`, while making sure that -// empty base optimizations are used. -// -// To query a part of the type do `get<my_part>(x)`; -// -// This is useful when putting together a type that merges various -// types coming from different policies. Some of them might be empty, -// so we shall enable empty base optimizations. But if we just -// inherit from all of them, we would break the "standard layout" -// rules, preventing us from using `offseof(...)`. So metafunction -// will generate the type by sometimes inheriting, sometimes adding as -// member. -// -// Note that the types are added to the combined type from right to -// left! -// -template <typename... Ts> -struct combine_standard_layout; - -template <typename... Ts> -using combine_standard_layout_t = typename combine_standard_layout<Ts...>::type; - -namespace csl { - -template <typename T> -struct type_t -{}; - -template <typename U, typename T> -U& get(T& x); - -template <typename U, typename T> -const U& get(const T& x); - -template <typename T, typename Next = void> -struct inherit -{ - struct type - : T - , Next - { - using Next::get_; - - template <typename U> - friend decltype(auto) get(type& x) - { - return x.get_(type_t<U>{}); - } - template <typename U> - friend decltype(auto) get(const type& x) - { - return x.get_(type_t<U>{}); - } - - T& get_(type_t<T>) { return *this; } - const T& get_(type_t<T>) const { return *this; } - }; -}; - -template <typename T> -struct inherit<T, void> -{ - struct type : T - { - template <typename U> - friend decltype(auto) get(type& x) - { - return x.get_(type_t<U>{}); - } - template <typename U> - friend decltype(auto) get(const type& x) - { - return x.get_(type_t<U>{}); - } - - T& get_(type_t<T>) { return *this; } - const T& get_(type_t<T>) const { return *this; } - }; -}; - -template <typename T, typename Next = void> -struct member -{ - struct type : Next - { - T d; - - using Next::get_; - - template <typename U> - friend decltype(auto) get(type& x) - { - return x.get_(type_t<U>{}); - } - template <typename U> - friend decltype(auto) get(const type& x) - { - return x.get_(type_t<U>{}); - } - - T& get_(type_t<T>) { return d; } - const T& get_(type_t<T>) const { return d; } - }; -}; - -template <typename T> -struct member<T, void> -{ - struct type - { - T d; - - template <typename U> - friend decltype(auto) get(type& x) - { - return x.get_(type_t<U>{}); - } - template <typename U> - friend decltype(auto) get(const type& x) - { - return x.get_(type_t<U>{}); - } - - T& get_(type_t<T>) { return d; } - const T& get_(type_t<T>) const { return d; } - }; -}; - -template <typename T, typename Next> -struct member_two -{ - struct type - { - Next n; - T d; - - template <typename U> - friend decltype(auto) get(type& x) - { - return x.get_(type_t<U>{}); - } - template <typename U> - friend decltype(auto) get(const type& x) - { - return x.get_(type_t<U>{}); - } - - T& get_(type_t<T>) { return d; } - const T& get_(type_t<T>) const { return d; } - - template <typename U> - auto get_(type_t<U> t) -> decltype(auto) - { - return n.get_(t); - } - template <typename U> - auto get_(type_t<U> t) const -> decltype(auto) - { - return n.get_(t); - } - }; -}; - -template <typename... Ts> -struct combine_standard_layout_aux; - -template <typename T> -struct combine_standard_layout_aux<T> -{ - static_assert(std::is_standard_layout<T>::value, ""); - - using type = typename std::conditional_t<std::is_empty<T>::value, - csl::inherit<T>, - csl::member<T>>::type; -}; - -template <typename T, typename... Ts> -struct combine_standard_layout_aux<T, Ts...> -{ - static_assert(std::is_standard_layout<T>::value, ""); - - using this_t = T; - using next_t = typename combine_standard_layout_aux<Ts...>::type; - - static constexpr auto empty_this = std::is_empty<this_t>::value; - static constexpr auto empty_next = std::is_empty<next_t>::value; - - using type = typename std::conditional_t< - empty_this, - inherit<this_t, next_t>, - std::conditional_t<empty_next, - member<this_t, next_t>, - member_two<this_t, next_t>>>::type; -}; - -} // namespace csl - -using csl::get; - -template <typename... Ts> -struct combine_standard_layout -{ - using type = typename csl::combine_standard_layout_aux<Ts...>::type; -#if !IMMER_BROKEN_STANDARD_LAYOUT_DETECTION - static_assert(std::is_standard_layout<type>::value, ""); -#endif -}; - -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/hamts/bits.hpp b/third_party/immer/immer/detail/hamts/bits.hpp deleted file mode 100644 index b02caf770666..000000000000 --- a/third_party/immer/immer/detail/hamts/bits.hpp +++ /dev/null @@ -1,108 +0,0 @@ -// -// 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 <cstdint> - -#if defined(_MSC_VER) -#include <intrin.h> // __popcnt -#endif - -namespace immer { -namespace detail { -namespace hamts { - -using size_t = std::size_t; -using hash_t = std::size_t; -using bits_t = std::uint32_t; -using count_t = std::uint32_t; -using shift_t = std::uint32_t; - -template <bits_t B> -struct get_bitmap_type -{ - static_assert(B < 6u, "B > 6 is not supported."); - - using type = std::uint32_t; -}; - -template <> -struct get_bitmap_type<6u> -{ - using type = std::uint64_t; -}; - -template <bits_t B, typename T = count_t> -constexpr T branches = T{1u} << B; - -template <bits_t B, typename T = size_t> -constexpr T mask = branches<B, T> - 1u; - -template <bits_t B, typename T = count_t> -constexpr T max_depth = (sizeof(hash_t) * 8u + B - 1u) / B; - -template <bits_t B, typename T = count_t> -constexpr T max_shift = max_depth<B, count_t>* B; - -#define IMMER_HAS_BUILTIN_POPCOUNT 1 - -inline auto popcount_fallback(std::uint32_t x) -{ - // More alternatives: - // https://en.wikipedia.org/wiki/Hamming_weight - // http://wm.ite.pl/articles/sse-popcount.html - // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel - x = x - ((x >> 1) & 0x55555555u); - x = (x & 0x33333333u) + ((x >> 2) & 0x33333333u); - return (((x + (x >> 4u)) & 0xF0F0F0Fu) * 0x1010101u) >> 24u; -} - -inline auto popcount_fallback(std::uint64_t x) -{ - x = x - ((x >> 1) & 0x5555555555555555u); - x = (x & 0x3333333333333333u) + ((x >> 2u) & 0x3333333333333333u); - return (((x + (x >> 4)) & 0x0F0F0F0F0F0F0F0Fu) * 0x0101010101010101u) >> - 56u; -} - -inline count_t popcount(std::uint32_t x) -{ -#if IMMER_HAS_BUILTIN_POPCOUNT -#if defined(_MSC_VER) - return __popcnt(x); -#else - return __builtin_popcount(x); -#endif -#else - return popcount_fallback(x); -#endif -} - -inline count_t popcount(std::uint64_t x) -{ -#if IMMER_HAS_BUILTIN_POPCOUNT -#if defined(_MSC_VER) -#if defined(_WIN64) - return __popcnt64(x); -#else - // TODO: benchmark against popcount_fallback(std::uint64_t x) - return popcount(static_cast<std::uint32_t>(x >> 32)) + - popcount(static_cast<std::uint32_t>(x)); -#endif -#else - return __builtin_popcountll(x); -#endif -#else - return popcount_fallback(x); -#endif -} - -} // namespace hamts -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/hamts/champ.hpp b/third_party/immer/immer/detail/hamts/champ.hpp deleted file mode 100644 index e3b55d397c72..000000000000 --- a/third_party/immer/immer/detail/hamts/champ.hpp +++ /dev/null @@ -1,473 +0,0 @@ -// -// 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 <immer/config.hpp> -#include <immer/detail/hamts/node.hpp> - -#include <algorithm> - -namespace immer { -namespace detail { -namespace hamts { - -template <typename T, - typename Hash, - typename Equal, - typename MemoryPolicy, - bits_t B> -struct champ -{ - static constexpr auto bits = B; - - using node_t = node<T, Hash, Equal, MemoryPolicy, B>; - using bitmap_t = typename get_bitmap_type<B>::type; - - static_assert(branches<B> <= sizeof(bitmap_t) * 8, ""); - - node_t* root; - size_t size; - - static const champ& empty() - { - static const champ empty_{ - node_t::make_inner_n(0), - 0, - }; - return empty_; - } - - champ(node_t* r, size_t sz) - : root{r} - , size{sz} - {} - - champ(const champ& other) - : champ{other.root, other.size} - { - inc(); - } - - champ(champ&& other) - : champ{empty()} - { - swap(*this, other); - } - - champ& operator=(const champ& other) - { - auto next = other; - swap(*this, next); - return *this; - } - - champ& operator=(champ&& other) - { - swap(*this, other); - return *this; - } - - friend void swap(champ& x, champ& y) - { - using std::swap; - swap(x.root, y.root); - swap(x.size, y.size); - } - - ~champ() { dec(); } - - void inc() const { root->inc(); } - - void dec() const - { - if (root->dec()) - node_t::delete_deep(root, 0); - } - - template <typename Fn> - void for_each_chunk(Fn&& fn) const - { - for_each_chunk_traversal(root, 0, fn); - } - - template <typename Fn> - void for_each_chunk_traversal(node_t* node, count_t depth, Fn&& fn) const - { - if (depth < max_depth<B>) { - auto datamap = node->datamap(); - if (datamap) - fn(node->values(), node->values() + popcount(datamap)); - auto nodemap = node->nodemap(); - if (nodemap) { - auto fst = node->children(); - auto lst = fst + popcount(nodemap); - for (; fst != lst; ++fst) - for_each_chunk_traversal(*fst, depth + 1, fn); - } - } else { - fn(node->collisions(), - node->collisions() + node->collision_count()); - } - } - - template <typename Project, typename Default, typename K> - decltype(auto) get(const K& k) const - { - auto node = root; - auto hash = Hash{}(k); - for (auto i = count_t{}; i < max_depth<B>; ++i) { - auto bit = bitmap_t{1u} << (hash & mask<B>); - if (node->nodemap() & bit) { - auto offset = popcount(node->nodemap() & (bit - 1)); - node = node->children()[offset]; - hash = hash >> B; - } else if (node->datamap() & bit) { - auto offset = popcount(node->datamap() & (bit - 1)); - auto val = node->values() + offset; - if (Equal{}(*val, k)) - return Project{}(*val); - else - return Default{}(); - } else { - return Default{}(); - } - } - auto fst = node->collisions(); - auto lst = fst + node->collision_count(); - for (; fst != lst; ++fst) - if (Equal{}(*fst, k)) - return Project{}(*fst); - return Default{}(); - } - - std::pair<node_t*, bool> - do_add(node_t* node, T v, hash_t hash, shift_t shift) const - { - if (shift == max_shift<B>) { - auto fst = node->collisions(); - auto lst = fst + node->collision_count(); - for (; fst != lst; ++fst) - if (Equal{}(*fst, v)) - return { - node_t::copy_collision_replace(node, fst, std::move(v)), - false}; - return {node_t::copy_collision_insert(node, std::move(v)), true}; - } else { - auto idx = (hash & (mask<B> << shift)) >> shift; - auto bit = bitmap_t{1u} << idx; - if (node->nodemap() & bit) { - auto offset = popcount(node->nodemap() & (bit - 1)); - auto result = do_add( - node->children()[offset], std::move(v), hash, shift + B); - try { - result.first = - node_t::copy_inner_replace(node, offset, result.first); - return result; - } catch (...) { - node_t::delete_deep_shift(result.first, shift + B); - throw; - } - } else if (node->datamap() & bit) { - auto offset = popcount(node->datamap() & (bit - 1)); - auto val = node->values() + offset; - if (Equal{}(*val, v)) - return {node_t::copy_inner_replace_value( - node, offset, std::move(v)), - false}; - else { - auto child = node_t::make_merged( - shift + B, std::move(v), hash, *val, Hash{}(*val)); - try { - return {node_t::copy_inner_replace_merged( - node, bit, offset, child), - true}; - } catch (...) { - node_t::delete_deep_shift(child, shift + B); - throw; - } - } - } else { - return { - node_t::copy_inner_insert_value(node, bit, std::move(v)), - true}; - } - } - } - - champ add(T v) const - { - auto hash = Hash{}(v); - auto res = do_add(root, std::move(v), hash, 0); - auto new_size = size + (res.second ? 1 : 0); - return {res.first, new_size}; - } - - template <typename Project, - typename Default, - typename Combine, - typename K, - typename Fn> - std::pair<node_t*, bool> - do_update(node_t* node, K&& k, Fn&& fn, hash_t hash, shift_t shift) const - { - if (shift == max_shift<B>) { - auto fst = node->collisions(); - auto lst = fst + node->collision_count(); - for (; fst != lst; ++fst) - if (Equal{}(*fst, k)) - return { - node_t::copy_collision_replace( - node, - fst, - Combine{}(std::forward<K>(k), - std::forward<Fn>(fn)(Project{}(*fst)))), - false}; - return {node_t::copy_collision_insert( - node, - Combine{}(std::forward<K>(k), - std::forward<Fn>(fn)(Default{}()))), - true}; - } else { - auto idx = (hash & (mask<B> << shift)) >> shift; - auto bit = bitmap_t{1u} << idx; - if (node->nodemap() & bit) { - auto offset = popcount(node->nodemap() & (bit - 1)); - auto result = do_update<Project, Default, Combine>( - node->children()[offset], - k, - std::forward<Fn>(fn), - hash, - shift + B); - try { - result.first = - node_t::copy_inner_replace(node, offset, result.first); - return result; - } catch (...) { - node_t::delete_deep_shift(result.first, shift + B); - throw; - } - } else if (node->datamap() & bit) { - auto offset = popcount(node->datamap() & (bit - 1)); - auto val = node->values() + offset; - if (Equal{}(*val, k)) - return { - node_t::copy_inner_replace_value( - node, - offset, - Combine{}(std::forward<K>(k), - std::forward<Fn>(fn)(Project{}(*val)))), - false}; - else { - auto child = node_t::make_merged( - shift + B, - Combine{}(std::forward<K>(k), - std::forward<Fn>(fn)(Default{}())), - hash, - *val, - Hash{}(*val)); - try { - return {node_t::copy_inner_replace_merged( - node, bit, offset, child), - true}; - } catch (...) { - node_t::delete_deep_shift(child, shift + B); - throw; - } - } - } else { - return {node_t::copy_inner_insert_value( - node, - bit, - Combine{}(std::forward<K>(k), - std::forward<Fn>(fn)(Default{}()))), - true}; - } - } - } - - template <typename Project, - typename Default, - typename Combine, - typename K, - typename Fn> - champ update(const K& k, Fn&& fn) const - { - auto hash = Hash{}(k); - auto res = do_update<Project, Default, Combine>( - root, k, std::forward<Fn>(fn), hash, 0); - auto new_size = size + (res.second ? 1 : 0); - return {res.first, new_size}; - } - - // basically: - // variant<monostate_t, T*, node_t*> - // boo bad we are not using... C++17 :'( - struct sub_result - { - enum kind_t - { - nothing, - singleton, - tree - }; - - union data_t - { - T* singleton; - node_t* tree; - }; - - kind_t kind; - data_t data; - - sub_result() - : kind{nothing} {}; - sub_result(T* x) - : kind{singleton} - { - data.singleton = x; - }; - sub_result(node_t* x) - : kind{tree} - { - data.tree = x; - }; - }; - - template <typename K> - sub_result - do_sub(node_t* node, const K& k, hash_t hash, shift_t shift) const - { - if (shift == max_shift<B>) { - auto fst = node->collisions(); - auto lst = fst + node->collision_count(); - for (auto cur = fst; cur != lst; ++cur) - if (Equal{}(*cur, k)) - return node->collision_count() > 2 - ? node_t::copy_collision_remove(node, cur) - : sub_result{fst + (cur == fst)}; - return {}; - } else { - auto idx = (hash & (mask<B> << shift)) >> shift; - auto bit = bitmap_t{1u} << idx; - if (node->nodemap() & bit) { - auto offset = popcount(node->nodemap() & (bit - 1)); - auto result = - do_sub(node->children()[offset], k, hash, shift + B); - switch (result.kind) { - case sub_result::nothing: - return {}; - case sub_result::singleton: - return node->datamap() == 0 && - popcount(node->nodemap()) == 1 && shift > 0 - ? result - : node_t::copy_inner_replace_inline( - node, bit, offset, *result.data.singleton); - case sub_result::tree: - try { - return node_t::copy_inner_replace( - node, offset, result.data.tree); - } catch (...) { - node_t::delete_deep_shift(result.data.tree, shift + B); - throw; - } - } - } else if (node->datamap() & bit) { - auto offset = popcount(node->datamap() & (bit - 1)); - auto val = node->values() + offset; - if (Equal{}(*val, k)) { - auto nv = popcount(node->datamap()); - if (node->nodemap() || nv > 2) - return node_t::copy_inner_remove_value( - node, bit, offset); - else if (nv == 2) { - return shift > 0 ? sub_result{node->values() + !offset} - : node_t::make_inner_n( - 0, - node->datamap() & ~bit, - node->values()[!offset]); - } else { - assert(shift == 0); - return empty().root->inc(); - } - } - } - return {}; - } - } - - template <typename K> - champ sub(const K& k) const - { - auto hash = Hash{}(k); - auto res = do_sub(root, k, hash, 0); - switch (res.kind) { - case sub_result::nothing: - return *this; - case sub_result::tree: - return {res.data.tree, size - 1}; - default: - IMMER_UNREACHABLE; - } - } - - template <typename Eq = Equal> - bool equals(const champ& other) const - { - return size == other.size && equals_tree<Eq>(root, other.root, 0); - } - - template <typename Eq> - static bool equals_tree(const node_t* a, const node_t* b, count_t depth) - { - if (a == b) - return true; - else if (depth == max_depth<B>) { - auto nv = a->collision_count(); - return nv == b->collision_count() && - equals_collisions<Eq>(a->collisions(), b->collisions(), nv); - } else { - if (a->nodemap() != b->nodemap() || a->datamap() != b->datamap()) - return false; - auto n = popcount(a->nodemap()); - for (auto i = count_t{}; i < n; ++i) - if (!equals_tree<Eq>( - a->children()[i], b->children()[i], depth + 1)) - return false; - auto nv = popcount(a->datamap()); - return !nv || equals_values<Eq>(a->values(), b->values(), nv); - } - } - - template <typename Eq> - static bool equals_values(const T* a, const T* b, count_t n) - { - return std::equal(a, a + n, b, Eq{}); - } - - template <typename Eq> - static bool equals_collisions(const T* a, const T* b, count_t n) - { - auto ae = a + n; - auto be = b + n; - for (; a != ae; ++a) { - for (auto fst = b; fst != be; ++fst) - if (Eq{}(*a, *fst)) - goto good; - return false; - good: - continue; - } - return true; - } -}; - -} // namespace hamts -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/hamts/champ_iterator.hpp b/third_party/immer/immer/detail/hamts/champ_iterator.hpp deleted file mode 100644 index 72673b41be03..000000000000 --- a/third_party/immer/immer/detail/hamts/champ_iterator.hpp +++ /dev/null @@ -1,148 +0,0 @@ -// -// 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 <immer/detail/hamts/champ.hpp> -#include <immer/detail/iterator_facade.hpp> - -namespace immer { -namespace detail { -namespace hamts { - -template <typename T, typename Hash, typename Eq, typename MP, bits_t B> -struct champ_iterator - : iterator_facade<champ_iterator<T, Hash, Eq, MP, B>, - std::forward_iterator_tag, - T, - const T&, - std::ptrdiff_t, - const T*> -{ - using tree_t = champ<T, Hash, Eq, MP, B>; - using node_t = typename tree_t::node_t; - - struct end_t - {}; - - champ_iterator() = default; - - champ_iterator(const tree_t& v) - : depth_{0} - { - if (v.root->datamap()) { - cur_ = v.root->values(); - end_ = v.root->values() + popcount(v.root->datamap()); - } else { - cur_ = end_ = nullptr; - } - path_[0] = &v.root; - ensure_valid_(); - } - - champ_iterator(const tree_t& v, end_t) - : cur_{nullptr} - , end_{nullptr} - , depth_{0} - { - path_[0] = &v.root; - } - - champ_iterator(const champ_iterator& other) - : cur_{other.cur_} - , end_{other.end_} - , depth_{other.depth_} - { - std::copy(other.path_, other.path_ + depth_ + 1, path_); - } - -private: - friend iterator_core_access; - - T* cur_; - T* end_; - count_t depth_; - node_t* const* path_[max_depth<B> + 1]; - - void increment() - { - ++cur_; - ensure_valid_(); - } - - bool step_down() - { - if (depth_ < max_depth<B>) { - auto parent = *path_[depth_]; - if (parent->nodemap()) { - ++depth_; - path_[depth_] = parent->children(); - auto child = *path_[depth_]; - if (depth_ < max_depth<B>) { - if (child->datamap()) { - cur_ = child->values(); - end_ = cur_ + popcount(child->datamap()); - } - } else { - cur_ = child->collisions(); - end_ = cur_ + child->collision_count(); - } - return true; - } - } - return false; - } - - bool step_right() - { - while (depth_ > 0) { - auto parent = *path_[depth_ - 1]; - auto last = parent->children() + popcount(parent->nodemap()); - auto next = path_[depth_] + 1; - if (next < last) { - path_[depth_] = next; - auto child = *path_[depth_]; - if (depth_ < max_depth<B>) { - if (child->datamap()) { - cur_ = child->values(); - end_ = cur_ + popcount(child->datamap()); - } - } else { - cur_ = child->collisions(); - end_ = cur_ + child->collision_count(); - } - return true; - } - --depth_; - } - return false; - } - - void ensure_valid_() - { - while (cur_ == end_) { - while (step_down()) - if (cur_ != end_) - return; - if (!step_right()) { - // end of sequence - assert(depth_ == 0); - cur_ = end_ = nullptr; - return; - } - } - } - - bool equal(const champ_iterator& other) const { return cur_ == other.cur_; } - - const T& dereference() const { return *cur_; } -}; - -} // namespace hamts -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/hamts/node.hpp b/third_party/immer/immer/detail/hamts/node.hpp deleted file mode 100644 index 216e82b7874f..000000000000 --- a/third_party/immer/immer/detail/hamts/node.hpp +++ /dev/null @@ -1,717 +0,0 @@ -// -// 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 <immer/detail/combine_standard_layout.hpp> -#include <immer/detail/hamts/bits.hpp> -#include <immer/detail/util.hpp> - -#include <cassert> - -namespace immer { -namespace detail { -namespace hamts { - -template <typename T, - typename Hash, - typename Equal, - typename MemoryPolicy, - bits_t B> -struct node -{ - using node_t = node; - - using memory = MemoryPolicy; - using heap_policy = typename memory::heap; - using heap = typename heap_policy::type; - using transience = typename memory::transience_t; - using refs_t = typename memory::refcount; - using ownee_t = typename transience::ownee; - using edit_t = typename transience::edit; - using value_t = T; - using bitmap_t = typename get_bitmap_type<B>::type; - - enum class kind_t - { - collision, - inner - }; - - struct collision_t - { - count_t count; - aligned_storage_for<T> buffer; - }; - - struct values_data_t - { - aligned_storage_for<T> buffer; - }; - - using values_t = combine_standard_layout_t<values_data_t, refs_t>; - - struct inner_t - { - bitmap_t nodemap; - bitmap_t datamap; - values_t* values; - aligned_storage_for<node_t*> buffer; - }; - - union data_t - { - inner_t inner; - collision_t collision; - }; - - struct impl_data_t - { -#if IMMER_TAGGED_NODE - kind_t kind; -#endif - data_t data; - }; - - using impl_t = combine_standard_layout_t<impl_data_t, refs_t>; - - impl_t impl; - - constexpr static std::size_t sizeof_values_n(count_t count) - { - return std::max(sizeof(values_t), - immer_offsetof(values_t, d.buffer) + - sizeof(values_data_t::buffer) * count); - } - - constexpr static std::size_t sizeof_collision_n(count_t count) - { - return immer_offsetof(impl_t, d.data.collision.buffer) + - sizeof(collision_t::buffer) * count; - } - - constexpr static std::size_t sizeof_inner_n(count_t count) - { - return immer_offsetof(impl_t, d.data.inner.buffer) + - sizeof(inner_t::buffer) * count; - } - -#if IMMER_TAGGED_NODE - kind_t kind() const { return impl.d.kind; } -#endif - - auto values() - { - IMMER_ASSERT_TAGGED(kind() == kind_t::inner); - assert(impl.d.data.inner.values); - return (T*) &impl.d.data.inner.values->d.buffer; - } - - auto values() const - { - IMMER_ASSERT_TAGGED(kind() == kind_t::inner); - assert(impl.d.data.inner.values); - return (const T*) &impl.d.data.inner.values->d.buffer; - } - - auto children() - { - IMMER_ASSERT_TAGGED(kind() == kind_t::inner); - return (node_t**) &impl.d.data.inner.buffer; - } - - auto children() const - { - IMMER_ASSERT_TAGGED(kind() == kind_t::inner); - return (const node_t* const*) &impl.d.data.inner.buffer; - } - - auto datamap() const - { - IMMER_ASSERT_TAGGED(kind() == kind_t::inner); - return impl.d.data.inner.datamap; - } - - auto nodemap() const - { - IMMER_ASSERT_TAGGED(kind() == kind_t::inner); - return impl.d.data.inner.nodemap; - } - - auto collision_count() const - { - IMMER_ASSERT_TAGGED(kind() == kind_t::collision); - return impl.d.data.collision.count; - } - - T* collisions() - { - IMMER_ASSERT_TAGGED(kind() == kind_t::collision); - return (T*) &impl.d.data.collision.buffer; - } - - const T* collisions() const - { - IMMER_ASSERT_TAGGED(kind() == kind_t::collision); - return (const T*) &impl.d.data.collision.buffer; - } - - static refs_t& refs(const values_t* x) - { - return auto_const_cast(get<refs_t>(*x)); - } - static const ownee_t& ownee(const values_t* x) { return get<ownee_t>(*x); } - static ownee_t& ownee(values_t* x) { return get<ownee_t>(*x); } - - static refs_t& refs(const node_t* x) - { - return auto_const_cast(get<refs_t>(x->impl)); - } - static const ownee_t& ownee(const node_t* x) - { - return get<ownee_t>(x->impl); - } - static ownee_t& ownee(node_t* x) { return get<ownee_t>(x->impl); } - - static node_t* make_inner_n(count_t n) - { - assert(n <= branches<B>); - auto m = heap::allocate(sizeof_inner_n(n)); - auto p = new (m) node_t; -#if IMMER_TAGGED_NODE - p->impl.d.kind = node_t::kind_t::inner; -#endif - p->impl.d.data.inner.nodemap = 0; - p->impl.d.data.inner.datamap = 0; - p->impl.d.data.inner.values = nullptr; - return p; - } - - static node_t* make_inner_n(count_t n, values_t* values) - { - auto p = make_inner_n(n); - if (values) { - p->impl.d.data.inner.values = values; - refs(values).inc(); - } - return p; - } - - static node_t* make_inner_n(count_t n, count_t nv) - { - assert(nv <= branches<B>); - auto p = make_inner_n(n); - if (nv) { - try { - p->impl.d.data.inner.values = - new (heap::allocate(sizeof_values_n(nv))) values_t{}; - } catch (...) { - deallocate_inner(p, n); - throw; - } - } - return p; - } - - static node_t* make_inner_n(count_t n, count_t idx, node_t* child) - { - assert(n >= 1); - auto p = make_inner_n(n); - p->impl.d.data.inner.nodemap = bitmap_t{1u} << idx; - p->children()[0] = child; - return p; - } - - static node_t* make_inner_n(count_t n, bitmap_t bitmap, T x) - { - auto p = make_inner_n(n, 1); - p->impl.d.data.inner.datamap = bitmap; - try { - new (p->values()) T{std::move(x)}; - } catch (...) { - deallocate_inner(p, n, 1); - throw; - } - return p; - } - - static node_t* - make_inner_n(count_t n, count_t idx1, T x1, count_t idx2, T x2) - { - assert(idx1 != idx2); - auto p = make_inner_n(n, 2); - p->impl.d.data.inner.datamap = - (bitmap_t{1u} << idx1) | (bitmap_t{1u} << idx2); - auto assign = [&](auto&& x1, auto&& x2) { - auto vp = p->values(); - try { - new (vp) T{std::move(x1)}; - try { - new (vp + 1) T{std::move(x2)}; - } catch (...) { - vp->~T(); - throw; - } - } catch (...) { - deallocate_inner(p, n, 2); - throw; - } - }; - if (idx1 < idx2) - assign(x1, x2); - else - assign(x2, x1); - return p; - } - - static node_t* make_collision_n(count_t n) - { - auto m = heap::allocate(sizeof_collision_n(n)); - auto p = new (m) node_t; -#if IMMER_TAGGED_NODE - p->impl.d.kind = node_t::kind_t::collision; -#endif - p->impl.d.data.collision.count = n; - return p; - } - - static node_t* make_collision(T v1, T v2) - { - auto m = heap::allocate(sizeof_collision_n(2)); - auto p = new (m) node_t; -#if IMMER_TAGGED_NODE - p->impl.d.kind = node_t::kind_t::collision; -#endif - p->impl.d.data.collision.count = 2; - auto cols = p->collisions(); - try { - new (cols) T{std::move(v1)}; - try { - new (cols + 1) T{std::move(v2)}; - } catch (...) { - cols->~T(); - throw; - } - } catch (...) { - deallocate_collision(p, 2); - throw; - } - return p; - } - - static node_t* copy_collision_insert(node_t* src, T v) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision); - auto n = src->collision_count(); - auto dst = make_collision_n(n + 1); - auto srcp = src->collisions(); - auto dstp = dst->collisions(); - try { - new (dstp) T{std::move(v)}; - try { - std::uninitialized_copy(srcp, srcp + n, dstp + 1); - } catch (...) { - dstp->~T(); - throw; - } - } catch (...) { - deallocate_collision(dst, n + 1); - throw; - } - return dst; - } - - static node_t* copy_collision_remove(node_t* src, T* v) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision); - assert(src->collision_count() > 1); - auto n = src->collision_count(); - auto dst = make_collision_n(n - 1); - auto srcp = src->collisions(); - auto dstp = dst->collisions(); - try { - dstp = std::uninitialized_copy(srcp, v, dstp); - try { - std::uninitialized_copy(v + 1, srcp + n, dstp); - } catch (...) { - destroy(dst->collisions(), dstp); - throw; - } - } catch (...) { - deallocate_collision(dst, n - 1); - throw; - } - return dst; - } - - static node_t* copy_collision_replace(node_t* src, T* pos, T v) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision); - auto n = src->collision_count(); - auto dst = make_collision_n(n); - auto srcp = src->collisions(); - auto dstp = dst->collisions(); - assert(pos >= srcp && pos < srcp + n); - try { - new (dstp) T{std::move(v)}; - try { - dstp = std::uninitialized_copy(srcp, pos, dstp + 1); - try { - std::uninitialized_copy(pos + 1, srcp + n, dstp); - } catch (...) { - destroy(dst->collisions(), dstp); - throw; - } - } catch (...) { - dst->collisions()->~T(); - throw; - } - } catch (...) { - deallocate_collision(dst, n); - throw; - } - return dst; - } - - static node_t* - copy_inner_replace(node_t* src, count_t offset, node_t* child) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - auto n = popcount(src->nodemap()); - auto dst = make_inner_n(n, src->impl.d.data.inner.values); - auto srcp = src->children(); - auto dstp = dst->children(); - dst->impl.d.data.inner.datamap = src->datamap(); - dst->impl.d.data.inner.nodemap = src->nodemap(); - std::uninitialized_copy(srcp, srcp + n, dstp); - inc_nodes(srcp, n); - srcp[offset]->dec_unsafe(); - dstp[offset] = child; - return dst; - } - - static node_t* copy_inner_replace_value(node_t* src, count_t offset, T v) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - assert(offset < popcount(src->datamap())); - auto n = popcount(src->nodemap()); - auto nv = popcount(src->datamap()); - auto dst = make_inner_n(n, nv); - dst->impl.d.data.inner.datamap = src->datamap(); - dst->impl.d.data.inner.nodemap = src->nodemap(); - try { - std::uninitialized_copy( - src->values(), src->values() + nv, dst->values()); - try { - dst->values()[offset] = std::move(v); - } catch (...) { - destroy_n(dst->values(), nv); - throw; - } - } catch (...) { - deallocate_inner(dst, n, nv); - throw; - } - inc_nodes(src->children(), n); - std::uninitialized_copy( - src->children(), src->children() + n, dst->children()); - return dst; - } - - static node_t* copy_inner_replace_merged(node_t* src, - bitmap_t bit, - count_t voffset, - node_t* node) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - assert(!(src->nodemap() & bit)); - assert(src->datamap() & bit); - assert(voffset == popcount(src->datamap() & (bit - 1))); - auto n = popcount(src->nodemap()); - auto nv = popcount(src->datamap()); - auto dst = make_inner_n(n + 1, nv - 1); - auto noffset = popcount(src->nodemap() & (bit - 1)); - dst->impl.d.data.inner.datamap = src->datamap() & ~bit; - dst->impl.d.data.inner.nodemap = src->nodemap() | bit; - if (nv > 1) { - try { - std::uninitialized_copy( - src->values(), src->values() + voffset, dst->values()); - try { - std::uninitialized_copy(src->values() + voffset + 1, - src->values() + nv, - dst->values() + voffset); - } catch (...) { - destroy_n(dst->values(), voffset); - throw; - } - } catch (...) { - deallocate_inner(dst, n + 1, nv - 1); - throw; - } - } - inc_nodes(src->children(), n); - std::uninitialized_copy( - src->children(), src->children() + noffset, dst->children()); - std::uninitialized_copy(src->children() + noffset, - src->children() + n, - dst->children() + noffset + 1); - dst->children()[noffset] = node; - return dst; - } - - static node_t* copy_inner_replace_inline(node_t* src, - bitmap_t bit, - count_t noffset, - T value) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - assert(!(src->datamap() & bit)); - assert(src->nodemap() & bit); - assert(noffset == popcount(src->nodemap() & (bit - 1))); - auto n = popcount(src->nodemap()); - auto nv = popcount(src->datamap()); - auto dst = make_inner_n(n - 1, nv + 1); - auto voffset = popcount(src->datamap() & (bit - 1)); - dst->impl.d.data.inner.nodemap = src->nodemap() & ~bit; - dst->impl.d.data.inner.datamap = src->datamap() | bit; - try { - if (nv) - std::uninitialized_copy( - src->values(), src->values() + voffset, dst->values()); - try { - new (dst->values() + voffset) T{std::move(value)}; - try { - if (nv) - std::uninitialized_copy(src->values() + voffset, - src->values() + nv, - dst->values() + voffset + 1); - } catch (...) { - dst->values()[voffset].~T(); - throw; - } - } catch (...) { - destroy_n(dst->values(), voffset); - throw; - } - } catch (...) { - deallocate_inner(dst, n - 1, nv + 1); - throw; - } - inc_nodes(src->children(), n); - src->children()[noffset]->dec_unsafe(); - std::uninitialized_copy( - src->children(), src->children() + noffset, dst->children()); - std::uninitialized_copy(src->children() + noffset + 1, - src->children() + n, - dst->children() + noffset); - return dst; - } - - static node_t* - copy_inner_remove_value(node_t* src, bitmap_t bit, count_t voffset) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - assert(!(src->nodemap() & bit)); - assert(src->datamap() & bit); - assert(voffset == popcount(src->datamap() & (bit - 1))); - auto n = popcount(src->nodemap()); - auto nv = popcount(src->datamap()); - auto dst = make_inner_n(n, nv - 1); - dst->impl.d.data.inner.datamap = src->datamap() & ~bit; - dst->impl.d.data.inner.nodemap = src->nodemap(); - if (nv > 1) { - try { - std::uninitialized_copy( - src->values(), src->values() + voffset, dst->values()); - try { - std::uninitialized_copy(src->values() + voffset + 1, - src->values() + nv, - dst->values() + voffset); - } catch (...) { - destroy_n(dst->values(), voffset); - throw; - } - } catch (...) { - deallocate_inner(dst, n, nv - 1); - throw; - } - } - inc_nodes(src->children(), n); - std::uninitialized_copy( - src->children(), src->children() + n, dst->children()); - return dst; - } - - static node_t* copy_inner_insert_value(node_t* src, bitmap_t bit, T v) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - auto n = popcount(src->nodemap()); - auto nv = popcount(src->datamap()); - auto offset = popcount(src->datamap() & (bit - 1)); - auto dst = make_inner_n(n, nv + 1); - dst->impl.d.data.inner.datamap = src->datamap() | bit; - dst->impl.d.data.inner.nodemap = src->nodemap(); - try { - if (nv) - std::uninitialized_copy( - src->values(), src->values() + offset, dst->values()); - try { - new (dst->values() + offset) T{std::move(v)}; - try { - if (nv) - std::uninitialized_copy(src->values() + offset, - src->values() + nv, - dst->values() + offset + 1); - } catch (...) { - dst->values()[offset].~T(); - throw; - } - } catch (...) { - destroy_n(dst->values(), offset); - throw; - } - } catch (...) { - deallocate_inner(dst, n, nv + 1); - throw; - } - inc_nodes(src->children(), n); - std::uninitialized_copy( - src->children(), src->children() + n, dst->children()); - return dst; - } - - static node_t* - make_merged(shift_t shift, T v1, hash_t hash1, T v2, hash_t hash2) - { - if (shift < max_shift<B>) { - auto idx1 = hash1 & (mask<B> << shift); - auto idx2 = hash2 & (mask<B> << shift); - if (idx1 == idx2) { - auto merged = make_merged( - shift + B, std::move(v1), hash1, std::move(v2), hash2); - try { - return make_inner_n(1, idx1 >> shift, merged); - } catch (...) { - delete_deep_shift(merged, shift + B); - throw; - } - } else { - return make_inner_n(0, - idx1 >> shift, - std::move(v1), - idx2 >> shift, - std::move(v2)); - } - } else { - return make_collision(std::move(v1), std::move(v2)); - } - } - - node_t* inc() - { - refs(this).inc(); - return this; - } - - const node_t* inc() const - { - refs(this).inc(); - return this; - } - - bool dec() const { return refs(this).dec(); } - void dec_unsafe() const { refs(this).dec_unsafe(); } - - static void inc_nodes(node_t** p, count_t n) - { - for (auto i = p, e = i + n; i != e; ++i) - refs(*i).inc(); - } - - static void delete_values(values_t* p, count_t n) - { - assert(p); - deallocate_values(p, n); - } - - static void delete_inner(node_t* p) - { - assert(p); - IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner); - auto vp = p->impl.d.data.inner.values; - if (vp && refs(vp).dec()) - delete_values(vp, popcount(p->datamap())); - deallocate_inner(p, popcount(p->nodemap())); - } - - static void delete_collision(node_t* p) - { - assert(p); - IMMER_ASSERT_TAGGED(p->kind() == kind_t::collision); - auto n = p->collision_count(); - deallocate_collision(p, n); - } - - static void delete_deep(node_t* p, shift_t s) - { - if (s == max_depth<B>) - delete_collision(p); - else { - auto fst = p->children(); - auto lst = fst + popcount(p->nodemap()); - for (; fst != lst; ++fst) - if ((*fst)->dec()) - delete_deep(*fst, s + 1); - delete_inner(p); - } - } - - static void delete_deep_shift(node_t* p, shift_t s) - { - if (s == max_shift<B>) - delete_collision(p); - else { - auto fst = p->children(); - auto lst = fst + popcount(p->nodemap()); - for (; fst != lst; ++fst) - if ((*fst)->dec()) - delete_deep_shift(*fst, s + B); - delete_inner(p); - } - } - - static void deallocate_values(values_t* p, count_t n) - { - destroy_n((T*) &p->d.buffer, n); - heap::deallocate(node_t::sizeof_values_n(n), p); - } - - static void deallocate_collision(node_t* p, count_t n) - { - destroy_n(p->collisions(), n); - heap::deallocate(node_t::sizeof_collision_n(n), p); - } - - static void deallocate_inner(node_t* p, count_t n) - { - heap::deallocate(node_t::sizeof_inner_n(n), p); - } - - static void deallocate_inner(node_t* p, count_t n, count_t nv) - { - assert(nv); - heap::deallocate(node_t::sizeof_values_n(nv), - p->impl.d.data.inner.values); - heap::deallocate(node_t::sizeof_inner_n(n), p); - } -}; - -} // namespace hamts -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/iterator_facade.hpp b/third_party/immer/immer/detail/iterator_facade.hpp deleted file mode 100644 index ffc237943e09..000000000000 --- a/third_party/immer/immer/detail/iterator_facade.hpp +++ /dev/null @@ -1,212 +0,0 @@ -// -// 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 <cstddef> -#include <iterator> -#include <type_traits> - -namespace immer { -namespace detail { - -struct iterator_core_access -{ - template <typename T> - static decltype(auto) dereference(T&& x) - { - return x.dereference(); - } - - template <typename T> - static decltype(auto) increment(T&& x) - { - return x.increment(); - } - - template <typename T> - static decltype(auto) decrement(T&& x) - { - return x.decrement(); - } - - template <typename T1, typename T2> - static decltype(auto) equal(T1&& x1, T2&& x2) - { - return x1.equal(x2); - } - - template <typename T, typename D> - static decltype(auto) advance(T&& x, D d) - { - return x.advance(d); - } - - template <typename T1, typename T2> - static decltype(auto) distance_to(T1&& x1, T2&& x2) - { - return x1.distance_to(x2); - } -}; - -/*! - * Minimalistic reimplementation of boost::iterator_facade - */ -template <typename DerivedT, - typename IteratorCategoryT, - typename T, - typename ReferenceT = T&, - typename DifferenceTypeT = std::ptrdiff_t, - typename PointerT = T*> -class iterator_facade -{ -public: - using iterator_category = IteratorCategoryT; - using value_type = T; - using difference_type = DifferenceTypeT; - using pointer = PointerT; - using reference = ReferenceT; - -protected: - using access_t = iterator_core_access; - - constexpr static auto is_random_access = - std::is_base_of<std::random_access_iterator_tag, - IteratorCategoryT>::value; - constexpr static auto is_bidirectional = - std::is_base_of<std::bidirectional_iterator_tag, - IteratorCategoryT>::value; - - class reference_proxy - { - friend iterator_facade; - DerivedT iter_; - - reference_proxy(DerivedT iter) - : iter_{std::move(iter)} - {} - - public: - operator ReferenceT() const { return *iter_; } - }; - - const DerivedT& derived() const - { - static_assert(std::is_base_of<iterator_facade, DerivedT>::value, - "must pass a derived thing"); - return *static_cast<const DerivedT*>(this); - } - DerivedT& derived() - { - static_assert(std::is_base_of<iterator_facade, DerivedT>::value, - "must pass a derived thing"); - return *static_cast<DerivedT*>(this); - } - -public: - ReferenceT operator*() const { return access_t::dereference(derived()); } - PointerT operator->() const { return &access_t::dereference(derived()); } - reference_proxy operator[](DifferenceTypeT n) const - { - static_assert(is_random_access, ""); - return derived() + n; - } - - bool operator==(const DerivedT& rhs) const - { - return access_t::equal(derived(), rhs); - } - bool operator!=(const DerivedT& rhs) const - { - return !access_t::equal(derived(), rhs); - } - - DerivedT& operator++() - { - access_t::increment(derived()); - return derived(); - } - DerivedT operator++(int) - { - auto tmp = derived(); - access_t::increment(derived()); - return tmp; - } - - DerivedT& operator--() - { - static_assert(is_bidirectional || is_random_access, ""); - access_t::decrement(derived()); - return derived(); - } - DerivedT operator--(int) - { - static_assert(is_bidirectional || is_random_access, ""); - auto tmp = derived(); - access_t::decrement(derived()); - return tmp; - } - - DerivedT& operator+=(DifferenceTypeT n) - { - access_t::advance(derived(), n); - return derived(); - } - DerivedT& operator-=(DifferenceTypeT n) - { - access_t::advance(derived(), -n); - return derived(); - } - - DerivedT operator+(DifferenceTypeT n) const - { - static_assert(is_random_access, ""); - auto tmp = derived(); - return tmp += n; - } - friend DerivedT operator+(DifferenceTypeT n, const DerivedT& i) - { - static_assert(is_random_access, ""); - return i + n; - } - DerivedT operator-(DifferenceTypeT n) const - { - static_assert(is_random_access, ""); - auto tmp = derived(); - return tmp -= n; - } - DifferenceTypeT operator-(const DerivedT& rhs) const - { - static_assert(is_random_access, ""); - return access_t::distance_to(rhs, derived()); - } - - bool operator<(const DerivedT& rhs) const - { - static_assert(is_random_access, ""); - return access_t::distance_to(derived(), rhs) > 0; - } - bool operator<=(const DerivedT& rhs) const - { - static_assert(is_random_access, ""); - return access_t::distance_to(derived(), rhs) >= 0; - } - bool operator>(const DerivedT& rhs) const - { - static_assert(is_random_access, ""); - return access_t::distance_to(derived(), rhs) < 0; - } - bool operator>=(const DerivedT& rhs) const - { - static_assert(is_random_access, ""); - return access_t::distance_to(derived(), rhs) <= 0; - } -}; - -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/rbts/bits.hpp b/third_party/immer/immer/detail/rbts/bits.hpp deleted file mode 100644 index 58d4e3c9c93a..000000000000 --- a/third_party/immer/immer/detail/rbts/bits.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// -// 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 <cstdint> - -namespace immer { -namespace detail { -namespace rbts { - -using bits_t = std::uint32_t; -using shift_t = std::uint32_t; -using count_t = std::uint32_t; -using size_t = std::size_t; - -template <bits_t B, typename T = count_t> -constexpr T branches = T{1} << B; - -template <bits_t B, typename T = size_t> -constexpr T mask = branches<B, T> - 1; - -template <bits_t B, bits_t BL> -constexpr shift_t endshift = shift_t{BL} - shift_t{B}; - -} // namespace rbts -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/rbts/node.hpp b/third_party/immer/immer/detail/rbts/node.hpp deleted file mode 100644 index e54e569636ac..000000000000 --- a/third_party/immer/immer/detail/rbts/node.hpp +++ /dev/null @@ -1,932 +0,0 @@ -// -// 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 <immer/detail/combine_standard_layout.hpp> -#include <immer/detail/rbts/bits.hpp> -#include <immer/detail/util.hpp> -#include <immer/heap/tags.hpp> - -#include <cassert> -#include <cstddef> -#include <memory> -#include <type_traits> - -namespace immer { -namespace detail { -namespace rbts { - -template <typename T, typename MemoryPolicy, bits_t B, bits_t BL> -struct node -{ - static constexpr auto bits = B; - static constexpr auto bits_leaf = BL; - - using node_t = node; - using memory = MemoryPolicy; - using heap_policy = typename memory::heap; - using transience = typename memory::transience_t; - using refs_t = typename memory::refcount; - using ownee_t = typename transience::ownee; - using edit_t = typename transience::edit; - using value_t = T; - - static constexpr bool embed_relaxed = memory::prefer_fewer_bigger_objects; - - enum class kind_t - { - leaf, - inner - }; - - struct relaxed_data_t - { - count_t count; - size_t sizes[branches<B>]; - }; - - using relaxed_data_with_meta_t = - combine_standard_layout_t<relaxed_data_t, refs_t, ownee_t>; - - using relaxed_data_no_meta_t = combine_standard_layout_t<relaxed_data_t>; - - using relaxed_t = std::conditional_t<embed_relaxed, - relaxed_data_no_meta_t, - relaxed_data_with_meta_t>; - - struct leaf_t - { - aligned_storage_for<T> buffer; - }; - - struct inner_t - { - relaxed_t* relaxed; - aligned_storage_for<node_t*> buffer; - }; - - union data_t - { - inner_t inner; - leaf_t leaf; - }; - - struct impl_data_t - { -#if IMMER_TAGGED_NODE - kind_t kind; -#endif - data_t data; - }; - - using impl_t = combine_standard_layout_t<impl_data_t, refs_t, ownee_t>; - - impl_t impl; - - // assume that we need to keep headroom space in the node when we - // are doing reference counting, since any node may become - // transient when it has only one reference - constexpr static bool keep_headroom = !std::is_empty<refs_t>{}; - - constexpr static std::size_t sizeof_packed_leaf_n(count_t count) - { - return immer_offsetof(impl_t, d.data.leaf.buffer) + - sizeof(leaf_t::buffer) * count; - } - - constexpr static std::size_t sizeof_packed_inner_n(count_t count) - { - return immer_offsetof(impl_t, d.data.inner.buffer) + - sizeof(inner_t::buffer) * count; - } - - constexpr static std::size_t sizeof_packed_relaxed_n(count_t count) - { - return immer_offsetof(relaxed_t, d.sizes) + sizeof(size_t) * count; - } - - constexpr static std::size_t sizeof_packed_inner_r_n(count_t count) - { - return embed_relaxed ? sizeof_packed_inner_n(count) + - sizeof_packed_relaxed_n(count) - : sizeof_packed_inner_n(count); - } - - constexpr static std::size_t max_sizeof_leaf = - sizeof_packed_leaf_n(branches<BL>); - - constexpr static std::size_t max_sizeof_inner = - sizeof_packed_inner_n(branches<B>); - - constexpr static std::size_t max_sizeof_relaxed = - sizeof_packed_relaxed_n(branches<B>); - - constexpr static std::size_t max_sizeof_inner_r = - sizeof_packed_inner_r_n(branches<B>); - - constexpr static std::size_t sizeof_inner_n(count_t n) - { - return keep_headroom ? max_sizeof_inner : sizeof_packed_inner_n(n); - } - - constexpr static std::size_t sizeof_inner_r_n(count_t n) - { - return keep_headroom ? max_sizeof_inner_r : sizeof_packed_inner_r_n(n); - } - - constexpr static std::size_t sizeof_relaxed_n(count_t n) - { - return keep_headroom ? max_sizeof_relaxed : sizeof_packed_relaxed_n(n); - } - - constexpr static std::size_t sizeof_leaf_n(count_t n) - { - return keep_headroom ? max_sizeof_leaf : sizeof_packed_leaf_n(n); - } - - using heap = - typename heap_policy::template optimized<max_sizeof_inner>::type; - -#if IMMER_TAGGED_NODE - kind_t kind() const { return impl.d.kind; } -#endif - - relaxed_t* relaxed() - { - IMMER_ASSERT_TAGGED(kind() == kind_t::inner); - return impl.d.data.inner.relaxed; - } - - const relaxed_t* relaxed() const - { - IMMER_ASSERT_TAGGED(kind() == kind_t::inner); - return impl.d.data.inner.relaxed; - } - - node_t** inner() - { - IMMER_ASSERT_TAGGED(kind() == kind_t::inner); - return reinterpret_cast<node_t**>(&impl.d.data.inner.buffer); - } - - T* leaf() - { - IMMER_ASSERT_TAGGED(kind() == kind_t::leaf); - return reinterpret_cast<T*>(&impl.d.data.leaf.buffer); - } - - static refs_t& refs(const relaxed_t* x) - { - return auto_const_cast(get<refs_t>(*x)); - } - static const ownee_t& ownee(const relaxed_t* x) { return get<ownee_t>(*x); } - static ownee_t& ownee(relaxed_t* x) { return get<ownee_t>(*x); } - - static refs_t& refs(const node_t* x) - { - return auto_const_cast(get<refs_t>(x->impl)); - } - static const ownee_t& ownee(const node_t* x) - { - return get<ownee_t>(x->impl); - } - static ownee_t& ownee(node_t* x) { return get<ownee_t>(x->impl); } - - static node_t* make_inner_n(count_t n) - { - assert(n <= branches<B>); - auto m = heap::allocate(sizeof_inner_n(n)); - auto p = new (m) node_t; - p->impl.d.data.inner.relaxed = nullptr; -#if IMMER_TAGGED_NODE - p->impl.d.kind = node_t::kind_t::inner; -#endif - return p; - } - - static node_t* make_inner_e(edit_t e) - { - auto m = heap::allocate(max_sizeof_inner); - auto p = new (m) node_t; - ownee(p) = e; - p->impl.d.data.inner.relaxed = nullptr; -#if IMMER_TAGGED_NODE - p->impl.d.kind = node_t::kind_t::inner; -#endif - return p; - } - - static node_t* make_inner_r_n(count_t n) - { - assert(n <= branches<B>); - auto mp = heap::allocate(sizeof_inner_r_n(n)); - auto mr = static_cast<void*>(nullptr); - if (embed_relaxed) { - mr = reinterpret_cast<unsigned char*>(mp) + sizeof_inner_n(n); - } else { - try { - mr = heap::allocate(sizeof_relaxed_n(n), norefs_tag{}); - } catch (...) { - heap::deallocate(sizeof_inner_r_n(n), mp); - throw; - } - } - auto p = new (mp) node_t; - auto r = new (mr) relaxed_t; - r->d.count = 0; - p->impl.d.data.inner.relaxed = r; -#if IMMER_TAGGED_NODE - p->impl.d.kind = node_t::kind_t::inner; -#endif - return p; - } - - static node_t* make_inner_sr_n(count_t n, relaxed_t* r) - { - return static_if<embed_relaxed, node_t*>( - [&](auto) { return node_t::make_inner_r_n(n); }, - [&](auto) { - auto p = - new (heap::allocate(node_t::sizeof_inner_r_n(n))) node_t; - assert(r->d.count >= n); - node_t::refs(r).inc(); - p->impl.d.data.inner.relaxed = r; -#if IMMER_TAGGED_NODE - p->impl.d.kind = node_t::kind_t::inner; -#endif - return p; - }); - } - - static node_t* make_inner_r_e(edit_t e) - { - auto mp = heap::allocate(max_sizeof_inner_r); - auto mr = static_cast<void*>(nullptr); - if (embed_relaxed) { - mr = reinterpret_cast<unsigned char*>(mp) + max_sizeof_inner; - } else { - try { - mr = heap::allocate(max_sizeof_relaxed, norefs_tag{}); - } catch (...) { - heap::deallocate(max_sizeof_inner_r, mp); - throw; - } - } - auto p = new (mp) node_t; - auto r = new (mr) relaxed_t; - ownee(p) = e; - static_if<!embed_relaxed>([&](auto) { node_t::ownee(r) = e; }); - r->d.count = 0; - p->impl.d.data.inner.relaxed = r; -#if IMMER_TAGGED_NODE - p->impl.d.kind = node_t::kind_t::inner; -#endif - return p; - } - - static node_t* make_inner_sr_e(edit_t e, relaxed_t* r) - { - return static_if<embed_relaxed, node_t*>( - [&](auto) { return node_t::make_inner_r_e(e); }, - [&](auto) { - auto p = - new (heap::allocate(node_t::max_sizeof_inner_r)) node_t; - node_t::refs(r).inc(); - p->impl.d.data.inner.relaxed = r; - node_t::ownee(p) = e; -#if IMMER_TAGGED_NODE - p->impl.d.kind = node_t::kind_t::inner; -#endif - return p; - }); - } - - static node_t* make_leaf_n(count_t n) - { - assert(n <= branches<BL>); - auto p = new (heap::allocate(sizeof_leaf_n(n))) node_t; -#if IMMER_TAGGED_NODE - p->impl.d.kind = node_t::kind_t::leaf; -#endif - return p; - } - - static node_t* make_leaf_e(edit_t e) - { - auto p = new (heap::allocate(max_sizeof_leaf)) node_t; - ownee(p) = e; -#if IMMER_TAGGED_NODE - p->impl.d.kind = node_t::kind_t::leaf; -#endif - return p; - } - - static node_t* make_inner_n(count_t n, node_t* x) - { - assert(n >= 1); - auto p = make_inner_n(n); - p->inner()[0] = x; - return p; - } - - static node_t* make_inner_n(edit_t n, node_t* x) - { - assert(n >= 1); - auto p = make_inner_n(n); - p->inner()[0] = x; - return p; - } - - static node_t* make_inner_n(count_t n, node_t* x, node_t* y) - { - assert(n >= 2); - auto p = make_inner_n(n); - p->inner()[0] = x; - p->inner()[1] = y; - return p; - } - - static node_t* make_inner_r_n(count_t n, node_t* x) - { - assert(n >= 1); - auto p = make_inner_r_n(n); - auto r = p->relaxed(); - p->inner()[0] = x; - r->d.count = 1; - return p; - } - - static node_t* make_inner_r_n(count_t n, node_t* x, size_t xs) - { - assert(n >= 1); - auto p = make_inner_r_n(n); - auto r = p->relaxed(); - p->inner()[0] = x; - r->d.sizes[0] = xs; - r->d.count = 1; - return p; - } - - static node_t* make_inner_r_n(count_t n, node_t* x, node_t* y) - { - assert(n >= 2); - auto p = make_inner_r_n(n); - auto r = p->relaxed(); - p->inner()[0] = x; - p->inner()[1] = y; - r->d.count = 2; - return p; - } - - static node_t* make_inner_r_n(count_t n, node_t* x, size_t xs, node_t* y) - { - assert(n >= 2); - auto p = make_inner_r_n(n); - auto r = p->relaxed(); - p->inner()[0] = x; - p->inner()[1] = y; - r->d.sizes[0] = xs; - r->d.count = 2; - return p; - } - - static node_t* - make_inner_r_n(count_t n, node_t* x, size_t xs, node_t* y, size_t ys) - { - assert(n >= 2); - auto p = make_inner_r_n(n); - auto r = p->relaxed(); - p->inner()[0] = x; - p->inner()[1] = y; - r->d.sizes[0] = xs; - r->d.sizes[1] = xs + ys; - r->d.count = 2; - return p; - } - - static node_t* make_inner_r_n(count_t n, - node_t* x, - size_t xs, - node_t* y, - size_t ys, - node_t* z, - size_t zs) - { - assert(n >= 3); - auto p = make_inner_r_n(n); - auto r = p->relaxed(); - p->inner()[0] = x; - p->inner()[1] = y; - p->inner()[2] = z; - r->d.sizes[0] = xs; - r->d.sizes[1] = xs + ys; - r->d.sizes[2] = xs + ys + zs; - r->d.count = 3; - return p; - } - - template <typename U> - static node_t* make_leaf_n(count_t n, U&& x) - { - assert(n >= 1); - auto p = make_leaf_n(n); - try { - new (p->leaf()) T{std::forward<U>(x)}; - } catch (...) { - heap::deallocate(node_t::sizeof_leaf_n(n), p); - throw; - } - return p; - } - - template <typename U> - static node_t* make_leaf_e(edit_t e, U&& x) - { - auto p = make_leaf_e(e); - try { - new (p->leaf()) T{std::forward<U>(x)}; - } catch (...) { - heap::deallocate(node_t::max_sizeof_leaf, p); - throw; - } - return p; - } - - static node_t* make_path(shift_t shift, node_t* node) - { - IMMER_ASSERT_TAGGED(node->kind() == kind_t::leaf); - if (shift == endshift<B, BL>) - return node; - else { - auto n = node_t::make_inner_n(1); - try { - n->inner()[0] = make_path(shift - B, node); - } catch (...) { - heap::deallocate(node_t::sizeof_inner_n(1), n); - throw; - } - return n; - } - } - - static node_t* make_path_e(edit_t e, shift_t shift, node_t* node) - { - IMMER_ASSERT_TAGGED(node->kind() == kind_t::leaf); - if (shift == endshift<B, BL>) - return node; - else { - auto n = node_t::make_inner_e(e); - try { - n->inner()[0] = make_path_e(e, shift - B, node); - } catch (...) { - heap::deallocate(node_t::max_sizeof_inner, n); - throw; - } - return n; - } - } - - static node_t* copy_inner(node_t* src, count_t n) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - auto dst = make_inner_n(n); - inc_nodes(src->inner(), n); - std::uninitialized_copy(src->inner(), src->inner() + n, dst->inner()); - return dst; - } - - static node_t* copy_inner_n(count_t allocn, node_t* src, count_t n) - { - assert(allocn >= n); - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - auto dst = make_inner_n(allocn); - return do_copy_inner(dst, src, n); - } - - static node_t* copy_inner_e(edit_t e, node_t* src, count_t n) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - auto dst = make_inner_e(e); - return do_copy_inner(dst, src, n); - } - - static node_t* do_copy_inner(node_t* dst, node_t* src, count_t n) - { - IMMER_ASSERT_TAGGED(dst->kind() == kind_t::inner); - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - auto p = src->inner(); - inc_nodes(p, n); - std::uninitialized_copy(p, p + n, dst->inner()); - return dst; - } - - static node_t* copy_inner_r(node_t* src, count_t n) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - auto dst = make_inner_r_n(n); - return do_copy_inner_r(dst, src, n); - } - - static node_t* copy_inner_r_n(count_t allocn, node_t* src, count_t n) - { - assert(allocn >= n); - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - auto dst = make_inner_r_n(allocn); - return do_copy_inner_r(dst, src, n); - } - - static node_t* copy_inner_r_e(edit_t e, node_t* src, count_t n) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - auto dst = make_inner_r_e(e); - return do_copy_inner_r(dst, src, n); - } - - static node_t* copy_inner_sr_e(edit_t e, node_t* src, count_t n) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - auto dst = make_inner_sr_e(e, src->relaxed()); - return do_copy_inner_sr(dst, src, n); - } - - static node_t* do_copy_inner_r(node_t* dst, node_t* src, count_t n) - { - IMMER_ASSERT_TAGGED(dst->kind() == kind_t::inner); - IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner); - auto src_r = src->relaxed(); - auto dst_r = dst->relaxed(); - inc_nodes(src->inner(), n); - std::copy(src->inner(), src->inner() + n, dst->inner()); - std::copy(src_r->d.sizes, src_r->d.sizes + n, dst_r->d.sizes); - dst_r->d.count = n; - return dst; - } - - static node_t* do_copy_inner_sr(node_t* dst, node_t* src, count_t n) - { - if (embed_relaxed) - return do_copy_inner_r(dst, src, n); - else { - inc_nodes(src->inner(), n); - std::copy(src->inner(), src->inner() + n, dst->inner()); - return dst; - } - } - - static node_t* copy_leaf(node_t* src, count_t n) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf); - auto dst = make_leaf_n(n); - try { - std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf()); - } catch (...) { - heap::deallocate(node_t::sizeof_leaf_n(n), dst); - throw; - } - return dst; - } - - static node_t* copy_leaf_e(edit_t e, node_t* src, count_t n) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf); - auto dst = make_leaf_e(e); - try { - std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf()); - } catch (...) { - heap::deallocate(node_t::max_sizeof_leaf, dst); - throw; - } - return dst; - } - - static node_t* copy_leaf_n(count_t allocn, node_t* src, count_t n) - { - assert(allocn >= n); - IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf); - auto dst = make_leaf_n(allocn); - try { - std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf()); - } catch (...) { - heap::deallocate(node_t::sizeof_leaf_n(allocn), dst); - throw; - } - return dst; - } - - static node_t* copy_leaf(node_t* src1, count_t n1, node_t* src2, count_t n2) - { - IMMER_ASSERT_TAGGED(src1->kind() == kind_t::leaf); - IMMER_ASSERT_TAGGED(src2->kind() == kind_t::leaf); - auto dst = make_leaf_n(n1 + n2); - try { - std::uninitialized_copy( - src1->leaf(), src1->leaf() + n1, dst->leaf()); - } catch (...) { - heap::deallocate(node_t::sizeof_leaf_n(n1 + n2), dst); - throw; - } - try { - std::uninitialized_copy( - src2->leaf(), src2->leaf() + n2, dst->leaf() + n1); - } catch (...) { - destroy_n(dst->leaf(), n1); - heap::deallocate(node_t::sizeof_leaf_n(n1 + n2), dst); - throw; - } - return dst; - } - - static node_t* - copy_leaf_e(edit_t e, node_t* src1, count_t n1, node_t* src2, count_t n2) - { - IMMER_ASSERT_TAGGED(src1->kind() == kind_t::leaf); - IMMER_ASSERT_TAGGED(src2->kind() == kind_t::leaf); - auto dst = make_leaf_e(e); - try { - std::uninitialized_copy( - src1->leaf(), src1->leaf() + n1, dst->leaf()); - } catch (...) { - heap::deallocate(max_sizeof_leaf, dst); - throw; - } - try { - std::uninitialized_copy( - src2->leaf(), src2->leaf() + n2, dst->leaf() + n1); - } catch (...) { - destroy_n(dst->leaf(), n1); - heap::deallocate(max_sizeof_leaf, dst); - throw; - } - return dst; - } - - static node_t* copy_leaf_e(edit_t e, node_t* src, count_t idx, count_t last) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf); - auto dst = make_leaf_e(e); - try { - std::uninitialized_copy( - src->leaf() + idx, src->leaf() + last, dst->leaf()); - } catch (...) { - heap::deallocate(max_sizeof_leaf, dst); - throw; - } - return dst; - } - - static node_t* copy_leaf(node_t* src, count_t idx, count_t last) - { - IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf); - auto dst = make_leaf_n(last - idx); - try { - std::uninitialized_copy( - src->leaf() + idx, src->leaf() + last, dst->leaf()); - } catch (...) { - heap::deallocate(node_t::sizeof_leaf_n(last - idx), dst); - throw; - } - return dst; - } - - template <typename U> - static node_t* copy_leaf_emplace(node_t* src, count_t n, U&& x) - { - auto dst = copy_leaf_n(n + 1, src, n); - try { - new (dst->leaf() + n) T{std::forward<U>(x)}; - } catch (...) { - destroy_n(dst->leaf(), n); - heap::deallocate(node_t::sizeof_leaf_n(n + 1), dst); - throw; - } - return dst; - } - - static void delete_inner(node_t* p, count_t n) - { - IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner); - assert(!p->relaxed()); - heap::deallocate(ownee(p).owned() ? node_t::max_sizeof_inner - : node_t::sizeof_inner_n(n), - p); - } - - static void delete_inner_e(node_t* p) - { - IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner); - assert(!p->relaxed()); - heap::deallocate(node_t::max_sizeof_inner, p); - } - - static void delete_inner_any(node_t* p, count_t n) - { - if (p->relaxed()) - delete_inner_r(p, n); - else - delete_inner(p, n); - } - - static void delete_inner_r(node_t* p, count_t n) - { - IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner); - auto r = p->relaxed(); - assert(r); - static_if<!embed_relaxed>([&](auto) { - if (node_t::refs(r).dec()) - heap::deallocate(node_t::ownee(r).owned() - ? node_t::max_sizeof_relaxed - : node_t::sizeof_relaxed_n(n), - r); - }); - heap::deallocate(ownee(p).owned() ? node_t::max_sizeof_inner_r - : node_t::sizeof_inner_r_n(n), - p); - } - - static void delete_inner_r_e(node_t* p) - { - IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner); - auto r = p->relaxed(); - assert(r); - static_if<!embed_relaxed>([&](auto) { - if (node_t::refs(r).dec()) - heap::deallocate(node_t::max_sizeof_relaxed, r); - }); - heap::deallocate(node_t::max_sizeof_inner_r, p); - } - - static void delete_leaf(node_t* p, count_t n) - { - IMMER_ASSERT_TAGGED(p->kind() == kind_t::leaf); - destroy_n(p->leaf(), n); - heap::deallocate(ownee(p).owned() ? node_t::max_sizeof_leaf - : node_t::sizeof_leaf_n(n), - p); - } - - bool can_mutate(edit_t e) const - { - return refs(this).unique() || ownee(this).can_mutate(e); - } - - bool can_relax() const { return !embed_relaxed || relaxed(); } - - relaxed_t* ensure_mutable_relaxed(edit_t e) - { - auto src_r = relaxed(); - return static_if<embed_relaxed, relaxed_t*>( - [&](auto) { return src_r; }, - [&](auto) { - if (node_t::refs(src_r).unique() || - node_t::ownee(src_r).can_mutate(e)) - return src_r; - else { - if (src_r) - node_t::refs(src_r).dec_unsafe(); - auto dst_r = impl.d.data.inner.relaxed = - new (heap::allocate(max_sizeof_relaxed)) relaxed_t; - node_t::ownee(dst_r) = e; - return dst_r; - } - }); - } - - relaxed_t* ensure_mutable_relaxed_e(edit_t e, edit_t ec) - { - auto src_r = relaxed(); - return static_if<embed_relaxed, relaxed_t*>( - [&](auto) { return src_r; }, - [&](auto) { - if (src_r && (node_t::refs(src_r).unique() || - node_t::ownee(src_r).can_mutate(e))) { - node_t::ownee(src_r) = ec; - return src_r; - } else { - if (src_r) - node_t::refs(src_r).dec_unsafe(); - auto dst_r = impl.d.data.inner.relaxed = - new (heap::allocate(max_sizeof_relaxed)) relaxed_t; - node_t::ownee(dst_r) = ec; - return dst_r; - } - }); - } - - relaxed_t* ensure_mutable_relaxed_n(edit_t e, count_t n) - { - auto src_r = relaxed(); - return static_if<embed_relaxed, relaxed_t*>( - [&](auto) { return src_r; }, - [&](auto) { - if (node_t::refs(src_r).unique() || - node_t::ownee(src_r).can_mutate(e)) - return src_r; - else { - if (src_r) - node_t::refs(src_r).dec_unsafe(); - auto dst_r = - new (heap::allocate(max_sizeof_relaxed)) relaxed_t; - std::copy( - src_r->d.sizes, src_r->d.sizes + n, dst_r->d.sizes); - node_t::ownee(dst_r) = e; - return impl.d.data.inner.relaxed = dst_r; - } - }); - } - - node_t* inc() - { - refs(this).inc(); - return this; - } - - const node_t* inc() const - { - refs(this).inc(); - return this; - } - - bool dec() const { return refs(this).dec(); } - void dec_unsafe() const { refs(this).dec_unsafe(); } - - static void inc_nodes(node_t** p, count_t n) - { - for (auto i = p, e = i + n; i != e; ++i) - refs(*i).inc(); - } - -#if IMMER_TAGGED_NODE - shift_t compute_shift() - { - if (kind() == kind_t::leaf) - return endshift<B, BL>; - else - return B + inner()[0]->compute_shift(); - } -#endif - - bool check(shift_t shift, size_t size) - { -#if IMMER_DEBUG_DEEP_CHECK - assert(size > 0); - if (shift == endshift<B, BL>) { - IMMER_ASSERT_TAGGED(kind() == kind_t::leaf); - assert(size <= branches<BL>); - } else if (auto r = relaxed()) { - auto count = r->d.count; - assert(count > 0); - assert(count <= branches<B>); - if (r->d.sizes[count - 1] != size) { - IMMER_TRACE_F("check"); - IMMER_TRACE_E(r->d.sizes[count - 1]); - IMMER_TRACE_E(size); - } - assert(r->d.sizes[count - 1] == size); - for (auto i = 1; i < count; ++i) - assert(r->d.sizes[i - 1] < r->d.sizes[i]); - auto last_size = size_t{}; - for (auto i = 0; i < count; ++i) { - assert(inner()[i]->check(shift - B, r->d.sizes[i] - last_size)); - last_size = r->d.sizes[i]; - } - } else { - assert(size <= branches<B> << shift); - auto count = - (size >> shift) + (size - ((size >> shift) << shift) > 0); - assert(count <= branches<B>); - if (count) { - for (auto i = 1; i < count - 1; ++i) - assert(inner()[i]->check(shift - B, 1 << shift)); - assert(inner()[count - 1]->check( - shift - B, size - ((count - 1) << shift))); - } - } -#endif // IMMER_DEBUG_DEEP_CHECK - return true; - } -}; - -template <typename T, typename MP, bits_t B> -constexpr bits_t derive_bits_leaf_aux() -{ - using node_t = node<T, MP, B, B>; - constexpr auto sizeof_elem = sizeof(T); - constexpr auto space = - node_t::max_sizeof_inner - node_t::sizeof_packed_leaf_n(0); - constexpr auto full_elems = space / sizeof_elem; - constexpr auto BL = log2(full_elems); - return BL; -} - -template <typename T, typename MP, bits_t B> -constexpr bits_t derive_bits_leaf = derive_bits_leaf_aux<T, MP, B>(); - -} // namespace rbts -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/rbts/operations.hpp b/third_party/immer/immer/detail/rbts/operations.hpp deleted file mode 100644 index ff703e892b42..000000000000 --- a/third_party/immer/immer/detail/rbts/operations.hpp +++ /dev/null @@ -1,2461 +0,0 @@ -// -// 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 <algorithm> -#include <memory> -#include <numeric> -#include <utility> - -#include <immer/config.hpp> -#include <immer/detail/rbts/position.hpp> -#include <immer/detail/rbts/visitor.hpp> -#include <immer/detail/util.hpp> -#include <immer/heap/tags.hpp> - -namespace immer { -namespace detail { -namespace rbts { - -template <typename T> -struct array_for_visitor : visitor_base<array_for_visitor<T>> -{ - using this_t = array_for_visitor; - - template <typename PosT> - static T* visit_inner(PosT&& pos, size_t idx) - { - return pos.descend(this_t{}, idx); - } - - template <typename PosT> - static T* visit_leaf(PosT&& pos, size_t) - { - return pos.node()->leaf(); - } -}; - -template <typename T> -struct region_for_visitor : visitor_base<region_for_visitor<T>> -{ - using this_t = region_for_visitor; - using result_t = std::tuple<T*, size_t, size_t>; - - template <typename PosT> - static result_t visit_inner(PosT&& pos, size_t idx) - { - return pos.towards(this_t{}, idx); - } - - template <typename PosT> - static result_t visit_leaf(PosT&& pos, size_t idx) - { - return std::make_tuple(pos.node()->leaf(), pos.index(idx), pos.count()); - } -}; - -template <typename T> -struct get_visitor : visitor_base<get_visitor<T>> -{ - using this_t = get_visitor; - - template <typename PosT> - static const T& visit_inner(PosT&& pos, size_t idx) - { - return pos.descend(this_t{}, idx); - } - - template <typename PosT> - static const T& visit_leaf(PosT&& pos, size_t idx) - { - return pos.node()->leaf()[pos.index(idx)]; - } -}; - -struct for_each_chunk_visitor : visitor_base<for_each_chunk_visitor> -{ - using this_t = for_each_chunk_visitor; - - template <typename Pos, typename Fn> - static void visit_inner(Pos&& pos, Fn&& fn) - { - pos.each(this_t{}, fn); - } - - template <typename Pos, typename Fn> - static void visit_leaf(Pos&& pos, Fn&& fn) - { - auto data = pos.node()->leaf(); - fn(data, data + pos.count()); - } -}; - -struct for_each_chunk_p_visitor : visitor_base<for_each_chunk_p_visitor> -{ - using this_t = for_each_chunk_p_visitor; - - template <typename Pos, typename Fn> - static bool visit_inner(Pos&& pos, Fn&& fn) - { - return pos.each_pred(this_t{}, fn); - } - - template <typename Pos, typename Fn> - static bool visit_leaf(Pos&& pos, Fn&& fn) - { - auto data = pos.node()->leaf(); - return fn(data, data + pos.count()); - } -}; - -struct for_each_chunk_left_visitor : visitor_base<for_each_chunk_left_visitor> -{ - using this_t = for_each_chunk_left_visitor; - - template <typename Pos, typename Fn> - static void visit_inner(Pos&& pos, size_t last, Fn&& fn) - { - auto l = pos.index(last); - pos.each_left(for_each_chunk_visitor{}, l, fn); - pos.towards_oh(this_t{}, last, l, fn); - } - - template <typename Pos, typename Fn> - static void visit_leaf(Pos&& pos, size_t last, Fn&& fn) - { - auto data = pos.node()->leaf(); - auto l = pos.index(last); - fn(data, data + l + 1); - } -}; - -struct for_each_chunk_right_visitor : visitor_base<for_each_chunk_right_visitor> -{ - using this_t = for_each_chunk_right_visitor; - - template <typename Pos, typename Fn> - static void visit_inner(Pos&& pos, size_t first, Fn&& fn) - { - auto f = pos.index(first); - pos.towards_oh(this_t{}, first, f, fn); - pos.each_right(for_each_chunk_visitor{}, f + 1, fn); - } - - template <typename Pos, typename Fn> - static void visit_leaf(Pos&& pos, size_t first, Fn&& fn) - { - auto data = pos.node()->leaf(); - auto f = pos.index(first); - fn(data + f, data + pos.count()); - } -}; - -struct for_each_chunk_i_visitor : visitor_base<for_each_chunk_i_visitor> -{ - using this_t = for_each_chunk_i_visitor; - - template <typename Pos, typename Fn> - static void visit_relaxed(Pos&& pos, size_t first, size_t last, Fn&& fn) - { - // we are going towards *two* indices, so we need to do the - // relaxed as a special case to correct the second index - if (first < last) { - auto f = pos.index(first); - auto l = pos.index(last - 1); - if (f == l) { - auto sbh = pos.size_before(f); - pos.towards_oh_sbh(this_t{}, first, f, sbh, last - sbh, fn); - } else { - assert(f < l); - pos.towards_oh(for_each_chunk_right_visitor{}, first, f, fn); - pos.each_i(for_each_chunk_visitor{}, f + 1, l, fn); - pos.towards_oh(for_each_chunk_left_visitor{}, last - 1, l, fn); - } - } - } - - template <typename Pos, typename Fn> - static void visit_regular(Pos&& pos, size_t first, size_t last, Fn&& fn) - { - if (first < last) { - auto f = pos.index(first); - auto l = pos.index(last - 1); - if (f == l) - pos.towards_oh(this_t{}, first, f, last, fn); - else { - assert(f < l); - pos.towards_oh(for_each_chunk_right_visitor{}, first, f, fn); - pos.each_i(for_each_chunk_visitor{}, f + 1, l, fn); - pos.towards_oh(for_each_chunk_left_visitor{}, last - 1, l, fn); - } - } - } - - template <typename Pos, typename Fn> - static void visit_leaf(Pos&& pos, size_t first, size_t last, Fn&& fn) - { - auto data = pos.node()->leaf(); - if (first < last) { - auto f = pos.index(first); - auto l = pos.index(last - 1); - fn(data + f, data + l + 1); - } - } -}; - -struct for_each_chunk_p_left_visitor - : visitor_base<for_each_chunk_p_left_visitor> -{ - using this_t = for_each_chunk_p_left_visitor; - - template <typename Pos, typename Fn> - static bool visit_inner(Pos&& pos, size_t last, Fn&& fn) - { - auto l = pos.index(last); - return pos.each_pred_left(for_each_chunk_p_visitor{}, l, fn) && - pos.towards_oh(this_t{}, last, l, fn); - } - - template <typename Pos, typename Fn> - static bool visit_leaf(Pos&& pos, size_t last, Fn&& fn) - { - auto data = pos.node()->leaf(); - auto l = pos.index(last); - return fn(data, data + l + 1); - } -}; - -struct for_each_chunk_p_right_visitor - : visitor_base<for_each_chunk_p_right_visitor> -{ - using this_t = for_each_chunk_p_right_visitor; - - template <typename Pos, typename Fn> - static bool visit_inner(Pos&& pos, size_t first, Fn&& fn) - { - auto f = pos.index(first); - return pos.towards_oh(this_t{}, first, f, fn) && - pos.each_pred_right(for_each_chunk_p_visitor{}, f + 1, fn); - } - - template <typename Pos, typename Fn> - static bool visit_leaf(Pos&& pos, size_t first, Fn&& fn) - { - auto data = pos.node()->leaf(); - auto f = pos.index(first); - return fn(data + f, data + pos.count()); - } -}; - -struct for_each_chunk_p_i_visitor : visitor_base<for_each_chunk_p_i_visitor> -{ - using this_t = for_each_chunk_p_i_visitor; - - template <typename Pos, typename Fn> - static bool visit_relaxed(Pos&& pos, size_t first, size_t last, Fn&& fn) - { - // we are going towards *two* indices, so we need to do the - // relaxed as a special case to correct the second index - if (first < last) { - auto f = pos.index(first); - auto l = pos.index(last - 1); - if (f == l) { - auto sbh = pos.size_before(f); - return pos.towards_oh_sbh( - this_t{}, first, f, sbh, last - sbh, fn); - } else { - assert(f < l); - return pos.towards_oh( - for_each_chunk_p_right_visitor{}, first, f, fn) && - pos.each_pred_i( - for_each_chunk_p_visitor{}, f + 1, l, fn) && - pos.towards_oh( - for_each_chunk_p_left_visitor{}, last - 1, l, fn); - } - } - return true; - } - - template <typename Pos, typename Fn> - static bool visit_regular(Pos&& pos, size_t first, size_t last, Fn&& fn) - { - if (first < last) { - auto f = pos.index(first); - auto l = pos.index(last - 1); - if (f == l) - return pos.towards_oh(this_t{}, first, f, last, fn); - else { - assert(f < l); - return pos.towards_oh( - for_each_chunk_p_right_visitor{}, first, f, fn) && - pos.each_pred_i( - for_each_chunk_p_visitor{}, f + 1, l, fn) && - pos.towards_oh( - for_each_chunk_p_left_visitor{}, last - 1, l, fn); - } - } - return true; - } - - template <typename Pos, typename Fn> - static bool visit_leaf(Pos&& pos, size_t first, size_t last, Fn&& fn) - { - auto data = pos.node()->leaf(); - if (first < last) { - auto f = pos.index(first); - auto l = pos.index(last - 1); - return fn(data + f, data + l + 1); - } - return true; - } -}; - -struct equals_visitor : visitor_base<equals_visitor> -{ - using this_t = equals_visitor; - - struct this_aux_t : visitor_base<this_aux_t> - { - template <typename PosR, typename PosL, typename Iter> - static bool visit_inner( - PosR&& posr, count_t i, PosL&& posl, Iter&& first, size_t idx) - { - return posl.nth_sub(i, this_t{}, posr, first, idx); - } - - template <typename PosR, typename PosL, typename Iter> - static bool visit_leaf( - PosR&& posr, count_t i, PosL&& posl, Iter&& first, size_t idx) - { - return posl.nth_sub_leaf(i, this_t{}, posr, first, idx); - } - }; - - struct rrb : visitor_base<rrb> - { - template <typename PosR, typename Iter, typename Node> - static bool visit_node(PosR&& posr, - Iter&& first, - Node* rootl, - shift_t shiftl, - size_t sizel) - { - assert(shiftl <= posr.shift()); - return shiftl == posr.shift() - ? visit_maybe_relaxed_sub(rootl, - shiftl, - sizel, - this_t{}, - posr, - first, - size_t{}) - : posr.first_sub_inner( - rrb{}, first, rootl, shiftl, sizel); - } - }; - - template <typename Iter> - static auto equal_chunk_p(Iter&& iter) - { - return [iter](auto f, auto e) mutable { - if (f == &*iter) { - iter += e - f; - return true; - } - for (; f != e; ++f, ++iter) - if (*f != *iter) - return false; - return true; - }; - } - - template <typename PosL, typename PosR, typename Iter> - static bool - visit_relaxed(PosL&& posl, PosR&& posr, Iter&& first, size_t idx) - { - auto nl = posl.node(); - auto nr = posr.node(); - if (nl == nr) - return true; - auto cl = posl.count(); - auto cr = posr.count(); - assert(cr > 0); - auto sbr = size_t{}; - auto i = count_t{}; - auto j = count_t{}; - for (; i < cl; ++i) { - auto sbl = posl.size_before(i); - for (; j + 1 < cr && (sbr = posr.size_before(j)) < sbl; ++j) - ; - auto res = - sbl == sbr - ? posr.nth_sub(j, this_aux_t{}, i, posl, first, idx + sbl) - : posl.nth_sub(i, - for_each_chunk_p_visitor{}, - this_t::equal_chunk_p(first + (idx + sbl))); - if (!res) - return false; - } - return true; - } - - template <typename PosL, typename PosR, typename Iter> - static std::enable_if_t<is_relaxed_v<PosR>, bool> - visit_regular(PosL&& posl, PosR&& posr, Iter&& first, size_t idx) - { - return this_t::visit_relaxed(posl, posr, first, idx); - } - - template <typename PosL, typename PosR, typename Iter> - static std::enable_if_t<!is_relaxed_v<PosR>, bool> - visit_regular(PosL&& posl, PosR&& posr, Iter&& first, size_t idx) - { - return posl.count() >= posr.count() - ? this_t::visit_regular(posl, posr.node()) - : this_t::visit_regular(posr, posl.node()); - } - - template <typename PosL, typename PosR, typename Iter> - static bool visit_leaf(PosL&& posl, PosR&& posr, Iter&& first, size_t idx) - { - if (posl.node() == posr.node()) - return true; - auto cl = posl.count(); - auto cr = posr.count(); - auto mp = std::min(cl, cr); - return std::equal(posl.node()->leaf(), - posl.node()->leaf() + mp, - posr.node()->leaf()) && - std::equal(posl.node()->leaf() + mp, - posl.node()->leaf() + posl.count(), - first + (idx + mp)); - } - - template <typename Pos, typename NodeT> - static bool visit_regular(Pos&& pos, NodeT* other) - { - auto node = pos.node(); - return node == other || pos.each_pred_zip(this_t{}, other); - } - - template <typename Pos, typename NodeT> - static bool visit_leaf(Pos&& pos, NodeT* other) - { - auto node = pos.node(); - return node == other || std::equal(node->leaf(), - node->leaf() + pos.count(), - other->leaf()); - } -}; - -template <typename NodeT> -struct update_visitor : visitor_base<update_visitor<NodeT>> -{ - using node_t = NodeT; - using this_t = update_visitor; - - template <typename Pos, typename Fn> - static node_t* visit_relaxed(Pos&& pos, size_t idx, Fn&& fn) - { - auto offset = pos.index(idx); - auto count = pos.count(); - auto node = node_t::make_inner_sr_n(count, pos.relaxed()); - try { - auto child = pos.towards_oh(this_t{}, idx, offset, fn); - node_t::do_copy_inner_sr(node, pos.node(), count); - node->inner()[offset]->dec_unsafe(); - node->inner()[offset] = child; - return node; - } catch (...) { - node_t::delete_inner_r(node, count); - throw; - } - } - - template <typename Pos, typename Fn> - static node_t* visit_regular(Pos&& pos, size_t idx, Fn&& fn) - { - auto offset = pos.index(idx); - auto count = pos.count(); - auto node = node_t::make_inner_n(count); - try { - auto child = pos.towards_oh_ch(this_t{}, idx, offset, count, fn); - node_t::do_copy_inner(node, pos.node(), count); - node->inner()[offset]->dec_unsafe(); - node->inner()[offset] = child; - return node; - } catch (...) { - node_t::delete_inner(node, count); - throw; - } - } - - template <typename Pos, typename Fn> - static node_t* visit_leaf(Pos&& pos, size_t idx, Fn&& fn) - { - auto offset = pos.index(idx); - auto node = node_t::copy_leaf(pos.node(), pos.count()); - try { - node->leaf()[offset] = - std::forward<Fn>(fn)(std::move(node->leaf()[offset])); - return node; - } catch (...) { - node_t::delete_leaf(node, pos.count()); - throw; - } - } -}; - -struct dec_visitor : visitor_base<dec_visitor> -{ - using this_t = dec_visitor; - - template <typename Pos> - static void visit_relaxed(Pos&& p) - { - using node_t = node_type<Pos>; - auto node = p.node(); - if (node->dec()) { - p.each(this_t{}); - node_t::delete_inner_r(node, p.count()); - } - } - - template <typename Pos> - static void visit_regular(Pos&& p) - { - using node_t = node_type<Pos>; - auto node = p.node(); - if (node->dec()) { - p.each(this_t{}); - node_t::delete_inner(node, p.count()); - } - } - - template <typename Pos> - static void visit_leaf(Pos&& p) - { - using node_t = node_type<Pos>; - auto node = p.node(); - if (node->dec()) { - node_t::delete_leaf(node, p.count()); - } - } -}; - -template <typename NodeT> -void dec_leaf(NodeT* node, count_t n) -{ - make_leaf_sub_pos(node, n).visit(dec_visitor{}); -} - -template <typename NodeT> -void dec_inner(NodeT* node, shift_t shift, size_t size) -{ - visit_maybe_relaxed_sub(node, shift, size, dec_visitor()); -} - -template <typename NodeT> -void dec_relaxed(NodeT* node, shift_t shift) -{ - make_relaxed_pos(node, shift, node->relaxed()).visit(dec_visitor()); -} - -template <typename NodeT> -void dec_regular(NodeT* node, shift_t shift, size_t size) -{ - make_regular_pos(node, shift, size).visit(dec_visitor()); -} - -template <typename NodeT> -void dec_empty_regular(NodeT* node) -{ - make_empty_regular_pos(node).visit(dec_visitor()); -} - -template <typename NodeT> -struct get_mut_visitor : visitor_base<get_mut_visitor<NodeT>> -{ - using node_t = NodeT; - using this_t = get_mut_visitor; - using value_t = typename NodeT::value_t; - using edit_t = typename NodeT::edit_t; - - template <typename Pos> - static value_t& - visit_relaxed(Pos&& pos, size_t idx, edit_t e, node_t** location) - { - auto offset = pos.index(idx); - auto count = pos.count(); - auto node = pos.node(); - if (node->can_mutate(e)) { - return pos.towards_oh( - this_t{}, idx, offset, e, &node->inner()[offset]); - } else { - auto new_node = node_t::copy_inner_sr_e(e, node, count); - try { - auto& res = pos.towards_oh( - this_t{}, idx, offset, e, &new_node->inner()[offset]); - pos.visit(dec_visitor{}); - *location = new_node; - return res; - } catch (...) { - dec_relaxed(new_node, pos.shift()); - throw; - } - } - } - - template <typename Pos> - static value_t& - visit_regular(Pos&& pos, size_t idx, edit_t e, node_t** location) - { - assert(pos.node() == *location); - auto offset = pos.index(idx); - auto count = pos.count(); - auto node = pos.node(); - if (node->can_mutate(e)) { - return pos.towards_oh_ch( - this_t{}, idx, offset, count, e, &node->inner()[offset]); - } else { - auto new_node = node_t::copy_inner_e(e, node, count); - try { - auto& res = pos.towards_oh_ch(this_t{}, - idx, - offset, - count, - e, - &new_node->inner()[offset]); - pos.visit(dec_visitor{}); - *location = new_node; - return res; - } catch (...) { - dec_regular(new_node, pos.shift(), pos.size()); - throw; - } - } - } - - template <typename Pos> - static value_t& - visit_leaf(Pos&& pos, size_t idx, edit_t e, node_t** location) - { - assert(pos.node() == *location); - auto node = pos.node(); - if (node->can_mutate(e)) { - return node->leaf()[pos.index(idx)]; - } else { - auto new_node = node_t::copy_leaf_e(e, pos.node(), pos.count()); - pos.visit(dec_visitor{}); - *location = new_node; - return new_node->leaf()[pos.index(idx)]; - } - } -}; - -template <typename NodeT, bool Mutating = true> -struct push_tail_mut_visitor - : visitor_base<push_tail_mut_visitor<NodeT, Mutating>> -{ - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - using this_t = push_tail_mut_visitor; - using this_no_mut_t = push_tail_mut_visitor<NodeT, false>; - using node_t = NodeT; - using edit_t = typename NodeT::edit_t; - - template <typename Pos> - static node_t* visit_relaxed(Pos&& pos, edit_t e, node_t* tail, count_t ts) - { - auto node = pos.node(); - auto level = pos.shift(); - auto idx = pos.count() - 1; - auto children = pos.size(idx); - auto new_idx = - children == size_t{1} << level || level == BL ? idx + 1 : idx; - auto new_child = static_cast<node_t*>(nullptr); - auto mutate = Mutating && node->can_mutate(e); - - if (new_idx >= branches<B>) - return nullptr; - else if (idx == new_idx) { - new_child = - mutate ? pos.last_oh_csh(this_t{}, idx, children, e, tail, ts) - : pos.last_oh_csh( - this_no_mut_t{}, idx, children, e, tail, ts); - if (!new_child) { - if (++new_idx < branches<B>) - new_child = node_t::make_path_e(e, level - B, tail); - else - return nullptr; - } - } else - new_child = node_t::make_path_e(e, level - B, tail); - - if (mutate) { - auto count = new_idx + 1; - auto relaxed = node->ensure_mutable_relaxed_n(e, new_idx); - node->inner()[new_idx] = new_child; - relaxed->d.sizes[new_idx] = pos.size() + ts; - relaxed->d.count = count; - return node; - } else { - try { - auto count = new_idx + 1; - auto new_node = node_t::copy_inner_r_e(e, pos.node(), new_idx); - auto relaxed = new_node->relaxed(); - new_node->inner()[new_idx] = new_child; - relaxed->d.sizes[new_idx] = pos.size() + ts; - relaxed->d.count = count; - if (Mutating) - pos.visit(dec_visitor{}); - return new_node; - } catch (...) { - auto shift = pos.shift(); - auto size = new_idx == idx ? children + ts : ts; - if (shift > BL) { - tail->inc(); - dec_inner(new_child, shift - B, size); - } - throw; - } - } - } - - template <typename Pos, typename... Args> - static node_t* visit_regular(Pos&& pos, edit_t e, node_t* tail, Args&&...) - { - assert((pos.size() & mask<BL>) == 0); - auto node = pos.node(); - auto idx = pos.index(pos.size() - 1); - auto new_idx = pos.index(pos.size() + branches<BL> - 1); - auto mutate = Mutating && node->can_mutate(e); - if (mutate) { - node->inner()[new_idx] = - idx == new_idx ? pos.last_oh(this_t{}, idx, e, tail) - /* otherwise */ - : node_t::make_path_e(e, pos.shift() - B, tail); - return node; - } else { - auto new_parent = node_t::make_inner_e(e); - try { - new_parent->inner()[new_idx] = - idx == new_idx - ? pos.last_oh(this_no_mut_t{}, idx, e, tail) - /* otherwise */ - : node_t::make_path_e(e, pos.shift() - B, tail); - node_t::do_copy_inner(new_parent, node, new_idx); - if (Mutating) - pos.visit(dec_visitor{}); - return new_parent; - } catch (...) { - node_t::delete_inner_e(new_parent); - throw; - } - } - } - - template <typename Pos, typename... Args> - static node_t* visit_leaf(Pos&& pos, edit_t e, node_t* tail, Args&&...) - { - IMMER_UNREACHABLE; - } -}; - -template <typename NodeT> -struct push_tail_visitor : visitor_base<push_tail_visitor<NodeT>> -{ - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - using this_t = push_tail_visitor; - using node_t = NodeT; - - template <typename Pos> - static node_t* visit_relaxed(Pos&& pos, node_t* tail, count_t ts) - { - auto level = pos.shift(); - auto idx = pos.count() - 1; - auto children = pos.size(idx); - auto new_idx = - children == size_t{1} << level || level == BL ? idx + 1 : idx; - auto new_child = static_cast<node_t*>(nullptr); - if (new_idx >= branches<B>) - return nullptr; - else if (idx == new_idx) { - new_child = pos.last_oh_csh(this_t{}, idx, children, tail, ts); - if (!new_child) { - if (++new_idx < branches<B>) - new_child = node_t::make_path(level - B, tail); - else - return nullptr; - } - } else - new_child = node_t::make_path(level - B, tail); - try { - auto count = new_idx + 1; - auto new_parent = - node_t::copy_inner_r_n(count, pos.node(), new_idx); - auto new_relaxed = new_parent->relaxed(); - new_parent->inner()[new_idx] = new_child; - new_relaxed->d.sizes[new_idx] = pos.size() + ts; - new_relaxed->d.count = count; - return new_parent; - } catch (...) { - auto shift = pos.shift(); - auto size = new_idx == idx ? children + ts : ts; - if (shift > BL) { - tail->inc(); - dec_inner(new_child, shift - B, size); - } - throw; - } - } - - template <typename Pos, typename... Args> - static node_t* visit_regular(Pos&& pos, node_t* tail, Args&&...) - { - assert((pos.size() & mask<BL>) == 0); - auto idx = pos.index(pos.size() - 1); - auto new_idx = pos.index(pos.size() + branches<BL> - 1); - auto count = new_idx + 1; - auto new_parent = node_t::make_inner_n(count); - try { - new_parent->inner()[new_idx] = - idx == new_idx ? pos.last_oh(this_t{}, idx, tail) - /* otherwise */ - : node_t::make_path(pos.shift() - B, tail); - } catch (...) { - node_t::delete_inner(new_parent, count); - throw; - } - return node_t::do_copy_inner(new_parent, pos.node(), new_idx); - } - - template <typename Pos, typename... Args> - static node_t* visit_leaf(Pos&& pos, node_t* tail, Args&&...) - { - IMMER_UNREACHABLE; - } -}; - -struct dec_right_visitor : visitor_base<dec_right_visitor> -{ - using this_t = dec_right_visitor; - using dec_t = dec_visitor; - - template <typename Pos> - static void visit_relaxed(Pos&& p, count_t idx) - { - using node_t = node_type<Pos>; - auto node = p.node(); - if (node->dec()) { - p.each_right(dec_t{}, idx); - node_t::delete_inner_r(node, p.count()); - } - } - - template <typename Pos> - static void visit_regular(Pos&& p, count_t idx) - { - using node_t = node_type<Pos>; - auto node = p.node(); - if (node->dec()) { - p.each_right(dec_t{}, idx); - node_t::delete_inner(node, p.count()); - } - } - - template <typename Pos> - static void visit_leaf(Pos&& p, count_t idx) - { - IMMER_UNREACHABLE; - } -}; - -template <typename NodeT, bool Collapse = true, bool Mutating = true> -struct slice_right_mut_visitor - : visitor_base<slice_right_mut_visitor<NodeT, Collapse, Mutating>> -{ - using node_t = NodeT; - using this_t = slice_right_mut_visitor; - using edit_t = typename NodeT::edit_t; - - // returns a new shift, new root, the new tail size and the new tail - using result_t = std::tuple<shift_t, NodeT*, count_t, NodeT*>; - using no_collapse_t = slice_right_mut_visitor<NodeT, false, true>; - using no_collapse_no_mut_t = slice_right_mut_visitor<NodeT, false, false>; - using no_mut_t = slice_right_mut_visitor<NodeT, Collapse, false>; - - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - template <typename PosT> - static result_t visit_relaxed(PosT&& pos, size_t last, edit_t e) - { - auto idx = pos.index(last); - auto node = pos.node(); - auto mutate = Mutating && node->can_mutate(e); - if (Collapse && idx == 0) { - auto res = mutate ? pos.towards_oh(this_t{}, last, idx, e) - : pos.towards_oh(no_mut_t{}, last, idx, e); - if (Mutating) - pos.visit(dec_right_visitor{}, count_t{1}); - return res; - } else { - using std::get; - auto subs = - mutate ? pos.towards_oh(no_collapse_t{}, last, idx, e) - : pos.towards_oh(no_collapse_no_mut_t{}, last, idx, e); - auto next = get<1>(subs); - auto ts = get<2>(subs); - auto tail = get<3>(subs); - try { - if (next) { - if (mutate) { - auto nodr = node->ensure_mutable_relaxed_n(e, idx); - pos.each_right(dec_visitor{}, idx + 1); - node->inner()[idx] = next; - nodr->d.sizes[idx] = last + 1 - ts; - nodr->d.count = idx + 1; - return std::make_tuple(pos.shift(), node, ts, tail); - } else { - auto newn = node_t::copy_inner_r_e(e, node, idx); - auto newr = newn->relaxed(); - newn->inner()[idx] = next; - newr->d.sizes[idx] = last + 1 - ts; - newr->d.count = idx + 1; - if (Mutating) - pos.visit(dec_visitor{}); - return std::make_tuple(pos.shift(), newn, ts, tail); - } - } else if (idx == 0) { - if (Mutating) - pos.visit(dec_right_visitor{}, count_t{1}); - return std::make_tuple(pos.shift(), nullptr, ts, tail); - } else if (Collapse && idx == 1 && pos.shift() > BL) { - auto newn = pos.node()->inner()[0]; - if (!mutate) - newn->inc(); - if (Mutating) - pos.visit(dec_right_visitor{}, count_t{2}); - return std::make_tuple(pos.shift() - B, newn, ts, tail); - } else { - if (mutate) { - pos.each_right(dec_visitor{}, idx + 1); - node->ensure_mutable_relaxed_n(e, idx)->d.count = idx; - return std::make_tuple(pos.shift(), node, ts, tail); - } else { - auto newn = node_t::copy_inner_r_e(e, node, idx); - if (Mutating) - pos.visit(dec_visitor{}); - return std::make_tuple(pos.shift(), newn, ts, tail); - } - } - } catch (...) { - assert(!mutate); - assert(!next || pos.shift() > BL); - if (next) - dec_inner(next, - pos.shift() - B, - last + 1 - ts - pos.size_before(idx)); - dec_leaf(tail, ts); - throw; - } - } - } - - template <typename PosT> - static result_t visit_regular(PosT&& pos, size_t last, edit_t e) - { - auto idx = pos.index(last); - auto node = pos.node(); - auto mutate = Mutating && node->can_mutate(e); - if (Collapse && idx == 0) { - auto res = mutate ? pos.towards_oh(this_t{}, last, idx, e) - : pos.towards_oh(no_mut_t{}, last, idx, e); - if (Mutating) - pos.visit(dec_right_visitor{}, count_t{1}); - return res; - } else { - using std::get; - auto subs = - mutate ? pos.towards_oh(no_collapse_t{}, last, idx, e) - : pos.towards_oh(no_collapse_no_mut_t{}, last, idx, e); - auto next = get<1>(subs); - auto ts = get<2>(subs); - auto tail = get<3>(subs); - try { - if (next) { - if (mutate) { - node->inner()[idx] = next; - pos.each_right(dec_visitor{}, idx + 1); - return std::make_tuple(pos.shift(), node, ts, tail); - } else { - auto newn = node_t::copy_inner_e(e, node, idx); - newn->inner()[idx] = next; - if (Mutating) - pos.visit(dec_visitor{}); - return std::make_tuple(pos.shift(), newn, ts, tail); - } - } else if (idx == 0) { - if (Mutating) - pos.visit(dec_right_visitor{}, count_t{1}); - return std::make_tuple(pos.shift(), nullptr, ts, tail); - } else if (Collapse && idx == 1 && pos.shift() > BL) { - auto newn = pos.node()->inner()[0]; - if (!mutate) - newn->inc(); - if (Mutating) - pos.visit(dec_right_visitor{}, count_t{2}); - return std::make_tuple(pos.shift() - B, newn, ts, tail); - } else { - if (mutate) { - pos.each_right(dec_visitor{}, idx + 1); - return std::make_tuple(pos.shift(), node, ts, tail); - } else { - auto newn = node_t::copy_inner_e(e, node, idx); - if (Mutating) - pos.visit(dec_visitor{}); - return std::make_tuple(pos.shift(), newn, ts, tail); - } - } - } catch (...) { - assert(!mutate); - assert(!next || pos.shift() > BL); - assert(tail); - if (next) - dec_regular(next, pos.shift() - B, last + 1 - ts); - dec_leaf(tail, ts); - throw; - } - } - } - - template <typename PosT> - static result_t visit_leaf(PosT&& pos, size_t last, edit_t e) - { - auto old_tail_size = pos.count(); - auto new_tail_size = pos.index(last) + 1; - auto node = pos.node(); - auto mutate = Mutating && node->can_mutate(e); - if (new_tail_size == old_tail_size) { - if (!Mutating) - node->inc(); - return std::make_tuple(0, nullptr, new_tail_size, node); - } else if (mutate) { - destroy_n(node->leaf() + new_tail_size, - old_tail_size - new_tail_size); - return std::make_tuple(0, nullptr, new_tail_size, node); - } else { - auto new_tail = node_t::copy_leaf_e(e, node, new_tail_size); - if (Mutating) - pos.visit(dec_visitor{}); - return std::make_tuple(0, nullptr, new_tail_size, new_tail); - } - } -}; - -template <typename NodeT, bool Collapse = true> -struct slice_right_visitor : visitor_base<slice_right_visitor<NodeT, Collapse>> -{ - using node_t = NodeT; - using this_t = slice_right_visitor; - - // returns a new shift, new root, the new tail size and the new tail - using result_t = std::tuple<shift_t, NodeT*, count_t, NodeT*>; - using no_collapse_t = slice_right_visitor<NodeT, false>; - - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - template <typename PosT> - static result_t visit_relaxed(PosT&& pos, size_t last) - { - auto idx = pos.index(last); - if (Collapse && idx == 0) { - return pos.towards_oh(this_t{}, last, idx); - } else { - using std::get; - auto subs = pos.towards_oh(no_collapse_t{}, last, idx); - auto next = get<1>(subs); - auto ts = get<2>(subs); - auto tail = get<3>(subs); - try { - if (next) { - auto count = idx + 1; - auto newn = node_t::copy_inner_r_n(count, pos.node(), idx); - auto newr = newn->relaxed(); - newn->inner()[idx] = next; - newr->d.sizes[idx] = last + 1 - ts; - newr->d.count = count; - return std::make_tuple(pos.shift(), newn, ts, tail); - } else if (idx == 0) { - return std::make_tuple(pos.shift(), nullptr, ts, tail); - } else if (Collapse && idx == 1 && pos.shift() > BL) { - auto newn = pos.node()->inner()[0]; - return std::make_tuple( - pos.shift() - B, newn->inc(), ts, tail); - } else { - auto newn = node_t::copy_inner_r(pos.node(), idx); - return std::make_tuple(pos.shift(), newn, ts, tail); - } - } catch (...) { - assert(!next || pos.shift() > BL); - if (next) - dec_inner(next, - pos.shift() - B, - last + 1 - ts - pos.size_before(idx)); - if (tail) - dec_leaf(tail, ts); - throw; - } - } - } - - template <typename PosT> - static result_t visit_regular(PosT&& pos, size_t last) - { - auto idx = pos.index(last); - if (Collapse && idx == 0) { - return pos.towards_oh(this_t{}, last, idx); - } else { - using std::get; - auto subs = pos.towards_oh(no_collapse_t{}, last, idx); - auto next = get<1>(subs); - auto ts = get<2>(subs); - auto tail = get<3>(subs); - try { - if (next) { - auto newn = node_t::copy_inner_n(idx + 1, pos.node(), idx); - newn->inner()[idx] = next; - return std::make_tuple(pos.shift(), newn, ts, tail); - } else if (idx == 0) { - return std::make_tuple(pos.shift(), nullptr, ts, tail); - } else if (Collapse && idx == 1 && pos.shift() > BL) { - auto newn = pos.node()->inner()[0]; - return std::make_tuple( - pos.shift() - B, newn->inc(), ts, tail); - } else { - auto newn = node_t::copy_inner_n(idx, pos.node(), idx); - return std::make_tuple(pos.shift(), newn, ts, tail); - } - } catch (...) { - assert(!next || pos.shift() > BL); - assert(tail); - if (next) - dec_regular(next, pos.shift() - B, last + 1 - ts); - dec_leaf(tail, ts); - throw; - } - } - } - - template <typename PosT> - static result_t visit_leaf(PosT&& pos, size_t last) - { - auto old_tail_size = pos.count(); - auto new_tail_size = pos.index(last) + 1; - auto new_tail = new_tail_size == old_tail_size - ? pos.node()->inc() - : node_t::copy_leaf(pos.node(), new_tail_size); - return std::make_tuple(0, nullptr, new_tail_size, new_tail); - } -}; - -struct dec_left_visitor : visitor_base<dec_left_visitor> -{ - using this_t = dec_left_visitor; - using dec_t = dec_visitor; - - template <typename Pos> - static void visit_relaxed(Pos&& p, count_t idx) - { - using node_t = node_type<Pos>; - auto node = p.node(); - if (node->dec()) { - p.each_left(dec_t{}, idx); - node_t::delete_inner_r(node, p.count()); - } - } - - template <typename Pos> - static void visit_regular(Pos&& p, count_t idx) - { - using node_t = node_type<Pos>; - auto node = p.node(); - if (node->dec()) { - p.each_left(dec_t{}, idx); - node_t::delete_inner(node, p.count()); - } - } - - template <typename Pos> - static void visit_leaf(Pos&& p, count_t idx) - { - IMMER_UNREACHABLE; - } -}; - -template <typename NodeT, bool Collapse = true, bool Mutating = true> -struct slice_left_mut_visitor - : visitor_base<slice_left_mut_visitor<NodeT, Collapse, Mutating>> -{ - using node_t = NodeT; - using this_t = slice_left_mut_visitor; - using edit_t = typename NodeT::edit_t; - using value_t = typename NodeT::value_t; - using relaxed_t = typename NodeT::relaxed_t; - // returns a new shift and new root - using result_t = std::tuple<shift_t, NodeT*>; - - using no_collapse_t = slice_left_mut_visitor<NodeT, false, true>; - using no_collapse_no_mut_t = slice_left_mut_visitor<NodeT, false, false>; - using no_mut_t = slice_left_mut_visitor<NodeT, Collapse, false>; - - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - template <typename PosT> - static result_t visit_relaxed(PosT&& pos, size_t first, edit_t e) - { - auto idx = pos.subindex(first); - auto count = pos.count(); - auto node = pos.node(); - auto mutate = Mutating && node->can_mutate(e); - auto left_size = pos.size_before(idx); - auto child_size = pos.size_sbh(idx, left_size); - auto dropped_size = first; - auto child_dropped_size = dropped_size - left_size; - if (Collapse && pos.shift() > BL && idx == pos.count() - 1) { - auto r = mutate ? pos.towards_sub_oh(this_t{}, first, idx, e) - : pos.towards_sub_oh(no_mut_t{}, first, idx, e); - if (Mutating) - pos.visit(dec_left_visitor{}, idx); - return r; - } else { - using std::get; - auto newn = mutate ? (node->ensure_mutable_relaxed(e), node) - : node_t::make_inner_r_e(e); - auto newr = newn->relaxed(); - auto newcount = count - idx; - auto new_child_size = child_size - child_dropped_size; - try { - auto subs = - mutate ? pos.towards_sub_oh(no_collapse_t{}, first, idx, e) - : pos.towards_sub_oh( - no_collapse_no_mut_t{}, first, idx, e); - if (mutate) - pos.each_left(dec_visitor{}, idx); - pos.copy_sizes( - idx + 1, newcount - 1, new_child_size, newr->d.sizes + 1); - std::uninitialized_copy(node->inner() + idx + 1, - node->inner() + count, - newn->inner() + 1); - newn->inner()[0] = get<1>(subs); - newr->d.sizes[0] = new_child_size; - newr->d.count = newcount; - if (!mutate) { - node_t::inc_nodes(newn->inner() + 1, newcount - 1); - if (Mutating) - pos.visit(dec_visitor{}); - } - return std::make_tuple(pos.shift(), newn); - } catch (...) { - if (!mutate) - node_t::delete_inner_r_e(newn); - throw; - } - } - } - - template <typename PosT> - static result_t visit_regular(PosT&& pos, size_t first, edit_t e) - { - auto idx = pos.subindex(first); - auto count = pos.count(); - auto node = pos.node(); - auto mutate = Mutating - // this is more restrictive than actually needed because - // it causes the algorithm to also avoid mutating the leaf - // in place - && !node_t::embed_relaxed && node->can_mutate(e); - auto left_size = pos.size_before(idx); - auto child_size = pos.size_sbh(idx, left_size); - auto dropped_size = first; - auto child_dropped_size = dropped_size - left_size; - if (Collapse && pos.shift() > BL && idx == pos.count() - 1) { - auto r = mutate ? pos.towards_sub_oh(this_t{}, first, idx, e) - : pos.towards_sub_oh(no_mut_t{}, first, idx, e); - if (Mutating) - pos.visit(dec_left_visitor{}, idx); - return r; - } else { - using std::get; - // if possible, we convert the node to a relaxed one - // simply by allocating a `relaxed_t` size table for - // it... maybe some of this magic should be moved as a - // `node<...>` static method... - auto newcount = count - idx; - auto newn = - mutate ? (node->impl.d.data.inner.relaxed = new ( - node_t::heap::allocate(node_t::max_sizeof_relaxed, - norefs_tag{})) relaxed_t, - node) - : node_t::make_inner_r_e(e); - auto newr = newn->relaxed(); - try { - auto subs = - mutate ? pos.towards_sub_oh(no_collapse_t{}, first, idx, e) - : pos.towards_sub_oh( - no_collapse_no_mut_t{}, first, idx, e); - if (mutate) - pos.each_left(dec_visitor{}, idx); - newr->d.sizes[0] = child_size - child_dropped_size; - pos.copy_sizes( - idx + 1, newcount - 1, newr->d.sizes[0], newr->d.sizes + 1); - newr->d.count = newcount; - newn->inner()[0] = get<1>(subs); - std::uninitialized_copy(node->inner() + idx + 1, - node->inner() + count, - newn->inner() + 1); - if (!mutate) { - node_t::inc_nodes(newn->inner() + 1, newcount - 1); - if (Mutating) - pos.visit(dec_visitor{}); - } - return std::make_tuple(pos.shift(), newn); - } catch (...) { - if (!mutate) - node_t::delete_inner_r_e(newn); - else { - // restore the regular node that we were - // attempting to relax... - node_t::heap::deallocate(node_t::max_sizeof_relaxed, - node->impl.d.data.inner.relaxed); - node->impl.d.data.inner.relaxed = nullptr; - } - throw; - } - } - } - - template <typename PosT> - static result_t visit_leaf(PosT&& pos, size_t first, edit_t e) - { - auto node = pos.node(); - auto idx = pos.index(first); - auto count = pos.count(); - auto mutate = Mutating && - std::is_nothrow_move_constructible<value_t>::value && - node->can_mutate(e); - if (mutate) { - auto data = node->leaf(); - auto newcount = count - idx; - std::move(data + idx, data + count, data); - destroy_n(data + newcount, idx); - return std::make_tuple(0, node); - } else { - auto newn = node_t::copy_leaf_e(e, node, idx, count); - if (Mutating) - pos.visit(dec_visitor{}); - return std::make_tuple(0, newn); - } - } -}; - -template <typename NodeT, bool Collapse = true> -struct slice_left_visitor : visitor_base<slice_left_visitor<NodeT, Collapse>> -{ - using node_t = NodeT; - using this_t = slice_left_visitor; - - // returns a new shift and new root - using result_t = std::tuple<shift_t, NodeT*>; - using no_collapse_t = slice_left_visitor<NodeT, false>; - - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - template <typename PosT> - static result_t visit_inner(PosT&& pos, size_t first) - { - auto idx = pos.subindex(first); - auto count = pos.count(); - auto left_size = pos.size_before(idx); - auto child_size = pos.size_sbh(idx, left_size); - auto dropped_size = first; - auto child_dropped_size = dropped_size - left_size; - if (Collapse && pos.shift() > BL && idx == pos.count() - 1) { - return pos.towards_sub_oh(this_t{}, first, idx); - } else { - using std::get; - auto n = pos.node(); - auto newc = count - idx; - auto newn = node_t::make_inner_r_n(newc); - try { - auto subs = pos.towards_sub_oh(no_collapse_t{}, first, idx); - auto newr = newn->relaxed(); - newr->d.count = count - idx; - newr->d.sizes[0] = child_size - child_dropped_size; - pos.copy_sizes(idx + 1, - newr->d.count - 1, - newr->d.sizes[0], - newr->d.sizes + 1); - assert(newr->d.sizes[newr->d.count - 1] == - pos.size() - dropped_size); - newn->inner()[0] = get<1>(subs); - std::uninitialized_copy(n->inner() + idx + 1, - n->inner() + count, - newn->inner() + 1); - node_t::inc_nodes(newn->inner() + 1, newr->d.count - 1); - return std::make_tuple(pos.shift(), newn); - } catch (...) { - node_t::delete_inner_r(newn, newc); - throw; - } - } - } - - template <typename PosT> - static result_t visit_leaf(PosT&& pos, size_t first) - { - auto n = node_t::copy_leaf(pos.node(), pos.index(first), pos.count()); - return std::make_tuple(0, n); - } -}; - -template <typename Node> -struct concat_center_pos -{ - static constexpr auto B = Node::bits; - static constexpr auto BL = Node::bits_leaf; - - static constexpr count_t max_children = 3; - - using node_t = Node; - using edit_t = typename Node::edit_t; - - shift_t shift_ = 0u; - count_t count_ = 0u; - node_t* nodes_[max_children]; - size_t sizes_[max_children]; - - auto shift() const { return shift_; } - - concat_center_pos(shift_t s, Node* n0, size_t s0) - : shift_{s} - , count_{1} - , nodes_{n0} - , sizes_{s0} - {} - - concat_center_pos(shift_t s, Node* n0, size_t s0, Node* n1, size_t s1) - : shift_{s} - , count_{2} - , nodes_{n0, n1} - , sizes_{s0, s1} - {} - - concat_center_pos(shift_t s, - Node* n0, - size_t s0, - Node* n1, - size_t s1, - Node* n2, - size_t s2) - : shift_{s} - , count_{3} - , nodes_{n0, n1, n2} - , sizes_{s0, s1, s2} - {} - - template <typename Visitor, typename... Args> - void each_sub(Visitor v, Args&&... args) - { - if (shift_ == BL) { - for (auto i = count_t{0}; i < count_; ++i) - make_leaf_sub_pos(nodes_[i], sizes_[i]).visit(v, args...); - } else { - for (auto i = count_t{0}; i < count_; ++i) - make_relaxed_pos(nodes_[i], shift_ - B, nodes_[i]->relaxed()) - .visit(v, args...); - } - } - - relaxed_pos<Node> realize() && - { - if (count_ > 1) { - try { - auto result = node_t::make_inner_r_n(count_); - auto r = result->relaxed(); - r->d.count = count_; - std::copy(nodes_, nodes_ + count_, result->inner()); - std::copy(sizes_, sizes_ + count_, r->d.sizes); - return {result, shift_, r}; - } catch (...) { - each_sub(dec_visitor{}); - throw; - } - } else { - assert(shift_ >= B + BL); - return {nodes_[0], shift_ - B, nodes_[0]->relaxed()}; - } - } - - relaxed_pos<Node> realize_e(edit_t e) - { - if (count_ > 1) { - auto result = node_t::make_inner_r_e(e); - auto r = result->relaxed(); - r->d.count = count_; - std::copy(nodes_, nodes_ + count_, result->inner()); - std::copy(sizes_, sizes_ + count_, r->d.sizes); - return {result, shift_, r}; - } else { - assert(shift_ >= B + BL); - return {nodes_[0], shift_ - B, nodes_[0]->relaxed()}; - } - } -}; - -template <typename Node> -struct concat_merger -{ - using node_t = Node; - static constexpr auto B = Node::bits; - static constexpr auto BL = Node::bits_leaf; - - using result_t = concat_center_pos<Node>; - - count_t* curr_; - count_t n_; - result_t result_; - - concat_merger(shift_t shift, count_t* counts, count_t n) - : curr_{counts} - , n_{n} - , result_{ - shift + B, node_t::make_inner_r_n(std::min(n_, branches<B>)), 0} - {} - - node_t* to_ = {}; - count_t to_offset_ = {}; - size_t to_size_ = {}; - - void add_child(node_t* p, size_t size) - { - ++curr_; - auto parent = result_.nodes_[result_.count_ - 1]; - auto relaxed = parent->relaxed(); - if (relaxed->d.count == branches<B>) { - assert(result_.count_ < result_t::max_children); - n_ -= branches<B>; - parent = node_t::make_inner_r_n(std::min(n_, branches<B>)); - relaxed = parent->relaxed(); - result_.nodes_[result_.count_] = parent; - result_.sizes_[result_.count_] = result_.sizes_[result_.count_ - 1]; - ++result_.count_; - } - auto idx = relaxed->d.count++; - result_.sizes_[result_.count_ - 1] += size; - relaxed->d.sizes[idx] = size + (idx ? relaxed->d.sizes[idx - 1] : 0); - parent->inner()[idx] = p; - }; - - template <typename Pos> - void merge_leaf(Pos&& p) - { - auto from = p.node(); - auto from_size = p.size(); - auto from_count = p.count(); - assert(from_size); - if (!to_ && *curr_ == from_count) { - add_child(from, from_size); - from->inc(); - } else { - auto from_offset = count_t{}; - auto from_data = from->leaf(); - do { - if (!to_) { - to_ = node_t::make_leaf_n(*curr_); - to_offset_ = 0; - } - auto data = to_->leaf(); - auto to_copy = - std::min(from_count - from_offset, *curr_ - to_offset_); - std::uninitialized_copy(from_data + from_offset, - from_data + from_offset + to_copy, - data + to_offset_); - to_offset_ += to_copy; - from_offset += to_copy; - if (*curr_ == to_offset_) { - add_child(to_, to_offset_); - to_ = nullptr; - } - } while (from_offset != from_count); - } - } - - template <typename Pos> - void merge_inner(Pos&& p) - { - auto from = p.node(); - auto from_size = p.size(); - auto from_count = p.count(); - assert(from_size); - if (!to_ && *curr_ == from_count) { - add_child(from, from_size); - from->inc(); - } else { - auto from_offset = count_t{}; - auto from_data = from->inner(); - do { - if (!to_) { - to_ = node_t::make_inner_r_n(*curr_); - to_offset_ = 0; - to_size_ = 0; - } - auto data = to_->inner(); - auto to_copy = - std::min(from_count - from_offset, *curr_ - to_offset_); - std::uninitialized_copy(from_data + from_offset, - from_data + from_offset + to_copy, - data + to_offset_); - node_t::inc_nodes(from_data + from_offset, to_copy); - auto sizes = to_->relaxed()->d.sizes; - p.copy_sizes( - from_offset, to_copy, to_size_, sizes + to_offset_); - to_offset_ += to_copy; - from_offset += to_copy; - to_size_ = sizes[to_offset_ - 1]; - if (*curr_ == to_offset_) { - to_->relaxed()->d.count = to_offset_; - add_child(to_, to_size_); - to_ = nullptr; - } - } while (from_offset != from_count); - } - } - - concat_center_pos<Node> finish() const - { - assert(!to_); - return result_; - } - - void abort() - { - auto shift = result_.shift_ - B; - if (to_) { - if (shift == BL) - node_t::delete_leaf(to_, to_offset_); - else { - to_->relaxed()->d.count = to_offset_; - dec_relaxed(to_, shift - B); - } - } - result_.each_sub(dec_visitor()); - } -}; - -struct concat_merger_visitor : visitor_base<concat_merger_visitor> -{ - using this_t = concat_merger_visitor; - - template <typename Pos, typename Merger> - static void visit_inner(Pos&& p, Merger& merger) - { - merger.merge_inner(p); - } - - template <typename Pos, typename Merger> - static void visit_leaf(Pos&& p, Merger& merger) - { - merger.merge_leaf(p); - } -}; - -struct concat_rebalance_plan_fill_visitor - : visitor_base<concat_rebalance_plan_fill_visitor> -{ - using this_t = concat_rebalance_plan_fill_visitor; - - template <typename Pos, typename Plan> - static void visit_node(Pos&& p, Plan& plan) - { - auto count = p.count(); - assert(plan.n < Plan::max_children); - plan.counts[plan.n++] = count; - plan.total += count; - } -}; - -template <bits_t B, bits_t BL> -struct concat_rebalance_plan -{ - static constexpr auto max_children = 2 * branches<B> + 1; - - count_t counts[max_children]; - count_t n = 0u; - count_t total = 0u; - - template <typename LPos, typename CPos, typename RPos> - void fill(LPos&& lpos, CPos&& cpos, RPos&& rpos) - { - assert(n == 0u); - assert(total == 0u); - using visitor_t = concat_rebalance_plan_fill_visitor; - lpos.each_left_sub(visitor_t{}, *this); - cpos.each_sub(visitor_t{}, *this); - rpos.each_right_sub(visitor_t{}, *this); - } - - void shuffle(shift_t shift) - { - // gcc seems to not really understand this code... :( -#if !defined(_MSC_VER) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" -#endif - constexpr count_t rrb_extras = 2; - constexpr count_t rrb_invariant = 1; - const auto bits = shift == BL ? BL : B; - const auto branches = count_t{1} << bits; - const auto optimal = ((total - 1) >> bits) + 1; - count_t i = 0; - while (n >= optimal + rrb_extras) { - // skip ok nodes - while (counts[i] > branches - rrb_invariant) - i++; - // short node, redistribute - auto remaining = counts[i]; - do { - auto count = std::min(remaining + counts[i + 1], branches); - counts[i] = count; - remaining += counts[i + 1] - count; - ++i; - } while (remaining > 0); - // remove node - std::move(counts + i + 1, counts + n, counts + i); - --n; - --i; - } -#if !defined(_MSC_VER) -#pragma GCC diagnostic pop -#endif - } - - template <typename LPos, typename CPos, typename RPos> - concat_center_pos<node_type<CPos>> - merge(LPos&& lpos, CPos&& cpos, RPos&& rpos) - { - using node_t = node_type<CPos>; - using merger_t = concat_merger<node_t>; - using visitor_t = concat_merger_visitor; - auto merger = merger_t{cpos.shift(), counts, n}; - try { - lpos.each_left_sub(visitor_t{}, merger); - cpos.each_sub(visitor_t{}, merger); - rpos.each_right_sub(visitor_t{}, merger); - cpos.each_sub(dec_visitor{}); - return merger.finish(); - } catch (...) { - merger.abort(); - throw; - } - } -}; - -template <typename Node, typename LPos, typename CPos, typename RPos> -concat_center_pos<Node> concat_rebalance(LPos&& lpos, CPos&& cpos, RPos&& rpos) -{ - auto plan = concat_rebalance_plan<Node::bits, Node::bits_leaf>{}; - plan.fill(lpos, cpos, rpos); - plan.shuffle(cpos.shift()); - try { - return plan.merge(lpos, cpos, rpos); - } catch (...) { - cpos.each_sub(dec_visitor{}); - throw; - } -} - -template <typename Node, typename LPos, typename TPos, typename RPos> -concat_center_pos<Node> concat_leafs(LPos&& lpos, TPos&& tpos, RPos&& rpos) -{ - static_assert(Node::bits >= 2, ""); - assert(lpos.shift() == tpos.shift()); - assert(lpos.shift() == rpos.shift()); - assert(lpos.shift() == 0); - if (tpos.count() > 0) - return { - Node::bits_leaf, - lpos.node()->inc(), - lpos.count(), - tpos.node()->inc(), - tpos.count(), - rpos.node()->inc(), - rpos.count(), - }; - else - return { - Node::bits_leaf, - lpos.node()->inc(), - lpos.count(), - rpos.node()->inc(), - rpos.count(), - }; -} - -template <typename Node> -struct concat_left_visitor; -template <typename Node> -struct concat_right_visitor; -template <typename Node> -struct concat_both_visitor; - -template <typename Node, typename LPos, typename TPos, typename RPos> -concat_center_pos<Node> concat_inners(LPos&& lpos, TPos&& tpos, RPos&& rpos) -{ - auto lshift = lpos.shift(); - auto rshift = rpos.shift(); - if (lshift > rshift) { - auto cpos = lpos.last_sub(concat_left_visitor<Node>{}, tpos, rpos); - return concat_rebalance<Node>(lpos, cpos, null_sub_pos{}); - } else if (lshift < rshift) { - auto cpos = rpos.first_sub(concat_right_visitor<Node>{}, lpos, tpos); - return concat_rebalance<Node>(null_sub_pos{}, cpos, rpos); - } else { - assert(lshift == rshift); - assert(Node::bits_leaf == 0u || lshift > 0); - auto cpos = lpos.last_sub(concat_both_visitor<Node>{}, tpos, rpos); - return concat_rebalance<Node>(lpos, cpos, rpos); - } -} - -template <typename Node> -struct concat_left_visitor : visitor_base<concat_left_visitor<Node>> -{ - using this_t = concat_left_visitor; - - template <typename LPos, typename TPos, typename RPos> - static concat_center_pos<Node> - visit_inner(LPos&& lpos, TPos&& tpos, RPos&& rpos) - { - return concat_inners<Node>(lpos, tpos, rpos); - } - - template <typename LPos, typename TPos, typename RPos> - static concat_center_pos<Node> - visit_leaf(LPos&& lpos, TPos&& tpos, RPos&& rpos) - { - IMMER_UNREACHABLE; - } -}; - -template <typename Node> -struct concat_right_visitor : visitor_base<concat_right_visitor<Node>> -{ - using this_t = concat_right_visitor; - - template <typename RPos, typename LPos, typename TPos> - static concat_center_pos<Node> - visit_inner(RPos&& rpos, LPos&& lpos, TPos&& tpos) - { - return concat_inners<Node>(lpos, tpos, rpos); - } - - template <typename RPos, typename LPos, typename TPos> - static concat_center_pos<Node> - visit_leaf(RPos&& rpos, LPos&& lpos, TPos&& tpos) - { - return concat_leafs<Node>(lpos, tpos, rpos); - } -}; - -template <typename Node> -struct concat_both_visitor : visitor_base<concat_both_visitor<Node>> -{ - using this_t = concat_both_visitor; - - template <typename LPos, typename TPos, typename RPos> - static concat_center_pos<Node> - visit_inner(LPos&& lpos, TPos&& tpos, RPos&& rpos) - { - return rpos.first_sub(concat_right_visitor<Node>{}, lpos, tpos); - } - - template <typename LPos, typename TPos, typename RPos> - static concat_center_pos<Node> - visit_leaf(LPos&& lpos, TPos&& tpos, RPos&& rpos) - { - return rpos.first_sub_leaf(concat_right_visitor<Node>{}, lpos, tpos); - } -}; - -template <typename Node> -struct concat_trees_right_visitor - : visitor_base<concat_trees_right_visitor<Node>> -{ - using this_t = concat_trees_right_visitor; - - template <typename RPos, typename LPos, typename TPos> - static concat_center_pos<Node> - visit_node(RPos&& rpos, LPos&& lpos, TPos&& tpos) - { - return concat_inners<Node>(lpos, tpos, rpos); - } -}; - -template <typename Node> -struct concat_trees_left_visitor : visitor_base<concat_trees_left_visitor<Node>> -{ - using this_t = concat_trees_left_visitor; - - template <typename LPos, typename TPos, typename... Args> - static concat_center_pos<Node> - visit_node(LPos&& lpos, TPos&& tpos, Args&&... args) - { - return visit_maybe_relaxed_sub( - args..., concat_trees_right_visitor<Node>{}, lpos, tpos); - } -}; - -template <typename Node> -relaxed_pos<Node> concat_trees(Node* lroot, - shift_t lshift, - size_t lsize, - Node* ltail, - count_t ltcount, - Node* rroot, - shift_t rshift, - size_t rsize) -{ - return visit_maybe_relaxed_sub(lroot, - lshift, - lsize, - concat_trees_left_visitor<Node>{}, - make_leaf_pos(ltail, ltcount), - rroot, - rshift, - rsize) - .realize(); -} - -template <typename Node> -relaxed_pos<Node> concat_trees( - Node* ltail, count_t ltcount, Node* rroot, shift_t rshift, size_t rsize) -{ - return make_singleton_regular_sub_pos(ltail, ltcount) - .visit(concat_trees_left_visitor<Node>{}, - empty_leaf_pos<Node>{}, - rroot, - rshift, - rsize) - .realize(); -} - -template <typename Node> -using concat_center_mut_pos = concat_center_pos<Node>; - -template <typename Node> -struct concat_merger_mut -{ - using node_t = Node; - using edit_t = typename Node::edit_t; - - static constexpr auto B = Node::bits; - static constexpr auto BL = Node::bits_leaf; - - using result_t = concat_center_pos<Node>; - - edit_t ec_ = {}; - - count_t* curr_; - count_t n_; - result_t result_; - count_t count_ = 0; - node_t* candidate_ = nullptr; - edit_t candidate_e_ = Node::memory::transience_t::noone; - - concat_merger_mut(edit_t ec, - shift_t shift, - count_t* counts, - count_t n, - edit_t candidate_e, - node_t* candidate) - : ec_{ec} - , curr_{counts} - , n_{n} - , result_{shift + B, nullptr, 0} - { - if (candidate) { - candidate->ensure_mutable_relaxed_e(candidate_e, ec); - result_.nodes_[0] = candidate; - } else { - result_.nodes_[0] = node_t::make_inner_r_e(ec); - } - } - - node_t* to_ = {}; - count_t to_offset_ = {}; - size_t to_size_ = {}; - - void set_candidate(edit_t candidate_e, node_t* candidate) - { - candidate_ = candidate; - candidate_e_ = candidate_e; - } - - void add_child(node_t* p, size_t size) - { - ++curr_; - auto parent = result_.nodes_[result_.count_ - 1]; - auto relaxed = parent->relaxed(); - if (count_ == branches<B>) { - parent->relaxed()->d.count = count_; - assert(result_.count_ < result_t::max_children); - n_ -= branches<B>; - if (candidate_) { - parent = candidate_; - parent->ensure_mutable_relaxed_e(candidate_e_, ec_); - candidate_ = nullptr; - } else - parent = node_t::make_inner_r_e(ec_); - count_ = 0; - relaxed = parent->relaxed(); - result_.nodes_[result_.count_] = parent; - result_.sizes_[result_.count_] = result_.sizes_[result_.count_ - 1]; - ++result_.count_; - } - auto idx = count_++; - result_.sizes_[result_.count_ - 1] += size; - relaxed->d.sizes[idx] = size + (idx ? relaxed->d.sizes[idx - 1] : 0); - parent->inner()[idx] = p; - }; - - template <typename Pos> - void merge_leaf(Pos&& p, edit_t e) - { - auto from = p.node(); - auto from_size = p.size(); - auto from_count = p.count(); - assert(from_size); - if (!to_ && *curr_ == from_count) { - add_child(from, from_size); - } else { - auto from_offset = count_t{}; - auto from_data = from->leaf(); - auto from_mutate = from->can_mutate(e); - do { - if (!to_) { - if (from_mutate) { - node_t::ownee(from) = ec_; - to_ = from->inc(); - assert(from_count); - } else { - to_ = node_t::make_leaf_e(ec_); - } - to_offset_ = 0; - } - auto data = to_->leaf(); - auto to_copy = - std::min(from_count - from_offset, *curr_ - to_offset_); - if (from == to_) { - if (from_offset != to_offset_) - std::move(from_data + from_offset, - from_data + from_offset + to_copy, - data + to_offset_); - } else { - if (!from_mutate) - std::uninitialized_copy(from_data + from_offset, - from_data + from_offset + - to_copy, - data + to_offset_); - else - detail::uninitialized_move(from_data + from_offset, - from_data + from_offset + - to_copy, - data + to_offset_); - } - to_offset_ += to_copy; - from_offset += to_copy; - if (*curr_ == to_offset_) { - add_child(to_, to_offset_); - to_ = nullptr; - } - } while (from_offset != from_count); - } - } - - template <typename Pos> - void merge_inner(Pos&& p, edit_t e) - { - auto from = p.node(); - auto from_size = p.size(); - auto from_count = p.count(); - assert(from_size); - if (!to_ && *curr_ == from_count) { - add_child(from, from_size); - } else { - auto from_offset = count_t{}; - auto from_data = from->inner(); - auto from_mutate = from->can_relax() && from->can_mutate(e); - do { - if (!to_) { - if (from_mutate) { - node_t::ownee(from) = ec_; - from->ensure_mutable_relaxed_e(e, ec_); - to_ = from; - } else { - to_ = node_t::make_inner_r_e(ec_); - } - to_offset_ = 0; - to_size_ = 0; - } - auto data = to_->inner(); - auto to_copy = - std::min(from_count - from_offset, *curr_ - to_offset_); - auto sizes = to_->relaxed()->d.sizes; - if (from != to_ || from_offset != to_offset_) { - std::copy(from_data + from_offset, - from_data + from_offset + to_copy, - data + to_offset_); - p.copy_sizes( - from_offset, to_copy, to_size_, sizes + to_offset_); - } - to_offset_ += to_copy; - from_offset += to_copy; - to_size_ = sizes[to_offset_ - 1]; - if (*curr_ == to_offset_) { - to_->relaxed()->d.count = to_offset_; - add_child(to_, to_size_); - to_ = nullptr; - } - } while (from_offset != from_count); - } - } - - concat_center_pos<Node> finish() const - { - assert(!to_); - result_.nodes_[result_.count_ - 1]->relaxed()->d.count = count_; - return result_; - } - - void abort() - { - // We may have mutated stuff the tree in place, leaving - // everything in a corrupted state... It should be possible - // to define cleanup properly, but that is a task for some - // other day... ;) - std::terminate(); - } -}; - -struct concat_merger_mut_visitor : visitor_base<concat_merger_mut_visitor> -{ - using this_t = concat_merger_mut_visitor; - - template <typename Pos, typename Merger> - static void visit_inner(Pos&& p, Merger& merger, edit_type<Pos> e) - { - merger.merge_inner(p, e); - } - - template <typename Pos, typename Merger> - static void visit_leaf(Pos&& p, Merger& merger, edit_type<Pos> e) - { - merger.merge_leaf(p, e); - } -}; - -template <bits_t B, bits_t BL> -struct concat_rebalance_plan_mut : concat_rebalance_plan<B, BL> -{ - using this_t = concat_rebalance_plan_mut; - - template <typename LPos, typename CPos, typename RPos> - concat_center_mut_pos<node_type<CPos>> merge(edit_type<CPos> ec, - edit_type<CPos> el, - LPos&& lpos, - CPos&& cpos, - edit_type<CPos> er, - RPos&& rpos) - { - using node_t = node_type<CPos>; - using merger_t = concat_merger_mut<node_t>; - using visitor_t = concat_merger_mut_visitor; - auto lnode = ((node_t*) lpos.node()); - auto rnode = ((node_t*) rpos.node()); - auto lmut2 = lnode && lnode->can_relax() && lnode->can_mutate(el); - auto rmut2 = rnode && rnode->can_relax() && rnode->can_mutate(er); - auto merger = merger_t{ec, - cpos.shift(), - this->counts, - this->n, - el, - lmut2 ? lnode : nullptr}; - try { - lpos.each_left_sub(visitor_t{}, merger, el); - cpos.each_sub(visitor_t{}, merger, ec); - if (rmut2) - merger.set_candidate(er, rnode); - rpos.each_right_sub(visitor_t{}, merger, er); - return merger.finish(); - } catch (...) { - merger.abort(); - throw; - } - } -}; - -template <typename Node, typename LPos, typename CPos, typename RPos> -concat_center_pos<Node> concat_rebalance_mut(edit_type<Node> ec, - edit_type<Node> el, - LPos&& lpos, - CPos&& cpos, - edit_type<Node> er, - RPos&& rpos) -{ - auto plan = concat_rebalance_plan_mut<Node::bits, Node::bits_leaf>{}; - plan.fill(lpos, cpos, rpos); - plan.shuffle(cpos.shift()); - return plan.merge(ec, el, lpos, cpos, er, rpos); -} - -template <typename Node, typename LPos, typename TPos, typename RPos> -concat_center_mut_pos<Node> concat_leafs_mut(edit_type<Node> ec, - edit_type<Node> el, - LPos&& lpos, - TPos&& tpos, - edit_type<Node> er, - RPos&& rpos) -{ - static_assert(Node::bits >= 2, ""); - assert(lpos.shift() == tpos.shift()); - assert(lpos.shift() == rpos.shift()); - assert(lpos.shift() == 0); - if (tpos.count() > 0) - return { - Node::bits_leaf, - lpos.node(), - lpos.count(), - tpos.node(), - tpos.count(), - rpos.node(), - rpos.count(), - }; - else - return { - Node::bits_leaf, - lpos.node(), - lpos.count(), - rpos.node(), - rpos.count(), - }; -} - -template <typename Node> -struct concat_left_mut_visitor; -template <typename Node> -struct concat_right_mut_visitor; -template <typename Node> -struct concat_both_mut_visitor; - -template <typename Node, typename LPos, typename TPos, typename RPos> -concat_center_mut_pos<Node> concat_inners_mut(edit_type<Node> ec, - edit_type<Node> el, - LPos&& lpos, - TPos&& tpos, - edit_type<Node> er, - RPos&& rpos) -{ - auto lshift = lpos.shift(); - auto rshift = rpos.shift(); - // lpos.node() can be null it is a singleton_regular_sub_pos<...>, - // this is, when the tree is just a tail... - if (lshift > rshift) { - auto cpos = lpos.last_sub( - concat_left_mut_visitor<Node>{}, ec, el, tpos, er, rpos); - return concat_rebalance_mut<Node>( - ec, el, lpos, cpos, er, null_sub_pos{}); - } else if (lshift < rshift) { - auto cpos = rpos.first_sub( - concat_right_mut_visitor<Node>{}, ec, el, lpos, tpos, er); - return concat_rebalance_mut<Node>( - ec, el, null_sub_pos{}, cpos, er, rpos); - } else { - assert(lshift == rshift); - assert(Node::bits_leaf == 0u || lshift > 0); - auto cpos = lpos.last_sub( - concat_both_mut_visitor<Node>{}, ec, el, tpos, er, rpos); - return concat_rebalance_mut<Node>(ec, el, lpos, cpos, er, rpos); - } -} - -template <typename Node> -struct concat_left_mut_visitor : visitor_base<concat_left_mut_visitor<Node>> -{ - using this_t = concat_left_mut_visitor; - using edit_t = typename Node::edit_t; - - template <typename LPos, typename TPos, typename RPos> - static concat_center_mut_pos<Node> visit_inner( - LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos) - { - return concat_inners_mut<Node>(ec, el, lpos, tpos, er, rpos); - } - - template <typename LPos, typename TPos, typename RPos> - static concat_center_mut_pos<Node> visit_leaf( - LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos) - { - IMMER_UNREACHABLE; - } -}; - -template <typename Node> -struct concat_right_mut_visitor : visitor_base<concat_right_mut_visitor<Node>> -{ - using this_t = concat_right_mut_visitor; - using edit_t = typename Node::edit_t; - - template <typename RPos, typename LPos, typename TPos> - static concat_center_mut_pos<Node> visit_inner( - RPos&& rpos, edit_t ec, edit_t el, LPos&& lpos, TPos&& tpos, edit_t er) - { - return concat_inners_mut<Node>(ec, el, lpos, tpos, er, rpos); - } - - template <typename RPos, typename LPos, typename TPos> - static concat_center_mut_pos<Node> visit_leaf( - RPos&& rpos, edit_t ec, edit_t el, LPos&& lpos, TPos&& tpos, edit_t er) - { - return concat_leafs_mut<Node>(ec, el, lpos, tpos, er, rpos); - } -}; - -template <typename Node> -struct concat_both_mut_visitor : visitor_base<concat_both_mut_visitor<Node>> -{ - using this_t = concat_both_mut_visitor; - using edit_t = typename Node::edit_t; - - template <typename LPos, typename TPos, typename RPos> - static concat_center_mut_pos<Node> visit_inner( - LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos) - { - return rpos.first_sub( - concat_right_mut_visitor<Node>{}, ec, el, lpos, tpos, er); - } - - template <typename LPos, typename TPos, typename RPos> - static concat_center_mut_pos<Node> visit_leaf( - LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos) - { - return rpos.first_sub_leaf( - concat_right_mut_visitor<Node>{}, ec, el, lpos, tpos, er); - } -}; - -template <typename Node> -struct concat_trees_right_mut_visitor - : visitor_base<concat_trees_right_mut_visitor<Node>> -{ - using this_t = concat_trees_right_mut_visitor; - using edit_t = typename Node::edit_t; - - template <typename RPos, typename LPos, typename TPos> - static concat_center_mut_pos<Node> visit_node( - RPos&& rpos, edit_t ec, edit_t el, LPos&& lpos, TPos&& tpos, edit_t er) - { - return concat_inners_mut<Node>(ec, el, lpos, tpos, er, rpos); - } -}; - -template <typename Node> -struct concat_trees_left_mut_visitor - : visitor_base<concat_trees_left_mut_visitor<Node>> -{ - using this_t = concat_trees_left_mut_visitor; - using edit_t = typename Node::edit_t; - - template <typename LPos, typename TPos, typename... Args> - static concat_center_mut_pos<Node> visit_node(LPos&& lpos, - edit_t ec, - edit_t el, - TPos&& tpos, - edit_t er, - Args&&... args) - { - return visit_maybe_relaxed_sub(args..., - concat_trees_right_mut_visitor<Node>{}, - ec, - el, - lpos, - tpos, - er); - } -}; - -template <typename Node> -relaxed_pos<Node> concat_trees_mut(edit_type<Node> ec, - edit_type<Node> el, - Node* lroot, - shift_t lshift, - size_t lsize, - Node* ltail, - count_t ltcount, - edit_type<Node> er, - Node* rroot, - shift_t rshift, - size_t rsize) -{ - return visit_maybe_relaxed_sub(lroot, - lshift, - lsize, - concat_trees_left_mut_visitor<Node>{}, - ec, - el, - make_leaf_pos(ltail, ltcount), - er, - rroot, - rshift, - rsize) - .realize_e(ec); -} - -template <typename Node> -relaxed_pos<Node> concat_trees_mut(edit_type<Node> ec, - edit_type<Node> el, - Node* ltail, - count_t ltcount, - edit_type<Node> er, - Node* rroot, - shift_t rshift, - size_t rsize) -{ - return make_singleton_regular_sub_pos(ltail, ltcount) - .visit(concat_trees_left_mut_visitor<Node>{}, - ec, - el, - empty_leaf_pos<Node>{}, - er, - rroot, - rshift, - rsize) - .realize_e(ec); -} - -} // namespace rbts -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/rbts/position.hpp b/third_party/immer/immer/detail/rbts/position.hpp deleted file mode 100644 index e9472b294088..000000000000 --- a/third_party/immer/immer/detail/rbts/position.hpp +++ /dev/null @@ -1,1977 +0,0 @@ -// -// 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 <immer/config.hpp> -#include <immer/detail/rbts/bits.hpp> - -#include <cassert> -#include <type_traits> -#include <utility> - -namespace immer { -namespace detail { -namespace rbts { - -template <typename Pos> -constexpr auto bits = std::decay_t<Pos>::node_t::bits; - -template <typename Pos> -constexpr auto bits_leaf = std::decay_t<Pos>::node_t::bits_leaf; - -template <typename Pos> -using node_type = typename std::decay<Pos>::type::node_t; - -template <typename Pos> -using edit_type = typename std::decay<Pos>::type::node_t::edit_t; - -template <typename NodeT> -struct empty_regular_pos -{ - using node_t = NodeT; - node_t* node_; - - count_t count() const { return 0; } - node_t* node() const { return node_; } - shift_t shift() const { return 0; } - size_t size() const { return 0; } - - template <typename Visitor, typename... Args> - void each(Visitor, Args&&...) - {} - template <typename Visitor, typename... Args> - bool each_pred(Visitor, Args&&...) - { - return true; - } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_regular(*this, std::forward<Args>(args)...); - } -}; - -template <typename NodeT> -empty_regular_pos<NodeT> make_empty_regular_pos(NodeT* node) -{ - return {node}; -} - -template <typename NodeT> -struct empty_leaf_pos -{ - using node_t = NodeT; - node_t* node_; - - count_t count() const { return 0; } - node_t* node() const { return node_; } - shift_t shift() const { return 0; } - size_t size() const { return 0; } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_leaf(*this, std::forward<Args>(args)...); - } -}; - -template <typename NodeT> -empty_leaf_pos<NodeT> make_empty_leaf_pos(NodeT* node) -{ - assert(node); - return {node}; -} - -template <typename NodeT> -struct leaf_pos -{ - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - using node_t = NodeT; - node_t* node_; - size_t size_; - - count_t count() const { return index(size_ - 1) + 1; } - node_t* node() const { return node_; } - size_t size() const { return size_; } - shift_t shift() const { return 0; } - count_t index(size_t idx) const { return idx & mask<BL>; } - count_t subindex(size_t idx) const { return idx; } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_leaf(*this, std::forward<Args>(args)...); - } -}; - -template <typename NodeT> -leaf_pos<NodeT> make_leaf_pos(NodeT* node, size_t size) -{ - assert(node); - assert(size > 0); - return {node, size}; -} - -template <typename NodeT> -struct leaf_sub_pos -{ - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - using node_t = NodeT; - node_t* node_; - count_t count_; - - count_t count() const { return count_; } - node_t* node() const { return node_; } - size_t size() const { return count_; } - shift_t shift() const { return 0; } - count_t index(size_t idx) const { return idx & mask<BL>; } - count_t subindex(size_t idx) const { return idx; } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_leaf(*this, std::forward<Args>(args)...); - } -}; - -template <typename NodeT> -leaf_sub_pos<NodeT> make_leaf_sub_pos(NodeT* node, count_t count) -{ - assert(node); - assert(count <= branches<NodeT::bits_leaf>); - return {node, count}; -} - -template <typename NodeT> -struct leaf_descent_pos -{ - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - using node_t = NodeT; - node_t* node_; - - node_t* node() const { return node_; } - shift_t shift() const { return 0; } - count_t index(size_t idx) const { return idx & mask<BL>; } - - template <typename... Args> - decltype(auto) descend(Args&&...) - {} - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_leaf(*this, std::forward<Args>(args)...); - } -}; - -template <typename NodeT> -leaf_descent_pos<NodeT> make_leaf_descent_pos(NodeT* node) -{ - assert(node); - return {node}; -} - -template <typename NodeT> -struct full_leaf_pos -{ - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - using node_t = NodeT; - node_t* node_; - - count_t count() const { return branches<BL>; } - node_t* node() const { return node_; } - size_t size() const { return branches<BL>; } - shift_t shift() const { return 0; } - count_t index(size_t idx) const { return idx & mask<BL>; } - count_t subindex(size_t idx) const { return idx; } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_leaf(*this, std::forward<Args>(args)...); - } -}; - -template <typename NodeT> -full_leaf_pos<NodeT> make_full_leaf_pos(NodeT* node) -{ - assert(node); - return {node}; -} - -template <typename NodeT> -struct regular_pos -{ - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - using node_t = NodeT; - node_t* node_; - shift_t shift_; - size_t size_; - - count_t count() const { return index(size_ - 1) + 1; } - node_t* node() const { return node_; } - size_t size() const { return size_; } - shift_t shift() const { return shift_; } - count_t index(size_t idx) const { return (idx >> shift_) & mask<B>; } - count_t subindex(size_t idx) const { return idx >> shift_; } - size_t this_size() const - { - return ((size_ - 1) & ~(~size_t{} << (shift_ + B))) + 1; - } - - template <typename Visitor, typename... Args> - void each(Visitor v, Args&&... args) - { - return each_regular(*this, v, args...); - } - - template <typename Visitor, typename... Args> - bool each_pred(Visitor v, Args&&... args) - { - return each_pred_regular(*this, v, args...); - } - - template <typename Visitor, typename... Args> - bool each_pred_zip(Visitor v, node_t* other, Args&&... args) - { - return each_pred_zip_regular(*this, v, other, args...); - } - - template <typename Visitor, typename... Args> - bool each_pred_i(Visitor v, count_t i, count_t n, Args&&... args) - { - return each_pred_i_regular(*this, v, i, n, args...); - } - - template <typename Visitor, typename... Args> - bool each_pred_right(Visitor v, count_t start, Args&&... args) - { - return each_pred_right_regular(*this, v, start, args...); - } - - template <typename Visitor, typename... Args> - bool each_pred_left(Visitor v, count_t n, Args&&... args) - { - return each_pred_left_regular(*this, v, n, args...); - } - - template <typename Visitor, typename... Args> - void each_i(Visitor v, count_t i, count_t n, Args&&... args) - { - return each_i_regular(*this, v, i, n, args...); - } - - template <typename Visitor, typename... Args> - void each_right(Visitor v, count_t start, Args&&... args) - { - return each_right_regular(*this, v, start, args...); - } - - template <typename Visitor, typename... Args> - void each_left(Visitor v, count_t n, Args&&... args) - { - return each_left_regular(*this, v, n, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) towards(Visitor v, size_t idx, Args&&... args) - { - return towards_oh_ch_regular( - *this, v, idx, index(idx), count(), args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) - towards_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args) - { - return towards_oh_ch_regular( - *this, v, idx, offset_hint, count(), args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) towards_oh_ch(Visitor v, - size_t idx, - count_t offset_hint, - count_t count_hint, - Args&&... args) - { - return towards_oh_ch_regular( - *this, v, idx, offset_hint, count(), args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) - towards_sub_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args) - { - return towards_sub_oh_regular(*this, v, idx, offset_hint, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) last_oh(Visitor v, count_t offset_hint, Args&&... args) - { - return last_oh_regular(*this, v, offset_hint, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_regular(*this, std::forward<Args>(args)...); - } -}; - -template <typename Pos, typename Visitor, typename... Args> -void each_regular(Pos&& p, Visitor v, Args&&... args) -{ - constexpr auto B = bits<Pos>; - constexpr auto BL = bits_leaf<Pos>; - auto n = p.node()->inner(); - auto last = p.count() - 1; - auto e = n + last; - if (p.shift() == BL) { - for (; n != e; ++n) { - IMMER_PREFETCH(n + 1); - make_full_leaf_pos(*n).visit(v, args...); - } - make_leaf_pos(*n, p.size()).visit(v, args...); - } else { - auto ss = p.shift() - B; - for (; n != e; ++n) - make_full_pos(*n, ss).visit(v, args...); - make_regular_pos(*n, ss, p.size()).visit(v, args...); - } -} - -template <typename Pos, typename Visitor, typename... Args> -bool each_pred_regular(Pos&& p, Visitor v, Args&&... args) -{ - constexpr auto B = bits<Pos>; - constexpr auto BL = bits_leaf<Pos>; - auto n = p.node()->inner(); - auto last = p.count() - 1; - auto e = n + last; - if (p.shift() == BL) { - for (; n != e; ++n) { - IMMER_PREFETCH(n + 1); - if (!make_full_leaf_pos(*n).visit(v, args...)) - return false; - } - return make_leaf_pos(*n, p.size()).visit(v, args...); - } else { - auto ss = p.shift() - B; - for (; n != e; ++n) - if (!make_full_pos(*n, ss).visit(v, args...)) - return false; - return make_regular_pos(*n, ss, p.size()).visit(v, args...); - } -} - -template <typename Pos, typename Visitor, typename... Args> -bool each_pred_zip_regular(Pos&& p, - Visitor v, - node_type<Pos>* other, - Args&&... args) -{ - constexpr auto B = bits<Pos>; - constexpr auto BL = bits_leaf<Pos>; - - auto n = p.node()->inner(); - auto n2 = other->inner(); - auto last = p.count() - 1; - auto e = n + last; - if (p.shift() == BL) { - for (; n != e; ++n, ++n2) { - IMMER_PREFETCH(n + 1); - IMMER_PREFETCH(n2 + 1); - if (!make_full_leaf_pos(*n).visit(v, *n2, args...)) - return false; - } - return make_leaf_pos(*n, p.size()).visit(v, *n2, args...); - } else { - auto ss = p.shift() - B; - for (; n != e; ++n, ++n2) - if (!make_full_pos(*n, ss).visit(v, *n2, args...)) - return false; - return make_regular_pos(*n, ss, p.size()).visit(v, *n2, args...); - } -} - -template <typename Pos, typename Visitor, typename... Args> -bool each_pred_i_regular( - Pos&& p, Visitor v, count_t f, count_t l, Args&&... args) -{ - constexpr auto B = bits<Pos>; - constexpr auto BL = bits_leaf<Pos>; - - if (p.shift() == BL) { - if (l > f) { - if (l < p.count()) { - auto n = p.node()->inner() + f; - auto e = p.node()->inner() + l; - for (; n < e; ++n) { - IMMER_PREFETCH(n + 1); - if (!make_full_leaf_pos(*n).visit(v, args...)) - return false; - } - } else { - auto n = p.node()->inner() + f; - auto e = p.node()->inner() + l - 1; - for (; n < e; ++n) { - IMMER_PREFETCH(n + 1); - if (!make_full_leaf_pos(*n).visit(v, args...)) - return false; - } - if (!make_leaf_pos(*n, p.size()).visit(v, args...)) - return false; - } - } - } else { - if (l > f) { - auto ss = p.shift() - B; - if (l < p.count()) { - auto n = p.node()->inner() + f; - auto e = p.node()->inner() + l; - for (; n < e; ++n) - if (!make_full_pos(*n, ss).visit(v, args...)) - return false; - } else { - auto n = p.node()->inner() + f; - auto e = p.node()->inner() + l - 1; - for (; n < e; ++n) - if (!make_full_pos(*n, ss).visit(v, args...)) - return false; - if (!make_regular_pos(*n, ss, p.size()).visit(v, args...)) - return false; - } - } - } - return true; -} - -template <typename Pos, typename Visitor, typename... Args> -bool each_pred_left_regular(Pos&& p, Visitor v, count_t last, Args&&... args) -{ - constexpr auto B = bits<Pos>; - constexpr auto BL = bits_leaf<Pos>; - assert(last < p.count()); - if (p.shift() == BL) { - auto n = p.node()->inner(); - auto e = n + last; - for (; n != e; ++n) { - IMMER_PREFETCH(n + 1); - if (!make_full_leaf_pos(*n).visit(v, args...)) - return false; - } - } else { - auto n = p.node()->inner(); - auto e = n + last; - auto ss = p.shift() - B; - for (; n != e; ++n) - if (!make_full_pos(*n, ss).visit(v, args...)) - return false; - } - return true; -} - -template <typename Pos, typename Visitor, typename... Args> -bool each_pred_right_regular(Pos&& p, Visitor v, count_t start, Args&&... args) -{ - constexpr auto B = bits<Pos>; - constexpr auto BL = bits_leaf<Pos>; - - if (p.shift() == BL) { - auto n = p.node()->inner() + start; - auto last = p.count() - 1; - auto e = p.node()->inner() + last; - if (n <= e) { - for (; n != e; ++n) { - IMMER_PREFETCH(n + 1); - if (!make_full_leaf_pos(*n).visit(v, args...)) - return false; - } - if (!make_leaf_pos(*n, p.size()).visit(v, args...)) - return false; - } - } else { - auto n = p.node()->inner() + start; - auto last = p.count() - 1; - auto e = p.node()->inner() + last; - auto ss = p.shift() - B; - if (n <= e) { - for (; n != e; ++n) - if (!make_full_pos(*n, ss).visit(v, args...)) - return false; - if (!make_regular_pos(*n, ss, p.size()).visit(v, args...)) - return false; - } - } - return true; -} - -template <typename Pos, typename Visitor, typename... Args> -void each_i_regular(Pos&& p, Visitor v, count_t f, count_t l, Args&&... args) -{ - constexpr auto B = bits<Pos>; - constexpr auto BL = bits_leaf<Pos>; - - if (p.shift() == BL) { - if (l > f) { - if (l < p.count()) { - auto n = p.node()->inner() + f; - auto e = p.node()->inner() + l; - for (; n < e; ++n) { - IMMER_PREFETCH(n + 1); - make_full_leaf_pos(*n).visit(v, args...); - } - } else { - auto n = p.node()->inner() + f; - auto e = p.node()->inner() + l - 1; - for (; n < e; ++n) { - IMMER_PREFETCH(n + 1); - make_full_leaf_pos(*n).visit(v, args...); - } - make_leaf_pos(*n, p.size()).visit(v, args...); - } - } - } else { - if (l > f) { - auto ss = p.shift() - B; - if (l < p.count()) { - auto n = p.node()->inner() + f; - auto e = p.node()->inner() + l; - for (; n < e; ++n) - make_full_pos(*n, ss).visit(v, args...); - } else { - auto n = p.node()->inner() + f; - auto e = p.node()->inner() + l - 1; - for (; n < e; ++n) - make_full_pos(*n, ss).visit(v, args...); - make_regular_pos(*n, ss, p.size()).visit(v, args...); - } - } - } -} - -template <typename Pos, typename Visitor, typename... Args> -void each_left_regular(Pos&& p, Visitor v, count_t last, Args&&... args) -{ - constexpr auto B = bits<Pos>; - constexpr auto BL = bits_leaf<Pos>; - assert(last < p.count()); - if (p.shift() == BL) { - auto n = p.node()->inner(); - auto e = n + last; - for (; n != e; ++n) { - IMMER_PREFETCH(n + 1); - make_full_leaf_pos(*n).visit(v, args...); - } - } else { - auto n = p.node()->inner(); - auto e = n + last; - auto ss = p.shift() - B; - for (; n != e; ++n) - make_full_pos(*n, ss).visit(v, args...); - } -} - -template <typename Pos, typename Visitor, typename... Args> -void each_right_regular(Pos&& p, Visitor v, count_t start, Args&&... args) -{ - constexpr auto B = bits<Pos>; - constexpr auto BL = bits_leaf<Pos>; - - if (p.shift() == BL) { - auto n = p.node()->inner() + start; - auto last = p.count() - 1; - auto e = p.node()->inner() + last; - if (n <= e) { - for (; n != e; ++n) { - IMMER_PREFETCH(n + 1); - make_full_leaf_pos(*n).visit(v, args...); - } - make_leaf_pos(*n, p.size()).visit(v, args...); - } - } else { - auto n = p.node()->inner() + start; - auto last = p.count() - 1; - auto e = p.node()->inner() + last; - auto ss = p.shift() - B; - if (n <= e) { - for (; n != e; ++n) - make_full_pos(*n, ss).visit(v, args...); - make_regular_pos(*n, ss, p.size()).visit(v, args...); - } - } -} - -template <typename Pos, typename Visitor, typename... Args> -decltype(auto) towards_oh_ch_regular(Pos&& p, - Visitor v, - size_t idx, - count_t offset_hint, - count_t count_hint, - Args&&... args) -{ - constexpr auto B = bits<Pos>; - constexpr auto BL = bits_leaf<Pos>; - assert(offset_hint == p.index(idx)); - assert(count_hint == p.count()); - auto is_leaf = p.shift() == BL; - auto child = p.node()->inner()[offset_hint]; - auto is_full = offset_hint + 1 != count_hint; - return is_full - ? (is_leaf ? make_full_leaf_pos(child).visit(v, idx, args...) - : make_full_pos(child, p.shift() - B) - .visit(v, idx, args...)) - : (is_leaf - ? make_leaf_pos(child, p.size()).visit(v, idx, args...) - : make_regular_pos(child, p.shift() - B, p.size()) - .visit(v, idx, args...)); -} - -template <typename Pos, typename Visitor, typename... Args> -decltype(auto) towards_sub_oh_regular( - Pos&& p, Visitor v, size_t idx, count_t offset_hint, Args&&... args) -{ - constexpr auto B = bits<Pos>; - constexpr auto BL = bits_leaf<Pos>; - assert(offset_hint == p.index(idx)); - auto is_leaf = p.shift() == BL; - auto child = p.node()->inner()[offset_hint]; - auto lsize = offset_hint << p.shift(); - auto size = p.this_size(); - auto is_full = (size - lsize) >= (size_t{1} << p.shift()); - return is_full - ? (is_leaf - ? make_full_leaf_pos(child).visit(v, idx - lsize, args...) - : make_full_pos(child, p.shift() - B) - .visit(v, idx - lsize, args...)) - : (is_leaf - ? make_leaf_sub_pos(child, size - lsize) - .visit(v, idx - lsize, args...) - : make_regular_sub_pos(child, p.shift() - B, size - lsize) - .visit(v, idx - lsize, args...)); -} - -template <typename Pos, typename Visitor, typename... Args> -decltype(auto) -last_oh_regular(Pos&& p, Visitor v, count_t offset_hint, Args&&... args) -{ - assert(offset_hint == p.count() - 1); - constexpr auto B = bits<Pos>; - constexpr auto BL = bits_leaf<Pos>; - auto child = p.node()->inner()[offset_hint]; - auto is_leaf = p.shift() == BL; - return is_leaf ? make_leaf_pos(child, p.size()).visit(v, args...) - : make_regular_pos(child, p.shift() - B, p.size()) - .visit(v, args...); -} - -template <typename NodeT> -regular_pos<NodeT> make_regular_pos(NodeT* node, shift_t shift, size_t size) -{ - assert(node); - assert(shift >= NodeT::bits_leaf); - assert(size > 0); - return {node, shift, size}; -} - -struct null_sub_pos -{ - auto node() const { return nullptr; } - - template <typename Visitor, typename... Args> - void each_sub(Visitor, Args&&...) - {} - template <typename Visitor, typename... Args> - void each_right_sub(Visitor, Args&&...) - {} - template <typename Visitor, typename... Args> - void each_left_sub(Visitor, Args&&...) - {} - template <typename Visitor, typename... Args> - void visit(Visitor, Args&&...) - {} -}; - -template <typename NodeT> -struct singleton_regular_sub_pos -{ - // this is a fake regular pos made out of a single child... useful - // to treat a single leaf node as a whole tree - - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - using node_t = NodeT; - node_t* leaf_; - count_t count_; - - count_t count() const { return 1; } - node_t* node() const { return nullptr; } - size_t size() const { return count_; } - shift_t shift() const { return BL; } - count_t index(size_t idx) const { return 0; } - count_t subindex(size_t idx) const { return 0; } - size_t size_before(count_t offset) const { return 0; } - size_t this_size() const { return count_; } - size_t size(count_t offset) { return count_; } - - template <typename Visitor, typename... Args> - void each_left_sub(Visitor v, Args&&... args) - {} - template <typename Visitor, typename... Args> - void each(Visitor v, Args&&... args) - {} - - template <typename Visitor, typename... Args> - decltype(auto) last_sub(Visitor v, Args&&... args) - { - return make_leaf_sub_pos(leaf_, count_).visit(v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_regular(*this, std::forward<Args>(args)...); - } -}; - -template <typename NodeT> -auto make_singleton_regular_sub_pos(NodeT* leaf, count_t count) -{ - assert(leaf); - IMMER_ASSERT_TAGGED(leaf->kind() == NodeT::kind_t::leaf); - assert(count > 0); - return singleton_regular_sub_pos<NodeT>{leaf, count}; -} - -template <typename NodeT> -struct regular_sub_pos -{ - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - using node_t = NodeT; - node_t* node_; - shift_t shift_; - size_t size_; - - count_t count() const { return subindex(size_ - 1) + 1; } - node_t* node() const { return node_; } - size_t size() const { return size_; } - shift_t shift() const { return shift_; } - count_t index(size_t idx) const { return (idx >> shift_) & mask<B>; } - count_t subindex(size_t idx) const { return idx >> shift_; } - size_t size_before(count_t offset) const { return offset << shift_; } - size_t this_size() const { return size_; } - - auto size(count_t offset) - { - return offset == subindex(size_ - 1) ? size_ - size_before(offset) - : size_t{1} << shift_; - } - - auto size_sbh(count_t offset, size_t size_before_hint) - { - assert(size_before_hint == size_before(offset)); - return offset == subindex(size_ - 1) ? size_ - size_before_hint - : size_t{1} << shift_; - } - - void copy_sizes(count_t offset, count_t n, size_t init, size_t* sizes) - { - if (n) { - auto last = offset + n - 1; - auto e = sizes + n - 1; - for (; sizes != e; ++sizes) - init = *sizes = init + (size_t{1} << shift_); - *sizes = init + size(last); - } - } - - template <typename Visitor, typename... Args> - void each(Visitor v, Args&&... args) - { - return each_regular(*this, v, args...); - } - - template <typename Visitor, typename... Args> - bool each_pred(Visitor v, Args&&... args) - { - return each_pred_regular(*this, v, args...); - } - - template <typename Visitor, typename... Args> - bool each_pred_zip(Visitor v, node_t* other, Args&&... args) - { - return each_pred_zip_regular(*this, v, other, args...); - } - - template <typename Visitor, typename... Args> - bool each_pred_i(Visitor v, count_t i, count_t n, Args&&... args) - { - return each_pred_i_regular(*this, v, i, n, args...); - } - - template <typename Visitor, typename... Args> - bool each_pred_right(Visitor v, count_t start, Args&&... args) - { - return each_pred_right_regular(*this, v, start, args...); - } - - template <typename Visitor, typename... Args> - bool each_pred_left(Visitor v, count_t last, Args&&... args) - { - return each_pred_left_regular(*this, v, last, args...); - } - - template <typename Visitor, typename... Args> - void each_i(Visitor v, count_t i, count_t n, Args&&... args) - { - return each_i_regular(*this, v, i, n, args...); - } - - template <typename Visitor, typename... Args> - void each_right(Visitor v, count_t start, Args&&... args) - { - return each_right_regular(*this, v, start, args...); - } - - template <typename Visitor, typename... Args> - void each_left(Visitor v, count_t last, Args&&... args) - { - return each_left_regular(*this, v, last, args...); - } - - template <typename Visitor, typename... Args> - void each_right_sub_(Visitor v, count_t i, Args&&... args) - { - auto last = count() - 1; - auto lsize = size_ - (last << shift_); - auto n = node()->inner() + i; - auto e = node()->inner() + last; - if (shift() == BL) { - for (; n != e; ++n) { - IMMER_PREFETCH(n + 1); - make_full_leaf_pos(*n).visit(v, args...); - } - make_leaf_sub_pos(*n, lsize).visit(v, args...); - } else { - auto ss = shift_ - B; - for (; n != e; ++n) - make_full_pos(*n, ss).visit(v, args...); - make_regular_sub_pos(*n, ss, lsize).visit(v, args...); - } - } - - template <typename Visitor, typename... Args> - void each_sub(Visitor v, Args&&... args) - { - each_right_sub_(v, 0, args...); - } - - template <typename Visitor, typename... Args> - void each_right_sub(Visitor v, Args&&... args) - { - if (count() > 1) - each_right_sub_(v, 1, args...); - } - - template <typename Visitor, typename... Args> - void each_left_sub(Visitor v, Args&&... args) - { - each_left(v, count() - 1, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) towards(Visitor v, size_t idx, Args&&... args) - { - return towards_oh_ch_regular( - *this, v, idx, index(idx), count(), args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) - towards_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args) - { - return towards_oh_ch_regular( - *this, v, idx, offset_hint, count(), args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) towards_oh_ch(Visitor v, - size_t idx, - count_t offset_hint, - count_t count_hint, - Args&&... args) - { - return towards_oh_ch_regular( - *this, v, idx, offset_hint, count(), args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) - towards_sub_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args) - { - return towards_sub_oh_regular(*this, v, idx, offset_hint, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) last_oh(Visitor v, count_t offset_hint, Args&&... args) - { - return last_oh_regular(*this, v, offset_hint, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) last_sub(Visitor v, Args&&... args) - { - auto offset = count() - 1; - auto child = node_->inner()[offset]; - auto is_leaf = shift_ == BL; - auto lsize = size_ - (offset << shift_); - return is_leaf ? make_leaf_sub_pos(child, lsize).visit(v, args...) - : make_regular_sub_pos(child, shift_ - B, lsize) - .visit(v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) first_sub(Visitor v, Args&&... args) - { - auto is_leaf = shift_ == BL; - auto child = node_->inner()[0]; - auto is_full = size_ >= (size_t{1} << shift_); - return is_full - ? (is_leaf - ? make_full_leaf_pos(child).visit(v, args...) - : make_full_pos(child, shift_ - B).visit(v, args...)) - : (is_leaf - ? make_leaf_sub_pos(child, size_).visit(v, args...) - : make_regular_sub_pos(child, shift_ - B, size_) - .visit(v, args...)); - } - - template <typename Visitor, typename... Args> - decltype(auto) first_sub_leaf(Visitor v, Args&&... args) - { - assert(shift_ == BL); - auto child = node_->inner()[0]; - auto is_full = size_ >= branches<BL>; - return is_full ? make_full_leaf_pos(child).visit(v, args...) - : make_leaf_sub_pos(child, size_).visit(v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) first_sub_inner(Visitor v, Args&&... args) - { - assert(shift_ >= BL); - auto child = node_->inner()[0]; - auto is_full = size_ >= branches<BL>; - return is_full ? make_full_pos(child, shift_ - B).visit(v, args...) - : make_regular_sub_pos(child, shift_ - B, size_) - .visit(v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) nth_sub(count_t idx, Visitor v, Args&&... args) - { - assert(idx < count()); - auto is_leaf = shift_ == BL; - auto child = node_->inner()[idx]; - auto lsize = size(idx); - auto is_full = idx + 1 < count(); - return is_full - ? (is_leaf - ? make_full_leaf_pos(child).visit(v, args...) - : make_full_pos(child, shift_ - B).visit(v, args...)) - : (is_leaf - ? make_leaf_sub_pos(child, lsize).visit(v, args...) - : make_regular_sub_pos(child, shift_ - B, lsize) - .visit(v, args...)); - } - - template <typename Visitor, typename... Args> - decltype(auto) nth_sub_leaf(count_t idx, Visitor v, Args&&... args) - { - assert(shift_ == BL); - auto child = node_->inner()[idx]; - auto lsize = size(idx); - auto is_full = idx + 1 < count(); - return is_full ? make_full_leaf_pos(child).visit(v, args...) - : make_leaf_sub_pos(child, lsize).visit(v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_regular(*this, std::forward<Args>(args)...); - } -}; - -template <typename NodeT> -regular_sub_pos<NodeT> -make_regular_sub_pos(NodeT* node, shift_t shift, size_t size) -{ - assert(node); - assert(shift >= NodeT::bits_leaf); - assert(size > 0); - assert(size <= (branches<NodeT::bits, size_t> << shift)); - return {node, shift, size}; -} - -template <typename NodeT, - shift_t Shift, - bits_t B = NodeT::bits, - bits_t BL = NodeT::bits_leaf> -struct regular_descent_pos -{ - static_assert(Shift > 0, "not leaf..."); - - using node_t = NodeT; - node_t* node_; - - node_t* node() const { return node_; } - shift_t shift() const { return Shift; } - count_t index(size_t idx) const - { -#if !defined(_MSC_VER) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshift-count-overflow" -#endif - return (idx >> Shift) & mask<B>; -#if !defined(_MSC_VER) -#pragma GCC diagnostic pop -#endif - } - - template <typename Visitor> - decltype(auto) descend(Visitor v, size_t idx) - { - auto offset = index(idx); - auto child = node_->inner()[offset]; - return regular_descent_pos<NodeT, Shift - B>{child}.visit(v, idx); - } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_regular(*this, std::forward<Args>(args)...); - } -}; - -template <typename NodeT, bits_t B, bits_t BL> -struct regular_descent_pos<NodeT, BL, B, BL> -{ - using node_t = NodeT; - node_t* node_; - - node_t* node() const { return node_; } - shift_t shift() const { return BL; } - count_t index(size_t idx) const { return (idx >> BL) & mask<B>; } - - template <typename Visitor> - decltype(auto) descend(Visitor v, size_t idx) - { - auto offset = index(idx); - auto child = node_->inner()[offset]; - return make_leaf_descent_pos(child).visit(v, idx); - } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_regular(*this, std::forward<Args>(args)...); - } -}; - -template <typename NodeT, typename Visitor> -decltype(auto) -visit_regular_descent(NodeT* node, shift_t shift, Visitor v, size_t idx) -{ - constexpr auto B = NodeT::bits; - constexpr auto BL = NodeT::bits_leaf; - assert(node); - assert(shift >= BL); - switch (shift) { - case BL + B * 0: - return regular_descent_pos<NodeT, BL + B * 0>{node}.visit(v, idx); - case BL + B * 1: - return regular_descent_pos<NodeT, BL + B * 1>{node}.visit(v, idx); - case BL + B * 2: - return regular_descent_pos<NodeT, BL + B * 2>{node}.visit(v, idx); - case BL + B * 3: - return regular_descent_pos<NodeT, BL + B * 3>{node}.visit(v, idx); - case BL + B * 4: - return regular_descent_pos<NodeT, BL + B * 4>{node}.visit(v, idx); - case BL + B * 5: - return regular_descent_pos<NodeT, BL + B * 5>{node}.visit(v, idx); -#if IMMER_DESCENT_DEEP - default: - for (auto level = shift; level != endshift<B, BL>; level -= B) - node = node->inner()[(idx >> level) & mask<B>]; - return make_leaf_descent_pos(node).visit(v, idx); -#endif // IMMER_DEEP_DESCENT - } - IMMER_UNREACHABLE; -} - -template <typename NodeT> -struct full_pos -{ - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - using node_t = NodeT; - node_t* node_; - shift_t shift_; - - count_t count() const { return branches<B>; } - node_t* node() const { return node_; } - size_t size() const { return branches<B> << shift_; } - shift_t shift() const { return shift_; } - count_t index(size_t idx) const { return (idx >> shift_) & mask<B>; } - count_t subindex(size_t idx) const { return idx >> shift_; } - size_t size(count_t offset) const { return size_t{1} << shift_; } - size_t size_sbh(count_t offset, size_t) const - { - return size_t{1} << shift_; - } - size_t size_before(count_t offset) const { return offset << shift_; } - - void copy_sizes(count_t offset, count_t n, size_t init, size_t* sizes) - { - auto e = sizes + n; - for (; sizes != e; ++sizes) - init = *sizes = init + (size_t{1} << shift_); - } - - template <typename Visitor, typename... Args> - void each(Visitor v, Args&&... args) - { - auto p = node_->inner(); - auto e = p + branches<B>; - if (shift_ == BL) { - for (; p != e; ++p) { - IMMER_PREFETCH(p + 1); - make_full_leaf_pos(*p).visit(v, args...); - } - } else { - auto ss = shift_ - B; - for (; p != e; ++p) - make_full_pos(*p, ss).visit(v, args...); - } - } - - template <typename Visitor, typename... Args> - bool each_pred(Visitor v, Args&&... args) - { - auto p = node_->inner(); - auto e = p + branches<B>; - if (shift_ == BL) { - for (; p != e; ++p) { - IMMER_PREFETCH(p + 1); - if (!make_full_leaf_pos(*p).visit(v, args...)) - return false; - } - } else { - auto ss = shift_ - B; - for (; p != e; ++p) - if (!make_full_pos(*p, ss).visit(v, args...)) - return false; - } - return true; - } - - template <typename Visitor, typename... Args> - bool each_pred_zip(Visitor v, node_t* other, Args&&... args) - { - auto p = node_->inner(); - auto p2 = other->inner(); - auto e = p + branches<B>; - if (shift_ == BL) { - for (; p != e; ++p, ++p2) { - IMMER_PREFETCH(p + 1); - if (!make_full_leaf_pos(*p).visit(v, *p2, args...)) - return false; - } - } else { - auto ss = shift_ - B; - for (; p != e; ++p, ++p2) - if (!make_full_pos(*p, ss).visit(v, *p2, args...)) - return false; - } - return true; - } - - template <typename Visitor, typename... Args> - bool each_pred_i(Visitor v, count_t i, count_t n, Args&&... args) - { - auto p = node_->inner() + i; - auto e = node_->inner() + n; - if (shift_ == BL) { - for (; p != e; ++p) { - IMMER_PREFETCH(p + 1); - if (!make_full_leaf_pos(*p).visit(v, args...)) - return false; - } - } else { - auto ss = shift_ - B; - for (; p != e; ++p) - if (!make_full_pos(*p, ss).visit(v, args...)) - return false; - } - return true; - } - - template <typename Visitor, typename... Args> - void each_i(Visitor v, count_t i, count_t n, Args&&... args) - { - auto p = node_->inner() + i; - auto e = node_->inner() + n; - if (shift_ == BL) { - for (; p != e; ++p) { - IMMER_PREFETCH(p + 1); - make_full_leaf_pos(*p).visit(v, args...); - } - } else { - auto ss = shift_ - B; - for (; p != e; ++p) - make_full_pos(*p, ss).visit(v, args...); - } - } - - template <typename Visitor, typename... Args> - bool each_pred_right(Visitor v, count_t start, Args&&... args) - { - return each_pred_i(v, start, branches<B>, args...); - } - - template <typename Visitor, typename... Args> - bool each_pred_left(Visitor v, count_t last, Args&&... args) - { - return each_pred_i(v, 0, last, args...); - } - - template <typename Visitor, typename... Args> - void each_sub(Visitor v, Args&&... args) - { - each(v, args...); - } - - template <typename Visitor, typename... Args> - void each_left_sub(Visitor v, Args&&... args) - { - each_i(v, 0, branches<B> - 1, args...); - } - - template <typename Visitor, typename... Args> - void each_right_sub(Visitor v, Args&&... args) - { - each_i(v, 1, branches<B>, args...); - } - - template <typename Visitor, typename... Args> - void each_right(Visitor v, count_t start, Args&&... args) - { - each_i(v, start, branches<B>, args...); - } - - template <typename Visitor, typename... Args> - void each_left(Visitor v, count_t last, Args&&... args) - { - each_i(v, 0, last, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) towards(Visitor v, size_t idx, Args&&... args) - { - return towards_oh(v, idx, index(idx), args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) towards_oh_ch( - Visitor v, size_t idx, count_t offset_hint, count_t, Args&&... args) - { - return towards_oh(v, idx, offset_hint, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) - towards_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args) - { - assert(offset_hint == index(idx)); - auto is_leaf = shift_ == BL; - auto child = node_->inner()[offset_hint]; - return is_leaf - ? make_full_leaf_pos(child).visit(v, idx, args...) - : make_full_pos(child, shift_ - B).visit(v, idx, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) - towards_sub_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args) - { - assert(offset_hint == index(idx)); - auto is_leaf = shift_ == BL; - auto child = node_->inner()[offset_hint]; - auto lsize = offset_hint << shift_; - return is_leaf - ? make_full_leaf_pos(child).visit(v, idx - lsize, args...) - : make_full_pos(child, shift_ - B) - .visit(v, idx - lsize, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) first_sub(Visitor v, Args&&... args) - { - auto is_leaf = shift_ == BL; - auto child = node_->inner()[0]; - return is_leaf ? make_full_leaf_pos(child).visit(v, args...) - : make_full_pos(child, shift_ - B).visit(v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) first_sub_leaf(Visitor v, Args&&... args) - { - assert(shift_ == BL); - auto child = node_->inner()[0]; - return make_full_leaf_pos(child).visit(v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) first_sub_inner(Visitor v, Args&&... args) - { - assert(shift_ >= BL); - auto child = node_->inner()[0]; - return make_full_pos(child, shift_ - B).visit(v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) nth_sub(count_t idx, Visitor v, Args&&... args) - { - assert(idx < count()); - auto is_leaf = shift_ == BL; - auto child = node_->inner()[idx]; - return is_leaf ? make_full_leaf_pos(child).visit(v, args...) - : make_full_pos(child, shift_ - B).visit(v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) nth_sub_leaf(count_t idx, Visitor v, Args&&... args) - { - assert(shift_ == BL); - assert(idx < count()); - auto child = node_->inner()[idx]; - return make_full_leaf_pos(child).visit(v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_regular(*this, std::forward<Args>(args)...); - } -}; - -template <typename NodeT> -full_pos<NodeT> make_full_pos(NodeT* node, shift_t shift) -{ - assert(node); - assert(shift >= NodeT::bits_leaf); - return {node, shift}; -} - -template <typename NodeT> -struct relaxed_pos -{ - static constexpr auto B = NodeT::bits; - static constexpr auto BL = NodeT::bits_leaf; - - using node_t = NodeT; - using relaxed_t = typename NodeT::relaxed_t; - node_t* node_; - shift_t shift_; - relaxed_t* relaxed_; - - count_t count() const { return relaxed_->d.count; } - node_t* node() const { return node_; } - size_t size() const { return relaxed_->d.sizes[relaxed_->d.count - 1]; } - shift_t shift() const { return shift_; } - count_t subindex(size_t idx) const { return index(idx); } - relaxed_t* relaxed() const { return relaxed_; } - - size_t size_before(count_t offset) const - { - return offset ? relaxed_->d.sizes[offset - 1] : 0; - } - - size_t size(count_t offset) const - { - return size_sbh(offset, size_before(offset)); - } - - size_t size_sbh(count_t offset, size_t size_before_hint) const - { - assert(size_before_hint == size_before(offset)); - return relaxed_->d.sizes[offset] - size_before_hint; - } - - count_t index(size_t idx) const - { - auto offset = idx >> shift_; - while (relaxed_->d.sizes[offset] <= idx) - ++offset; - return offset; - } - - void copy_sizes(count_t offset, count_t n, size_t init, size_t* sizes) - { - auto e = sizes + n; - auto prev = size_before(offset); - auto these = relaxed_->d.sizes + offset; - for (; sizes != e; ++sizes, ++these) { - auto this_size = *these; - init = *sizes = init + (this_size - prev); - prev = this_size; - } - } - - template <typename Visitor, typename... Args> - void each(Visitor v, Args&&... args) - { - each_left(v, relaxed_->d.count, args...); - } - - template <typename Visitor, typename... Args> - bool each_pred(Visitor v, Args&&... args) - { - auto p = node_->inner(); - auto s = size_t{}; - auto n = count(); - if (shift_ == BL) { - for (auto i = count_t{0}; i < n; ++i) { - IMMER_PREFETCH(p + i + 1); - if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) - .visit(v, args...)) - return false; - s = relaxed_->d.sizes[i]; - } - } else { - auto ss = shift_ - B; - for (auto i = count_t{0}; i < n; ++i) { - if (!visit_maybe_relaxed_sub( - p[i], ss, relaxed_->d.sizes[i] - s, v, args...)) - return false; - s = relaxed_->d.sizes[i]; - } - } - return true; - } - - template <typename Visitor, typename... Args> - bool each_pred_i(Visitor v, count_t i, count_t n, Args&&... args) - { - if (shift_ == BL) { - auto p = node_->inner(); - auto s = i > 0 ? relaxed_->d.sizes[i - 1] : 0; - for (; i < n; ++i) { - IMMER_PREFETCH(p + i + 1); - if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) - .visit(v, args...)) - return false; - s = relaxed_->d.sizes[i]; - } - } else { - auto p = node_->inner(); - auto s = i > 0 ? relaxed_->d.sizes[i - 1] : 0; - auto ss = shift_ - B; - for (; i < n; ++i) { - if (!visit_maybe_relaxed_sub( - p[i], ss, relaxed_->d.sizes[i] - s, v, args...)) - return false; - s = relaxed_->d.sizes[i]; - } - } - return true; - } - - template <typename Visitor, typename... Args> - bool each_pred_left(Visitor v, count_t n, Args&&... args) - { - auto p = node_->inner(); - auto s = size_t{}; - if (shift_ == BL) { - for (auto i = count_t{0}; i < n; ++i) { - IMMER_PREFETCH(p + i + 1); - if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) - .visit(v, args...)) - return false; - s = relaxed_->d.sizes[i]; - } - } else { - auto ss = shift_ - B; - for (auto i = count_t{0}; i < n; ++i) { - if (!visit_maybe_relaxed_sub( - p[i], ss, relaxed_->d.sizes[i] - s, v, args...)) - return false; - s = relaxed_->d.sizes[i]; - } - } - return true; - } - - template <typename Visitor, typename... Args> - bool each_pred_right(Visitor v, count_t start, Args&&... args) - { - assert(start > 0); - assert(start <= relaxed_->d.count); - auto s = relaxed_->d.sizes[start - 1]; - auto p = node_->inner(); - if (shift_ == BL) { - for (auto i = start; i < relaxed_->d.count; ++i) { - IMMER_PREFETCH(p + i + 1); - if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) - .visit(v, args...)) - return false; - s = relaxed_->d.sizes[i]; - } - } else { - auto ss = shift_ - B; - for (auto i = start; i < relaxed_->d.count; ++i) { - if (!visit_maybe_relaxed_sub( - p[i], ss, relaxed_->d.sizes[i] - s, v, args...)) - return false; - s = relaxed_->d.sizes[i]; - } - } - return true; - } - - template <typename Visitor, typename... Args> - void each_i(Visitor v, count_t i, count_t n, Args&&... args) - { - if (shift_ == BL) { - auto p = node_->inner(); - auto s = i > 0 ? relaxed_->d.sizes[i - 1] : 0; - for (; i < n; ++i) { - IMMER_PREFETCH(p + i + 1); - make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) - .visit(v, args...); - s = relaxed_->d.sizes[i]; - } - } else { - auto p = node_->inner(); - auto s = i > 0 ? relaxed_->d.sizes[i - 1] : 0; - auto ss = shift_ - B; - for (; i < n; ++i) { - visit_maybe_relaxed_sub( - p[i], ss, relaxed_->d.sizes[i] - s, v, args...); - s = relaxed_->d.sizes[i]; - } - } - } - - template <typename Visitor, typename... Args> - void each_sub(Visitor v, Args&&... args) - { - each_left(v, relaxed_->d.count, args...); - } - - template <typename Visitor, typename... Args> - void each_left_sub(Visitor v, Args&&... args) - { - each_left(v, relaxed_->d.count - 1, args...); - } - - template <typename Visitor, typename... Args> - void each_left(Visitor v, count_t n, Args&&... args) - { - auto p = node_->inner(); - auto s = size_t{}; - if (shift_ == BL) { - for (auto i = count_t{0}; i < n; ++i) { - IMMER_PREFETCH(p + i + 1); - make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) - .visit(v, args...); - s = relaxed_->d.sizes[i]; - } - } else { - auto ss = shift_ - B; - for (auto i = count_t{0}; i < n; ++i) { - visit_maybe_relaxed_sub( - p[i], ss, relaxed_->d.sizes[i] - s, v, args...); - s = relaxed_->d.sizes[i]; - } - } - } - - template <typename Visitor, typename... Args> - void each_right_sub(Visitor v, Args&&... args) - { - each_right(v, 1, std::forward<Args>(args)...); - } - - template <typename Visitor, typename... Args> - void each_right(Visitor v, count_t start, Args&&... args) - { - assert(start > 0); - assert(start <= relaxed_->d.count); - auto s = relaxed_->d.sizes[start - 1]; - auto p = node_->inner(); - if (shift_ == BL) { - for (auto i = start; i < relaxed_->d.count; ++i) { - IMMER_PREFETCH(p + i + 1); - make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) - .visit(v, args...); - s = relaxed_->d.sizes[i]; - } - } else { - auto ss = shift_ - B; - for (auto i = start; i < relaxed_->d.count; ++i) { - visit_maybe_relaxed_sub( - p[i], ss, relaxed_->d.sizes[i] - s, v, args...); - s = relaxed_->d.sizes[i]; - } - } - } - - template <typename Visitor, typename... Args> - decltype(auto) towards(Visitor v, size_t idx, Args&&... args) - { - return towards_oh(v, idx, subindex(idx), args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) - towards_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args) - { - assert(offset_hint == index(idx)); - auto left_size = offset_hint ? relaxed_->d.sizes[offset_hint - 1] : 0; - return towards_oh_sbh(v, idx, offset_hint, left_size, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) towards_oh_sbh(Visitor v, - size_t idx, - count_t offset_hint, - size_t left_size_hint, - Args&&... args) - { - return towards_sub_oh_sbh(v, idx, offset_hint, left_size_hint, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) - towards_sub_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args) - { - assert(offset_hint == index(idx)); - auto left_size = offset_hint ? relaxed_->d.sizes[offset_hint - 1] : 0; - return towards_sub_oh_sbh(v, idx, offset_hint, left_size, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) towards_sub_oh_sbh(Visitor v, - size_t idx, - count_t offset_hint, - size_t left_size_hint, - Args&&... args) - { - assert(offset_hint == index(idx)); - assert(left_size_hint == - (offset_hint ? relaxed_->d.sizes[offset_hint - 1] : 0)); - auto child = node_->inner()[offset_hint]; - auto is_leaf = shift_ == BL; - auto next_size = relaxed_->d.sizes[offset_hint] - left_size_hint; - auto next_idx = idx - left_size_hint; - return is_leaf - ? make_leaf_sub_pos(child, next_size) - .visit(v, next_idx, args...) - : visit_maybe_relaxed_sub( - child, shift_ - B, next_size, v, next_idx, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) last_oh_csh(Visitor v, - count_t offset_hint, - size_t child_size_hint, - Args&&... args) - { - assert(offset_hint == count() - 1); - assert(child_size_hint == size(offset_hint)); - auto child = node_->inner()[offset_hint]; - auto is_leaf = shift_ == BL; - return is_leaf - ? make_leaf_sub_pos(child, child_size_hint).visit(v, args...) - : visit_maybe_relaxed_sub( - child, shift_ - B, child_size_hint, v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) last_sub(Visitor v, Args&&... args) - { - auto offset = relaxed_->d.count - 1; - auto child = node_->inner()[offset]; - auto child_size = size(offset); - auto is_leaf = shift_ == BL; - return is_leaf ? make_leaf_sub_pos(child, child_size).visit(v, args...) - : visit_maybe_relaxed_sub( - child, shift_ - B, child_size, v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) first_sub(Visitor v, Args&&... args) - { - auto child = node_->inner()[0]; - auto child_size = relaxed_->d.sizes[0]; - auto is_leaf = shift_ == BL; - return is_leaf ? make_leaf_sub_pos(child, child_size).visit(v, args...) - : visit_maybe_relaxed_sub( - child, shift_ - B, child_size, v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) first_sub_leaf(Visitor v, Args&&... args) - { - assert(shift_ == BL); - auto child = node_->inner()[0]; - auto child_size = relaxed_->d.sizes[0]; - return make_leaf_sub_pos(child, child_size).visit(v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) first_sub_inner(Visitor v, Args&&... args) - { - assert(shift_ > BL); - auto child = node_->inner()[0]; - auto child_size = relaxed_->d.sizes[0]; - return visit_maybe_relaxed_sub( - child, shift_ - B, child_size, v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) nth_sub(count_t offset, Visitor v, Args&&... args) - { - auto child = node_->inner()[offset]; - auto child_size = size(offset); - auto is_leaf = shift_ == BL; - return is_leaf ? make_leaf_sub_pos(child, child_size).visit(v, args...) - : visit_maybe_relaxed_sub( - child, shift_ - B, child_size, v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) nth_sub_leaf(count_t offset, Visitor v, Args&&... args) - { - assert(shift_ == BL); - auto child = node_->inner()[offset]; - auto child_size = size(offset); - return make_leaf_sub_pos(child, child_size).visit(v, args...); - } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_relaxed(*this, std::forward<Args>(args)...); - } -}; - -template <typename Pos> -using is_relaxed = std::is_same<relaxed_pos<typename std::decay_t<Pos>::node_t>, - std::decay_t<Pos>>; - -template <typename Pos> -constexpr auto is_relaxed_v = is_relaxed<Pos>::value; - -template <typename NodeT> -relaxed_pos<NodeT> -make_relaxed_pos(NodeT* node, shift_t shift, typename NodeT::relaxed_t* relaxed) -{ - assert(node); - assert(relaxed); - assert(shift >= NodeT::bits_leaf); - return {node, shift, relaxed}; -} - -template <typename NodeT, typename Visitor, typename... Args> -decltype(auto) visit_maybe_relaxed_sub( - NodeT* node, shift_t shift, size_t size, Visitor v, Args&&... args) -{ - assert(node); - auto relaxed = node->relaxed(); - if (relaxed) { - assert(size == relaxed->d.sizes[relaxed->d.count - 1]); - return make_relaxed_pos(node, shift, relaxed) - .visit(v, std::forward<Args>(args)...); - } else { - return make_regular_sub_pos(node, shift, size) - .visit(v, std::forward<Args>(args)...); - } -} - -template <typename NodeT, - shift_t Shift, - bits_t B = NodeT::bits, - bits_t BL = NodeT::bits_leaf> -struct relaxed_descent_pos -{ - static_assert(Shift > 0, "not leaf..."); - - using node_t = NodeT; - using relaxed_t = typename NodeT::relaxed_t; - node_t* node_; - relaxed_t* relaxed_; - - count_t count() const { return relaxed_->d.count; } - node_t* node() const { return node_; } - shift_t shift() const { return Shift; } - size_t size() const { return relaxed_->d.sizes[relaxed_->d.count - 1]; } - - count_t index(size_t idx) const - { - // make gcc happy -#if !defined(_MSC_VER) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshift-count-overflow" -#endif - auto offset = idx >> Shift; -#if !defined(_MSC_VER) -#pragma GCC diagnostic pop -#endif - while (relaxed_->d.sizes[offset] <= idx) - ++offset; - return offset; - } - - template <typename Visitor> - decltype(auto) descend(Visitor v, size_t idx) - { - auto offset = index(idx); - auto child = node_->inner()[offset]; - auto left_size = offset ? relaxed_->d.sizes[offset - 1] : 0; - auto next_idx = idx - left_size; - auto r = child->relaxed(); - return r ? relaxed_descent_pos<NodeT, Shift - B>{child, r}.visit( - v, next_idx) - : regular_descent_pos<NodeT, Shift - B>{child}.visit(v, - next_idx); - } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_relaxed(*this, std::forward<Args>(args)...); - } -}; - -template <typename NodeT, bits_t B, bits_t BL> -struct relaxed_descent_pos<NodeT, BL, B, BL> -{ - using node_t = NodeT; - using relaxed_t = typename NodeT::relaxed_t; - node_t* node_; - relaxed_t* relaxed_; - - count_t count() const { return relaxed_->d.count; } - node_t* node() const { return node_; } - shift_t shift() const { return BL; } - size_t size() const { return relaxed_->d.sizes[relaxed_->d.count - 1]; } - - count_t index(size_t idx) const - { - auto offset = (idx >> BL) & mask<B>; - while (relaxed_->d.sizes[offset] <= idx) - ++offset; - return offset; - } - - template <typename Visitor> - decltype(auto) descend(Visitor v, size_t idx) - { - auto offset = index(idx); - auto child = node_->inner()[offset]; - auto left_size = offset ? relaxed_->d.sizes[offset - 1] : 0; - auto next_idx = idx - left_size; - return leaf_descent_pos<NodeT>{child}.visit(v, next_idx); - } - - template <typename Visitor, typename... Args> - decltype(auto) visit(Visitor v, Args&&... args) - { - return Visitor::visit_relaxed(*this, std::forward<Args>(args)...); - } -}; - -template <typename NodeT, typename Visitor, typename... Args> -decltype(auto) -visit_maybe_relaxed_descent(NodeT* node, shift_t shift, Visitor v, size_t idx) -{ - constexpr auto B = NodeT::bits; - constexpr auto BL = NodeT::bits_leaf; - assert(node); - assert(shift >= BL); - auto r = node->relaxed(); - if (r) { - switch (shift) { - case BL + B * 0: - return relaxed_descent_pos<NodeT, BL + B * 0>{node, r}.visit(v, - idx); - case BL + B * 1: - return relaxed_descent_pos<NodeT, BL + B * 1>{node, r}.visit(v, - idx); - case BL + B * 2: - return relaxed_descent_pos<NodeT, BL + B * 2>{node, r}.visit(v, - idx); - case BL + B * 3: - return relaxed_descent_pos<NodeT, BL + B * 3>{node, r}.visit(v, - idx); - case BL + B * 4: - return relaxed_descent_pos<NodeT, BL + B * 4>{node, r}.visit(v, - idx); - case BL + B * 5: - return relaxed_descent_pos<NodeT, BL + B * 5>{node, r}.visit(v, - idx); -#if IMMER_DESCENT_DEEP - default: - for (auto level = shift; level != endshift<B, BL>; level -= B) { - auto r = node->relaxed(); - if (r) { - auto node_idx = (idx >> level) & mask<B>; - while (r->d.sizes[node_idx] <= idx) - ++node_idx; - if (node_idx) - idx -= r->d.sizes[node_idx - 1]; - node = node->inner()[node_idx]; - } else { - do { - node = node->inner()[(idx >> level) & mask<B>]; - } while ((level -= B) != endshift<B, BL>); - return make_leaf_descent_pos(node).visit(v, idx); - } - } - return make_leaf_descent_pos(node).visit(v, idx); -#endif // IMMER_DESCENT_DEEP - } - IMMER_UNREACHABLE; - } else { - return visit_regular_descent(node, shift, v, idx); - } -} - -} // namespace rbts -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/rbts/rbtree.hpp b/third_party/immer/immer/detail/rbts/rbtree.hpp deleted file mode 100644 index b31f5e9b0788..000000000000 --- a/third_party/immer/immer/detail/rbts/rbtree.hpp +++ /dev/null @@ -1,509 +0,0 @@ -// -// 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 <immer/detail/rbts/node.hpp> -#include <immer/detail/rbts/operations.hpp> -#include <immer/detail/rbts/position.hpp> - -#include <immer/detail/type_traits.hpp> - -#include <cassert> -#include <memory> -#include <numeric> - -namespace immer { -namespace detail { -namespace rbts { - -template <typename T, typename MemoryPolicy, bits_t B, bits_t BL> -struct rbtree -{ - using node_t = node<T, MemoryPolicy, B, BL>; - using edit_t = typename node_t::edit_t; - using owner_t = typename MemoryPolicy::transience_t::owner; - - size_t size; - shift_t shift; - node_t* root; - node_t* tail; - - static const rbtree& empty() - { - static const rbtree empty_{ - 0, BL, node_t::make_inner_n(0u), node_t::make_leaf_n(0u)}; - return empty_; - } - - template <typename U> - static auto from_initializer_list(std::initializer_list<U> values) - { - auto e = owner_t{}; - auto result = rbtree{empty()}; - for (auto&& v : values) - result.push_back_mut(e, v); - return result; - } - - template <typename Iter, - typename Sent, - std::enable_if_t<compatible_sentinel_v<Iter, Sent>, bool> = true> - static auto from_range(Iter first, Sent last) - { - auto e = owner_t{}; - auto result = rbtree{empty()}; - for (; first != last; ++first) - result.push_back_mut(e, *first); - return result; - } - - static auto from_fill(size_t n, T v) - { - auto e = owner_t{}; - auto result = rbtree{empty()}; - while (n-- > 0) - result.push_back_mut(e, v); - return result; - } - - rbtree(size_t sz, shift_t sh, node_t* r, node_t* t) - : size{sz} - , shift{sh} - , root{r} - , tail{t} - { - assert(check_tree()); - } - - rbtree(const rbtree& other) - : rbtree{other.size, other.shift, other.root, other.tail} - { - inc(); - } - - rbtree(rbtree&& other) - : rbtree{empty()} - { - swap(*this, other); - } - - rbtree& operator=(const rbtree& other) - { - auto next = other; - swap(*this, next); - return *this; - } - - rbtree& operator=(rbtree&& other) - { - swap(*this, other); - return *this; - } - - friend void swap(rbtree& x, rbtree& y) - { - using std::swap; - swap(x.size, y.size); - swap(x.shift, y.shift); - swap(x.root, y.root); - swap(x.tail, y.tail); - } - - ~rbtree() { dec(); } - - void inc() const - { - root->inc(); - tail->inc(); - } - - void dec() const { traverse(dec_visitor()); } - - auto tail_size() const { return size ? ((size - 1) & mask<BL>) +1 : 0; } - - auto tail_offset() const { return size ? (size - 1) & ~mask<BL> : 0; } - - template <typename Visitor, typename... Args> - void traverse(Visitor v, Args&&... args) const - { - auto tail_off = tail_offset(); - auto tail_size = size - tail_off; - - if (tail_off) - make_regular_sub_pos(root, shift, tail_off).visit(v, args...); - else - make_empty_regular_pos(root).visit(v, args...); - - make_leaf_sub_pos(tail, tail_size).visit(v, args...); - } - - template <typename Visitor, typename... Args> - void traverse(Visitor v, size_t first, size_t last, Args&&... args) const - { - auto tail_off = tail_offset(); - auto tail_size = size - tail_off; - - if (first < tail_off) - make_regular_sub_pos(root, shift, tail_off) - .visit(v, first, last < tail_off ? last : tail_off, args...); - if (last > tail_off) - make_leaf_sub_pos(tail, tail_size) - .visit(v, - first > tail_off ? first - tail_off : 0, - last - tail_off, - args...); - } - - template <typename Visitor, typename... Args> - bool traverse_p(Visitor v, Args&&... args) const - { - auto tail_off = tail_offset(); - auto tail_size = size - tail_off; - return (tail_off ? make_regular_sub_pos(root, shift, tail_off) - .visit(v, args...) - : make_empty_regular_pos(root).visit(v, args...)) && - make_leaf_sub_pos(tail, tail_size).visit(v, args...); - } - - template <typename Visitor, typename... Args> - bool traverse_p(Visitor v, size_t first, size_t last, Args&&... args) const - { - auto tail_off = tail_offset(); - auto tail_size = size - tail_off; - - return (first < tail_off ? make_regular_sub_pos(root, shift, tail_off) - .visit(v, - first, - last < tail_off ? last : tail_off, - args...) - : true) && - (last > tail_off - ? make_leaf_sub_pos(tail, tail_size) - .visit(v, - first > tail_off ? first - tail_off : 0, - last - tail_off, - args...) - : true); - } - - template <typename Visitor> - decltype(auto) descend(Visitor v, size_t idx) const - { - auto tail_off = tail_offset(); - return idx >= tail_off ? make_leaf_descent_pos(tail).visit(v, idx) - : visit_regular_descent(root, shift, v, idx); - } - - template <typename Fn> - void for_each_chunk(Fn&& fn) const - { - traverse(for_each_chunk_visitor{}, std::forward<Fn>(fn)); - } - - template <typename Fn> - void for_each_chunk(size_t first, size_t last, Fn&& fn) const - { - traverse(for_each_chunk_i_visitor{}, first, last, std::forward<Fn>(fn)); - } - - template <typename Fn> - bool for_each_chunk_p(Fn&& fn) const - { - return traverse_p(for_each_chunk_p_visitor{}, std::forward<Fn>(fn)); - } - - template <typename Fn> - bool for_each_chunk_p(size_t first, size_t last, Fn&& fn) const - { - return traverse_p( - for_each_chunk_p_i_visitor{}, first, last, std::forward<Fn>(fn)); - } - - bool equals(const rbtree& other) const - { - if (size != other.size) - return false; - if (size == 0) - return true; - return (size <= branches<BL> || - make_regular_sub_pos(root, shift, tail_offset()) - .visit(equals_visitor{}, other.root)) && - make_leaf_sub_pos(tail, tail_size()) - .visit(equals_visitor{}, other.tail); - } - - void ensure_mutable_tail(edit_t e, count_t n) - { - if (!tail->can_mutate(e)) { - auto new_tail = node_t::copy_leaf_e(e, tail, n); - dec_leaf(tail, n); - tail = new_tail; - } - } - - void push_back_mut(edit_t e, T value) - { - auto tail_off = tail_offset(); - auto ts = size - tail_off; - if (ts < branches<BL>) { - ensure_mutable_tail(e, ts); - new (&tail->leaf()[ts]) T{std::move(value)}; - } else { - auto new_tail = node_t::make_leaf_e(e, std::move(value)); - try { - if (tail_off == size_t{branches<B>} << shift) { - auto new_root = node_t::make_inner_e(e); - try { - auto path = node_t::make_path_e(e, shift, tail); - new_root->inner()[0] = root; - new_root->inner()[1] = path; - root = new_root; - tail = new_tail; - shift += B; - } catch (...) { - node_t::delete_inner_e(new_root); - throw; - } - } else if (tail_off) { - auto new_root = - make_regular_sub_pos(root, shift, tail_off) - .visit(push_tail_mut_visitor<node_t>{}, e, tail); - root = new_root; - tail = new_tail; - } else { - auto new_root = node_t::make_path_e(e, shift, tail); - assert(tail_off == 0); - dec_empty_regular(root); - root = new_root; - tail = new_tail; - } - } catch (...) { - node_t::delete_leaf(new_tail, 1); - throw; - } - } - ++size; - } - - rbtree push_back(T value) const - { - auto tail_off = tail_offset(); - auto ts = size - tail_off; - if (ts < branches<BL>) { - auto new_tail = - node_t::copy_leaf_emplace(tail, ts, std::move(value)); - return {size + 1, shift, root->inc(), new_tail}; - } else { - auto new_tail = node_t::make_leaf_n(1, std::move(value)); - try { - if (tail_off == size_t{branches<B>} << shift) { - auto new_root = node_t::make_inner_n(2); - try { - auto path = node_t::make_path(shift, tail); - new_root->inner()[0] = root; - new_root->inner()[1] = path; - root->inc(); - tail->inc(); - return {size + 1, shift + B, new_root, new_tail}; - } catch (...) { - node_t::delete_inner(new_root, 2); - throw; - } - } else if (tail_off) { - auto new_root = - make_regular_sub_pos(root, shift, tail_off) - .visit(push_tail_visitor<node_t>{}, tail); - tail->inc(); - return {size + 1, shift, new_root, new_tail}; - } else { - auto new_root = node_t::make_path(shift, tail); - tail->inc(); - return {size + 1, shift, new_root, new_tail}; - } - } catch (...) { - node_t::delete_leaf(new_tail, 1); - throw; - } - } - } - - const T* array_for(size_t index) const - { - return descend(array_for_visitor<T>(), index); - } - - T& get_mut(edit_t e, size_t idx) - { - auto tail_off = tail_offset(); - if (idx >= tail_off) { - ensure_mutable_tail(e, size - tail_off); - return tail->leaf()[idx & mask<BL>]; - } else { - return make_regular_sub_pos(root, shift, tail_off) - .visit(get_mut_visitor<node_t>{}, idx, e, &root); - } - } - - const T& get(size_t index) const - { - return descend(get_visitor<T>(), index); - } - - const T& get_check(size_t index) const - { - if (index >= size) - throw std::out_of_range{"index out of range"}; - return descend(get_visitor<T>(), index); - } - - const T& front() const { return get(0); } - - const T& back() const { return tail->leaf()[(size - 1) & mask<BL>]; } - - template <typename FnT> - void update_mut(edit_t e, size_t idx, FnT&& fn) - { - auto& elem = get_mut(e, idx); - elem = std::forward<FnT>(fn)(std::move(elem)); - } - - template <typename FnT> - rbtree update(size_t idx, FnT&& fn) const - { - auto tail_off = tail_offset(); - if (idx >= tail_off) { - auto tail_size = size - tail_off; - auto new_tail = - make_leaf_sub_pos(tail, tail_size) - .visit(update_visitor<node_t>{}, idx - tail_off, fn); - return {size, shift, root->inc(), new_tail}; - } else { - auto new_root = make_regular_sub_pos(root, shift, tail_off) - .visit(update_visitor<node_t>{}, idx, fn); - return {size, shift, new_root, tail->inc()}; - } - } - - void assoc_mut(edit_t e, size_t idx, T value) - { - update_mut(e, idx, [&](auto&&) { return std::move(value); }); - } - - rbtree assoc(size_t idx, T value) const - { - return update(idx, [&](auto&&) { return std::move(value); }); - } - - rbtree take(size_t new_size) const - { - auto tail_off = tail_offset(); - if (new_size == 0) { - return empty(); - } else if (new_size >= size) { - return *this; - } else if (new_size > tail_off) { - auto new_tail = node_t::copy_leaf(tail, new_size - tail_off); - return {new_size, shift, root->inc(), new_tail}; - } else { - using std::get; - auto l = new_size - 1; - auto v = slice_right_visitor<node_t>(); - auto r = make_regular_sub_pos(root, shift, tail_off).visit(v, l); - auto new_shift = get<0>(r); - auto new_root = get<1>(r); - auto new_tail = get<3>(r); - if (new_root) { - IMMER_ASSERT_TAGGED(new_root->compute_shift() == get<0>(r)); - assert(new_root->check(new_shift, new_size - get<2>(r))); - return {new_size, new_shift, new_root, new_tail}; - } else { - return {new_size, BL, empty().root->inc(), new_tail}; - } - } - } - - void take_mut(edit_t e, size_t new_size) - { - auto tail_off = tail_offset(); - if (new_size == 0) { - // todo: more efficient? - *this = empty(); - } else if (new_size >= size) { - return; - } else if (new_size > tail_off) { - auto ts = size - tail_off; - auto newts = new_size - tail_off; - if (tail->can_mutate(e)) { - destroy_n(tail->leaf() + newts, ts - newts); - } else { - auto new_tail = node_t::copy_leaf_e(e, tail, newts); - dec_leaf(tail, ts); - tail = new_tail; - } - size = new_size; - return; - } else { - using std::get; - auto l = new_size - 1; - auto v = slice_right_mut_visitor<node_t>(); - auto r = make_regular_sub_pos(root, shift, tail_off).visit(v, l, e); - auto new_shift = get<0>(r); - auto new_root = get<1>(r); - auto new_tail = get<3>(r); - if (new_root) { - root = new_root; - shift = new_shift; - } else { - root = empty().root->inc(); - shift = BL; - } - dec_leaf(tail, size - tail_off); - size = new_size; - tail = new_tail; - return; - } - } - - bool check_tree() const - { -#if IMMER_DEBUG_DEEP_CHECK - assert(shift >= BL); - assert(tail_offset() <= size); - assert(check_root()); - assert(check_tail()); -#endif - return true; - } - - bool check_tail() const - { -#if IMMER_DEBUG_DEEP_CHECK - if (tail_size() > 0) - assert(tail->check(0, tail_size())); -#endif - return true; - } - - bool check_root() const - { -#if IMMER_DEBUG_DEEP_CHECK - if (tail_offset() > 0) - assert(root->check(shift, tail_offset())); - else { - IMMER_ASSERT_TAGGED(root->kind() == node_t::kind_t::inner); - assert(shift == BL); - } -#endif - return true; - } -}; - -} // namespace rbts -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/rbts/rbtree_iterator.hpp b/third_party/immer/immer/detail/rbts/rbtree_iterator.hpp deleted file mode 100644 index 90613b10b98e..000000000000 --- a/third_party/immer/immer/detail/rbts/rbtree_iterator.hpp +++ /dev/null @@ -1,99 +0,0 @@ -// -// 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 <immer/detail/iterator_facade.hpp> -#include <immer/detail/rbts/rbtree.hpp> - -namespace immer { -namespace detail { -namespace rbts { - -template <typename T, typename MP, bits_t B, bits_t BL> -struct rbtree_iterator - : iterator_facade<rbtree_iterator<T, MP, B, BL>, - std::random_access_iterator_tag, - T, - const T&, - std::ptrdiff_t, - const T*> -{ - using tree_t = rbtree<T, MP, B, BL>; - - struct end_t - {}; - - rbtree_iterator() = default; - - rbtree_iterator(const tree_t& v) - : v_{&v} - , i_{0} - , base_{~size_t{}} - , curr_{nullptr} - {} - - rbtree_iterator(const tree_t& v, end_t) - : v_{&v} - , i_{v.size} - , base_{~size_t{}} - , curr_{nullptr} - {} - - const tree_t& impl() const { return *v_; } - size_t index() const { return i_; } - -private: - friend iterator_core_access; - - const tree_t* v_; - size_t i_; - mutable size_t base_; - mutable const T* curr_ = nullptr; - - void increment() - { - assert(i_ < v_->size); - ++i_; - } - - void decrement() - { - assert(i_ > 0); - --i_; - } - - void advance(std::ptrdiff_t n) - { - assert(n <= 0 || i_ + static_cast<size_t>(n) <= v_->size); - assert(n >= 0 || static_cast<size_t>(-n) <= i_); - i_ += n; - } - - bool equal(const rbtree_iterator& other) const { return i_ == other.i_; } - - std::ptrdiff_t distance_to(const rbtree_iterator& other) const - { - return other.i_ > i_ ? static_cast<std::ptrdiff_t>(other.i_ - i_) - : -static_cast<std::ptrdiff_t>(i_ - other.i_); - } - - const T& dereference() const - { - auto base = i_ & ~mask<BL>; - if (base_ != base) { - base_ = base; - curr_ = v_->array_for(i_); - } - return curr_[i_ & mask<BL>]; - } -}; - -} // namespace rbts -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/rbts/rrbtree.hpp b/third_party/immer/immer/detail/rbts/rrbtree.hpp deleted file mode 100644 index 7bf59e6e92b1..000000000000 --- a/third_party/immer/immer/detail/rbts/rrbtree.hpp +++ /dev/null @@ -1,1396 +0,0 @@ -// -// 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 <immer/config.hpp> -#include <immer/detail/rbts/node.hpp> -#include <immer/detail/rbts/operations.hpp> -#include <immer/detail/rbts/position.hpp> - -#include <immer/detail/type_traits.hpp> - -#include <cassert> -#include <memory> -#include <numeric> - -namespace immer { -namespace detail { -namespace rbts { - -template <typename T, typename MemoryPolicy, bits_t B, bits_t BL> -struct rrbtree_iterator; - -template <typename T, typename MemoryPolicy, bits_t B, bits_t BL> -struct rrbtree -{ - using node_t = node<T, MemoryPolicy, B, BL>; - using edit_t = typename node_t::edit_t; - using owner_t = typename MemoryPolicy::transience_t::owner; - - size_t size; - shift_t shift; - node_t* root; - node_t* tail; - - static const rrbtree& empty() - { - static const rrbtree empty_{ - 0, BL, node_t::make_inner_n(0u), node_t::make_leaf_n(0u)}; - return empty_; - } - - template <typename U> - static auto from_initializer_list(std::initializer_list<U> values) - { - auto e = owner_t{}; - auto result = rrbtree{empty()}; - for (auto&& v : values) - result.push_back_mut(e, v); - return result; - } - - template <typename Iter, - typename Sent, - std::enable_if_t<compatible_sentinel_v<Iter, Sent>, bool> = true> - static auto from_range(Iter first, Sent last) - { - auto e = owner_t{}; - auto result = rrbtree{empty()}; - for (; first != last; ++first) - result.push_back_mut(e, *first); - return result; - } - - static auto from_fill(size_t n, T v) - { - auto e = owner_t{}; - auto result = rrbtree{empty()}; - while (n-- > 0) - result.push_back_mut(e, v); - return result; - } - - rrbtree(size_t sz, shift_t sh, node_t* r, node_t* t) - : size{sz} - , shift{sh} - , root{r} - , tail{t} - { - assert(check_tree()); - } - - rrbtree(const rrbtree& other) - : rrbtree{other.size, other.shift, other.root, other.tail} - { - inc(); - } - - rrbtree(rrbtree&& other) - : rrbtree{empty()} - { - swap(*this, other); - } - - rrbtree& operator=(const rrbtree& other) - { - auto next{other}; - swap(*this, next); - return *this; - } - - rrbtree& operator=(rrbtree&& other) - { - swap(*this, other); - return *this; - } - - friend void swap(rrbtree& x, rrbtree& y) - { - using std::swap; - swap(x.size, y.size); - swap(x.shift, y.shift); - swap(x.root, y.root); - swap(x.tail, y.tail); - } - - ~rrbtree() { dec(); } - - void inc() const - { - root->inc(); - tail->inc(); - } - - void dec() const { traverse(dec_visitor()); } - - auto tail_size() const { return size - tail_offset(); } - - auto tail_offset() const - { - auto r = root->relaxed(); - assert(r == nullptr || r->d.count); - return r ? r->d.sizes[r->d.count - 1] - : size ? (size - 1) & ~mask<BL> - /* otherwise */ : 0; - } - - template <typename Visitor, typename... Args> - void traverse(Visitor v, Args&&... args) const - { - auto tail_off = tail_offset(); - auto tail_size = size - tail_off; - - if (tail_off) - visit_maybe_relaxed_sub(root, shift, tail_off, v, args...); - else - make_empty_regular_pos(root).visit(v, args...); - - if (tail_size) - make_leaf_sub_pos(tail, tail_size).visit(v, args...); - else - make_empty_leaf_pos(tail).visit(v, args...); - } - - template <typename Visitor, typename... Args> - void traverse(Visitor v, size_t first, size_t last, Args&&... args) const - { - auto tail_off = tail_offset(); - auto tail_size = size - tail_off; - - if (first < tail_off) - visit_maybe_relaxed_sub(root, - shift, - tail_off, - v, - first, - last < tail_off ? last : tail_off, - args...); - if (last > tail_off) - make_leaf_sub_pos(tail, tail_size) - .visit(v, - first > tail_off ? first - tail_off : 0, - last - tail_off, - args...); - } - - template <typename Visitor, typename... Args> - bool traverse_p(Visitor v, Args&&... args) const - { - auto tail_off = tail_offset(); - auto tail_size = size - tail_off; - return (tail_off - ? visit_maybe_relaxed_sub(root, shift, tail_off, v, args...) - : make_empty_regular_pos(root).visit(v, args...)) && - (tail_size ? make_leaf_sub_pos(tail, tail_size).visit(v, args...) - : make_empty_leaf_pos(tail).visit(v, args...)); - } - - template <typename Visitor, typename... Args> - bool traverse_p(Visitor v, size_t first, size_t last, Args&&... args) const - { - auto tail_off = tail_offset(); - auto tail_size = size - tail_off; - return (first < tail_off - ? visit_maybe_relaxed_sub(root, - shift, - tail_off, - v, - first, - last < tail_off ? last : tail_off, - args...) - : true) && - (last > tail_off - ? make_leaf_sub_pos(tail, tail_size) - .visit(v, - first > tail_off ? first - tail_off : 0, - last - tail_off, - args...) - : true); - } - - template <typename Visitor> - decltype(auto) descend(Visitor v, size_t idx) const - { - auto tail_off = tail_offset(); - return idx >= tail_off - ? make_leaf_descent_pos(tail).visit(v, idx - tail_off) - : visit_maybe_relaxed_descent(root, shift, v, idx); - } - - template <typename Fn> - void for_each_chunk(Fn&& fn) const - { - traverse(for_each_chunk_visitor{}, std::forward<Fn>(fn)); - } - - template <typename Fn> - void for_each_chunk(size_t first, size_t last, Fn&& fn) const - { - traverse(for_each_chunk_i_visitor{}, first, last, std::forward<Fn>(fn)); - } - - template <typename Fn> - bool for_each_chunk_p(Fn&& fn) const - { - return traverse_p(for_each_chunk_p_visitor{}, std::forward<Fn>(fn)); - } - - template <typename Fn> - bool for_each_chunk_p(size_t first, size_t last, Fn&& fn) const - { - return traverse_p( - for_each_chunk_p_i_visitor{}, first, last, std::forward<Fn>(fn)); - } - - bool equals(const rrbtree& other) const - { - using iter_t = rrbtree_iterator<T, MemoryPolicy, B, BL>; - if (size != other.size) - return false; - if (size == 0) - return true; - auto tail_off = tail_offset(); - auto tail_off_other = other.tail_offset(); - // compare trees - if (tail_off > 0 && tail_off_other > 0) { - // other.shift != shift is a theoretical possibility for - // relaxed trees that sadly we haven't managed to exercise - // in tests yet... - if (other.shift >= shift) { - if (!visit_maybe_relaxed_sub(other.root, - other.shift, - tail_off_other, - equals_visitor::rrb{}, - iter_t{other}, - root, - shift, - tail_off)) - return false; - } else { - if (!visit_maybe_relaxed_sub(root, - shift, - tail_off, - equals_visitor::rrb{}, - iter_t{*this}, - other.root, - other.shift, - tail_off_other)) - return false; - } - } - return tail_off == tail_off_other - ? make_leaf_sub_pos(tail, tail_size()) - .visit(equals_visitor{}, other.tail) - : tail_off > tail_off_other - ? std::equal(tail->leaf(), - tail->leaf() + (size - tail_off), - other.tail->leaf() + - (tail_off - tail_off_other)) - /* otherwise */ - : std::equal(tail->leaf(), - tail->leaf() + (size - tail_off), - iter_t{other} + tail_off); - } - - std::tuple<shift_t, node_t*> push_tail(node_t* root, - shift_t shift, - size_t size, - node_t* tail, - count_t tail_size) const - { - if (auto r = root->relaxed()) { - auto new_root = - make_relaxed_pos(root, shift, r) - .visit(push_tail_visitor<node_t>{}, tail, tail_size); - if (new_root) - return std::make_tuple(shift, new_root); - else { - auto new_root = node_t::make_inner_r_n(2); - try { - auto new_path = node_t::make_path(shift, tail); - new_root->inner()[0] = root->inc(); - new_root->inner()[1] = new_path; - new_root->relaxed()->d.sizes[0] = size; - new_root->relaxed()->d.sizes[1] = size + tail_size; - new_root->relaxed()->d.count = 2u; - } catch (...) { - node_t::delete_inner_r(new_root, 2); - throw; - } - return std::make_tuple(shift + B, new_root); - } - } else if (size == size_t{branches<B>} << shift) { - auto new_root = node_t::make_inner_n(2); - try { - auto new_path = node_t::make_path(shift, tail); - new_root->inner()[0] = root->inc(); - new_root->inner()[1] = new_path; - } catch (...) { - node_t::delete_inner(new_root, 2); - throw; - } - return std::make_tuple(shift + B, new_root); - } else if (size) { - auto new_root = make_regular_sub_pos(root, shift, size) - .visit(push_tail_visitor<node_t>{}, tail); - return std::make_tuple(shift, new_root); - } else { - return std::make_tuple(shift, node_t::make_path(shift, tail)); - } - } - - void - push_tail_mut(edit_t e, size_t tail_off, node_t* tail, count_t tail_size) - { - if (auto r = root->relaxed()) { - auto new_root = - make_relaxed_pos(root, shift, r) - .visit(push_tail_mut_visitor<node_t>{}, e, tail, tail_size); - if (new_root) { - root = new_root; - } else { - auto new_root = node_t::make_inner_r_e(e); - try { - auto new_path = node_t::make_path_e(e, shift, tail); - new_root->inner()[0] = root; - new_root->inner()[1] = new_path; - new_root->relaxed()->d.sizes[0] = tail_off; - new_root->relaxed()->d.sizes[1] = tail_off + tail_size; - new_root->relaxed()->d.count = 2u; - root = new_root; - shift += B; - } catch (...) { - node_t::delete_inner_r_e(new_root); - throw; - } - } - } else if (tail_off == size_t{branches<B>} << shift) { - auto new_root = node_t::make_inner_e(e); - try { - auto new_path = node_t::make_path_e(e, shift, tail); - new_root->inner()[0] = root; - new_root->inner()[1] = new_path; - root = new_root; - shift += B; - } catch (...) { - node_t::delete_inner_e(new_root); - throw; - } - } else if (tail_off) { - auto new_root = - make_regular_sub_pos(root, shift, tail_off) - .visit(push_tail_mut_visitor<node_t>{}, e, tail); - root = new_root; - } else { - auto new_root = node_t::make_path_e(e, shift, tail); - dec_empty_regular(root); - root = new_root; - } - } - - void ensure_mutable_tail(edit_t e, count_t n) - { - if (!tail->can_mutate(e)) { - auto new_tail = node_t::copy_leaf_e(e, tail, n); - dec_leaf(tail, n); - tail = new_tail; - } - } - - void push_back_mut(edit_t e, T value) - { - auto ts = tail_size(); - if (ts < branches<BL>) { - ensure_mutable_tail(e, ts); - new (&tail->leaf()[ts]) T{std::move(value)}; - } else { - using std::get; - auto new_tail = node_t::make_leaf_e(e, std::move(value)); - auto tail_off = tail_offset(); - try { - push_tail_mut(e, tail_off, tail, ts); - tail = new_tail; - } catch (...) { - node_t::delete_leaf(new_tail, 1u); - throw; - } - } - ++size; - } - - rrbtree push_back(T value) const - { - auto ts = tail_size(); - if (ts < branches<BL>) { - auto new_tail = - node_t::copy_leaf_emplace(tail, ts, std::move(value)); - return {size + 1, shift, root->inc(), new_tail}; - } else { - using std::get; - auto new_tail = node_t::make_leaf_n(1u, std::move(value)); - auto tail_off = tail_offset(); - try { - auto new_root = - push_tail(root, shift, tail_off, tail, size - tail_off); - tail->inc(); - return {size + 1, get<0>(new_root), get<1>(new_root), new_tail}; - } catch (...) { - node_t::delete_leaf(new_tail, 1u); - throw; - } - } - } - - std::tuple<const T*, size_t, size_t> region_for(size_t idx) const - { - using std::get; - auto tail_off = tail_offset(); - if (idx >= tail_off) { - return std::make_tuple(tail->leaf(), tail_off, size); - } else { - auto subs = visit_maybe_relaxed_sub( - root, shift, tail_off, region_for_visitor<T>(), idx); - auto first = idx - get<1>(subs); - auto end = first + get<2>(subs); - return std::make_tuple(get<0>(subs), first, end); - } - } - - T& get_mut(edit_t e, size_t idx) - { - auto tail_off = tail_offset(); - if (idx >= tail_off) { - ensure_mutable_tail(e, size - tail_off); - return tail->leaf()[(idx - tail_off) & mask<BL>]; - } else { - return visit_maybe_relaxed_sub(root, - shift, - tail_off, - get_mut_visitor<node_t>{}, - idx, - e, - &root); - } - } - - const T& get(size_t index) const - { - return descend(get_visitor<T>(), index); - } - - const T& get_check(size_t index) const - { - if (index >= size) - throw std::out_of_range{"out of range"}; - return descend(get_visitor<T>(), index); - } - - const T& front() const { return get(0); } - - const T& back() const { return get(size - 1); } - - template <typename FnT> - void update_mut(edit_t e, size_t idx, FnT&& fn) - { - auto& elem = get_mut(e, idx); - elem = std::forward<FnT>(fn)(std::move(elem)); - } - - template <typename FnT> - rrbtree update(size_t idx, FnT&& fn) const - { - auto tail_off = tail_offset(); - if (idx >= tail_off) { - auto tail_size = size - tail_off; - auto new_tail = - make_leaf_sub_pos(tail, tail_size) - .visit(update_visitor<node_t>{}, idx - tail_off, fn); - return {size, shift, root->inc(), new_tail}; - } else { - auto new_root = visit_maybe_relaxed_sub( - root, shift, tail_off, update_visitor<node_t>{}, idx, fn); - return {size, shift, new_root, tail->inc()}; - } - } - - void assoc_mut(edit_t e, size_t idx, T value) - { - update_mut(e, idx, [&](auto&&) { return std::move(value); }); - } - - rrbtree assoc(size_t idx, T value) const - { - return update(idx, [&](auto&&) { return std::move(value); }); - } - - void take_mut(edit_t e, size_t new_size) - { - auto tail_off = tail_offset(); - if (new_size == 0) { - *this = empty(); - } else if (new_size >= size) { - return; - } else if (new_size > tail_off) { - auto ts = size - tail_off; - auto newts = new_size - tail_off; - if (tail->can_mutate(e)) { - destroy_n(tail->leaf() + newts, ts - newts); - } else { - auto new_tail = node_t::copy_leaf_e(e, tail, newts); - dec_leaf(tail, ts); - tail = new_tail; - } - size = new_size; - return; - } else { - using std::get; - auto l = new_size - 1; - auto v = slice_right_mut_visitor<node_t>(); - auto r = visit_maybe_relaxed_sub(root, shift, tail_off, v, l, e); - auto new_shift = get<0>(r); - auto new_root = get<1>(r); - auto new_tail = get<3>(r); - if (new_root) { - root = new_root; - shift = new_shift; - } else { - root = empty().root->inc(); - shift = BL; - } - dec_leaf(tail, size - tail_off); - size = new_size; - tail = new_tail; - return; - } - } - - rrbtree take(size_t new_size) const - { - auto tail_off = tail_offset(); - if (new_size == 0) { - return empty(); - } else if (new_size >= size) { - return *this; - } else if (new_size > tail_off) { - auto new_tail = node_t::copy_leaf(tail, new_size - tail_off); - return {new_size, shift, root->inc(), new_tail}; - } else { - using std::get; - auto l = new_size - 1; - auto v = slice_right_visitor<node_t>(); - auto r = visit_maybe_relaxed_sub(root, shift, tail_off, v, l); - auto new_shift = get<0>(r); - auto new_root = get<1>(r); - auto new_tail = get<3>(r); - if (new_root) { - IMMER_ASSERT_TAGGED(new_root->compute_shift() == get<0>(r)); - assert(new_root->check(new_shift, new_size - get<2>(r))); - return {new_size, new_shift, new_root, new_tail}; - } else { - return {new_size, BL, empty().root->inc(), new_tail}; - } - } - } - - void drop_mut(edit_t e, size_t elems) - { - using std::get; - auto tail_off = tail_offset(); - if (elems == 0) { - return; - } else if (elems >= size) { - *this = empty(); - } else if (elems == tail_off) { - dec_inner(root, shift, tail_off); - shift = BL; - root = empty().root->inc(); - size -= elems; - return; - } else if (elems > tail_off) { - auto v = slice_left_mut_visitor<node_t>(); - tail = get<1>(make_leaf_sub_pos(tail, size - tail_off) - .visit(v, elems - tail_off, e)); - if (root != empty().root) { - dec_inner(root, shift, tail_off); - shift = BL; - root = empty().root->inc(); - } - size -= elems; - return; - } else { - auto v = slice_left_mut_visitor<node_t>(); - auto r = - visit_maybe_relaxed_sub(root, shift, tail_off, v, elems, e); - shift = get<0>(r); - root = get<1>(r); - size -= elems; - return; - } - } - - rrbtree drop(size_t elems) const - { - if (elems == 0) { - return *this; - } else if (elems >= size) { - return empty(); - } else if (elems == tail_offset()) { - return {size - elems, BL, empty().root->inc(), tail->inc()}; - } else if (elems > tail_offset()) { - auto tail_off = tail_offset(); - auto new_tail = - node_t::copy_leaf(tail, elems - tail_off, size - tail_off); - return {size - elems, BL, empty().root->inc(), new_tail}; - } else { - using std::get; - auto v = slice_left_visitor<node_t>(); - auto r = - visit_maybe_relaxed_sub(root, shift, tail_offset(), v, elems); - auto new_root = get<1>(r); - auto new_shift = get<0>(r); - return {size - elems, new_shift, new_root, tail->inc()}; - } - return *this; - } - - rrbtree concat(const rrbtree& r) const - { - assert(r.size < (std::numeric_limits<size_t>::max() - size)); - using std::get; - if (size == 0) - return r; - else if (r.size == 0) - return *this; - else if (r.tail_offset() == 0) { - // just concat the tail, similar to push_back - auto tail_offst = tail_offset(); - auto tail_size = size - tail_offst; - if (tail_size == branches<BL>) { - auto new_root = - push_tail(root, shift, tail_offst, tail, tail_size); - tail->inc(); - return {size + r.size, - get<0>(new_root), - get<1>(new_root), - r.tail->inc()}; - } else if (tail_size + r.size <= branches<BL>) { - auto new_tail = - node_t::copy_leaf(tail, tail_size, r.tail, r.size); - return {size + r.size, shift, root->inc(), new_tail}; - } else { - auto remaining = branches<BL> - tail_size; - auto add_tail = - node_t::copy_leaf(tail, tail_size, r.tail, remaining); - try { - auto new_tail = - node_t::copy_leaf(r.tail, remaining, r.size); - try { - auto new_root = push_tail( - root, shift, tail_offst, add_tail, branches<BL>); - return {size + r.size, - get<0>(new_root), - get<1>(new_root), - new_tail}; - } catch (...) { - node_t::delete_leaf(new_tail, r.size - remaining); - throw; - } - } catch (...) { - node_t::delete_leaf(add_tail, branches<BL>); - throw; - } - } - } else if (tail_offset() == 0) { - auto tail_offst = tail_offset(); - auto tail_size = size - tail_offst; - auto concated = - concat_trees(tail, tail_size, r.root, r.shift, r.tail_offset()); - auto new_shift = concated.shift(); - auto new_root = concated.node(); - IMMER_ASSERT_TAGGED(new_shift == new_root->compute_shift()); - assert(new_root->check(new_shift, size + r.tail_offset())); - return {size + r.size, new_shift, new_root, r.tail->inc()}; - } else { - auto tail_offst = tail_offset(); - auto tail_size = size - tail_offst; - auto concated = concat_trees(root, - shift, - tail_offst, - tail, - tail_size, - r.root, - r.shift, - r.tail_offset()); - auto new_shift = concated.shift(); - auto new_root = concated.node(); - IMMER_ASSERT_TAGGED(new_shift == new_root->compute_shift()); - assert(new_root->check(new_shift, size + r.tail_offset())); - return {size + r.size, new_shift, new_root, r.tail->inc()}; - } - } - - constexpr static bool supports_transient_concat = - !std::is_empty<edit_t>::value; - - friend void concat_mut_l(rrbtree& l, edit_t el, const rrbtree& r) - { - assert(&l != &r); - assert(r.size < (std::numeric_limits<size_t>::max() - l.size)); - using std::get; - if (l.size == 0) - l = r; - else if (r.size == 0) - return; - else if (r.tail_offset() == 0) { - // just concat the tail, similar to push_back - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - if (tail_size == branches<BL>) { - l.push_tail_mut(el, tail_offst, l.tail, tail_size); - l.tail = r.tail->inc(); - l.size += r.size; - return; - } else if (tail_size + r.size <= branches<BL>) { - l.ensure_mutable_tail(el, tail_size); - std::uninitialized_copy(r.tail->leaf(), - r.tail->leaf() + r.size, - l.tail->leaf() + tail_size); - l.size += r.size; - return; - } else { - auto remaining = branches<BL> - tail_size; - l.ensure_mutable_tail(el, tail_size); - std::uninitialized_copy(r.tail->leaf(), - r.tail->leaf() + remaining, - l.tail->leaf() + tail_size); - try { - auto new_tail = - node_t::copy_leaf_e(el, r.tail, remaining, r.size); - try { - l.push_tail_mut(el, tail_offst, l.tail, branches<BL>); - l.tail = new_tail; - l.size += r.size; - return; - } catch (...) { - node_t::delete_leaf(new_tail, r.size - remaining); - throw; - } - } catch (...) { - destroy_n(r.tail->leaf() + tail_size, remaining); - throw; - } - } - } else if (l.tail_offset() == 0) { - if (supports_transient_concat) { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = - concat_trees_mut(el, - el, - l.tail, - tail_size, - MemoryPolicy::transience_t::noone, - r.root, - r.shift, - r.tail_offset()); - IMMER_ASSERT_TAGGED(concated.shift() == - concated.node()->compute_shift()); - assert(concated.node()->check(concated.shift(), - l.size + r.tail_offset())); - l.size += r.size; - l.shift = concated.shift(); - l.root = concated.node(); - l.tail = r.tail; - } else { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = concat_trees( - l.tail, tail_size, r.root, r.shift, r.tail_offset()); - l = {l.size + r.size, - concated.shift(), - concated.node(), - r.tail->inc()}; - return; - } - } else { - if (supports_transient_concat) { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = - concat_trees_mut(el, - el, - l.root, - l.shift, - tail_offst, - l.tail, - tail_size, - MemoryPolicy::transience_t::noone, - r.root, - r.shift, - r.tail_offset()); - IMMER_ASSERT_TAGGED(concated.shift() == - concated.node()->compute_shift()); - assert(concated.node()->check(concated.shift(), - l.size + r.tail_offset())); - l.size += r.size; - l.shift = concated.shift(); - l.root = concated.node(); - l.tail = r.tail; - } else { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = concat_trees(l.root, - l.shift, - tail_offst, - l.tail, - tail_size, - r.root, - r.shift, - r.tail_offset()); - l = {l.size + r.size, - concated.shift(), - concated.node(), - r.tail->inc()}; - } - } - } - - friend void concat_mut_r(const rrbtree& l, rrbtree& r, edit_t er) - { - assert(&l != &r); - assert(r.size < (std::numeric_limits<size_t>::max() - l.size)); - using std::get; - if (r.size == 0) - r = std::move(l); - else if (l.size == 0) - return; - else if (r.tail_offset() == 0) { - // just concat the tail, similar to push_back - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - if (tail_size == branches<BL>) { - // this could be improved by making sure that the - // newly created nodes as part of the `push_tail()` - // are tagged with `er` - auto res = - l.push_tail(l.root, l.shift, tail_offst, l.tail, tail_size); - l.tail->inc(); // note: leak if mutably concatenated - // with itself, but this is forbidden - // by the interface - r = {l.size + r.size, get<0>(res), get<1>(res), r.tail->inc()}; - return; - } else if (tail_size + r.size <= branches<BL>) { - // doing this in a exception way mutating way is very - // tricky while potential performance gains are - // minimal (we need to move every element of the right - // tail anyways to make space for the left tail) - // - // we could however improve this by at least moving the - // elements of the right tail... - auto new_tail = - node_t::copy_leaf(l.tail, tail_size, r.tail, r.size); - r = {l.size + r.size, l.shift, l.root->inc(), new_tail}; - return; - } else { - // like the immutable version - auto remaining = branches<BL> - tail_size; - auto add_tail = node_t::copy_leaf_e( - er, l.tail, tail_size, r.tail, remaining); - try { - auto new_tail = - node_t::copy_leaf_e(er, r.tail, remaining, r.size); - try { - // this could be improved by making sure that the - // newly created nodes as part of the `push_tail()` - // are tagged with `er` - auto new_root = l.push_tail(l.root, - l.shift, - tail_offst, - add_tail, - branches<BL>); - r = {l.size + r.size, - get<0>(new_root), - get<1>(new_root), - new_tail}; - return; - } catch (...) { - node_t::delete_leaf(new_tail, r.size - remaining); - throw; - } - } catch (...) { - node_t::delete_leaf(add_tail, branches<BL>); - throw; - } - return; - } - } else if (l.tail_offset() == 0) { - if (supports_transient_concat) { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = - concat_trees_mut(er, - MemoryPolicy::transience_t::noone, - l.tail, - tail_size, - er, - r.root, - r.shift, - r.tail_offset()); - IMMER_ASSERT_TAGGED(concated.shift() == - concated.node()->compute_shift()); - assert(concated.node()->check(concated.shift(), - l.size + r.tail_offset())); - r.size += l.size; - r.shift = concated.shift(); - r.root = concated.node(); - } else { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = concat_trees( - l.tail, tail_size, r.root, r.shift, r.tail_offset()); - r = {l.size + r.size, - concated.shift(), - concated.node(), - r.tail->inc()}; - return; - } - } else { - if (supports_transient_concat) { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = - concat_trees_mut(er, - MemoryPolicy::transience_t::noone, - l.root, - l.shift, - tail_offst, - l.tail, - tail_size, - er, - r.root, - r.shift, - r.tail_offset()); - IMMER_ASSERT_TAGGED(concated.shift() == - concated.node()->compute_shift()); - assert(concated.node()->check(concated.shift(), - l.size + r.tail_offset())); - r.size += l.size; - r.shift = concated.shift(); - r.root = concated.node(); - return; - } else { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = concat_trees(l.root, - l.shift, - tail_offst, - l.tail, - tail_size, - r.root, - r.shift, - r.tail_offset()); - r = {l.size + r.size, - concated.shift(), - concated.node(), - r.tail->inc()}; - return; - } - } - } - - friend void concat_mut_lr_l(rrbtree& l, edit_t el, rrbtree& r, edit_t er) - { - assert(&l != &r); - assert(r.size < (std::numeric_limits<size_t>::max() - l.size)); - using std::get; - if (l.size == 0) - l = r; - else if (r.size == 0) - return; - else if (r.tail_offset() == 0) { - // just concat the tail, similar to push_back - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - if (tail_size == branches<BL>) { - l.push_tail_mut(el, tail_offst, l.tail, tail_size); - l.tail = r.tail->inc(); - l.size += r.size; - return; - } else if (tail_size + r.size <= branches<BL>) { - l.ensure_mutable_tail(el, tail_size); - if (r.tail->can_mutate(er)) - detail::uninitialized_move(r.tail->leaf(), - r.tail->leaf() + r.size, - l.tail->leaf() + tail_size); - else - std::uninitialized_copy(r.tail->leaf(), - r.tail->leaf() + r.size, - l.tail->leaf() + tail_size); - l.size += r.size; - return; - } else { - auto remaining = branches<BL> - tail_size; - l.ensure_mutable_tail(el, tail_size); - if (r.tail->can_mutate(er)) - detail::uninitialized_move(r.tail->leaf(), - r.tail->leaf() + remaining, - l.tail->leaf() + tail_size); - else - std::uninitialized_copy(r.tail->leaf(), - r.tail->leaf() + remaining, - l.tail->leaf() + tail_size); - try { - auto new_tail = - node_t::copy_leaf_e(el, r.tail, remaining, r.size); - try { - l.push_tail_mut(el, tail_offst, l.tail, branches<BL>); - l.tail = new_tail; - l.size += r.size; - return; - } catch (...) { - node_t::delete_leaf(new_tail, r.size - remaining); - throw; - } - } catch (...) { - destroy_n(r.tail->leaf() + tail_size, remaining); - throw; - } - } - } else if (l.tail_offset() == 0) { - if (supports_transient_concat) { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = concat_trees_mut(el, - el, - l.tail, - tail_size, - er, - r.root, - r.shift, - r.tail_offset()); - IMMER_ASSERT_TAGGED(concated.shift() == - concated.node()->compute_shift()); - assert(concated.node()->check(concated.shift(), - l.size + r.tail_offset())); - l.size += r.size; - l.shift = concated.shift(); - l.root = concated.node(); - l.tail = r.tail; - r.hard_reset(); - } else { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = concat_trees( - l.tail, tail_size, r.root, r.shift, r.tail_offset()); - l = {l.size + r.size, - concated.shift(), - concated.node(), - r.tail->inc()}; - return; - } - } else { - if (supports_transient_concat) { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = concat_trees_mut(el, - el, - l.root, - l.shift, - tail_offst, - l.tail, - tail_size, - er, - r.root, - r.shift, - r.tail_offset()); - IMMER_ASSERT_TAGGED(concated.shift() == - concated.node()->compute_shift()); - assert(concated.node()->check(concated.shift(), - l.size + r.tail_offset())); - l.size += r.size; - l.shift = concated.shift(); - l.root = concated.node(); - l.tail = r.tail; - r.hard_reset(); - } else { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = concat_trees(l.root, - l.shift, - tail_offst, - l.tail, - tail_size, - r.root, - r.shift, - r.tail_offset()); - l = {l.size + r.size, - concated.shift(), - concated.node(), - r.tail->inc()}; - } - } - } - - friend void concat_mut_lr_r(rrbtree& l, edit_t el, rrbtree& r, edit_t er) - { - assert(&l != &r); - assert(r.size < (std::numeric_limits<size_t>::max() - l.size)); - using std::get; - if (r.size == 0) - r = l; - else if (l.size == 0) - return; - else if (r.tail_offset() == 0) { - // just concat the tail, similar to push_back - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - if (tail_size == branches<BL>) { - // this could be improved by making sure that the - // newly created nodes as part of the `push_tail()` - // are tagged with `er` - auto res = - l.push_tail(l.root, l.shift, tail_offst, l.tail, tail_size); - r = {l.size + r.size, get<0>(res), get<1>(res), r.tail->inc()}; - return; - } else if (tail_size + r.size <= branches<BL>) { - // doing this in a exception way mutating way is very - // tricky while potential performance gains are - // minimal (we need to move every element of the right - // tail anyways to make space for the left tail) - // - // we could however improve this by at least moving the - // elements of the mutable tails... - auto new_tail = - node_t::copy_leaf(l.tail, tail_size, r.tail, r.size); - r = {l.size + r.size, l.shift, l.root->inc(), new_tail}; - return; - } else { - // like the immutable version. - // we could improve this also by moving elements - // instead of just copying them - auto remaining = branches<BL> - tail_size; - auto add_tail = node_t::copy_leaf_e( - er, l.tail, tail_size, r.tail, remaining); - try { - auto new_tail = - node_t::copy_leaf_e(er, r.tail, remaining, r.size); - try { - // this could be improved by making sure that the - // newly created nodes as part of the `push_tail()` - // are tagged with `er` - auto new_root = l.push_tail(l.root, - l.shift, - tail_offst, - add_tail, - branches<BL>); - r = {l.size + r.size, - get<0>(new_root), - get<1>(new_root), - new_tail}; - return; - } catch (...) { - node_t::delete_leaf(new_tail, r.size - remaining); - throw; - } - } catch (...) { - node_t::delete_leaf(add_tail, branches<BL>); - throw; - } - return; - } - } else if (l.tail_offset() == 0) { - if (supports_transient_concat) { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = concat_trees_mut(er, - el, - l.tail, - tail_size, - er, - r.root, - r.shift, - r.tail_offset()); - IMMER_ASSERT_TAGGED(concated.shift() == - concated.node()->compute_shift()); - assert(concated.node()->check(concated.shift(), - l.size + r.tail_offset())); - r.size += l.size; - r.shift = concated.shift(); - r.root = concated.node(); - l.hard_reset(); - } else { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = concat_trees( - l.tail, tail_size, r.root, r.shift, r.tail_offset()); - r = {l.size + r.size, - concated.shift(), - concated.node(), - r.tail->inc()}; - return; - } - } else { - if (supports_transient_concat) { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = concat_trees_mut(er, - el, - l.root, - l.shift, - tail_offst, - l.tail, - tail_size, - er, - r.root, - r.shift, - r.tail_offset()); - IMMER_ASSERT_TAGGED(concated.shift() == - concated.node()->compute_shift()); - assert(concated.node()->check(concated.shift(), - l.size + r.tail_offset())); - r.size += l.size; - r.shift = concated.shift(); - r.root = concated.node(); - l.hard_reset(); - } else { - auto tail_offst = l.tail_offset(); - auto tail_size = l.size - tail_offst; - auto concated = concat_trees(l.root, - l.shift, - tail_offst, - l.tail, - tail_size, - r.root, - r.shift, - r.tail_offset()); - r = {l.size + r.size, - concated.shift(), - concated.node(), - r.tail->inc()}; - } - } - } - - void hard_reset() - { - assert(supports_transient_concat); - auto&& empty_ = empty(); - size = empty_.size; - shift = empty_.shift; - root = empty_.root; - tail = empty_.tail; - } - - bool check_tree() const - { - assert(shift <= sizeof(size_t) * 8 - BL); - assert(shift >= BL); - assert(tail_offset() <= size); - assert(tail_size() <= branches<BL>); -#if IMMER_DEBUG_DEEP_CHECK - assert(check_root()); - assert(check_tail()); -#endif - return true; - } - - bool check_tail() const - { -#if IMMER_DEBUG_DEEP_CHECK - if (tail_size() > 0) - assert(tail->check(endshift<B, BL>, tail_size())); -#endif - return true; - } - - bool check_root() const - { -#if IMMER_DEBUG_DEEP_CHECK - if (tail_offset() > 0) - assert(root->check(shift, tail_offset())); - else { - IMMER_ASSERT_TAGGED(root->kind() == node_t::kind_t::inner); - assert(shift == BL); - } -#endif - return true; - } - -#if IMMER_DEBUG_PRINT - void debug_print(std::ostream& out) const - { - out << "--" << std::endl - << "{" << std::endl - << " size = " << size << std::endl - << " shift = " << shift << std::endl - << " root = " << std::endl; - debug_print_node(out, root, shift, tail_offset()); - out << " tail = " << std::endl; - debug_print_node(out, tail, endshift<B, BL>, tail_size()); - out << "}" << std::endl; - } - - void debug_print_indent(std::ostream& out, unsigned indent) const - { - while (indent-- > 0) - out << ' '; - } - - void debug_print_node(std::ostream& out, - node_t* node, - shift_t shift, - size_t size, - unsigned indent = 8) const - { - const auto indent_step = 4; - - if (shift == endshift<B, BL>) { - debug_print_indent(out, indent); - out << "- {" << size << "} " - << pretty_print_array(node->leaf(), size) << std::endl; - } else if (auto r = node->relaxed()) { - auto count = r->d.count; - debug_print_indent(out, indent); - out << "# {" << size << "} " - << pretty_print_array(r->d.sizes, r->d.count) << std::endl; - auto last_size = size_t{}; - for (auto i = count_t{}; i < count; ++i) { - debug_print_node(out, - node->inner()[i], - shift - B, - r->d.sizes[i] - last_size, - indent + indent_step); - last_size = r->d.sizes[i]; - } - } else { - debug_print_indent(out, indent); - out << "+ {" << size << "}" << std::endl; - auto count = - (size >> shift) + (size - ((size >> shift) << shift) > 0); - if (count) { - for (auto i = count_t{}; i < count - 1; ++i) - debug_print_node(out, - node->inner()[i], - shift - B, - 1 << shift, - indent + indent_step); - debug_print_node(out, - node->inner()[count - 1], - shift - B, - size - ((count - 1) << shift), - indent + indent_step); - } - } - } -#endif // IMMER_DEBUG_PRINT -}; - -} // namespace rbts -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/rbts/rrbtree_iterator.hpp b/third_party/immer/immer/detail/rbts/rrbtree_iterator.hpp deleted file mode 100644 index af967774e7ef..000000000000 --- a/third_party/immer/immer/detail/rbts/rrbtree_iterator.hpp +++ /dev/null @@ -1,98 +0,0 @@ -// -// 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 <immer/detail/iterator_facade.hpp> -#include <immer/detail/rbts/rrbtree.hpp> - -namespace immer { -namespace detail { -namespace rbts { - -template <typename T, typename MP, bits_t B, bits_t BL> -struct rrbtree_iterator - : iterator_facade<rrbtree_iterator<T, MP, B, BL>, - std::random_access_iterator_tag, - T, - const T&, - std::ptrdiff_t, - const T*> -{ - using tree_t = rrbtree<T, MP, B, BL>; - using region_t = std::tuple<const T*, size_t, size_t>; - - struct end_t - {}; - - const tree_t& impl() const { return *v_; } - size_t index() const { return i_; } - - rrbtree_iterator() = default; - - rrbtree_iterator(const tree_t& v) - : v_{&v} - , i_{0} - , curr_{nullptr, ~size_t{}, ~size_t{}} - {} - - rrbtree_iterator(const tree_t& v, end_t) - : v_{&v} - , i_{v.size} - , curr_{nullptr, ~size_t{}, ~size_t{}} - {} - -private: - friend iterator_core_access; - - const tree_t* v_; - size_t i_; - mutable region_t curr_; - - void increment() - { - using std::get; - assert(i_ < v_->size); - ++i_; - } - - void decrement() - { - using std::get; - assert(i_ > 0); - --i_; - } - - void advance(std::ptrdiff_t n) - { - using std::get; - assert(n <= 0 || i_ + static_cast<size_t>(n) <= v_->size); - assert(n >= 0 || static_cast<size_t>(-n) <= i_); - i_ += n; - } - - bool equal(const rrbtree_iterator& other) const { return i_ == other.i_; } - - std::ptrdiff_t distance_to(const rrbtree_iterator& other) const - { - return other.i_ > i_ ? static_cast<std::ptrdiff_t>(other.i_ - i_) - : -static_cast<std::ptrdiff_t>(i_ - other.i_); - } - - const T& dereference() const - { - using std::get; - if (i_ < get<1>(curr_) || i_ >= get<2>(curr_)) - curr_ = v_->region_for(i_); - return get<0>(curr_)[i_ - get<1>(curr_)]; - } -}; - -} // namespace rbts -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/rbts/visitor.hpp b/third_party/immer/immer/detail/rbts/visitor.hpp deleted file mode 100644 index 38cd030c4a9f..000000000000 --- a/third_party/immer/immer/detail/rbts/visitor.hpp +++ /dev/null @@ -1,56 +0,0 @@ -// -// 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 <immer/config.hpp> - -#include <tuple> -#include <utility> - -namespace immer { -namespace detail { -namespace rbts { - -template <typename Deriv> -struct visitor_base -{ - template <typename... Args> - static decltype(auto) visit_node(Args&&... args) - { - IMMER_UNREACHABLE; - } - - template <typename... Args> - static decltype(auto) visit_relaxed(Args&&... args) - { - return Deriv::visit_inner(std::forward<Args>(args)...); - } - - template <typename... Args> - static decltype(auto) visit_regular(Args&&... args) - { - return Deriv::visit_inner(std::forward<Args>(args)...); - } - - template <typename... Args> - static decltype(auto) visit_inner(Args&&... args) - { - return Deriv::visit_node(std::forward<Args>(args)...); - } - - template <typename... Args> - static decltype(auto) visit_leaf(Args&&... args) - { - return Deriv::visit_node(std::forward<Args>(args)...); - } -}; - -} // namespace rbts -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/ref_count_base.hpp b/third_party/immer/immer/detail/ref_count_base.hpp deleted file mode 100644 index 28f2e46a99f9..000000000000 --- a/third_party/immer/immer/detail/ref_count_base.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// -// 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 <atomic> - -namespace immer { -namespace detail { - -template <typename Deriv> -struct ref_count_base -{ - mutable std::atomic<int> ref_count{0}; - - friend void intrusive_ptr_add_ref(const Deriv* x) - { - x->ref_count.fetch_add(1, std::memory_order_relaxed); - } - - friend void intrusive_ptr_release(const Deriv* x) - { - if (x->ref_count.fetch_sub(1, std::memory_order_release) == 1) { - std::atomic_thread_fence(std::memory_order_acquire); - delete x; - } - } -}; - -} /* namespace detail */ -} /* namespace immer */ diff --git a/third_party/immer/immer/detail/type_traits.hpp b/third_party/immer/immer/detail/type_traits.hpp deleted file mode 100644 index afb2652c55a8..000000000000 --- a/third_party/immer/immer/detail/type_traits.hpp +++ /dev/null @@ -1,223 +0,0 @@ -// -// 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 <algorithm> -#include <iterator> -#include <memory> -#include <type_traits> -#include <utility> - -namespace immer { -namespace detail { - -template <typename... Ts> -struct make_void -{ - using type = void; -}; - -template <typename... Ts> -using void_t = typename make_void<Ts...>::type; - -template <typename T, typename = void> -struct is_dereferenceable : std::false_type -{}; - -template <typename T> -struct is_dereferenceable<T, void_t<decltype(*(std::declval<T&>()))>> - : std::true_type -{}; - -template <typename T> -constexpr bool is_dereferenceable_v = is_dereferenceable<T>::value; - -template <typename T, typename U = T, typename = void> -struct is_equality_comparable : std::false_type -{}; - -template <typename T, typename U> -struct is_equality_comparable< - T, - U, - std::enable_if_t<std::is_same<bool, - decltype(std::declval<T&>() == - std::declval<U&>())>::value>> - : std::true_type -{}; - -template <typename T, typename U = T> -constexpr bool is_equality_comparable_v = is_equality_comparable<T, U>::value; - -template <typename T, typename U = T, typename = void> -struct is_inequality_comparable : std::false_type -{}; - -template <typename T, typename U> -struct is_inequality_comparable< - T, - U, - std::enable_if_t<std::is_same<bool, - decltype(std::declval<T&>() != - std::declval<U&>())>::value>> - : std::true_type -{}; - -template <typename T, typename U = T> -constexpr bool is_inequality_comparable_v = - is_inequality_comparable<T, U>::value; - -template <typename T, typename = void> -struct is_preincrementable : std::false_type -{}; - -template <typename T> -struct is_preincrementable< - T, - std::enable_if_t<std::is_same<T&, decltype(++(std::declval<T&>()))>::value>> - : std::true_type -{}; - -template <typename T> -constexpr bool is_preincrementable_v = is_preincrementable<T>::value; - -template <typename T, typename U = T, typename = void> -struct is_subtractable : std::false_type -{}; - -template <typename T, typename U> -struct is_subtractable< - T, - U, - void_t<decltype(std::declval<T&>() - std::declval<U&>())>> : std::true_type -{}; - -template <typename T, typename U = T> -constexpr bool is_subtractable_v = is_subtractable<T, U>::value; - -namespace swappable { - -using std::swap; - -template <typename T, typename U, typename = void> -struct with : std::false_type -{}; - -// Does not account for non-referenceable types -template <typename T, typename U> -struct with<T, - U, - void_t<decltype(swap(std::declval<T&>(), std::declval<U&>())), - decltype(swap(std::declval<U&>(), std::declval<T&>()))>> - : std::true_type -{}; - -} // namespace swappable - -template <typename T, typename U> -using is_swappable_with = swappable::with<T, U>; - -template <typename T> -using is_swappable = is_swappable_with<T, T>; - -template <typename T> -constexpr bool is_swappable_v = is_swappable_with<T&, T&>::value; - -template <typename T, typename = void> -struct is_iterator : std::false_type -{}; - -// See http://en.cppreference.com/w/cpp/concept/Iterator -template <typename T> -struct is_iterator< - T, - void_t< - std::enable_if_t<is_preincrementable_v<T> && - is_dereferenceable_v<T> - // accounts for non-referenceable types - && std::is_copy_constructible<T>::value && - std::is_copy_assignable<T>::value && - std::is_destructible<T>::value && is_swappable_v<T>>, - typename std::iterator_traits<T>::value_type, - typename std::iterator_traits<T>::difference_type, - typename std::iterator_traits<T>::reference, - typename std::iterator_traits<T>::pointer, - typename std::iterator_traits<T>::iterator_category>> : std::true_type -{}; - -template <typename T> -constexpr bool is_iterator_v = is_iterator<T>::value; - -template <typename T, typename U, typename = void> -struct compatible_sentinel : std::false_type -{}; - -template <typename T, typename U> -struct compatible_sentinel< - T, - U, - std::enable_if_t<is_iterator_v<T> && is_equality_comparable_v<T, U> && - is_inequality_comparable_v<T, U>>> : std::true_type -{}; - -template <typename T, typename U> -constexpr bool compatible_sentinel_v = compatible_sentinel<T, U>::value; - -template <typename T, typename = void> -struct is_forward_iterator : std::false_type -{}; - -template <typename T> -struct is_forward_iterator< - T, - std::enable_if_t<is_iterator_v<T> && - std::is_base_of<std::forward_iterator_tag, - typename std::iterator_traits< - T>::iterator_category>::value>> - : std::true_type -{}; - -template <typename T> -constexpr bool is_forward_iterator_v = is_forward_iterator<T>::value; - -template <typename T, typename U, typename = void> -struct std_distance_supports : std::false_type -{}; - -template <typename T, typename U> -struct std_distance_supports< - T, - U, - void_t<decltype(std::distance(std::declval<T>(), std::declval<U>()))>> - : std::true_type -{}; - -template <typename T, typename U> -constexpr bool std_distance_supports_v = std_distance_supports<T, U>::value; - -template <typename T, typename U, typename V, typename = void> -struct std_uninitialized_copy_supports : std::false_type -{}; - -template <typename T, typename U, typename V> -struct std_uninitialized_copy_supports< - T, - U, - V, - void_t<decltype(std::uninitialized_copy( - std::declval<T>(), std::declval<U>(), std::declval<V>()))>> - : std::true_type -{}; - -template <typename T, typename U, typename V> -constexpr bool std_uninitialized_copy_supports_v = - std_uninitialized_copy_supports<T, U, V>::value; - -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/detail/util.hpp b/third_party/immer/immer/detail/util.hpp deleted file mode 100644 index fb2a520fc78f..000000000000 --- a/third_party/immer/immer/detail/util.hpp +++ /dev/null @@ -1,258 +0,0 @@ -// -// 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 <immer/config.hpp> - -#include <cstddef> -#include <memory> -#include <new> -#include <type_traits> - -#include <immer/detail/type_traits.hpp> - -#if defined(_MSC_VER) -#include <intrin.h> // for __lzcnt* -#endif - -namespace immer { -namespace detail { - -template <typename T> -using aligned_storage_for = - typename std::aligned_storage<sizeof(T), alignof(T)>::type; - -template <typename T> -T& auto_const_cast(const T& x) -{ - return const_cast<T&>(x); -} -template <typename T> -T&& auto_const_cast(const T&& x) -{ - return const_cast<T&&>(std::move(x)); -} - -template <typename Iter1, typename Iter2> -auto uninitialized_move(Iter1 in1, Iter1 in2, Iter2 out) -{ - return std::uninitialized_copy( - std::make_move_iterator(in1), std::make_move_iterator(in2), out); -} - -template <class T> -void destroy(T* first, T* last) -{ - for (; first != last; ++first) - first->~T(); -} - -template <class T, class Size> -void destroy_n(T* p, Size n) -{ - auto e = p + n; - for (; p != e; ++p) - p->~T(); -} - -template <typename Heap, typename T, typename... Args> -T* make(Args&&... args) -{ - auto ptr = Heap::allocate(sizeof(T)); - try { - return new (ptr) T{std::forward<Args>(args)...}; - } catch (...) { - Heap::deallocate(sizeof(T), ptr); - throw; - } -} - -struct not_supported_t -{}; -struct empty_t -{}; - -template <typename T> -struct exact_t -{ - T value; - exact_t(T v) - : value{v} {}; -}; - -template <typename T> -inline constexpr auto clz_(T) -> not_supported_t -{ - IMMER_UNREACHABLE; - return {}; -} -#if defined(_MSC_VER) -// inline auto clz_(unsigned short x) { return __lzcnt16(x); } -// inline auto clz_(unsigned int x) { return __lzcnt(x); } -// inline auto clz_(unsigned __int64 x) { return __lzcnt64(x); } -#else -inline constexpr auto clz_(unsigned int x) { return __builtin_clz(x); } -inline constexpr auto clz_(unsigned long x) { return __builtin_clzl(x); } -inline constexpr auto clz_(unsigned long long x) { return __builtin_clzll(x); } -#endif - -template <typename T> -inline constexpr T log2_aux(T x, T r = 0) -{ - return x <= 1 ? r : log2_aux(x >> 1, r + 1); -} - -template <typename T> -inline constexpr auto log2(T x) -> std:: - enable_if_t<!std::is_same<decltype(clz_(x)), not_supported_t>::value, T> -{ - return x == 0 ? 0 : sizeof(std::size_t) * 8 - 1 - clz_(x); -} - -template <typename T> -inline constexpr auto log2(T x) - -> std::enable_if_t<std::is_same<decltype(clz_(x)), not_supported_t>::value, - T> -{ - return log2_aux(x); -} - -template <bool b, typename F> -auto static_if(F&& f) -> std::enable_if_t<b> -{ - std::forward<F>(f)(empty_t{}); -} -template <bool b, typename F> -auto static_if(F&& f) -> std::enable_if_t<!b> -{} - -template <bool b, typename R = void, typename F1, typename F2> -auto static_if(F1&& f1, F2&& f2) -> std::enable_if_t<b, R> -{ - return std::forward<F1>(f1)(empty_t{}); -} -template <bool b, typename R = void, typename F1, typename F2> -auto static_if(F1&& f1, F2&& f2) -> std::enable_if_t<!b, R> -{ - return std::forward<F2>(f2)(empty_t{}); -} - -template <typename T, T value> -struct constantly -{ - template <typename... Args> - T operator()(Args&&...) const - { - return value; - } -}; - -/*! - * An alias to `std::distance` - */ -template <typename Iterator, - typename Sentinel, - std::enable_if_t<detail::std_distance_supports_v<Iterator, Sentinel>, - bool> = true> -typename std::iterator_traits<Iterator>::difference_type -distance(Iterator first, Sentinel last) -{ - return std::distance(first, last); -} - -/*! - * Equivalent of the `std::distance` applied to the sentinel-delimited - * forward range @f$ [first, last) @f$ - */ -template <typename Iterator, - typename Sentinel, - std::enable_if_t< - (!detail::std_distance_supports_v<Iterator, Sentinel>) &&detail:: - is_forward_iterator_v<Iterator> && - detail::compatible_sentinel_v<Iterator, Sentinel> && - (!detail::is_subtractable_v<Sentinel, Iterator>), - bool> = true> -typename std::iterator_traits<Iterator>::difference_type -distance(Iterator first, Sentinel last) -{ - std::size_t result = 0; - while (first != last) { - ++first; - ++result; - } - return result; -} - -/*! - * Equivalent of the `std::distance` applied to the sentinel-delimited - * random access range @f$ [first, last) @f$ - */ -template <typename Iterator, - typename Sentinel, - std::enable_if_t< - (!detail::std_distance_supports_v<Iterator, Sentinel>) &&detail:: - is_forward_iterator_v<Iterator> && - detail::compatible_sentinel_v<Iterator, Sentinel> && - detail::is_subtractable_v<Sentinel, Iterator>, - bool> = true> -typename std::iterator_traits<Iterator>::difference_type -distance(Iterator first, Sentinel last) -{ - return last - first; -} - -/*! - * An alias to `std::uninitialized_copy` - */ -template < - typename Iterator, - typename Sentinel, - typename SinkIter, - std::enable_if_t< - detail::std_uninitialized_copy_supports_v<Iterator, Sentinel, SinkIter>, - bool> = true> -SinkIter uninitialized_copy(Iterator first, Sentinel last, SinkIter d_first) -{ - return std::uninitialized_copy(first, last, d_first); -} - -/*! - * Equivalent of the `std::uninitialized_copy` applied to the - * sentinel-delimited forward range @f$ [first, last) @f$ - */ -template <typename SourceIter, - typename Sent, - typename SinkIter, - std::enable_if_t< - (!detail::std_uninitialized_copy_supports_v<SourceIter, - Sent, - SinkIter>) &&detail:: - compatible_sentinel_v<SourceIter, Sent> && - detail::is_forward_iterator_v<SinkIter>, - bool> = true> -SinkIter uninitialized_copy(SourceIter first, Sent last, SinkIter d_first) -{ - auto current = d_first; - try { - while (first != last) { - *current++ = *first; - ++first; - } - } catch (...) { - using Value = typename std::iterator_traits<SinkIter>::value_type; - for (; d_first != current; ++d_first) { - d_first->~Value(); - } - throw; - } - return current; -} - -} // namespace detail -} // namespace immer diff --git a/third_party/immer/immer/experimental/detail/dvektor_impl.hpp b/third_party/immer/immer/experimental/detail/dvektor_impl.hpp deleted file mode 100644 index 81dbbc59f5f3..000000000000 --- a/third_party/immer/immer/experimental/detail/dvektor_impl.hpp +++ /dev/null @@ -1,498 +0,0 @@ -// -// 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 <immer/heap/heap_policy.hpp> -#include <immer/refcount/enable_intrusive_ptr.hpp> -#include <immer/refcount/refcount_policy.hpp> - -#include <boost/intrusive_ptr.hpp> -#include <boost/iterator/iterator_facade.hpp> -#include <boost/smart_ptr/intrusive_ref_counter.hpp> - -#include <cassert> -#include <limits> - -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 <int B, typename T = std::size_t> -constexpr T branches = T{1} << B; - -template <int B, typename T = std::size_t> -constexpr T mask = branches<B, T> - 1; - -template <int B, typename T = std::size_t> -constexpr auto - max_depth = fast_log2(std::numeric_limits<std::size_t>::max()) / B; - -template <typename T, int B, typename MP> -struct node; - -template <typename T, int B, typename MP> -using node_ptr = boost::intrusive_ptr<node<T, B, MP>>; - -template <typename T, int B> -using leaf_node = std::array<T, 1 << B>; - -template <typename T, int B, typename MP> -using inner_node = std::array<node_ptr<T, B, MP>, 1 << B>; - -template <typename T, int B, typename MP> -struct node - : enable_intrusive_ptr<node<T, B, MP>, typename MP::refcount> - , enable_optimized_heap_policy<node<T, B, MP>, typename MP::heap> -{ - using leaf_node_t = leaf_node<T, B>; - using inner_node_t = inner_node<T, B, MP>; - - 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<T, B> n) - : kind{leaf_kind} - , data{std::move(n)} - {} - - node(inner_node<T, B, MP> 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 <typename T, int B, typename MP, typename... Ts> -auto make_node(Ts&&... xs) -> boost::intrusive_ptr<node<T, B, MP>> -{ - return new node<T, B, MP>(std::forward<Ts>(xs)...); -} - -template <typename T, int B, typename MP> -struct ref -{ - using inner_t = inner_node<T, B, MP>; - using leaf_t = leaf_node<T, B>; - using node_t = node<T, B, MP>; - using node_ptr_t = node_ptr<T, B, MP>; - - unsigned depth; - std::array<node_ptr_t, max_depth<B>> display; - - template <typename... Ts> - static auto make_node(Ts&&... xs) - { - return dvektor::make_node<T, B, MP>(std::forward<Ts>(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<B>].get(); - shift -= B; - } - return node->leaf()[index & mask<B>]; - } - - 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<B>] = 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<B>); - shift -= B; - } - display[0] = - null_slot_and_copy_leaf(display[1], (index >> B) & mask<B>); - } - } - - 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<B>] = - 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<B>); - } - display[0] = - null_slot_and_copy_leaf(display[1], (new_index >> B) & mask<B>); - } - } - - 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<B>] = - display[display_idx - 1]; - ++depth; - } - while (--display_idx) { - auto node = display[display_idx + 1] - ->inner()[(new_index >> shift) & mask<B>]; - 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<B>]; - 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<B>]; - shift -= B; - } while (--display_idx); - } - } -}; - -template <typename T, int B, typename MP> -struct impl -{ - using inner_t = inner_node<T, B, MP>; - using leaf_t = leaf_node<T, B>; - using node_t = node<T, B, MP>; - using node_ptr_t = node_ptr<T, B, MP>; - using ref_t = ref<T, B, MP>; - - std::size_t size; - std::size_t focus; - bool dirty; - ref_t p; - - template <typename... Ts> - static auto make_node(Ts&&... xs) - { - return dvektor::make_node<T, B, MP>(std::forward<Ts>(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<B>; - auto lo = size & mask<B>; - 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 <typename FnT> - 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<B>]; - 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 <typename T, int B, typename MP> -const impl<T, B, MP> empty = {0, 0, false, ref<T, B, MP>{1, {}}}; - -template <typename T, int B, typename MP> -struct iterator - : boost::iterator_facade<iterator<T, B, MP>, - T, - boost::random_access_traversal_tag, - const T&> -{ - struct end_t - {}; - - iterator() = default; - - iterator(const impl<T, B, MP>& 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<T, B, MP>& v, end_t) - : p_{v.p} - , i_{v.size} - , base_{(v.size - 1) & ~mask<B>} - { - 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<T, B>::const_iterator; - - ref<T, B, MP> p_; - std::size_t i_; - std::size_t base_; - leaf_iterator curr_; - - void increment() - { - ++i_; - if (i_ - base_ < branches<B>) { - ++curr_; - } else { - auto new_base = base_ + branches<B>; - 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<B>; - 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<B>) { - curr_ += n; - } else { - auto new_base = i_ & ~mask<B>; - 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<std::ptrdiff_t>(other.i_ - i_) - : -static_cast<std::ptrdiff_t>(i_ - other.i_); - } - - const T& dereference() const { return *curr_; } -}; - -} /* namespace dvektor */ -} /* namespace detail */ -} /* namespace immer */ diff --git a/third_party/immer/immer/experimental/dvektor.hpp b/third_party/immer/immer/experimental/dvektor.hpp deleted file mode 100644 index e809e749d5df..000000000000 --- a/third_party/immer/immer/experimental/dvektor.hpp +++ /dev/null @@ -1,69 +0,0 @@ -// -// 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 <immer/experimental/detail/dvektor_impl.hpp> - -#include <immer/memory_policy.hpp> - -namespace immer { - -template <typename T, int B = 5, typename MemoryPolicy = default_memory_policy> -class dvektor -{ - using impl_t = detail::dvektor::impl<T, B, MemoryPolicy>; - -public: - using value_type = T; - using reference = const T&; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using const_reference = const T&; - - using iterator = detail::dvektor::iterator<T, B, MemoryPolicy>; - using const_iterator = iterator; - using reverse_iterator = std::reverse_iterator<iterator>; - - dvektor() = default; - - iterator begin() const { return {impl_}; } - iterator end() const { return {impl_, typename iterator::end_t{}}; } - - reverse_iterator rbegin() const { return reverse_iterator{end()}; } - reverse_iterator rend() const { return reverse_iterator{begin()}; } - - std::size_t size() const { return impl_.size; } - bool empty() const { return impl_.size == 0; } - - reference operator[](size_type index) const { return impl_.get(index); } - - dvektor push_back(value_type value) const - { - return {impl_.push_back(std::move(value))}; - } - - dvektor assoc(std::size_t idx, value_type value) const - { - return {impl_.assoc(idx, std::move(value))}; - } - - template <typename FnT> - dvektor update(std::size_t idx, FnT&& fn) const - { - return {impl_.update(idx, std::forward<FnT>(fn))}; - } - -private: - dvektor(impl_t impl) - : impl_(std::move(impl)) - {} - impl_t impl_ = detail::dvektor::empty<T, B, MemoryPolicy>; -}; - -} // namespace immer diff --git a/third_party/immer/immer/flex_vector.hpp b/third_party/immer/immer/flex_vector.hpp deleted file mode 100644 index d03c3f7e4585..000000000000 --- a/third_party/immer/immer/flex_vector.hpp +++ /dev/null @@ -1,608 +0,0 @@ -// -// 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 <immer/detail/rbts/rrbtree.hpp> -#include <immer/detail/rbts/rrbtree_iterator.hpp> -#include <immer/memory_policy.hpp> - -namespace immer { - -template <typename T, - typename MP, - detail::rbts::bits_t B, - detail::rbts::bits_t BL> -class vector; - -template <typename T, - typename MP, - detail::rbts::bits_t B, - detail::rbts::bits_t BL> -class flex_vector_transient; - -/*! - * Immutable sequential container supporting both random access, - * structural sharing and efficient concatenation and slicing. - * - * @tparam T The type of the values to be stored in the container. - * @tparam MemoryPolicy Memory management policy. See @ref - * memory_policy. - * - * @rst - * - * This container is very similar to `vector`_ but also supports - * :math:`O(log(size))` *concatenation*, *slicing* and *insertion* at - * any point. Its performance characteristics are almost identical - * until one of these operations is performed. After that, - * performance is degraded by a constant factor that usually oscilates - * in the range :math:`[1, 2)` depending on the operation and the - * amount of flexible operations that have been performed. - * - * .. tip:: A `vector`_ can be converted to a `flex_vector`_ in - * constant time without any allocation. This is so because the - * internal structure of a *vector* is a strict subset of the - * internal structure of a *flexible vector*. You can take - * advantage of this property by creating normal vectors as long as - * the flexible operations are not needed, and convert later in - * your processing pipeline once and if these are needed. - * - * @endrst - */ -template <typename T, - typename MemoryPolicy = default_memory_policy, - detail::rbts::bits_t B = default_bits, - detail::rbts::bits_t BL = - detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>> -class flex_vector -{ - using impl_t = detail::rbts::rrbtree<T, MemoryPolicy, B, BL>; - - using move_t = - std::integral_constant<bool, MemoryPolicy::use_transient_rvalues>; - -public: - static constexpr auto bits = B; - static constexpr auto bits_leaf = BL; - using memory_policy = MemoryPolicy; - - using value_type = T; - using reference = const T&; - using size_type = detail::rbts::size_t; - using difference_type = std::ptrdiff_t; - using const_reference = const T&; - - using iterator = detail::rbts::rrbtree_iterator<T, MemoryPolicy, B, BL>; - using const_iterator = iterator; - using reverse_iterator = std::reverse_iterator<iterator>; - - using transient_type = flex_vector_transient<T, MemoryPolicy, B, BL>; - - /*! - * Default constructor. It creates a flex_vector of `size() == 0`. - * It does not allocate memory and its complexity is @f$ O(1) @f$. - */ - flex_vector() = default; - - /*! - * Constructs a flex_vector containing the elements in `values`. - */ - flex_vector(std::initializer_list<T> values) - : impl_{impl_t::from_initializer_list(values)} - {} - - /*! - * Constructs a flex_vector containing the elements in the range - * defined by the input iterator `first` and range sentinel `last`. - */ - template <typename Iter, - typename Sent, - std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent>, - bool> = true> - flex_vector(Iter first, Sent last) - : impl_{impl_t::from_range(first, last)} - {} - - /*! - * Constructs a vector containing the element `val` repeated `n` - * times. - */ - flex_vector(size_type n, T v = {}) - : impl_{impl_t::from_fill(n, v)} - {} - - /*! - * Default constructor. It creates a flex_vector with the same - * contents as `v`. It does not allocate memory and is - * @f$ O(1) @f$. - */ - flex_vector(vector<T, MemoryPolicy, B, BL> v) - : impl_{v.impl_.size, - v.impl_.shift, - v.impl_.root->inc(), - v.impl_.tail->inc()} - {} - - /*! - * Returns an iterator pointing at the first element of the - * collection. It does not allocate memory and its complexity is - * @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator begin() const { return {impl_}; } - - /*! - * Returns an iterator pointing just after the last element of the - * collection. It does not allocate and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator end() const - { - return {impl_, typename iterator::end_t{}}; - } - - /*! - * Returns an iterator that traverses the collection backwards, - * pointing at the first element of the reversed collection. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD reverse_iterator rbegin() const - { - return reverse_iterator{end()}; - } - - /*! - * Returns an iterator that traverses the collection backwards, - * pointing after the last element of the reversed collection. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD reverse_iterator rend() const - { - return reverse_iterator{begin()}; - } - - /*! - * Returns the number of elements in the container. It does - * not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD size_type size() const { return impl_.size; } - - /*! - * Returns `true` if there are no elements in the container. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD bool empty() const { return impl_.size == 0; } - - /*! - * Access the last element. - */ - IMMER_NODISCARD const T& back() const { return impl_.back(); } - - /*! - * Access the first element. - */ - IMMER_NODISCARD const T& front() const { return impl_.front(); } - - /*! - * Returns a `const` reference to the element at position `index`. - * It is undefined when @f$ 0 index \geq size() @f$. It does not - * allocate memory and its complexity is *effectively* @f$ O(1) - * @f$. - */ - IMMER_NODISCARD reference operator[](size_type index) const - { - return impl_.get(index); - } - - /*! - * Returns a `const` reference to the element at position - * `index`. It throws an `std::out_of_range` exception when @f$ - * index \geq size() @f$. It does not allocate memory and its - * complexity is *effectively* @f$ O(1) @f$. - */ - reference at(size_type index) const { return impl_.get_check(index); } - - /*! - * Returns whether the vectors are equal. - */ - IMMER_NODISCARD bool operator==(const flex_vector& other) const - { - return impl_.equals(other.impl_); - } - IMMER_NODISCARD bool operator!=(const flex_vector& other) const - { - return !(*this == other); - } - - /*! - * Returns a flex_vector with `value` inserted at the end. It may - * allocate memory and its complexity is *effectively* @f$ O(1) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/flex-vector/flex-vector.cpp - * :language: c++ - * :dedent: 8 - * :start-after: push-back/start - * :end-before: push-back/end - * - * @endrst - */ - IMMER_NODISCARD flex_vector push_back(value_type value) const& - { - return impl_.push_back(std::move(value)); - } - - IMMER_NODISCARD decltype(auto) push_back(value_type value) && - { - return push_back_move(move_t{}, std::move(value)); - } - - /*! - * Returns a flex_vector with `value` inserted at the frony. It may - * allocate memory and its complexity is @f$ O(log(size)) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/flex-vector/flex-vector.cpp - * :language: c++ - * :dedent: 8 - * :start-after: push-front/start - * :end-before: push-front/end - * - * @endrst - */ - IMMER_NODISCARD flex_vector push_front(value_type value) const - { - return flex_vector{}.push_back(value) + *this; - } - - /*! - * Returns a flex_vector containing value `value` at position `index`. - * Undefined for `index >= size()`. - * It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/flex-vector/flex-vector.cpp - * :language: c++ - * :dedent: 8 - * :start-after: set/start - * :end-before: set/end - * - * @endrst - */ - IMMER_NODISCARD flex_vector set(size_type index, value_type value) const& - { - return impl_.assoc(index, std::move(value)); - } - - IMMER_NODISCARD decltype(auto) set(size_type index, value_type value) && - { - return set_move(move_t{}, index, std::move(value)); - } - - /*! - * Returns a vector containing the result of the expression - * `fn((*this)[idx])` at position `idx`. - * Undefined for `index >= size()`. - * It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/flex-vector/flex-vector.cpp - * :language: c++ - * :dedent: 8 - * :start-after: update/start - * :end-before: update/end - * - * @endrst - - */ - template <typename FnT> - IMMER_NODISCARD flex_vector update(size_type index, FnT&& fn) const& - { - return impl_.update(index, std::forward<FnT>(fn)); - } - - template <typename FnT> - IMMER_NODISCARD decltype(auto) update(size_type index, FnT&& fn) && - { - return update_move(move_t{}, index, std::forward<FnT>(fn)); - } - - /*! - * Returns a vector containing only the first `min(elems, size())` - * elements. It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/flex-vector/flex-vector.cpp - * :language: c++ - * :dedent: 8 - * :start-after: take/start - * :end-before: take/end - * - * @endrst - */ - IMMER_NODISCARD flex_vector take(size_type elems) const& - { - return impl_.take(elems); - } - - IMMER_NODISCARD decltype(auto) take(size_type elems) && - { - return take_move(move_t{}, elems); - } - - /*! - * Returns a vector without the first `min(elems, size())` - * elements. It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/flex-vector/flex-vector.cpp - * :language: c++ - * :dedent: 8 - * :start-after: drop/start - * :end-before: drop/end - * - * @endrst - */ - IMMER_NODISCARD flex_vector drop(size_type elems) const& - { - return impl_.drop(elems); - } - - IMMER_NODISCARD decltype(auto) drop(size_type elems) && - { - return drop_move(move_t{}, elems); - } - - /*! - * Concatenation operator. Returns a flex_vector with the contents - * of `l` followed by those of `r`. It may allocate memory - * and its complexity is @f$ O(log(max(size_r, size_l))) @f$ - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/flex-vector/flex-vector.cpp - * :language: c++ - * :dedent: 8 - * :start-after: concat/start - * :end-before: concat/end - * - * @endrst - */ - IMMER_NODISCARD friend flex_vector operator+(const flex_vector& l, - const flex_vector& r) - { - return l.impl_.concat(r.impl_); - } - - IMMER_NODISCARD friend decltype(auto) operator+(flex_vector&& l, - const flex_vector& r) - { - return concat_move(move_t{}, std::move(l), r); - } - - IMMER_NODISCARD friend decltype(auto) operator+(const flex_vector& l, - flex_vector&& r) - { - return concat_move(move_t{}, l, std::move(r)); - } - - IMMER_NODISCARD friend decltype(auto) operator+(flex_vector&& l, - flex_vector&& r) - { - return concat_move(move_t{}, std::move(l), std::move(r)); - } - - /*! - * Returns a flex_vector with the `value` inserted at index - * `pos`. It may allocate memory and its complexity is @f$ - * O(log(size)) @f$ - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/flex-vector/flex-vector.cpp - * :language: c++ - * :dedent: 8 - * :start-after: insert/start - * :end-before: insert/end - * - * @endrst - */ - IMMER_NODISCARD flex_vector insert(size_type pos, T value) const& - { - return take(pos).push_back(std::move(value)) + drop(pos); - } - IMMER_NODISCARD decltype(auto) insert(size_type pos, T value) && - { - using std::move; - auto rs = drop(pos); - return std::move(*this).take(pos).push_back(std::move(value)) + - std::move(rs); - } - - IMMER_NODISCARD flex_vector insert(size_type pos, flex_vector value) const& - { - return take(pos) + std::move(value) + drop(pos); - } - IMMER_NODISCARD decltype(auto) insert(size_type pos, flex_vector value) && - { - using std::move; - auto rs = drop(pos); - return std::move(*this).take(pos) + std::move(value) + std::move(rs); - } - - /*! - * Returns a flex_vector without the element at index `pos`. It - * may allocate memory and its complexity is @f$ O(log(size)) @f$ - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/flex-vector/flex-vector.cpp - * :language: c++ - * :dedent: 8 - * :start-after: erase/start - * :end-before: erase/end - * - * @endrst - */ - IMMER_NODISCARD flex_vector erase(size_type pos) const& - { - return take(pos) + drop(pos + 1); - } - IMMER_NODISCARD decltype(auto) erase(size_type pos) && - { - auto rs = drop(pos + 1); - return std::move(*this).take(pos) + std::move(rs); - } - - IMMER_NODISCARD flex_vector erase(size_type pos, size_type lpos) const& - { - return lpos > pos ? take(pos) + drop(lpos) : *this; - } - IMMER_NODISCARD decltype(auto) erase(size_type pos, size_type lpos) && - { - if (lpos > pos) { - auto rs = drop(lpos); - return std::move(*this).take(pos) + std::move(rs); - } else { - return std::move(*this); - } - } - - /*! - * Returns an @a transient form of this container, an - * `immer::flex_vector_transient`. - */ - IMMER_NODISCARD transient_type transient() const& - { - return transient_type{impl_}; - } - IMMER_NODISCARD transient_type transient() && - { - return transient_type{std::move(impl_)}; - } - - // Semi-private - const impl_t& impl() const { return impl_; } - -#if IMMER_DEBUG_PRINT - void debug_print(std::ostream& out = std::cerr) const - { - impl_.debug_print(out); - } -#endif - -private: - friend transient_type; - - flex_vector(impl_t impl) - : impl_(std::move(impl)) - { -#if IMMER_DEBUG_PRINT - // force the compiler to generate debug_print, so we can call - // it from a debugger - [](volatile auto) {}(&flex_vector::debug_print); -#endif - } - - flex_vector&& push_back_move(std::true_type, value_type value) - { - impl_.push_back_mut({}, std::move(value)); - return std::move(*this); - } - flex_vector push_back_move(std::false_type, value_type value) - { - return impl_.push_back(std::move(value)); - } - - flex_vector&& set_move(std::true_type, size_type index, value_type value) - { - impl_.assoc_mut({}, index, std::move(value)); - return std::move(*this); - } - flex_vector set_move(std::false_type, size_type index, value_type value) - { - return impl_.assoc(index, std::move(value)); - } - - template <typename Fn> - flex_vector&& update_move(std::true_type, size_type index, Fn&& fn) - { - impl_.update_mut({}, index, std::forward<Fn>(fn)); - return std::move(*this); - } - template <typename Fn> - flex_vector update_move(std::false_type, size_type index, Fn&& fn) - { - return impl_.update(index, std::forward<Fn>(fn)); - } - - flex_vector&& take_move(std::true_type, size_type elems) - { - impl_.take_mut({}, elems); - return std::move(*this); - } - flex_vector take_move(std::false_type, size_type elems) - { - return impl_.take(elems); - } - - flex_vector&& drop_move(std::true_type, size_type elems) - { - impl_.drop_mut({}, elems); - return std::move(*this); - } - flex_vector drop_move(std::false_type, size_type elems) - { - return impl_.drop(elems); - } - - static flex_vector&& - concat_move(std::true_type, flex_vector&& l, const flex_vector& r) - { - concat_mut_l(l.impl_, {}, r.impl_); - return std::move(l); - } - static flex_vector&& - concat_move(std::true_type, const flex_vector& l, flex_vector&& r) - { - concat_mut_r(l.impl_, r.impl_, {}); - return std::move(r); - } - static flex_vector&& - concat_move(std::true_type, flex_vector&& l, flex_vector&& r) - { - concat_mut_lr_l(l.impl_, {}, r.impl_, {}); - return std::move(l); - } - static flex_vector - concat_move(std::false_type, const flex_vector& l, const flex_vector& r) - { - return l.impl_.concat(r.impl_); - } - - impl_t impl_ = impl_t::empty(); -}; - -} // namespace immer diff --git a/third_party/immer/immer/flex_vector_transient.hpp b/third_party/immer/immer/flex_vector_transient.hpp deleted file mode 100644 index fe0a6a534e10..000000000000 --- a/third_party/immer/immer/flex_vector_transient.hpp +++ /dev/null @@ -1,251 +0,0 @@ -// -// 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 <immer/detail/rbts/rrbtree.hpp> -#include <immer/detail/rbts/rrbtree_iterator.hpp> -#include <immer/memory_policy.hpp> - -namespace immer { - -template <typename T, - typename MemoryPolicy, - detail::rbts::bits_t B, - detail::rbts::bits_t BL> -class flex_vector; - -template <typename T, - typename MemoryPolicy, - detail::rbts::bits_t B, - detail::rbts::bits_t BL> -class vector_transient; - -/*! - * Mutable version of `immer::flex_vector`. - * - * @rst - * - * Refer to :doc:`transients` to learn more about when and how to use - * the mutable versions of immutable containers. - * - * @endrst - */ -template <typename T, - typename MemoryPolicy = default_memory_policy, - detail::rbts::bits_t B = default_bits, - detail::rbts::bits_t BL = - detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>> -class flex_vector_transient : MemoryPolicy::transience_t::owner -{ - using impl_t = detail::rbts::rrbtree<T, MemoryPolicy, B, BL>; - using base_t = typename MemoryPolicy::transience_t::owner; - using owner_t = typename MemoryPolicy::transience_t::owner; - -public: - static constexpr auto bits = B; - static constexpr auto bits_leaf = BL; - using memory_policy = MemoryPolicy; - - using value_type = T; - using reference = const T&; - using size_type = detail::rbts::size_t; - using difference_type = std::ptrdiff_t; - using const_reference = const T&; - - using iterator = detail::rbts::rrbtree_iterator<T, MemoryPolicy, B, BL>; - using const_iterator = iterator; - using reverse_iterator = std::reverse_iterator<iterator>; - - using persistent_type = flex_vector<T, MemoryPolicy, B, BL>; - - /*! - * Default constructor. It creates a flex_vector of `size() == 0`. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - flex_vector_transient() = default; - - /*! - * Default constructor. It creates a flex_vector with the same - * contents as `v`. It does not allocate memory and is - * @f$ O(1) @f$. - */ - flex_vector_transient(vector_transient<T, MemoryPolicy, B, BL> v) - : base_t{std::move(static_cast<base_t&>(v))} - , impl_{v.impl_.size, - v.impl_.shift, - v.impl_.root->inc(), - v.impl_.tail->inc()} - {} - - /*! - * Returns an iterator pointing at the first element of the - * collection. It does not allocate memory and its complexity is - * @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator begin() const { return {impl_}; } - - /*! - * Returns an iterator pointing just after the last element of the - * collection. It does not allocate and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator end() const - { - return {impl_, typename iterator::end_t{}}; - } - - /*! - * Returns an iterator that traverses the collection backwards, - * pointing at the first element of the reversed collection. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD reverse_iterator rbegin() const - { - return reverse_iterator{end()}; - } - - /*! - * Returns an iterator that traverses the collection backwards, - * pointing after the last element of the reversed collection. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD reverse_iterator rend() const - { - return reverse_iterator{begin()}; - } - - /*! - * Returns the number of elements in the container. It does - * not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD size_type size() const { return impl_.size; } - - /*! - * Returns `true` if there are no elements in the container. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD bool empty() const { return impl_.size == 0; } - - /*! - * Returns a `const` reference to the element at position `index`. - * It is undefined when @f$ 0 index \geq size() @f$. It does not - * allocate memory and its complexity is *effectively* @f$ O(1) - * @f$. - */ - reference operator[](size_type index) const { return impl_.get(index); } - - /*! - * Returns a `const` reference to the element at position - * `index`. It throws an `std::out_of_range` exception when @f$ - * index \geq size() @f$. It does not allocate memory and its - * complexity is *effectively* @f$ O(1) @f$. - */ - reference at(size_type index) const { return impl_.get_check(index); } - - /*! - * Inserts `value` at the end. It may allocate memory and its - * complexity is *effectively* @f$ O(1) @f$. - */ - void push_back(value_type value) - { - impl_.push_back_mut(*this, std::move(value)); - } - - /*! - * Sets to the value `value` at position `idx`. - * Undefined for `index >= size()`. - * It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - */ - void set(size_type index, value_type value) - { - impl_.assoc_mut(*this, index, std::move(value)); - } - - /*! - * Updates the vector to contain the result of the expression - * `fn((*this)[idx])` at position `idx`. - * Undefined for `0 >= size()`. - * It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - */ - template <typename FnT> - void update(size_type index, FnT&& fn) - { - impl_.update_mut(*this, index, std::forward<FnT>(fn)); - } - - /*! - * Resizes the vector to only contain the first `min(elems, size())` - * elements. It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - */ - void take(size_type elems) { impl_.take_mut(*this, elems); } - - /*! - * Removes the first the first `min(elems, size())` - * elements. It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - */ - void drop(size_type elems) { impl_.drop_mut(*this, elems); } - - /*! - * Appends the contents of the `r` at the end. It may allocate - * memory and its complexity is: - * @f$ O(log(max(size_r, size_l))) @f$ - */ - void append(flex_vector_transient& r) - { - r.owner_t::operator=(owner_t{}); - concat_mut_l(impl_, *this, r.impl_); - } - void append(flex_vector_transient&& r) - { - concat_mut_lr_l(impl_, *this, r.impl_, r); - } - - /*! - * Prepends the contents of the `l` at the beginning. It may - * allocate memory and its complexity is: - * @f$ O(log(max(size_r, size_l))) @f$ - */ - void prepend(flex_vector_transient& l) - { - l.owner_t::operator=(owner_t{}); - concat_mut_r(l.impl_, impl_, *this); - } - void prepend(flex_vector_transient&& l) - { - concat_mut_lr_r(l.impl_, l, impl_, *this); - } - - /*! - * Returns an @a immutable form of this container, an - * `immer::flex_vector`. - */ - IMMER_NODISCARD persistent_type persistent() & - { - this->owner_t::operator=(owner_t{}); - return persistent_type{impl_}; - } - IMMER_NODISCARD persistent_type persistent() && - { - return persistent_type{std::move(impl_)}; - } - -private: - friend persistent_type; - - flex_vector_transient(impl_t impl) - : impl_(std::move(impl)) - {} - - impl_t impl_ = impl_t::empty(); -}; - -} // namespace immer diff --git a/third_party/immer/immer/heap/cpp_heap.hpp b/third_party/immer/immer/heap/cpp_heap.hpp deleted file mode 100644 index cd129b406bea..000000000000 --- a/third_party/immer/immer/heap/cpp_heap.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// -// 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 <memory> - -namespace immer { - -/*! - * A heap that uses `operator new` and `operator delete`. - */ -struct cpp_heap -{ - /*! - * Returns a pointer to a memory region of size `size`, if the - * allocation was successful, and throws otherwise. - */ - template <typename... Tags> - static void* allocate(std::size_t size, Tags...) - { - return ::operator new(size); - } - - /*! - * Releases a memory region `data` that was previously returned by - * `allocate`. One must not use nor deallocate again a memory - * region that once it has been deallocated. - */ - static void deallocate(std::size_t size, void* data) - { - ::operator delete(data); - } -}; - -} // namespace immer diff --git a/third_party/immer/immer/heap/debug_size_heap.hpp b/third_party/immer/immer/heap/debug_size_heap.hpp deleted file mode 100644 index d5288c646f8d..000000000000 --- a/third_party/immer/immer/heap/debug_size_heap.hpp +++ /dev/null @@ -1,69 +0,0 @@ -// -// 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 <immer/config.hpp> -#include <immer/heap/identity_heap.hpp> - -#include <cassert> -#include <cstddef> -#include <type_traits> -#include <memory> - -namespace immer { - -#if IMMER_ENABLE_DEBUG_SIZE_HEAP - -/*! - * A heap that in debug mode ensures that the sizes for allocation and - * deallocation do match. - */ -template <typename Base> -struct debug_size_heap -{ -#if defined(__MINGW32__) && !defined(__MINGW64__) - // There is a bug in MinGW 32bit: - // https://sourceforge.net/p/mingw-w64/bugs/778/ It causes different - // versions of std::max_align_t to be defined, depending on inclusion order - // of stddef.h and stdint.h. As we have no control over the inclusion order - // here (as it might be set in stone by the outside world), we can't easily - // pin it to one of both versions of std::max_align_t. This means, we have - // to hardcode extra_size for MinGW 32bit builds until the mentioned bug is - // fixed. - constexpr static auto extra_size = 8; -#else - constexpr static auto extra_size = sizeof( - std::aligned_storage_t<sizeof(std::size_t), alignof(std::max_align_t)>); -#endif - - template <typename... Tags> - static void* allocate(std::size_t size, Tags... tags) - { - auto p = (std::size_t*) Base::allocate(size + extra_size, tags...); - new (p) std::size_t{size}; - return ((char*) p) + extra_size; - } - - template <typename... Tags> - static void deallocate(std::size_t size, void* data, Tags... tags) - { - auto p = (std::size_t*) (((char*) data) - extra_size); - assert(*p == size); - Base::deallocate(size + extra_size, p, tags...); - } -}; - -#else // IMMER_ENABLE_DEBUG_SIZE_HEAP - -template <typename Base> -using debug_size_heap = identity_heap<Base>; - -#endif // !IMMER_ENABLE_DEBUG_SIZE_HEAP - -} // namespace immer diff --git a/third_party/immer/immer/heap/free_list_heap.hpp b/third_party/immer/immer/heap/free_list_heap.hpp deleted file mode 100644 index dc25b10184a1..000000000000 --- a/third_party/immer/immer/heap/free_list_heap.hpp +++ /dev/null @@ -1,83 +0,0 @@ -// -// 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 <immer/heap/free_list_node.hpp> -#include <immer/heap/with_data.hpp> - -#include <atomic> -#include <cassert> - -namespace immer { - -/*! - * Adaptor that does not release the memory to the parent heap but - * instead it keeps the memory in a thread-safe global free list. Must - * be preceded by a `with_data<free_list_node, ...>` heap adaptor. - * - * @tparam Size Maximum size of the objects to be allocated. - * @tparam Base Type of the parent heap. - */ -template <std::size_t Size, std::size_t Limit, typename Base> -struct free_list_heap : Base -{ - using base_t = Base; - - template <typename... Tags> - static void* allocate(std::size_t size, Tags...) - { - assert(size <= sizeof(free_list_node) + Size); - assert(size >= sizeof(free_list_node)); - - free_list_node* n; - do { - n = head().data; - if (!n) { - auto p = base_t::allocate(Size + sizeof(free_list_node)); - return static_cast<free_list_node*>(p); - } - } while (!head().data.compare_exchange_weak(n, n->next)); - head().count.fetch_sub(1u, std::memory_order_relaxed); - return n; - } - - template <typename... Tags> - static void deallocate(std::size_t size, void* data, Tags...) - { - assert(size <= sizeof(free_list_node) + Size); - assert(size >= sizeof(free_list_node)); - - // we use relaxed, because we are fine with temporarily having - // a few more/less buffers in free list - if (head().count.load(std::memory_order_relaxed) >= Limit) { - base_t::deallocate(Size + sizeof(free_list_node), data); - } else { - auto n = static_cast<free_list_node*>(data); - do { - n->next = head().data; - } while (!head().data.compare_exchange_weak(n->next, n)); - head().count.fetch_add(1u, std::memory_order_relaxed); - } - } - -private: - struct head_t - { - std::atomic<free_list_node*> data; - std::atomic<std::size_t> count; - }; - - static head_t& head() - { - static head_t head_{{nullptr}, {0}}; - return head_; - } -}; - -} // namespace immer diff --git a/third_party/immer/immer/heap/free_list_node.hpp b/third_party/immer/immer/heap/free_list_node.hpp deleted file mode 100644 index acab4779aa43..000000000000 --- a/third_party/immer/immer/heap/free_list_node.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// 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 <immer/heap/with_data.hpp> - -namespace immer { - -struct free_list_node -{ - free_list_node* next; -}; - -template <typename Base> -struct with_free_list_node : with_data<free_list_node, Base> -{}; - -} // namespace immer diff --git a/third_party/immer/immer/heap/gc_heap.hpp b/third_party/immer/immer/heap/gc_heap.hpp deleted file mode 100644 index 8494bd26694f..000000000000 --- a/third_party/immer/immer/heap/gc_heap.hpp +++ /dev/null @@ -1,127 +0,0 @@ -// -// 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 <immer/config.hpp> -#include <immer/heap/tags.hpp> - -#if IMMER_HAS_LIBGC -#include <gc/gc.h> -#else -#error "Using garbage collection requires libgc" -#endif - -#include <cstdlib> -#include <memory> - -namespace immer { - -#ifdef __APPLE__ -#define IMMER_GC_REQUIRE_INIT 1 -#else -#define IMMER_GC_REQUIRE_INIT 0 -#endif - -#if IMMER_GC_REQUIRE_INIT - -namespace detail { - -template <int Dummy = 0> -struct gc_initializer -{ - gc_initializer() { GC_init(); } - static gc_initializer init; -}; - -template <int D> -gc_initializer<D> gc_initializer<D>::init{}; - -inline void gc_initializer_guard() -{ - static gc_initializer<> init_ = gc_initializer<>::init; - (void) init_; -} - -} // namespace detail - -#define IMMER_GC_INIT_GUARD_ ::immer::detail::gc_initializer_guard() - -#else - -#define IMMER_GC_INIT_GUARD_ - -#endif // IMMER_GC_REQUIRE_INIT - -/*! - * Heap that uses a tracing garbage collector. - * - * @rst - * - * This heap uses the `Boehm's conservative garbage collector`_ under - * the hood. This is a tracing garbage collector that automatically - * reclaims unused memory. Thus, it is not needed to call - * ``deallocate()`` in order to release memory. - * - * .. admonition:: Dependencies - * :class: tip - * - * In order to use this header file, you need to make sure that - * Boehm's ``libgc`` is your include path and link to its binary - * library. - * - * .. caution:: Memory that is allocated with the standard ``malloc`` - * and ``free`` is not visible to ``libgc`` when it is looking for - * references. This means that if, let's say, you store a - * :cpp:class:`immer::vector` using a ``gc_heap`` inside a - * ``std::vector`` that uses a standard allocator, the memory of - * the former might be released automatically at unexpected times - * causing crashes. - * - * .. caution:: When using a ``gc_heap`` in combination with immutable - * containers, the destructors of the contained objects will never - * be called. It is ok to store containers inside containers as - * long as all of them use a ``gc_heap`` consistently, but storing - * other kinds of objects with relevant destructors - * (e.g. containers with reference counting or other kinds of - * *resource handles*) might cause memory leaks and other problems. - * - * .. _boehm's conservative garbage collector: https://github.com/ivmai/bdwgc - * - * @endrst - */ -class gc_heap -{ -public: - static void* allocate(std::size_t n) - { - IMMER_GC_INIT_GUARD_; - auto p = GC_malloc(n); - if (IMMER_UNLIKELY(!p)) - throw std::bad_alloc{}; - return p; - } - - static void* allocate(std::size_t n, norefs_tag) - { - IMMER_GC_INIT_GUARD_; - auto p = GC_malloc_atomic(n); - if (IMMER_UNLIKELY(!p)) - throw std::bad_alloc{}; - return p; - } - - static void deallocate(std::size_t, void* data) { GC_free(data); } - - static void deallocate(std::size_t, void* data, norefs_tag) - { - GC_free(data); - } -}; - -} // namespace immer diff --git a/third_party/immer/immer/heap/heap_policy.hpp b/third_party/immer/immer/heap/heap_policy.hpp deleted file mode 100644 index 582c113f334f..000000000000 --- a/third_party/immer/immer/heap/heap_policy.hpp +++ /dev/null @@ -1,141 +0,0 @@ -// -// 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 <immer/config.hpp> -#include <immer/heap/debug_size_heap.hpp> -#include <immer/heap/free_list_heap.hpp> -#include <immer/heap/split_heap.hpp> -#include <immer/heap/thread_local_free_list_heap.hpp> - -#include <algorithm> -#include <cstdlib> - -namespace immer { - -/*! - * Heap policy that unconditionally uses its `Heap` argument. - */ -template <typename Heap> -struct heap_policy -{ - using type = Heap; - - template <std::size_t> - struct optimized - { - using type = Heap; - }; -}; - -template <typename Deriv, typename HeapPolicy> -struct enable_optimized_heap_policy -{ - static void* operator new(std::size_t size) - { - using heap_type = - typename HeapPolicy ::template optimized<sizeof(Deriv)>::type; - - return heap_type::allocate(size); - } - - static void operator delete(void* data, std::size_t size) - { - using heap_type = - typename HeapPolicy ::template optimized<sizeof(Deriv)>::type; - - heap_type::deallocate(size, data); - } -}; - -/*! - * Heap policy that returns a heap with a free list of objects - * of `max_size = max(Sizes...)` on top an underlying `Heap`. Note - * these two properties of the resulting heap: - * - * - Allocating an object that is bigger than `max_size` may trigger - * *undefined behavior*. - * - * - Allocating an object of size less than `max_size` still - * returns an object of `max_size`. - * - * Basically, this heap will always return objects of `max_size`. - * When an object is freed, it does not directly invoke `std::free`, - * but it keeps the object in a global linked list instead. When a - * new object is requested, it does not need to call `std::malloc` but - * it can directly pop and return the other object from the global - * list, a much faster operation. - * - * This actually creates a hierarchy with two free lists: - * - * - A `thread_local` free list is used first. It does not need any - * kind of synchronization and is very fast. When the thread - * finishes, its contents are returned to the next free list. - * - * - A global free list using lock-free access via atomics. - * - * @tparam Heap Heap to be used when the free list is empty. - * - * @rst - * - * .. tip:: For many applications that use immutable data structures - * significantly, this is actually the best heap policy, and it - * might become the default in the future. - * - * Note that most our data structures internally use trees with the - * same big branching factors. This means that all *vectors*, - * *maps*, etc. can just allocate elements from the same free-list - * optimized heap. Not only does this lowers the allocation time, - * but also makes up for more efficient *cache utilization*. When - * a new node is needed, there are high chances the allocator will - * return a node that was just accessed. When batches of immutable - * updates are made, this can make a significant difference. - * - * @endrst - */ -template <typename Heap, std::size_t Limit = default_free_list_size> -struct free_list_heap_policy -{ - using type = debug_size_heap<Heap>; - - template <std::size_t Size> - struct optimized - { - using type = - split_heap<Size, - with_free_list_node<thread_local_free_list_heap< - Size, - Limit, - free_list_heap<Size, Limit, debug_size_heap<Heap>>>>, - debug_size_heap<Heap>>; - }; -}; - -/*! - * Similar to @ref free_list_heap_policy, but it assumes no - * multi-threading, so a single global free list with no concurrency - * checks is used. - */ -template <typename Heap, std::size_t Limit = default_free_list_size> -struct unsafe_free_list_heap_policy -{ - using type = Heap; - - template <std::size_t Size> - struct optimized - { - using type = split_heap< - Size, - with_free_list_node< - unsafe_free_list_heap<Size, Limit, debug_size_heap<Heap>>>, - debug_size_heap<Heap>>; - }; -}; - -} // namespace immer diff --git a/third_party/immer/immer/heap/identity_heap.hpp b/third_party/immer/immer/heap/identity_heap.hpp deleted file mode 100644 index 032cb3f221d0..000000000000 --- a/third_party/immer/immer/heap/identity_heap.hpp +++ /dev/null @@ -1,34 +0,0 @@ -// -// 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 <cstdlib> - -namespace immer { - -/*! - * A heap that simply passes on to the parent heap. - */ -template <typename Base> -struct identity_heap : Base -{ - template <typename... Tags> - static void* allocate(std::size_t size, Tags... tags) - { - return Base::allocate(size, tags...); - } - - template <typename... Tags> - static void deallocate(std::size_t size, void* data, Tags... tags) - { - Base::deallocate(size, data, tags...); - } -}; - -} // namespace immer diff --git a/third_party/immer/immer/heap/malloc_heap.hpp b/third_party/immer/immer/heap/malloc_heap.hpp deleted file mode 100644 index a0074d17c0fb..000000000000 --- a/third_party/immer/immer/heap/malloc_heap.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// -// 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 <immer/config.hpp> - -#include <cstdlib> -#include <memory> - -namespace immer { - -/*! - * A heap that uses `std::malloc` and `std::free` to manage memory. - */ -struct malloc_heap -{ - /*! - * Returns a pointer to a memory region of size `size`, if the - * allocation was successful and throws `std::bad_alloc` otherwise. - */ - template <typename... Tags> - static void* allocate(std::size_t size, Tags...) - { - auto p = std::malloc(size); - if (IMMER_UNLIKELY(!p)) - throw std::bad_alloc{}; - return p; - } - - /*! - * Releases a memory region `data` that was previously returned by - * `allocate`. One must not use nor deallocate again a memory - * region that once it has been deallocated. - */ - static void deallocate(std::size_t, void* data) { std::free(data); } -}; - -} // namespace immer diff --git a/third_party/immer/immer/heap/split_heap.hpp b/third_party/immer/immer/heap/split_heap.hpp deleted file mode 100644 index 37272d30ecc1..000000000000 --- a/third_party/immer/immer/heap/split_heap.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// -// 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 <atomic> -#include <cassert> - -namespace immer { - -/*! - * Adaptor that uses `SmallHeap` for allocations that are smaller or - * equal to `Size` and `BigHeap` otherwise. - */ -template <std::size_t Size, typename SmallHeap, typename BigHeap> -struct split_heap -{ - template <typename... Tags> - static void* allocate(std::size_t size, Tags... tags) - { - return size <= Size ? SmallHeap::allocate(size, tags...) - : BigHeap::allocate(size, tags...); - } - - template <typename... Tags> - static void deallocate(std::size_t size, void* data, Tags... tags) - { - if (size <= Size) - SmallHeap::deallocate(size, data, tags...); - else - BigHeap::deallocate(size, data, tags...); - } -}; - -} // namespace immer diff --git a/third_party/immer/immer/heap/tags.hpp b/third_party/immer/immer/heap/tags.hpp deleted file mode 100644 index d1ce48d05c89..000000000000 --- a/third_party/immer/immer/heap/tags.hpp +++ /dev/null @@ -1,16 +0,0 @@ -// -// 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 - -namespace immer { - -struct norefs_tag -{}; - -} // namespace immer diff --git a/third_party/immer/immer/heap/thread_local_free_list_heap.hpp b/third_party/immer/immer/heap/thread_local_free_list_heap.hpp deleted file mode 100644 index 9f7f48f43f6c..000000000000 --- a/third_party/immer/immer/heap/thread_local_free_list_heap.hpp +++ /dev/null @@ -1,55 +0,0 @@ -// -// 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 <immer/heap/unsafe_free_list_heap.hpp> - -namespace immer { -namespace detail { - -template <typename Heap> -struct thread_local_free_list_storage -{ - struct head_t - { - free_list_node* data; - std::size_t count; - - ~head_t() { Heap::clear(); } - }; - - static head_t& head() - { - thread_local static head_t head_{nullptr, 0}; - return head_; - } -}; - -} // namespace detail - -/*! - * Adaptor that does not release the memory to the parent heap but - * instead it keeps the memory in a `thread_local` global free - * list. Must be preceded by a `with_data<free_list_node, ...>` heap - * adaptor. When the current thread finishes, the memory is returned - * to the parent heap. - * - * @tparam Size Maximum size of the objects to be allocated. - * @tparam Limit Maximum number of elements to keep in the free list. - * @tparam Base Type of the parent heap. - */ -template <std::size_t Size, std::size_t Limit, typename Base> -struct thread_local_free_list_heap - : detail::unsafe_free_list_heap_impl<detail::thread_local_free_list_storage, - Size, - Limit, - Base> -{}; - -} // namespace immer diff --git a/third_party/immer/immer/heap/unsafe_free_list_heap.hpp b/third_party/immer/immer/heap/unsafe_free_list_heap.hpp deleted file mode 100644 index 942f07802476..000000000000 --- a/third_party/immer/immer/heap/unsafe_free_list_heap.hpp +++ /dev/null @@ -1,109 +0,0 @@ -// -// 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 <cassert> -#include <immer/config.hpp> -#include <immer/heap/free_list_node.hpp> - -namespace immer { -namespace detail { - -template <typename Heap> -struct unsafe_free_list_storage -{ - struct head_t - { - free_list_node* data; - std::size_t count; - }; - - static head_t& head() - { - static head_t head_{nullptr, 0}; - return head_; - } -}; - -template <template <class> class Storage, - std::size_t Size, - std::size_t Limit, - typename Base> -class unsafe_free_list_heap_impl : Base -{ - using storage = Storage<unsafe_free_list_heap_impl>; - -public: - using base_t = Base; - - template <typename... Tags> - static void* allocate(std::size_t size, Tags...) - { - assert(size <= sizeof(free_list_node) + Size); - assert(size >= sizeof(free_list_node)); - - auto n = storage::head().data; - if (!n) { - auto p = base_t::allocate(Size + sizeof(free_list_node)); - return static_cast<free_list_node*>(p); - } - --storage::head().count; - storage::head().data = n->next; - return n; - } - - template <typename... Tags> - static void deallocate(std::size_t size, void* data, Tags...) - { - assert(size <= sizeof(free_list_node) + Size); - assert(size >= sizeof(free_list_node)); - - if (storage::head().count >= Limit) - base_t::deallocate(Size + sizeof(free_list_node), data); - else { - auto n = static_cast<free_list_node*>(data); - n->next = storage::head().data; - storage::head().data = n; - ++storage::head().count; - } - } - - static void clear() - { - while (storage::head().data) { - auto n = storage::head().data->next; - base_t::deallocate(Size + sizeof(free_list_node), - storage::head().data); - storage::head().data = n; - --storage::head().count; - } - } -}; - -} // namespace detail - -/*! - * Adaptor that does not release the memory to the parent heap but - * instead it keeps the memory in a global free list that **is not - * thread-safe**. Must be preceded by a `with_data<free_list_node, - * ...>` heap adaptor. - * - * @tparam Size Maximum size of the objects to be allocated. - * @tparam Limit Maximum number of elements to keep in the free list. - * @tparam Base Type of the parent heap. - */ -template <std::size_t Size, std::size_t Limit, typename Base> -struct unsafe_free_list_heap - : detail::unsafe_free_list_heap_impl<detail::unsafe_free_list_storage, - Size, - Limit, - Base> -{}; - -} // namespace immer diff --git a/third_party/immer/immer/heap/with_data.hpp b/third_party/immer/immer/heap/with_data.hpp deleted file mode 100644 index 1e8c2abb082e..000000000000 --- a/third_party/immer/immer/heap/with_data.hpp +++ /dev/null @@ -1,43 +0,0 @@ -// -// 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 <cstdio> - -namespace immer { - -/*! - * Appends a default constructed extra object of type `T` at the - * *before* the requested region. - * - * @tparam T Type of the appended data. - * @tparam Base Type of the parent heap. - */ -template <typename T, typename Base> -struct with_data : Base -{ - using base_t = Base; - - template <typename... Tags> - static void* allocate(std::size_t size, Tags... tags) - { - auto p = base_t::allocate(size + sizeof(T), tags...); - return new (p) T{} + 1; - } - - template <typename... Tags> - static void deallocate(std::size_t size, void* p, Tags... tags) - { - auto dp = static_cast<T*>(p) - 1; - dp->~T(); - base_t::deallocate(size + sizeof(T), dp, tags...); - } -}; - -} // namespace immer diff --git a/third_party/immer/immer/map.hpp b/third_party/immer/immer/map.hpp deleted file mode 100644 index 58a84d2de939..000000000000 --- a/third_party/immer/immer/map.hpp +++ /dev/null @@ -1,342 +0,0 @@ -// -// 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 <immer/detail/hamts/champ.hpp> -#include <immer/detail/hamts/champ_iterator.hpp> -#include <immer/memory_policy.hpp> - -#include <functional> - -namespace immer { - -template <typename K, - typename T, - typename Hash, - typename Equal, - typename MemoryPolicy, - detail::hamts::bits_t B> -class map_transient; - -/*! - * Immutable unordered mapping of values from type `K` to type `T`. - * - * @tparam K The type of the keys. - * @tparam T The type of the values to be stored in the container. - * @tparam Hash The type of a function object capable of hashing - * values of type `T`. - * @tparam Equal The type of a function object capable of comparing - * values of type `T`. - * @tparam MemoryPolicy Memory management policy. See @ref - * memory_policy. - * - * @rst - * - * This cotainer provides a good trade-off between cache locality, - * search, update performance and structural sharing. It does so by - * storing the data in contiguous chunks of :math:`2^{B}` elements. - * When storing big objects, the size of these contiguous chunks can - * become too big, damaging performance. If this is measured to be - * problematic for a specific use-case, it can be solved by using a - * `immer::box` to wrap the type `T`. - * - * **Example** - * .. literalinclude:: ../example/map/intro.cpp - * :language: c++ - * :start-after: intro/start - * :end-before: intro/end - * - * @endrst - * - */ -template <typename K, - typename T, - typename Hash = std::hash<K>, - typename Equal = std::equal_to<K>, - typename MemoryPolicy = default_memory_policy, - detail::hamts::bits_t B = default_bits> -class map -{ - using value_t = std::pair<K, T>; - - struct project_value - { - const T& operator()(const value_t& v) const noexcept - { - return v.second; - } - }; - - struct project_value_ptr - { - const T* operator()(const value_t& v) const noexcept - { - return &v.second; - } - }; - - struct combine_value - { - template <typename Kf, typename Tf> - value_t operator()(Kf&& k, Tf&& v) const - { - return {std::forward<Kf>(k), std::forward<Tf>(v)}; - } - }; - - struct default_value - { - const T& operator()() const - { - static T v{}; - return v; - } - }; - - struct error_value - { - const T& operator()() const - { - throw std::out_of_range{"key not found"}; - } - }; - - struct hash_key - { - auto operator()(const value_t& v) { return Hash{}(v.first); } - - auto operator()(const K& v) { return Hash{}(v); } - }; - - struct equal_key - { - auto operator()(const value_t& a, const value_t& b) - { - return Equal{}(a.first, b.first); - } - - auto operator()(const value_t& a, const K& b) - { - return Equal{}(a.first, b); - } - }; - - struct equal_value - { - auto operator()(const value_t& a, const value_t& b) - { - return Equal{}(a.first, b.first) && a.second == b.second; - } - }; - - using impl_t = - detail::hamts::champ<value_t, hash_key, equal_key, MemoryPolicy, B>; - -public: - using key_type = K; - using mapped_type = T; - using value_type = std::pair<K, T>; - using size_type = detail::hamts::size_t; - using diference_type = std::ptrdiff_t; - using hasher = Hash; - using key_equal = Equal; - using reference = const value_type&; - using const_reference = const value_type&; - - using iterator = detail::hamts:: - champ_iterator<value_t, hash_key, equal_key, MemoryPolicy, B>; - using const_iterator = iterator; - - using transient_type = map_transient<K, T, Hash, Equal, MemoryPolicy, B>; - - /*! - * Default constructor. It creates a set of `size() == 0`. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - map() = default; - - /*! - * Returns an iterator pointing at the first element of the - * collection. It does not allocate memory and its complexity is - * @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator begin() const { return {impl_}; } - - /*! - * Returns an iterator pointing just after the last element of the - * collection. It does not allocate and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator end() const - { - return {impl_, typename iterator::end_t{}}; - } - - /*! - * Returns the number of elements in the container. It does - * not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD size_type size() const { return impl_.size; } - - /*! - * Returns `true` if there are no elements in the container. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD bool empty() const { return impl_.size == 0; } - - /*! - * Returns `1` when the key `k` is contained in the map or `0` - * otherwise. It won't allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - */ - IMMER_NODISCARD size_type count(const K& k) const - { - return impl_.template get<detail::constantly<size_type, 1>, - detail::constantly<size_type, 0>>(k); - } - - /*! - * Returns a `const` reference to the values associated to the key - * `k`. If the key is not contained in the map, it returns a - * default constructed value. It does not allocate memory and its - * complexity is *effectively* @f$ O(1) @f$. - */ - IMMER_NODISCARD const T& operator[](const K& k) const - { - return impl_.template get<project_value, default_value>(k); - } - - /*! - * Returns a `const` reference to the values associated to the key - * `k`. If the key is not contained in the map, throws an - * `std::out_of_range` error. It does not allocate memory and its - * complexity is *effectively* @f$ O(1) @f$. - */ - const T& at(const K& k) const - { - return impl_.template get<project_value, error_value>(k); - } - - /*! - * Returns a pointer to the value associated with the key `k`. If - * the key is not contained in the map, a `nullptr` is returned. - * It does not allocate memory and its complexity is *effectively* - * @f$ O(1) @f$. - * - * @rst - * - * .. admonition:: Why doesn't this function return an iterator? - * - * Associative containers from the C++ standard library provide a - * ``find`` method that returns an iterator pointing to the - * element in the container or ``end()`` when the key is missing. - * In the case of an unordered container, the only meaningful - * thing one may do with it is to compare it with the end, to - * test if the find was succesfull, and dereference it. This - * comparison is cumbersome compared to testing for a non-empty - * optional value. Furthermore, for an immutable container, - * returning an iterator would have some additional performance - * cost, with no benefits otherwise. - * - * In our opinion, this function should return a - * ``std::optional<const T&>`` but this construction is not valid - * in any current standard. As a compromise we return a - * pointer, which has similar syntactic properties yet it is - * unfortunatelly unnecessarily unrestricted. - * - * @endrst - */ - IMMER_NODISCARD const T* find(const K& k) const - { - return impl_.template get<project_value_ptr, - detail::constantly<const T*, nullptr>>(k); - } - - /*! - * Returns whether the sets are equal. - */ - IMMER_NODISCARD bool operator==(const map& other) const - { - return impl_.template equals<equal_value>(other.impl_); - } - IMMER_NODISCARD bool operator!=(const map& other) const - { - return !(*this == other); - } - - /*! - * Returns a map containing the association `value`. If the key is - * already in the map, it replaces its association in the map. - * It may allocate memory and its complexity is *effectively* @f$ - * O(1) @f$. - */ - IMMER_NODISCARD map insert(value_type value) const - { - return impl_.add(std::move(value)); - } - - /*! - * Returns a map containing the association `(k, v)`. If the key - * is already in the map, it replaces its association in the map. - * It may allocate memory and its complexity is *effectively* @f$ - * O(1) @f$. - */ - IMMER_NODISCARD map set(key_type k, mapped_type v) const - { - return impl_.add({std::move(k), std::move(v)}); - } - - /*! - * Returns a map replacing the association `(k, v)` by the - * association new association `(k, fn(v))`, where `v` is the - * currently associated value for `k` in the map or a default - * constructed value otherwise. It may allocate memory - * and its complexity is *effectively* @f$ O(1) @f$. - */ - template <typename Fn> - IMMER_NODISCARD map update(key_type k, Fn&& fn) const - { - return impl_ - .template update<project_value, default_value, combine_value>( - std::move(k), std::forward<Fn>(fn)); - } - - /*! - * Returns a map without the key `k`. If the key is not - * associated in the map it returns the same map. It may allocate - * memory and its complexity is *effectively* @f$ O(1) @f$. - */ - IMMER_NODISCARD map erase(const K& k) const { return impl_.sub(k); } - - /*! - * Returns an @a transient form of this container, a - * `immer::map_transient`. - */ - IMMER_NODISCARD transient_type transient() const& - { - return transient_type{impl_}; - } - IMMER_NODISCARD transient_type transient() && - { - return transient_type{std::move(impl_)}; - } - - // Semi-private - const impl_t& impl() const { return impl_; } - -private: - friend transient_type; - - map(impl_t impl) - : impl_(std::move(impl)) - {} - - impl_t impl_ = impl_t::empty(); -}; - -} // namespace immer diff --git a/third_party/immer/immer/map_transient.hpp b/third_party/immer/immer/map_transient.hpp deleted file mode 100644 index 8821c5d12b56..000000000000 --- a/third_party/immer/immer/map_transient.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// -// 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 <immer/detail/hamts/champ.hpp> -#include <immer/memory_policy.hpp> - -#include <functional> - -namespace immer { - -/*! - * @rst - * - * .. admonition:: Become a sponsor! - * :class: danger - * - * This component is planned but it has **not been implemented yet**. - * - * Transiens can critically improve the performance of applications - * intensively using ``set`` and ``map``. If you are working for an - * organization using the library in a commercial project, please consider - * **sponsoring this work**: juanpe@sinusoid.al - * - * @endrst - */ -template <typename K, - typename T, - typename Hash = std::hash<K>, - typename Equal = std::equal_to<K>, - typename MemoryPolicy = default_memory_policy, - detail::hamts::bits_t B = default_bits> -class map_transient; - -} // namespace immer diff --git a/third_party/immer/immer/memory_policy.hpp b/third_party/immer/immer/memory_policy.hpp deleted file mode 100644 index b4f665bf621c..000000000000 --- a/third_party/immer/immer/memory_policy.hpp +++ /dev/null @@ -1,135 +0,0 @@ -// -// 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 <immer/heap/cpp_heap.hpp> -#include <immer/heap/heap_policy.hpp> -#include <immer/refcount/no_refcount_policy.hpp> -#include <immer/refcount/refcount_policy.hpp> -#include <immer/refcount/unsafe_refcount_policy.hpp> -#include <immer/transience/gc_transience_policy.hpp> -#include <immer/transience/no_transience_policy.hpp> -#include <type_traits> - -namespace immer { - -/*! - * Metafunction that returns the best *transience policy* to use for a - * given *refcount policy*. - */ -template <typename RefcountPolicy> -struct get_transience_policy - : std::conditional<std::is_same<RefcountPolicy, no_refcount_policy>::value, - gc_transience_policy, - no_transience_policy> -{}; - -template <typename T> -using get_transience_policy_t = typename get_transience_policy<T>::type; - -/*! - * Metafunction that returns wether to *prefer fewer bigger objects* - * to use for a given *heap policy*. - */ -template <typename HeapPolicy> -struct get_prefer_fewer_bigger_objects - : std::integral_constant< - bool, - std::is_same<HeapPolicy, heap_policy<cpp_heap>>::value> -{}; - -template <typename T> -constexpr auto get_prefer_fewer_bigger_objects_v = - get_prefer_fewer_bigger_objects<T>::value; - -/*! - * Metafunction that returns wether to use *transient R-Values* - * for a given *refcount policy*. - */ -template <typename RefcountPolicy> -struct get_use_transient_rvalues - : std::integral_constant< - bool, - !std::is_same<RefcountPolicy, no_refcount_policy>::value> -{}; - -template <typename T> -constexpr auto get_use_transient_rvalues_v = - get_use_transient_rvalues<T>::value; - -/*! - * This is a default implementation of a *memory policy*. A memory - * policy is just a bag of other policies plus some flags with hints - * to the user about the best way to use these strategies. - * - * @tparam HeapPolicy A *heap policy*, for example, @ref heap_policy. - * @tparam RefcountPolicy A *reference counting policy*, for example, - * @ref refcount_policy. - * @tparam TransiencePolicy A *transience policy*, for example, - * @ref no_transience_policy. - * @tparam PreferFewerBiggerObjects Boolean flag indicating whether - * the user should prefer to allocate memory in bigger chungs - * --e.g. by putting various objects in the same memory - * region-- or not. - * @tparam UseTransientRValues Boolean flag indicating whether - * immutable containers should try to modify contents in-place - * when manipulating an r-value reference. - */ -template <typename HeapPolicy, - typename RefcountPolicy, - typename TransiencePolicy = get_transience_policy_t<RefcountPolicy>, - bool PreferFewerBiggerObjects = - get_prefer_fewer_bigger_objects_v<HeapPolicy>, - bool UseTransientRValues = - get_use_transient_rvalues_v<RefcountPolicy>> -struct memory_policy -{ - using heap = HeapPolicy; - using refcount = RefcountPolicy; - using transience = TransiencePolicy; - - static constexpr bool prefer_fewer_bigger_objects = - PreferFewerBiggerObjects; - - static constexpr bool use_transient_rvalues = UseTransientRValues; - - using transience_t = typename transience::template apply<heap>::type; -}; - -/*! - * The default *heap policy* just uses the standard heap with a - * @ref free_list_heap_policy. If `IMMER_NO_FREE_LIST` is defined to `1` - * then it just uses the standard heap. - */ -#if IMMER_NO_FREE_LIST -using default_heap_policy = heap_policy<debug_size_heap<cpp_heap>>; -#else -#if IMMER_NO_THREAD_SAFETY -using default_heap_policy = unsafe_free_list_heap_policy<cpp_heap>; -#else -using default_heap_policy = free_list_heap_policy<cpp_heap>; -#endif -#endif - -/*! - * By default we use thread safe reference counting. - */ -#if IMMER_NO_THREAD_SAFETY -using default_refcount_policy = unsafe_refcount_policy; -#else -using default_refcount_policy = refcount_policy; -#endif - -/*! - * The default memory policy. - */ -using default_memory_policy = - memory_policy<default_heap_policy, default_refcount_policy>; - -} // namespace immer diff --git a/third_party/immer/immer/refcount/enable_intrusive_ptr.hpp b/third_party/immer/immer/refcount/enable_intrusive_ptr.hpp deleted file mode 100644 index 1185a219fd9b..000000000000 --- a/third_party/immer/immer/refcount/enable_intrusive_ptr.hpp +++ /dev/null @@ -1,37 +0,0 @@ -// -// 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 <immer/refcount/no_refcount_policy.hpp> - -namespace immer { - -template <typename Deriv, typename RefcountPolicy> -class enable_intrusive_ptr -{ - mutable RefcountPolicy refcount_data_; - -public: - enable_intrusive_ptr() - : refcount_data_{disowned{}} - {} - - friend void intrusive_ptr_add_ref(const Deriv* x) - { - x->refcount_data_.inc(); - } - - friend void intrusive_ptr_release(const Deriv* x) - { - if (x->refcount_data_.dec()) - delete x; - } -}; - -} // namespace immer diff --git a/third_party/immer/immer/refcount/no_refcount_policy.hpp b/third_party/immer/immer/refcount/no_refcount_policy.hpp deleted file mode 100644 index 24f9d489f54d..000000000000 --- a/third_party/immer/immer/refcount/no_refcount_policy.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// -// 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 - -namespace immer { - -struct disowned -{}; - -struct no_spinlock -{ - bool try_lock() { return true; } - void lock() {} - void unlock() {} - - struct scoped_lock - { - scoped_lock(no_spinlock&) {} - }; -}; - -/*! - * Disables reference counting, to be used with an alternative garbage - * collection strategy like a `gc_heap`. - */ -struct no_refcount_policy -{ - using spinlock_type = no_spinlock; - - no_refcount_policy(){}; - no_refcount_policy(disowned) {} - - void inc() {} - bool dec() { return false; } - void dec_unsafe() {} - bool unique() { return false; } -}; - -} // namespace immer diff --git a/third_party/immer/immer/refcount/refcount_policy.hpp b/third_party/immer/immer/refcount/refcount_policy.hpp deleted file mode 100644 index a7a282cd13a7..000000000000 --- a/third_party/immer/immer/refcount/refcount_policy.hpp +++ /dev/null @@ -1,101 +0,0 @@ -// -// 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 <immer/refcount/no_refcount_policy.hpp> - -#include <atomic> -#include <cassert> -#include <thread> -#include <utility> - -// This has been shamelessly copied from boost... -#if defined(_MSC_VER) && _MSC_VER >= 1310 && \ - (defined(_M_IX86) || defined(_M_X64)) && !defined(__c2__) -extern "C" void _mm_pause(); -#define IMMER_SMT_PAUSE _mm_pause() -#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -#define IMMER_SMT_PAUSE __asm__ __volatile__("rep; nop" : : : "memory") -#endif - -namespace immer { - -// This is an atomic spinlock similar to the one used by boost to provide -// "atomic" shared_ptr operations. It also does not differ much from the one -// from libc++ or libstdc++... -struct spinlock -{ - std::atomic_flag v_{}; - - bool try_lock() { return !v_.test_and_set(std::memory_order_acquire); } - - void lock() - { - for (auto k = 0u; !try_lock(); ++k) { - if (k < 4) - continue; -#ifdef IMMER_SMT_PAUSE - else if (k < 16) - IMMER_SMT_PAUSE; -#endif - else - std::this_thread::yield(); - } - } - - void unlock() { v_.clear(std::memory_order_release); } - - struct scoped_lock - { - scoped_lock(const scoped_lock&) = delete; - scoped_lock& operator=(const scoped_lock&) = delete; - - explicit scoped_lock(spinlock& sp) - : sp_{sp} - { - sp.lock(); - } - - ~scoped_lock() { sp_.unlock(); } - - private: - spinlock& sp_; - }; -}; - -/*! - * A reference counting policy implemented using an *atomic* `int` - * count. It is **thread-safe**. - */ -struct refcount_policy -{ - using spinlock_type = spinlock; - - mutable std::atomic<int> refcount; - - refcount_policy() - : refcount{1} {}; - refcount_policy(disowned) - : refcount{0} - {} - - void inc() { refcount.fetch_add(1, std::memory_order_relaxed); } - - bool dec() { return 1 == refcount.fetch_sub(1, std::memory_order_acq_rel); } - - void dec_unsafe() - { - assert(refcount.load() > 1); - refcount.fetch_sub(1, std::memory_order_relaxed); - } - - bool unique() { return refcount == 1; } -}; - -} // namespace immer diff --git a/third_party/immer/immer/refcount/unsafe_refcount_policy.hpp b/third_party/immer/immer/refcount/unsafe_refcount_policy.hpp deleted file mode 100644 index bcf24578de27..000000000000 --- a/third_party/immer/immer/refcount/unsafe_refcount_policy.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// -// 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 <immer/refcount/no_refcount_policy.hpp> - -#include <atomic> -#include <utility> - -namespace immer { - -/*! - * A reference counting policy implemented using a raw `int` count. - * It is **not thread-safe**. - */ -struct unsafe_refcount_policy -{ - using spinlock_type = no_spinlock; - - mutable int refcount; - - unsafe_refcount_policy() - : refcount{1} {}; - unsafe_refcount_policy(disowned) - : refcount{0} - {} - - void inc() { ++refcount; } - bool dec() { return --refcount == 0; } - void dec_unsafe() { --refcount; } - bool unique() { return refcount == 1; } -}; - -} // namespace immer diff --git a/third_party/immer/immer/set.hpp b/third_party/immer/immer/set.hpp deleted file mode 100644 index a152ac955334..000000000000 --- a/third_party/immer/immer/set.hpp +++ /dev/null @@ -1,198 +0,0 @@ -// -// 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 <immer/detail/hamts/champ.hpp> -#include <immer/detail/hamts/champ_iterator.hpp> -#include <immer/memory_policy.hpp> - -#include <functional> - -namespace immer { - -template <typename T, - typename Hash, - typename Equal, - typename MemoryPolicy, - detail::hamts::bits_t B> -class set_transient; - -/*! - * Immutable set representing an unordered bag of values. - * - * @tparam T The type of the values to be stored in the container. - * @tparam Hash The type of a function object capable of hashing - * values of type `T`. - * @tparam Equal The type of a function object capable of comparing - * values of type `T`. - * @tparam MemoryPolicy Memory management policy. See @ref - * memory_policy. - * - * @rst - * - * This container provides a good trade-off between cache locality, - * membership checks, update performance and structural sharing. It - * does so by storing the data in contiguous chunks of :math:`2^{B}` - * elements. When storing big objects, the size of these contiguous - * chunks can become too big, damaging performance. If this is - * measured to be problematic for a specific use-case, it can be - * solved by using a `immer::box` to wrap the type `T`. - * - * **Example** - * .. literalinclude:: ../example/set/intro.cpp - * :language: c++ - * :start-after: intro/start - * :end-before: intro/end - * - * @endrst - * - */ -template <typename T, - typename Hash = std::hash<T>, - typename Equal = std::equal_to<T>, - typename MemoryPolicy = default_memory_policy, - detail::hamts::bits_t B = default_bits> -class set -{ - using impl_t = detail::hamts::champ<T, Hash, Equal, MemoryPolicy, B>; - - struct project_value_ptr - { - const T* operator()(const T& v) const noexcept { return &v; } - }; - -public: - using key_type = T; - using value_type = T; - using size_type = detail::hamts::size_t; - using diference_type = std::ptrdiff_t; - using hasher = Hash; - using key_equal = Equal; - using reference = const T&; - using const_reference = const T&; - - using iterator = - detail::hamts::champ_iterator<T, Hash, Equal, MemoryPolicy, B>; - using const_iterator = iterator; - - using transient_type = set_transient<T, Hash, Equal, MemoryPolicy, B>; - - /*! - * Default constructor. It creates a set of `size() == 0`. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - set() = default; - - /*! - * Returns an iterator pointing at the first element of the - * collection. It does not allocate memory and its complexity is - * @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator begin() const { return {impl_}; } - - /*! - * Returns an iterator pointing just after the last element of the - * collection. It does not allocate and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator end() const - { - return {impl_, typename iterator::end_t{}}; - } - - /*! - * Returns the number of elements in the container. It does - * not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD size_type size() const { return impl_.size; } - - /*! - * Returns `true` if there are no elements in the container. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD bool empty() const { return impl_.size == 0; } - - /*! - * Returns `1` when `value` is contained in the set or `0` - * otherwise. It won't allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - */ - IMMER_NODISCARD size_type count(const T& value) const - { - return impl_.template get<detail::constantly<size_type, 1>, - detail::constantly<size_type, 0>>(value); - } - - /*! - * Returns a pointer to the value if `value` is contained in the - * set, or nullptr otherwise. - * It does not allocate memory and its complexity is *effectively* - * @f$ O(1) @f$. - */ - IMMER_NODISCARD const T* find(const T& value) const - { - return impl_.template get<project_value_ptr, - detail::constantly<const T*, nullptr>>(value); - } - - /*! - * Returns whether the sets are equal. - */ - IMMER_NODISCARD bool operator==(const set& other) const - { - return impl_.equals(other.impl_); - } - IMMER_NODISCARD bool operator!=(const set& other) const - { - return !(*this == other); - } - - /*! - * Returns a set containing `value`. If the `value` is already in - * the set, it returns the same set. It may allocate memory and - * its complexity is *effectively* @f$ O(1) @f$. - */ - IMMER_NODISCARD set insert(T value) const - { - return impl_.add(std::move(value)); - } - - /*! - * Returns a set without `value`. If the `value` is not in the - * set it returns the same set. It may allocate memory and its - * complexity is *effectively* @f$ O(1) @f$. - */ - IMMER_NODISCARD set erase(const T& value) const { return impl_.sub(value); } - - /*! - * Returns an @a transient form of this container, a - * `immer::set_transient`. - */ - IMMER_NODISCARD transient_type transient() const& - { - return transient_type{impl_}; - } - IMMER_NODISCARD transient_type transient() && - { - return transient_type{std::move(impl_)}; - } - - // Semi-private - const impl_t& impl() const { return impl_; } - -private: - friend transient_type; - - set(impl_t impl) - : impl_(std::move(impl)) - {} - - impl_t impl_ = impl_t::empty(); -}; - -} // namespace immer diff --git a/third_party/immer/immer/set_transient.hpp b/third_party/immer/immer/set_transient.hpp deleted file mode 100644 index cd0f652b95f6..000000000000 --- a/third_party/immer/immer/set_transient.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// -// 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 <immer/detail/hamts/champ.hpp> -#include <immer/memory_policy.hpp> - -#include <functional> - -namespace immer { - -/*! - * @rst - * - * .. admonition:: Become a sponsor! - * :class: danger - * - * This component is planned but it has **not been implemented yet**. - * - * Transiens can critically improve the performance of applications - * intensively using ``set`` and ``map``. If you are working for an - * organization using the library in a commercial project, please consider - * **sponsoring this work**: juanpe@sinusoid.al - * - * @endrst - */ -template <typename T, - typename Hash = std::hash<T>, - typename Equal = std::equal_to<T>, - typename MemoryPolicy = default_memory_policy, - detail::hamts::bits_t B = default_bits> -class set_transient; - -} // namespace immer diff --git a/third_party/immer/immer/transience/gc_transience_policy.hpp b/third_party/immer/immer/transience/gc_transience_policy.hpp deleted file mode 100644 index d3c30efa1cf7..000000000000 --- a/third_party/immer/immer/transience/gc_transience_policy.hpp +++ /dev/null @@ -1,110 +0,0 @@ -// -// 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 <immer/heap/tags.hpp> - -#include <atomic> -#include <memory> -#include <utility> - -namespace immer { - -/*! - * Provides transience ownership tracking when a *tracing garbage - * collector* is used instead of reference counting. - * - * @rst - * - * .. warning:: Using this policy without an allocation scheme that - * includes automatic tracing garbage collection may cause memory - * leaks. - * - * @endrst - */ -struct gc_transience_policy -{ - template <typename HeapPolicy> - struct apply - { - struct type - { - using heap_ = typename HeapPolicy::type; - - struct edit - { - void* v; - edit() = delete; - bool operator==(edit x) const { return v == x.v; } - bool operator!=(edit x) const { return v != x.v; } - }; - - struct owner - { - void* make_token_() - { - return heap_::allocate(1, norefs_tag{}); - }; - - mutable std::atomic<void*> token_; - - operator edit() { return {token_}; } - - owner() - : token_{make_token_()} - {} - owner(const owner& o) - : token_{make_token_()} - { - o.token_ = make_token_(); - } - owner(owner&& o) noexcept - : token_{o.token_.load()} - {} - owner& operator=(const owner& o) - { - o.token_ = make_token_(); - token_ = make_token_(); - return *this; - } - owner& operator=(owner&& o) noexcept - { - token_ = o.token_.load(); - return *this; - } - }; - - struct ownee - { - edit token_{nullptr}; - - ownee& operator=(edit e) - { - assert(e != noone); - // This would be a nice safety plug but it sadly - // does not hold during transient concatenation. - // assert(token_ == e || token_ == edit{nullptr}); - token_ = e; - return *this; - } - - bool can_mutate(edit t) const { return token_ == t; } - bool owned() const { return token_ != edit{nullptr}; } - }; - - static owner noone; - }; - }; -}; - -template <typename HP> -typename gc_transience_policy::apply<HP>::type::owner - gc_transience_policy::apply<HP>::type::noone = {}; - -} // namespace immer diff --git a/third_party/immer/immer/transience/no_transience_policy.hpp b/third_party/immer/immer/transience/no_transience_policy.hpp deleted file mode 100644 index 2f87df7a39b0..000000000000 --- a/third_party/immer/immer/transience/no_transience_policy.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// -// 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 - -namespace immer { - -/*! - * Disables any special *transience* tracking. To be used when - * *reference counting* is available instead. - */ -struct no_transience_policy -{ - template <typename> - struct apply - { - struct type - { - struct edit - {}; - - struct owner - { - operator edit() const { return {}; } - }; - - struct ownee - { - ownee& operator=(edit) { return *this; }; - bool can_mutate(edit) const { return false; } - bool owned() const { return false; } - }; - - static owner noone; - }; - }; -}; - -template <typename HP> -typename no_transience_policy::apply<HP>::type::owner - no_transience_policy::apply<HP>::type::noone = {}; - -} // namespace immer diff --git a/third_party/immer/immer/vector.hpp b/third_party/immer/immer/vector.hpp deleted file mode 100644 index 4f1a148ccd00..000000000000 --- a/third_party/immer/immer/vector.hpp +++ /dev/null @@ -1,412 +0,0 @@ -// -// 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 <immer/detail/rbts/rbtree.hpp> -#include <immer/detail/rbts/rbtree_iterator.hpp> -#include <immer/memory_policy.hpp> - -#if IMMER_DEBUG_PRINT -#include <immer/flex_vector.hpp> -#endif - -namespace immer { - -template <typename T, - typename MemoryPolicy, - detail::rbts::bits_t B, - detail::rbts::bits_t BL> -class flex_vector; - -template <typename T, - typename MemoryPolicy, - detail::rbts::bits_t B, - detail::rbts::bits_t BL> -class vector_transient; - -/*! - * Immutable sequential container supporting both random access and - * structural sharing. - * - * @tparam T The type of the values to be stored in the container. - * @tparam MemoryPolicy Memory management policy. See @ref - * memory_policy. - * - * @rst - * - * This cotainer provides a good trade-off between cache locality, - * random access, update performance and structural sharing. It does - * so by storing the data in contiguous chunks of :math:`2^{BL}` - * elements. By default, when ``sizeof(T) == sizeof(void*)`` then - * :math:`B=BL=5`, such that data would be stored in contiguous - * chunks of :math:`32` elements. - * - * You may learn more about the meaning and implications of ``B`` and - * ``BL`` parameters in the :doc:`implementation` section. - * - * .. note:: In several methods we say that their complexity is - * *effectively* :math:`O(...)`. Do not confuse this with the word - * *amortized*, which has a very different meaning. In this - * context, *effective* means that while the - * mathematically rigurous - * complexity might be higher, for all practical matters the - * provided complexity is more useful to think about the actual - * cost of the operation. - * - * **Example** - * .. literalinclude:: ../example/vector/intro.cpp - * :language: c++ - * :start-after: intro/start - * :end-before: intro/end - * - * @endrst - */ -template <typename T, - typename MemoryPolicy = default_memory_policy, - detail::rbts::bits_t B = default_bits, - detail::rbts::bits_t BL = - detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>> -class vector -{ - using impl_t = detail::rbts::rbtree<T, MemoryPolicy, B, BL>; - using flex_t = flex_vector<T, MemoryPolicy, B, BL>; - - using move_t = - std::integral_constant<bool, MemoryPolicy::use_transient_rvalues>; - -public: - static constexpr auto bits = B; - static constexpr auto bits_leaf = BL; - using memory_policy = MemoryPolicy; - - using value_type = T; - using reference = const T&; - using size_type = detail::rbts::size_t; - using difference_type = std::ptrdiff_t; - using const_reference = const T&; - - using iterator = detail::rbts::rbtree_iterator<T, MemoryPolicy, B, BL>; - using const_iterator = iterator; - using reverse_iterator = std::reverse_iterator<iterator>; - - using transient_type = vector_transient<T, MemoryPolicy, B, BL>; - - /*! - * Default constructor. It creates a vector of `size() == 0`. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - vector() = default; - - /*! - * Constructs a vector containing the elements in `values`. - */ - vector(std::initializer_list<T> values) - : impl_{impl_t::from_initializer_list(values)} - {} - - /*! - * Constructs a vector containing the elements in the range - * defined by the input iterator `first` and range sentinel `last`. - */ - template <typename Iter, - typename Sent, - std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent>, - bool> = true> - vector(Iter first, Sent last) - : impl_{impl_t::from_range(first, last)} - {} - - /*! - * Constructs a vector containing the element `val` repeated `n` - * times. - */ - vector(size_type n, T v = {}) - : impl_{impl_t::from_fill(n, v)} - {} - - /*! - * Returns an iterator pointing at the first element of the - * collection. It does not allocate memory and its complexity is - * @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator begin() const { return {impl_}; } - - /*! - * Returns an iterator pointing just after the last element of the - * collection. It does not allocate and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator end() const - { - return {impl_, typename iterator::end_t{}}; - } - - /*! - * Returns an iterator that traverses the collection backwards, - * pointing at the first element of the reversed collection. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD reverse_iterator rbegin() const - { - return reverse_iterator{end()}; - } - - /*! - * Returns an iterator that traverses the collection backwards, - * pointing after the last element of the reversed collection. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD reverse_iterator rend() const - { - return reverse_iterator{begin()}; - } - - /*! - * Returns the number of elements in the container. It does - * not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD size_type size() const { return impl_.size; } - - /*! - * Returns `true` if there are no elements in the container. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD bool empty() const { return impl_.size == 0; } - - /*! - * Access the last element. - */ - IMMER_NODISCARD const T& back() const { return impl_.back(); } - - /*! - * Access the first element. - */ - IMMER_NODISCARD const T& front() const { return impl_.front(); } - - /*! - * Returns a `const` reference to the element at position `index`. - * It is undefined when @f$ 0 index \geq size() @f$. It does not - * allocate memory and its complexity is *effectively* @f$ O(1) - * @f$. - */ - IMMER_NODISCARD reference operator[](size_type index) const - { - return impl_.get(index); - } - - /*! - * Returns a `const` reference to the element at position - * `index`. It throws an `std::out_of_range` exception when @f$ - * index \geq size() @f$. It does not allocate memory and its - * complexity is *effectively* @f$ O(1) @f$. - */ - reference at(size_type index) const { return impl_.get_check(index); } - - /*! - * Returns whether the vectors are equal. - */ - IMMER_NODISCARD bool operator==(const vector& other) const - { - return impl_.equals(other.impl_); - } - IMMER_NODISCARD bool operator!=(const vector& other) const - { - return !(*this == other); - } - - /*! - * Returns a vector with `value` inserted at the end. It may - * allocate memory and its complexity is *effectively* @f$ O(1) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/vector/vector.cpp - * :language: c++ - * :dedent: 8 - * :start-after: push-back/start - * :end-before: push-back/end - * - * @endrst - */ - IMMER_NODISCARD vector push_back(value_type value) const& - { - return impl_.push_back(std::move(value)); - } - - IMMER_NODISCARD decltype(auto) push_back(value_type value) && - { - return push_back_move(move_t{}, std::move(value)); - } - - /*! - * Returns a vector containing value `value` at position `idx`. - * Undefined for `index >= size()`. - * It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/vector/vector.cpp - * :language: c++ - * :dedent: 8 - * :start-after: set/start - * :end-before: set/end - * - * @endrst - */ - IMMER_NODISCARD vector set(size_type index, value_type value) const& - { - return impl_.assoc(index, std::move(value)); - } - - IMMER_NODISCARD decltype(auto) set(size_type index, value_type value) && - { - return set_move(move_t{}, index, std::move(value)); - } - - /*! - * Returns a vector containing the result of the expression - * `fn((*this)[idx])` at position `idx`. - * Undefined for `0 >= size()`. - * It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/vector/vector.cpp - * :language: c++ - * :dedent: 8 - * :start-after: update/start - * :end-before: update/end - * - * @endrst - */ - template <typename FnT> - IMMER_NODISCARD vector update(size_type index, FnT&& fn) const& - { - return impl_.update(index, std::forward<FnT>(fn)); - } - - template <typename FnT> - IMMER_NODISCARD decltype(auto) update(size_type index, FnT&& fn) && - { - return update_move(move_t{}, index, std::forward<FnT>(fn)); - } - - /*! - * Returns a vector containing only the first `min(elems, size())` - * elements. It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - * - * @rst - * - * **Example** - * .. literalinclude:: ../example/vector/vector.cpp - * :language: c++ - * :dedent: 8 - * :start-after: take/start - * :end-before: take/end - * - * @endrst - */ - IMMER_NODISCARD vector take(size_type elems) const& - { - return impl_.take(elems); - } - - IMMER_NODISCARD decltype(auto) take(size_type elems) && - { - return take_move(move_t{}, elems); - } - - /*! - * Returns an @a transient form of this container, an - * `immer::vector_transient`. - */ - IMMER_NODISCARD transient_type transient() const& - { - return transient_type{impl_}; - } - IMMER_NODISCARD transient_type transient() && - { - return transient_type{std::move(impl_)}; - } - - // Semi-private - const impl_t& impl() const { return impl_; } - -#if IMMER_DEBUG_PRINT - void debug_print(std::ostream& out = std::cerr) const - { - flex_t{*this}.debug_print(out); - } -#endif - -private: - friend flex_t; - friend transient_type; - - vector(impl_t impl) - : impl_(std::move(impl)) - { -#if IMMER_DEBUG_PRINT - // force the compiler to generate debug_print, so we can call - // it from a debugger - [](volatile auto) {}(&vector::debug_print); -#endif - } - - vector&& push_back_move(std::true_type, value_type value) - { - impl_.push_back_mut({}, std::move(value)); - return std::move(*this); - } - vector push_back_move(std::false_type, value_type value) - { - return impl_.push_back(std::move(value)); - } - - vector&& set_move(std::true_type, size_type index, value_type value) - { - impl_.assoc_mut({}, index, std::move(value)); - return std::move(*this); - } - vector set_move(std::false_type, size_type index, value_type value) - { - return impl_.assoc(index, std::move(value)); - } - - template <typename Fn> - vector&& update_move(std::true_type, size_type index, Fn&& fn) - { - impl_.update_mut({}, index, std::forward<Fn>(fn)); - return std::move(*this); - } - template <typename Fn> - vector update_move(std::false_type, size_type index, Fn&& fn) - { - return impl_.update(index, std::forward<Fn>(fn)); - } - - vector&& take_move(std::true_type, size_type elems) - { - impl_.take_mut({}, elems); - return std::move(*this); - } - vector take_move(std::false_type, size_type elems) - { - return impl_.take(elems); - } - - impl_t impl_ = impl_t::empty(); -}; - -} // namespace immer diff --git a/third_party/immer/immer/vector_transient.hpp b/third_party/immer/immer/vector_transient.hpp deleted file mode 100644 index 4d648cab07db..000000000000 --- a/third_party/immer/immer/vector_transient.hpp +++ /dev/null @@ -1,203 +0,0 @@ -// -// 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 <immer/detail/rbts/rbtree.hpp> -#include <immer/detail/rbts/rbtree_iterator.hpp> -#include <immer/memory_policy.hpp> - -namespace immer { - -template <typename T, - typename MemoryPolicy, - detail::rbts::bits_t B, - detail::rbts::bits_t BL> -class vector; - -template <typename T, - typename MemoryPolicy, - detail::rbts::bits_t B, - detail::rbts::bits_t BL> -class flex_vector_transient; - -/*! - * Mutable version of `immer::vector`. - * - * @rst - * - * Refer to :doc:`transients` to learn more about when and how to use - * the mutable versions of immutable containers. - * - * @endrst - */ -template <typename T, - typename MemoryPolicy = default_memory_policy, - detail::rbts::bits_t B = default_bits, - detail::rbts::bits_t BL = - detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>> -class vector_transient : MemoryPolicy::transience_t::owner -{ - using impl_t = detail::rbts::rbtree<T, MemoryPolicy, B, BL>; - using flex_t = flex_vector_transient<T, MemoryPolicy, B, BL>; - using owner_t = typename MemoryPolicy::transience_t::owner; - -public: - static constexpr auto bits = B; - static constexpr auto bits_leaf = BL; - using memory_policy = MemoryPolicy; - - using value_type = T; - using reference = const T&; - using size_type = detail::rbts::size_t; - using difference_type = std::ptrdiff_t; - using const_reference = const T&; - - using iterator = detail::rbts::rbtree_iterator<T, MemoryPolicy, B, BL>; - using const_iterator = iterator; - using reverse_iterator = std::reverse_iterator<iterator>; - - using persistent_type = vector<T, MemoryPolicy, B, BL>; - - /*! - * Default constructor. It creates a mutable vector of `size() == - * 0`. It does not allocate memory and its complexity is - * @f$ O(1) @f$. - */ - vector_transient() = default; - - /*! - * Returns an iterator pointing at the first element of the - * collection. It does not allocate memory and its complexity is - * @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator begin() const { return {impl_}; } - - /*! - * Returns an iterator pointing just after the last element of the - * collection. It does not allocate and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD iterator end() const - { - return {impl_, typename iterator::end_t{}}; - } - - /*! - * Returns an iterator that traverses the collection backwards, - * pointing at the first element of the reversed collection. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD reverse_iterator rbegin() const - { - return reverse_iterator{end()}; - } - - /*! - * Returns an iterator that traverses the collection backwards, - * pointing after the last element of the reversed collection. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD reverse_iterator rend() const - { - return reverse_iterator{begin()}; - } - - /*! - * Returns the number of elements in the container. It does - * not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD size_type size() const { return impl_.size; } - - /*! - * Returns `true` if there are no elements in the container. It - * does not allocate memory and its complexity is @f$ O(1) @f$. - */ - IMMER_NODISCARD bool empty() const { return impl_.size == 0; } - - /*! - * Returns a `const` reference to the element at position `index`. - * It is undefined when @f$ 0 index \geq size() @f$. It does not - * allocate memory and its complexity is *effectively* @f$ O(1) - * @f$. - */ - reference operator[](size_type index) const { return impl_.get(index); } - - /*! - * Returns a `const` reference to the element at position - * `index`. It throws an `std::out_of_range` exception when @f$ - * index \geq size() @f$. It does not allocate memory and its - * complexity is *effectively* @f$ O(1) @f$. - */ - reference at(size_type index) const { return impl_.get_check(index); } - - /*! - * Inserts `value` at the end. It may allocate memory and its - * complexity is *effectively* @f$ O(1) @f$. - */ - void push_back(value_type value) - { - impl_.push_back_mut(*this, std::move(value)); - } - - /*! - * Sets to the value `value` at position `idx`. - * Undefined for `index >= size()`. - * It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - */ - void set(size_type index, value_type value) - { - impl_.assoc_mut(*this, index, std::move(value)); - } - - /*! - * Updates the vector to contain the result of the expression - * `fn((*this)[idx])` at position `idx`. - * Undefined for `0 >= size()`. - * It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - */ - template <typename FnT> - void update(size_type index, FnT&& fn) - { - impl_.update_mut(*this, index, std::forward<FnT>(fn)); - } - - /*! - * Resizes the vector to only contain the first `min(elems, size())` - * elements. It may allocate memory and its complexity is - * *effectively* @f$ O(1) @f$. - */ - void take(size_type elems) { impl_.take_mut(*this, elems); } - - /*! - * Returns an @a immutable form of this container, an - * `immer::vector`. - */ - IMMER_NODISCARD persistent_type persistent() & - { - this->owner_t::operator=(owner_t{}); - return persistent_type{impl_}; - } - IMMER_NODISCARD persistent_type persistent() && - { - return persistent_type{std::move(impl_)}; - } - -private: - friend flex_t; - friend persistent_type; - - vector_transient(impl_t impl) - : impl_(std::move(impl)) - {} - - impl_t impl_ = impl_t::empty(); -}; - -} // namespace immer |