From 31ee7a2bfb0b6ea49b73547338d8291992bc669a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 1 Apr 2019 06:45:02 +0200 Subject: [PATCH 1/2] Update immer library to current master (0a718d2d76bab6ebdcf43de943bd6c7d2dbfe2f9) --- src/immer/array.hpp | 19 +- src/immer/atom.hpp | 259 ++++++++++++++++ src/immer/box.hpp | 15 +- src/immer/config.hpp | 12 +- src/immer/detail/arrays/no_capacity.hpp | 10 +- src/immer/detail/arrays/node.hpp | 9 +- src/immer/detail/arrays/with_capacity.hpp | 9 +- src/immer/detail/hamts/bits.hpp | 72 ++++- src/immer/detail/hamts/champ.hpp | 15 +- src/immer/detail/hamts/champ_iterator.hpp | 26 +- src/immer/detail/hamts/node.hpp | 82 ++--- src/immer/detail/rbts/operations.hpp | 285 ++++++++++-------- src/immer/detail/rbts/position.hpp | 38 ++- src/immer/detail/rbts/rbtree.hpp | 8 +- src/immer/detail/rbts/rbtree_iterator.hpp | 4 +- src/immer/detail/rbts/rrbtree.hpp | 8 +- src/immer/detail/rbts/rrbtree_iterator.hpp | 4 +- src/immer/detail/rbts/visitor.hpp | 95 +++--- src/immer/detail/type_traits.hpp | 191 ++++++++++++ src/immer/detail/util.hpp | 108 ++++++- src/immer/flex_vector.hpp | 12 +- src/immer/heap/debug_size_heap.hpp | 13 +- src/immer/map.hpp | 6 +- src/immer/refcount/no_refcount_policy.hpp | 14 + src/immer/refcount/refcount_policy.hpp | 59 ++++ src/immer/refcount/unsafe_refcount_policy.hpp | 2 + src/immer/set.hpp | 6 +- src/immer/vector.hpp | 8 +- 28 files changed, 1065 insertions(+), 324 deletions(-) create mode 100644 src/immer/atom.hpp create mode 100644 src/immer/detail/type_traits.hpp diff --git a/src/immer/array.hpp b/src/immer/array.hpp index b3ad63e33421..0f73649fb3ac 100644 --- a/src/immer/array.hpp +++ b/src/immer/array.hpp @@ -35,10 +35,6 @@ class array_transient; * of doubt, measure. For basic types, using an `array` when * :math:`n < 100` is a good heuristic. * - * .. warning:: The current implementation depends on - * ``boost::intrusive_ptr`` and does not support :doc:`memory - * policies`. This will be fixed soon. - * * @endrst */ template @@ -73,23 +69,26 @@ class array array() = default; /*! - * Constructs a vector containing the elements in `values`. + * Constructs an array containing the elements in `values`. */ array(std::initializer_list values) : impl_{impl_t::from_initializer_list(values)} {} /*! - * Constructs a vector containing the elements in the range - * defined by the input iterators `first` and `last`. + * Constructs a array containing the elements in the range + * defined by the forward iterator `first` and range sentinel `last`. */ - template - array(Iter first, Iter last) + template + && detail::is_forward_iterator_v, bool> = true> + array(Iter first, Sent last) : impl_{impl_t::from_range(first, last)} {} /*! - * Constructs a vector containing the element `val` repeated `n` + * Constructs a array containing the element `val` repeated `n` * times. */ array(size_type n, T v = {}) diff --git a/src/immer/atom.hpp b/src/immer/atom.hpp new file mode 100644 index 000000000000..206f3c497a09 --- /dev/null +++ b/src/immer/atom.hpp @@ -0,0 +1,259 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +#include +#include + +namespace immer { + +namespace detail { + +template +struct refcount_atom_impl +{ + using box_type = box; + 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 std::move(b); + } + + template + 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 +struct gc_atom_impl +{ + using box_type = box; + using value_type = T; + using memory_policy = MemoryPolicy; + + static_assert( + std::is_same::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 + 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 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`` 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`` 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`` underlying object is immutable, using + * ``atom`` 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 +class atom +{ +public: + using box_type = box; + 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. + */ + 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. + */ + 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 + box_type update(Fn&& fn) + { return impl_.update(std::forward(fn)); } + +private: + struct get_refcount_atom_impl + { + template + struct apply + { + using type = detail::refcount_atom_impl; + }; + }; + + struct get_gc_atom_impl + { + template + struct apply + { + using type = detail::gc_atom_impl; + }; + }; + + // 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::value, + get_gc_atom_impl, + get_refcount_atom_impl + >::template apply::type; + + impl_t impl_; +}; + +} diff --git a/src/immer/box.hpp b/src/immer/box.hpp index 2d9f159db474..b8ad19e1a499 100644 --- a/src/immer/box.hpp +++ b/src/immer/box.hpp @@ -13,6 +13,16 @@ namespace immer { +namespace detail { + +template +struct gc_atom_impl; + +template +struct refcount_atom_impl; + +} // namespace detail + /*! * Immutable box for a single value of type `T`. * @@ -21,9 +31,12 @@ namespace immer { * moving just copy the underlying pointers. */ template + typename MemoryPolicy = default_memory_policy> class box { + friend struct detail::gc_atom_impl; + friend struct detail::refcount_atom_impl; + struct holder : MemoryPolicy::refcount { T value; diff --git a/src/immer/config.hpp b/src/immer/config.hpp index f3f19f1ad584..67697ef36b07 100644 --- a/src/immer/config.hpp +++ b/src/immer/config.hpp @@ -35,12 +35,20 @@ #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_PREFETCH(p) __builtin_prefetch(p) -#define IMMER_PREFETCH(p) #define IMMER_FORCEINLINE inline __attribute__ ((always_inline)) +#define IMMER_PREFETCH(p) +// #define IMMER_PREFETCH(p) __builtin_prefetch(p) +#endif #define IMMER_DESCENT_DEEP 0 diff --git a/src/immer/detail/arrays/no_capacity.hpp b/src/immer/detail/arrays/no_capacity.hpp index 381750cc1142..33135d0cc37e 100644 --- a/src/immer/detail/arrays/no_capacity.hpp +++ b/src/immer/detail/arrays/no_capacity.hpp @@ -8,6 +8,7 @@ #pragma once +#include #include namespace immer { @@ -90,10 +91,13 @@ struct no_capacity T* data() { return ptr->data(); } const T* data() const { return ptr->data(); } - template - static no_capacity from_range(Iter first, Iter last) + template + && compatible_sentinel_v, bool> = true> + static no_capacity from_range(Iter first, Sent last) { - auto count = static_cast(std::distance(first, last)); + auto count = static_cast(distance(first, last)); return { node_t::copy_n(count, first, last), count, diff --git a/src/immer/detail/arrays/node.hpp b/src/immer/detail/arrays/node.hpp index 5721f5779d30..7883d167f2f4 100644 --- a/src/immer/detail/arrays/node.hpp +++ b/src/immer/detail/arrays/node.hpp @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include @@ -92,12 +93,14 @@ struct node } } - template - static node_t* copy_n(size_t n, Iter first, Iter last) + template , bool> = true> + static node_t* copy_n(size_t n, Iter first, Sent last) { auto p = make_n(n); try { - std::uninitialized_copy(first, last, p->data()); + uninitialized_copy(first, last, p->data()); return p; } catch (...) { heap::deallocate(sizeof_n(n), p); diff --git a/src/immer/detail/arrays/with_capacity.hpp b/src/immer/detail/arrays/with_capacity.hpp index 79f97ad4c95c..d408b38aa3a5 100644 --- a/src/immer/detail/arrays/with_capacity.hpp +++ b/src/immer/detail/arrays/with_capacity.hpp @@ -111,10 +111,13 @@ struct with_capacity } } - template - static with_capacity from_range(Iter first, Iter last) + template + && compatible_sentinel_v, bool> = true> + static with_capacity from_range(Iter first, Sent last) { - auto count = static_cast(std::distance(first, last)); + auto count = static_cast(distance(first, last)); return { node_t::copy_n(count, first, last), count, diff --git a/src/immer/detail/hamts/bits.hpp b/src/immer/detail/hamts/bits.hpp index a308b1517255..92c7e45cf2ef 100644 --- a/src/immer/detail/hamts/bits.hpp +++ b/src/immer/detail/hamts/bits.hpp @@ -10,43 +10,89 @@ #include +#if defined(_MSC_VER) +#include // __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 bitmap_t = std::uint32_t; using count_t = std::uint32_t; using shift_t = std::uint32_t; -using size_t = std::size_t; -using hash_t = std::size_t; + +template +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 -constexpr T branches = T{1} << B; +constexpr T branches = T{1u} << B; template -constexpr T mask = branches - 1; +constexpr T mask = branches - 1u; template -constexpr T max_depth = (sizeof(hash_t) * 8 + B - 1) / B; +constexpr T max_depth = (sizeof(hash_t) * 8u + B - 1u) / B; template constexpr T max_shift = max_depth * B; #define IMMER_HAS_BUILTIN_POPCOUNT 1 -inline count_t popcount(bitmap_t x) +inline auto popcount_fallback(std::uint32_t x) { -#if IMMER_HAS_BUILTIN_POPCOUNT - return __builtin_popcount(x); -#else // 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) & 0x55555555); - x = (x & 0x33333333) + ((x >> 2) & 0x33333333); - return ((x + (x >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; + 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) + return __popcnt64(x); +# else + return __builtin_popcountll(x); +# endif +#else + return popcount_fallback(x); #endif } diff --git a/src/immer/detail/hamts/champ.hpp b/src/immer/detail/hamts/champ.hpp index 11c3435c145b..d3687ffbee18 100644 --- a/src/immer/detail/hamts/champ.hpp +++ b/src/immer/detail/hamts/champ.hpp @@ -24,11 +24,12 @@ template struct champ { - static_assert(branches <= sizeof(bitmap_t) * 8, ""); - static constexpr auto bits = B; using node_t = node; + using bitmap_t = typename get_bitmap_type::type; + + static_assert(branches <= sizeof(bitmap_t) * 8, ""); node_t* root; size_t size; @@ -126,7 +127,7 @@ struct champ auto node = root; auto hash = Hash{}(k); for (auto i = count_t{}; i < max_depth; ++i) { - auto bit = 1 << (hash & mask); + auto bit = bitmap_t{1u} << (hash & mask); if (node->nodemap() & bit) { auto offset = popcount(node->nodemap() & (bit - 1)); node = node->children() [offset]; @@ -168,7 +169,7 @@ struct champ }; } else { auto idx = (hash & (mask << shift)) >> shift; - auto bit = 1 << idx; + auto bit = bitmap_t{1u} << idx; if (node->nodemap() & bit) { auto offset = popcount(node->nodemap() & (bit - 1)); auto result = do_add(node->children() [offset], @@ -250,7 +251,7 @@ struct champ }; } else { auto idx = (hash & (mask << shift)) >> shift; - auto bit = 1 << idx; + auto bit = bitmap_t{1u} << idx; if (node->nodemap() & bit) { auto offset = popcount(node->nodemap() & (bit - 1)); auto result = do_update( @@ -355,7 +356,7 @@ struct champ return {}; } else { auto idx = (hash & (mask << shift)) >> shift; - auto bit = 1 << idx; + auto bit = bitmap_t{1u} << idx; if (node->nodemap() & bit) { auto offset = popcount(node->nodemap() & (bit - 1)); auto result = do_sub(node->children() [offset], @@ -444,7 +445,7 @@ struct champ if (!equals_tree(a->children()[i], b->children()[i], depth + 1)) return false; auto nv = popcount(a->datamap()); - return equals_values(a->values(), b->values(), nv); + return !nv || equals_values(a->values(), b->values(), nv); } } diff --git a/src/immer/detail/hamts/champ_iterator.hpp b/src/immer/detail/hamts/champ_iterator.hpp index a0f101bfaf96..07d552dacab2 100644 --- a/src/immer/detail/hamts/champ_iterator.hpp +++ b/src/immer/detail/hamts/champ_iterator.hpp @@ -20,7 +20,9 @@ struct champ_iterator : iterator_facade, std::forward_iterator_tag, T, - const T&> + const T&, + std::ptrdiff_t, + const T*> { using tree_t = champ; using node_t = typename tree_t::node_t; @@ -30,10 +32,14 @@ struct champ_iterator champ_iterator() = default; champ_iterator(const tree_t& v) - : cur_ { v.root->values() } - , end_ { v.root->values() + popcount(v.root->datamap()) } - , depth_ { 0 } + : 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_(); } @@ -77,8 +83,10 @@ struct champ_iterator path_[depth_] = parent->children(); auto child = *path_[depth_]; if (depth_ < max_depth) { - cur_ = child->values(); - end_ = cur_ + popcount(child->datamap()); + if (child->datamap()) { + cur_ = child->values(); + end_ = cur_ + popcount(child->datamap()); + } } else { cur_ = child->collisions(); end_ = cur_ + child->collision_count(); @@ -99,8 +107,10 @@ struct champ_iterator path_[depth_] = next; auto child = *path_[depth_]; if (depth_ < max_depth) { - cur_ = child->values(); - end_ = cur_ + popcount(child->datamap()); + if (child->datamap()) { + cur_ = child->values(); + end_ = cur_ + popcount(child->datamap()); + } } else { cur_ = child->collisions(); end_ = cur_ + child->collision_count(); diff --git a/src/immer/detail/hamts/node.hpp b/src/immer/detail/hamts/node.hpp index cfe7d195fd29..61aa381336f0 100644 --- a/src/immer/detail/hamts/node.hpp +++ b/src/immer/detail/hamts/node.hpp @@ -41,6 +41,7 @@ struct node using ownee_t = typename transience::ownee; using edit_t = typename transience::edit; using value_t = T; + using bitmap_t = typename get_bitmap_type::type; enum class kind_t { @@ -117,12 +118,14 @@ struct node auto values() { assert(kind() == kind_t::inner); + assert(impl.d.data.inner.values); return (T*) &impl.d.data.inner.values->d.buffer; } auto values() const { assert(kind() == kind_t::inner); + assert(impl.d.data.inner.values); return (const T*) &impl.d.data.inner.values->d.buffer; } @@ -220,7 +223,7 @@ struct node { assert(n >= 1); auto p = make_inner_n(n); - p->impl.d.data.inner.nodemap = 1 << idx; + p->impl.d.data.inner.nodemap = bitmap_t{1u} << idx; p->children()[0] = child; return p; } @@ -246,7 +249,7 @@ struct node { assert(idx1 != idx2); auto p = make_inner_n(n, 2); - p->impl.d.data.inner.datamap = (1 << idx1) | (1 << idx2); + 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 { @@ -438,21 +441,23 @@ struct node 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; - try { - std::uninitialized_copy( - src->values(), src->values() + voffset, - dst->values()); + if (nv > 1) { try { std::uninitialized_copy( - src->values() + voffset + 1, src->values() + nv, - dst->values() + voffset); + 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 (...) { - destroy_n(dst->values(), voffset); + deallocate_inner(dst, n + 1, nv - 1); throw; } - } catch (...) { - deallocate_inner(dst, n + 1, nv - 1); - throw; } inc_nodes(src->children(), n); std::uninitialized_copy( @@ -479,15 +484,17 @@ struct node dst->impl.d.data.inner.nodemap = src->nodemap() & ~bit; dst->impl.d.data.inner.datamap = src->datamap() | bit; try { - std::uninitialized_copy( - src->values(), src->values() + voffset, - dst->values()); + if (nv) + std::uninitialized_copy( + src->values(), src->values() + voffset, + dst->values()); try { new (dst->values() + voffset) T{std::move(value)}; try { - std::uninitialized_copy( - src->values() + voffset, src->values() + nv, - dst->values() + voffset + 1); + if (nv) + std::uninitialized_copy( + src->values() + voffset, src->values() + nv, + dst->values() + voffset + 1); } catch (...) { dst->values()[voffset].~T(); throw; @@ -523,21 +530,23 @@ struct node 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 { - std::uninitialized_copy( - src->values(), src->values() + voffset, - dst->values()); + if (nv > 1) { try { std::uninitialized_copy( - src->values() + voffset + 1, src->values() + nv, - dst->values() + voffset); + 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 (...) { - destroy_n(dst->values(), voffset); + deallocate_inner(dst, n, nv - 1); throw; } - } catch (...) { - deallocate_inner(dst, n, nv - 1); - throw; } inc_nodes(src->children(), n); std::uninitialized_copy( @@ -555,14 +564,16 @@ struct node dst->impl.d.data.inner.datamap = src->datamap() | bit; dst->impl.d.data.inner.nodemap = src->nodemap(); try { - std::uninitialized_copy( - src->values(), src->values() + offset, dst->values()); + if (nv) + std::uninitialized_copy( + src->values(), src->values() + offset, dst->values()); try { new (dst->values() + offset) T{std::move(v)}; try { - std::uninitialized_copy( - src->values() + offset, src->values() + nv, - dst->values() + offset + 1); + if (nv) + std::uninitialized_copy( + src->values() + offset, src->values() + nv, + dst->values() + offset + 1); } catch (...) { dst->values()[offset].~T(); throw; @@ -632,7 +643,6 @@ struct node static void delete_values(values_t* p, count_t n) { assert(p); - destroy_n(&p->d.buffer, n); deallocate_values(p, n); } @@ -651,7 +661,6 @@ struct node assert(p); assert(p->kind() == kind_t::collision); auto n = p->collision_count(); - destroy_n(p->collisions(), n); deallocate_collision(p, n); } @@ -702,7 +711,8 @@ struct node static void deallocate_inner(node_t* p, count_t n, count_t nv) { - deallocate_values(p->impl.d.data.inner.values, 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); } }; diff --git a/src/immer/detail/rbts/operations.hpp b/src/immer/detail/rbts/operations.hpp index 3d54e92fa40b..17ec9911ef1c 100644 --- a/src/immer/detail/rbts/operations.hpp +++ b/src/immer/detail/rbts/operations.hpp @@ -24,86 +24,86 @@ namespace detail { namespace rbts { template -struct array_for_visitor +struct array_for_visitor : visitor_base> { using this_t = array_for_visitor; template - friend T* visit_inner(this_t, PosT&& pos, size_t idx) + static T* visit_inner(PosT&& pos, size_t idx) { return pos.descend(this_t{}, idx); } template - friend T* visit_leaf(this_t, PosT&& pos, size_t) + static T* visit_leaf(PosT&& pos, size_t) { return pos.node()->leaf(); } }; template -struct region_for_visitor +struct region_for_visitor : visitor_base> { using this_t = region_for_visitor; using result_t = std::tuple; template - friend result_t visit_inner(this_t, PosT&& pos, size_t idx) + static result_t visit_inner(PosT&& pos, size_t idx) { return pos.towards(this_t{}, idx); } template - friend result_t visit_leaf(this_t, PosT&& pos, size_t idx) + static result_t visit_leaf(PosT&& pos, size_t idx) { return { pos.node()->leaf(), pos.index(idx), pos.count() }; } }; template -struct get_visitor +struct get_visitor : visitor_base> { using this_t = get_visitor; template - friend const T& visit_inner(this_t, PosT&& pos, size_t idx) + static const T& visit_inner(PosT&& pos, size_t idx) { return pos.descend(this_t{}, idx); } template - friend const T& visit_leaf(this_t, PosT&& pos, size_t idx) + static const T& visit_leaf(PosT&& pos, size_t idx) { return pos.node()->leaf() [pos.index(idx)]; } }; -struct for_each_chunk_visitor +struct for_each_chunk_visitor : visitor_base { using this_t = for_each_chunk_visitor; template - friend void visit_inner(this_t, Pos&& pos, Fn&& fn) + static void visit_inner(Pos&& pos, Fn&& fn) { pos.each(this_t{}, fn); } template - friend void visit_leaf(this_t, Pos&& pos, Fn&& 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 +struct for_each_chunk_p_visitor : visitor_base { using this_t = for_each_chunk_p_visitor; template - friend bool visit_inner(this_t, Pos&& pos, Fn&& fn) + static bool visit_inner(Pos&& pos, Fn&& fn) { return pos.each_pred(this_t{}, fn); } template - friend bool visit_leaf(this_t, Pos&& pos, Fn&& 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 +struct for_each_chunk_left_visitor : visitor_base { using this_t = for_each_chunk_left_visitor; template - friend void visit_inner(this_t, Pos&& pos, + static void visit_inner(Pos&& pos, size_t last, Fn&& fn) { auto l = pos.index(last); @@ -112,7 +112,7 @@ struct for_each_chunk_left_visitor } template - friend void visit_leaf(this_t, Pos&& pos, + static void visit_leaf(Pos&& pos, size_t last, Fn&& fn) { @@ -122,12 +122,12 @@ struct for_each_chunk_left_visitor } }; -struct for_each_chunk_right_visitor +struct for_each_chunk_right_visitor : visitor_base { using this_t = for_each_chunk_right_visitor; template - friend void visit_inner(this_t, Pos&& pos, + static void visit_inner(Pos&& pos, size_t first, Fn&& fn) { auto f = pos.index(first); @@ -136,7 +136,7 @@ struct for_each_chunk_right_visitor } template - friend void visit_leaf(this_t, Pos&& pos, + static void visit_leaf(Pos&& pos, size_t first, Fn&& fn) { @@ -146,12 +146,12 @@ struct for_each_chunk_right_visitor } }; -struct for_each_chunk_i_visitor +struct for_each_chunk_i_visitor : visitor_base { using this_t = for_each_chunk_i_visitor; template - friend void visit_relaxed(this_t, Pos&& pos, + static void visit_relaxed(Pos&& pos, size_t first, size_t last, Fn&& fn) { @@ -173,7 +173,7 @@ struct for_each_chunk_i_visitor } template - friend void visit_regular(this_t, Pos&& pos, + static void visit_regular(Pos&& pos, size_t first, size_t last, Fn&& fn) { @@ -192,7 +192,7 @@ struct for_each_chunk_i_visitor } template - friend void visit_leaf(this_t, Pos&& pos, + static void visit_leaf(Pos&& pos, size_t first, size_t last, Fn&& fn) { @@ -206,11 +206,12 @@ struct for_each_chunk_i_visitor }; struct for_each_chunk_p_left_visitor + : visitor_base { using this_t = for_each_chunk_p_left_visitor; template - friend bool visit_inner(this_t, Pos&& pos, + static bool visit_inner(Pos&& pos, size_t last, Fn&& fn) { auto l = pos.index(last); @@ -219,7 +220,7 @@ struct for_each_chunk_p_left_visitor } template - friend bool visit_leaf(this_t, Pos&& pos, + static bool visit_leaf(Pos&& pos, size_t last, Fn&& fn) { @@ -230,11 +231,12 @@ struct for_each_chunk_p_left_visitor }; struct for_each_chunk_p_right_visitor + : visitor_base { using this_t = for_each_chunk_p_right_visitor; template - friend bool visit_inner(this_t, Pos&& pos, + static bool visit_inner(Pos&& pos, size_t first, Fn&& fn) { auto f = pos.index(first); @@ -243,7 +245,7 @@ struct for_each_chunk_p_right_visitor } template - friend bool visit_leaf(this_t, Pos&& pos, + static bool visit_leaf(Pos&& pos, size_t first, Fn&& fn) { @@ -253,12 +255,12 @@ struct for_each_chunk_p_right_visitor } }; -struct for_each_chunk_p_i_visitor +struct for_each_chunk_p_i_visitor : visitor_base { using this_t = for_each_chunk_p_i_visitor; template - friend bool visit_relaxed(this_t, Pos&& pos, + static bool visit_relaxed(Pos&& pos, size_t first, size_t last, Fn&& fn) { @@ -281,7 +283,7 @@ struct for_each_chunk_p_i_visitor } template - friend bool visit_regular(this_t, Pos&& pos, + static bool visit_regular(Pos&& pos, size_t first, size_t last, Fn&& fn) { @@ -301,7 +303,7 @@ struct for_each_chunk_p_i_visitor } template - friend bool visit_leaf(this_t, Pos&& pos, + static bool visit_leaf(Pos&& pos, size_t first, size_t last, Fn&& fn) { @@ -315,29 +317,29 @@ struct for_each_chunk_p_i_visitor } }; -struct equals_visitor +struct equals_visitor : visitor_base { using this_t = equals_visitor; - struct this_aux_t + struct this_aux_t : visitor_base { template - friend bool visit_inner(this_aux_t, PosR&& posr, - count_t i, PosL&& posl, - Iter&& first, size_t idx) + 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 - friend bool visit_leaf(this_aux_t, PosR&& posr, + 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 + struct rrb : visitor_base { template - friend bool visit_node(rrb, PosR&& posr, Iter&& first, + static bool visit_node(PosR&& posr, Iter&& first, Node* rootl, shift_t shiftl, size_t sizel) { assert(shiftl <= posr.shift()); @@ -364,7 +366,7 @@ struct equals_visitor } template - friend bool visit_relaxed(this_t, PosL&& posl, PosR&& posr, + static bool visit_relaxed(PosL&& posl, PosR&& posr, Iter&& first, size_t idx) { auto nl = posl.node(); @@ -390,23 +392,23 @@ struct equals_visitor } template - friend std::enable_if_t, bool> - visit_regular(this_t, PosL&& posl, PosR&& posr, Iter&& first, size_t idx) + static std::enable_if_t, bool> + visit_regular(PosL&& posl, PosR&& posr, Iter&& first, size_t idx) { - return visit_relaxed(this_t{}, posl, posr, first, idx); + return this_t::visit_relaxed(posl, posr, first, idx); } template - friend std::enable_if_t, bool> - visit_regular(this_t, PosL&& posl, PosR&& posr, Iter&& first, size_t idx) + static std::enable_if_t, bool> + visit_regular(PosL&& posl, PosR&& posr, Iter&& first, size_t idx) { return posl.count() >= posr.count() - ? visit_regular(this_t{}, posl, posr.node()) - : visit_regular(this_t{}, posr, posl.node()); + ? this_t::visit_regular(posl, posr.node()) + : this_t::visit_regular(posr, posl.node()); } template - friend bool visit_leaf(this_t, PosL&& posl, + static bool visit_leaf(PosL&& posl, PosR&& posr, Iter&& first, size_t idx) { if (posl.node() == posr.node()) @@ -424,7 +426,7 @@ struct equals_visitor } template - friend bool visit_regular(this_t, Pos&& pos, NodeT* other) + static bool visit_regular(Pos&& pos, NodeT* other) { auto node = pos.node(); return node == other @@ -432,7 +434,7 @@ struct equals_visitor } template - friend bool visit_leaf(this_t, Pos&& pos, NodeT* other) + static bool visit_leaf(Pos&& pos, NodeT* other) { auto node = pos.node(); return node == other @@ -442,13 +444,13 @@ struct equals_visitor }; template -struct update_visitor +struct update_visitor : visitor_base> { using node_t = NodeT; using this_t = update_visitor; template - friend node_t* visit_relaxed(this_t, Pos&& pos, size_t idx, Fn&& fn) + static node_t* visit_relaxed(Pos&& pos, size_t idx, Fn&& fn) { auto offset = pos.index(idx); auto count = pos.count(); @@ -466,7 +468,7 @@ struct update_visitor } template - friend node_t* visit_regular(this_t, Pos&& pos, size_t idx, Fn&& fn) + static node_t* visit_regular(Pos&& pos, size_t idx, Fn&& fn) { auto offset = pos.index(idx); auto count = pos.count(); @@ -484,7 +486,7 @@ struct update_visitor } template - friend node_t* visit_leaf(this_t, Pos&& pos, size_t idx, Fn&& 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()); @@ -499,12 +501,12 @@ struct update_visitor } }; -struct dec_visitor +struct dec_visitor : visitor_base { using this_t = dec_visitor; template - friend void visit_relaxed(this_t, Pos&& p) + static void visit_relaxed(Pos&& p) { using node_t = node_type; auto node = p.node(); @@ -515,7 +517,7 @@ struct dec_visitor } template - friend void visit_regular(this_t, Pos&& p) + static void visit_regular(Pos&& p) { using node_t = node_type; auto node = p.node(); @@ -526,7 +528,7 @@ struct dec_visitor } template - friend void visit_leaf(this_t, Pos&& p) + static void visit_leaf(Pos&& p) { using node_t = node_type; auto node = p.node(); @@ -567,7 +569,7 @@ void dec_empty_regular(NodeT* node) } template -struct get_mut_visitor +struct get_mut_visitor : visitor_base> { using node_t = NodeT; using this_t = get_mut_visitor; @@ -575,7 +577,7 @@ struct get_mut_visitor using edit_t = typename NodeT::edit_t; template - friend value_t& visit_relaxed(this_t, Pos&& pos, size_t idx, + static value_t& visit_relaxed(Pos&& pos, size_t idx, edit_t e, node_t** location) { auto offset = pos.index(idx); @@ -600,7 +602,7 @@ struct get_mut_visitor } template - friend value_t& visit_regular(this_t, Pos&& pos, size_t idx, + static value_t& visit_regular(Pos&& pos, size_t idx, edit_t e, node_t** location) { assert(pos.node() == *location); @@ -626,7 +628,7 @@ struct get_mut_visitor } template - friend value_t& visit_leaf(this_t, Pos&& pos, size_t idx, + static value_t& visit_leaf(Pos&& pos, size_t idx, edit_t e, node_t** location) { assert(pos.node() == *location); @@ -644,6 +646,7 @@ struct get_mut_visitor template struct push_tail_mut_visitor + : visitor_base> { static constexpr auto B = NodeT::bits; static constexpr auto BL = NodeT::bits_leaf; @@ -654,7 +657,7 @@ struct push_tail_mut_visitor using edit_t = typename NodeT::edit_t; template - friend node_t* visit_relaxed(this_t, Pos&& pos, edit_t e, node_t* tail, count_t ts) + static node_t* visit_relaxed(Pos&& pos, edit_t e, node_t* tail, count_t ts) { auto node = pos.node(); auto level = pos.shift(); @@ -710,7 +713,7 @@ struct push_tail_mut_visitor } template - friend node_t* visit_regular(this_t, Pos&& pos, edit_t e, node_t* tail, Args&&...) + static node_t* visit_regular(Pos&& pos, edit_t e, node_t* tail, Args&&...) { assert((pos.size() & mask) == 0); auto node = pos.node(); @@ -739,12 +742,12 @@ struct push_tail_mut_visitor } template - friend node_t* visit_leaf(this_t, Pos&& pos, edit_t e, node_t* tail, Args&&...) + static node_t* visit_leaf(Pos&& pos, edit_t e, node_t* tail, Args&&...) { IMMER_UNREACHABLE; } }; template -struct push_tail_visitor +struct push_tail_visitor : visitor_base> { static constexpr auto B = NodeT::bits; static constexpr auto BL = NodeT::bits_leaf; @@ -753,7 +756,7 @@ struct push_tail_visitor using node_t = NodeT; template - friend node_t* visit_relaxed(this_t, Pos&& pos, node_t* tail, count_t ts) + static node_t* visit_relaxed(Pos&& pos, node_t* tail, count_t ts) { auto level = pos.shift(); auto idx = pos.count() - 1; @@ -793,7 +796,7 @@ struct push_tail_visitor } template - friend node_t* visit_regular(this_t, Pos&& pos, node_t* tail, Args&&...) + static node_t* visit_regular(Pos&& pos, node_t* tail, Args&&...) { assert((pos.size() & mask) == 0); auto idx = pos.index(pos.size() - 1); @@ -812,17 +815,17 @@ struct push_tail_visitor } template - friend node_t* visit_leaf(this_t, Pos&& pos, node_t* tail, Args&&...) + static node_t* visit_leaf(Pos&& pos, node_t* tail, Args&&...) { IMMER_UNREACHABLE; } }; -struct dec_right_visitor +struct dec_right_visitor : visitor_base { using this_t = dec_right_visitor; using dec_t = dec_visitor; template - friend void visit_relaxed(this_t, Pos&& p, count_t idx) + static void visit_relaxed(Pos&& p, count_t idx) { using node_t = node_type; auto node = p.node(); @@ -833,7 +836,7 @@ struct dec_right_visitor } template - friend void visit_regular(this_t, Pos&& p, count_t idx) + static void visit_regular(Pos&& p, count_t idx) { using node_t = node_type; auto node = p.node(); @@ -844,12 +847,13 @@ struct dec_right_visitor } template - friend void visit_leaf(this_t, Pos&& p, count_t idx) + static void visit_leaf(Pos&& p, count_t idx) { IMMER_UNREACHABLE; } }; template struct slice_right_mut_visitor + : visitor_base> { using node_t = NodeT; using this_t = slice_right_mut_visitor; @@ -865,7 +869,7 @@ struct slice_right_mut_visitor static constexpr auto BL = NodeT::bits_leaf; template - friend result_t visit_relaxed(this_t, PosT&& pos, size_t last, edit_t e) + static result_t visit_relaxed(PosT&& pos, size_t last, edit_t e) { auto idx = pos.index(last); auto node = pos.node(); @@ -934,7 +938,7 @@ struct slice_right_mut_visitor } template - friend result_t visit_regular(this_t, PosT&& pos, size_t last, edit_t e) + static result_t visit_regular(PosT&& pos, size_t last, edit_t e) { auto idx = pos.index(last); auto node = pos.node(); @@ -995,7 +999,7 @@ struct slice_right_mut_visitor } template - friend result_t visit_leaf(this_t, PosT&& pos, size_t last, edit_t e) + 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; @@ -1017,7 +1021,7 @@ struct slice_right_mut_visitor }; template -struct slice_right_visitor +struct slice_right_visitor : visitor_base> { using node_t = NodeT; using this_t = slice_right_visitor; @@ -1030,7 +1034,7 @@ struct slice_right_visitor static constexpr auto BL = NodeT::bits_leaf; template - friend result_t visit_relaxed(this_t, PosT&& pos, size_t last) + static result_t visit_relaxed(PosT&& pos, size_t last) { auto idx = pos.index(last); if (Collapse && idx == 0) { @@ -1070,7 +1074,7 @@ struct slice_right_visitor } template - friend result_t visit_regular(this_t, PosT&& pos, size_t last) + static result_t visit_regular(PosT&& pos, size_t last) { auto idx = pos.index(last); if (Collapse && idx == 0) { @@ -1106,7 +1110,7 @@ struct slice_right_visitor } template - friend result_t visit_leaf(this_t, PosT&& pos, size_t last) + static result_t visit_leaf(PosT&& pos, size_t last) { auto old_tail_size = pos.count(); auto new_tail_size = pos.index(last) + 1; @@ -1117,13 +1121,13 @@ struct slice_right_visitor } }; -struct dec_left_visitor +struct dec_left_visitor : visitor_base { using this_t = dec_left_visitor; using dec_t = dec_visitor; template - friend void visit_relaxed(this_t, Pos&& p, count_t idx) + static void visit_relaxed(Pos&& p, count_t idx) { using node_t = node_type; auto node = p.node(); @@ -1134,7 +1138,7 @@ struct dec_left_visitor } template - friend void visit_regular(this_t, Pos&& p, count_t idx) + static void visit_regular(Pos&& p, count_t idx) { using node_t = node_type; auto node = p.node(); @@ -1145,12 +1149,13 @@ struct dec_left_visitor } template - friend void visit_leaf(this_t, Pos&& p, count_t idx) + static void visit_leaf(Pos&& p, count_t idx) { IMMER_UNREACHABLE; } }; template struct slice_left_mut_visitor + : visitor_base> { using node_t = NodeT; using this_t = slice_left_mut_visitor; @@ -1168,7 +1173,7 @@ struct slice_left_mut_visitor static constexpr auto BL = NodeT::bits_leaf; template - friend result_t visit_relaxed(this_t, PosT&& pos, size_t first, edit_t e) + static result_t visit_relaxed(PosT&& pos, size_t first, edit_t e) { auto idx = pos.subindex(first); auto count = pos.count(); @@ -1218,7 +1223,7 @@ struct slice_left_mut_visitor } template - friend result_t visit_regular(this_t, PosT&& pos, size_t first, edit_t e) + static result_t visit_regular(PosT&& pos, size_t first, edit_t e) { auto idx = pos.subindex(first); auto count = pos.count(); @@ -1287,7 +1292,7 @@ struct slice_left_mut_visitor } template - friend result_t visit_leaf(this_t, PosT&& pos, size_t first, edit_t e) + static result_t visit_leaf(PosT&& pos, size_t first, edit_t e) { auto node = pos.node(); auto idx = pos.index(first); @@ -1310,7 +1315,7 @@ struct slice_left_mut_visitor }; template -struct slice_left_visitor +struct slice_left_visitor : visitor_base> { using node_t = NodeT; using this_t = slice_left_visitor; @@ -1323,7 +1328,7 @@ struct slice_left_visitor static constexpr auto BL = NodeT::bits_leaf; template - friend result_t visit_inner(this_t, PosT&& pos, size_t first) + static result_t visit_inner(PosT&& pos, size_t first) { auto idx = pos.subindex(first); auto count = pos.count(); @@ -1360,7 +1365,7 @@ struct slice_left_visitor } template - friend result_t visit_leaf(this_t, PosT&& pos, size_t first) + static result_t visit_leaf(PosT&& pos, size_t first) { auto n = node_t::copy_leaf(pos.node(), pos.index(first), pos.count()); return { 0, n }; @@ -1587,25 +1592,26 @@ struct concat_merger } }; -struct concat_merger_visitor +struct concat_merger_visitor : visitor_base { using this_t = concat_merger_visitor; template - friend void visit_inner(this_t, Pos&& p, Merger& merger) + static void visit_inner(Pos&& p, Merger& merger) { merger.merge_inner(p); } template - friend void visit_leaf(this_t, Pos&& p, Merger& merger) + static void visit_leaf(Pos&& p, Merger& merger) { merger.merge_leaf(p); } }; struct concat_rebalance_plan_fill_visitor + : visitor_base { using this_t = concat_rebalance_plan_fill_visitor; template - friend void visit_node(this_t, Pos&& p, Plan& plan) + static void visit_node(Pos&& p, Plan& plan) { auto count = p.count(); assert(plan.n < Plan::max_children); @@ -1637,8 +1643,10 @@ struct concat_rebalance_plan 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; @@ -1661,7 +1669,9 @@ struct concat_rebalance_plan --n; --i; } +#if !defined(_MSC_VER) #pragma GCC diagnostic pop +#endif } template @@ -1751,72 +1761,75 @@ concat_inners(LPos&& lpos, TPos&& tpos, RPos&& rpos) } template -struct concat_left_visitor +struct concat_left_visitor : visitor_base> { using this_t = concat_left_visitor; template - friend concat_center_pos - visit_inner(this_t, LPos&& lpos, TPos&& tpos, RPos&& rpos) + static concat_center_pos + visit_inner(LPos&& lpos, TPos&& tpos, RPos&& rpos) { return concat_inners(lpos, tpos, rpos); } template - friend concat_center_pos - visit_leaf(this_t, LPos&& lpos, TPos&& tpos, RPos&& rpos) + static concat_center_pos + visit_leaf(LPos&& lpos, TPos&& tpos, RPos&& rpos) { IMMER_UNREACHABLE; } }; template -struct concat_right_visitor +struct concat_right_visitor : visitor_base> { using this_t = concat_right_visitor; template - friend concat_center_pos - visit_inner(this_t, RPos&& rpos, LPos&& lpos, TPos&& tpos) + static concat_center_pos + visit_inner(RPos&& rpos, LPos&& lpos, TPos&& tpos) { return concat_inners(lpos, tpos, rpos); } template - friend concat_center_pos - visit_leaf(this_t, RPos&& rpos, LPos&& lpos, TPos&& tpos) + static concat_center_pos + visit_leaf(RPos&& rpos, LPos&& lpos, TPos&& tpos) { return concat_leafs(lpos, tpos, rpos); } }; template struct concat_both_visitor + : visitor_base> { using this_t = concat_both_visitor; template - friend concat_center_pos - visit_inner(this_t, LPos&& lpos, TPos&& tpos, RPos&& rpos) + static concat_center_pos + visit_inner(LPos&& lpos, TPos&& tpos, RPos&& rpos) { return rpos.first_sub(concat_right_visitor{}, lpos, tpos); } template - friend concat_center_pos - visit_leaf(this_t, LPos&& lpos, TPos&& tpos, RPos&& rpos) + static concat_center_pos + visit_leaf(LPos&& lpos, TPos&& tpos, RPos&& rpos) { return rpos.first_sub_leaf(concat_right_visitor{}, lpos, tpos); } }; template struct concat_trees_right_visitor + : visitor_base> { using this_t = concat_trees_right_visitor; template - friend concat_center_pos - visit_node(this_t, RPos&& rpos, LPos&& lpos, TPos&& tpos) + static concat_center_pos + visit_node(RPos&& rpos, LPos&& lpos, TPos&& tpos) { return concat_inners(lpos, tpos, rpos); } }; template struct concat_trees_left_visitor + : visitor_base> { using this_t = concat_trees_left_visitor; template - friend concat_center_pos - visit_node(this_t, LPos&& lpos, TPos&& tpos, Args&& ...args) + static concat_center_pos + visit_node(LPos&& lpos, TPos&& tpos, Args&& ...args) { return visit_maybe_relaxed_sub( args..., concat_trees_right_visitor{}, @@ -2039,17 +2052,17 @@ struct concat_merger_mut } }; -struct concat_merger_mut_visitor +struct concat_merger_mut_visitor : visitor_base { using this_t = concat_merger_mut_visitor; template - friend void visit_inner(this_t, Pos&& p, + static void visit_inner(Pos&& p, Merger& merger, edit_type e) { merger.merge_inner(p, e); } template - friend void visit_leaf(this_t, Pos&& p, + static void visit_leaf(Pos&& p, Merger& merger, edit_type e) { merger.merge_leaf(p, e); } }; @@ -2167,22 +2180,22 @@ concat_inners_mut(edit_type ec, } template -struct concat_left_mut_visitor +struct concat_left_mut_visitor : visitor_base> { using this_t = concat_left_mut_visitor; using edit_t = typename Node::edit_t; template - friend concat_center_mut_pos - visit_inner(this_t, LPos&& lpos, edit_t ec, + static concat_center_mut_pos + visit_inner(LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos) { return concat_inners_mut( ec, el, lpos, tpos, er, rpos); } template - friend concat_center_mut_pos - visit_leaf(this_t, LPos&& lpos, edit_t ec, + static concat_center_mut_pos + visit_leaf(LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos) { IMMER_UNREACHABLE; } @@ -2190,21 +2203,22 @@ struct concat_left_mut_visitor template struct concat_right_mut_visitor + : visitor_base> { using this_t = concat_right_mut_visitor; using edit_t = typename Node::edit_t; template - friend concat_center_mut_pos - visit_inner(this_t, RPos&& rpos, edit_t ec, + static concat_center_mut_pos + visit_inner(RPos&& rpos, edit_t ec, edit_t el, LPos&& lpos, TPos&& tpos, edit_t er) { return concat_inners_mut( ec, el, lpos, tpos, er, rpos); } template - friend concat_center_mut_pos - visit_leaf(this_t, RPos&& rpos, edit_t ec, + static concat_center_mut_pos + visit_leaf(RPos&& rpos, edit_t ec, edit_t el, LPos&& lpos, TPos&& tpos, edit_t er) { return concat_leafs_mut( @@ -2213,21 +2227,22 @@ struct concat_right_mut_visitor template struct concat_both_mut_visitor + : visitor_base> { using this_t = concat_both_mut_visitor; using edit_t = typename Node::edit_t; template - friend concat_center_mut_pos - visit_inner(this_t, LPos&& lpos, edit_t ec, + static concat_center_mut_pos + 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{}, ec, el, lpos, tpos, er); } template - friend concat_center_mut_pos - visit_leaf(this_t, LPos&& lpos, edit_t ec, + static concat_center_mut_pos + 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{}, @@ -2236,13 +2251,14 @@ struct concat_both_mut_visitor template struct concat_trees_right_mut_visitor + : visitor_base> { using this_t = concat_trees_right_mut_visitor; using edit_t = typename Node::edit_t; template - friend concat_center_mut_pos - visit_node(this_t, RPos&& rpos, edit_t ec, + static concat_center_mut_pos + visit_node(RPos&& rpos, edit_t ec, edit_t el, LPos&& lpos, TPos&& tpos, edit_t er) { return concat_inners_mut( @@ -2251,13 +2267,14 @@ struct concat_trees_right_mut_visitor template struct concat_trees_left_mut_visitor + : visitor_base> { using this_t = concat_trees_left_mut_visitor; using edit_t = typename Node::edit_t; template - friend concat_center_mut_pos - visit_node(this_t, LPos&& lpos, edit_t ec, + static concat_center_mut_pos + visit_node(LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, Args&& ...args) { return visit_maybe_relaxed_sub( diff --git a/src/immer/detail/rbts/position.hpp b/src/immer/detail/rbts/position.hpp index f4cdf88db68d..4ff579f8f90b 100644 --- a/src/immer/detail/rbts/position.hpp +++ b/src/immer/detail/rbts/position.hpp @@ -50,7 +50,7 @@ struct empty_regular_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_regular(v, *this, std::forward(args)...); + return Visitor::visit_regular(*this, std::forward(args)...); } }; @@ -74,7 +74,7 @@ struct empty_leaf_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_leaf(v, *this, std::forward(args)...); + return Visitor::visit_leaf(*this, std::forward(args)...); } }; @@ -105,7 +105,7 @@ struct leaf_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_leaf(v, *this, std::forward(args)...); + return Visitor::visit_leaf(*this, std::forward(args)...); } }; @@ -137,7 +137,7 @@ struct leaf_sub_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_leaf(v, *this, std::forward(args)...); + return Visitor::visit_leaf(*this, std::forward(args)...); } }; @@ -168,7 +168,7 @@ struct leaf_descent_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_leaf(v, *this, std::forward(args)...); + return Visitor::visit_leaf(*this, std::forward(args)...); } }; @@ -198,7 +198,7 @@ struct full_leaf_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_leaf(v, *this, std::forward(args)...); + return Visitor::visit_leaf(*this, std::forward(args)...); } }; @@ -294,7 +294,7 @@ struct regular_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_regular(v, *this, std::forward(args)...); + return Visitor::visit_regular(*this, std::forward(args)...); } }; @@ -703,7 +703,7 @@ struct singleton_regular_sub_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_regular(v, *this, std::forward(args)...); + return Visitor::visit_regular(*this, std::forward(args)...); } }; @@ -942,7 +942,7 @@ struct regular_sub_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_regular(v, *this, std::forward(args)...); + return Visitor::visit_regular(*this, std::forward(args)...); } }; @@ -971,10 +971,14 @@ struct regular_descent_pos 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; +#if !defined(_MSC_VER) #pragma GCC diagnostic pop +#endif } template @@ -988,7 +992,7 @@ struct regular_descent_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_regular(v, *this, std::forward(args)...); + return Visitor::visit_regular(*this, std::forward(args)...); } }; @@ -1013,7 +1017,7 @@ struct regular_descent_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_regular(v, *this, std::forward(args)...); + return Visitor::visit_regular(*this, std::forward(args)...); } }; @@ -1281,7 +1285,7 @@ struct full_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_regular(v, *this, std::forward(args)...); + return Visitor::visit_regular(*this, std::forward(args)...); } }; @@ -1668,7 +1672,7 @@ struct relaxed_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_relaxed(v, *this, std::forward(args)...); + return Visitor::visit_relaxed(*this, std::forward(args)...); } }; @@ -1726,10 +1730,14 @@ struct relaxed_descent_pos 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; } @@ -1750,7 +1758,7 @@ struct relaxed_descent_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_relaxed(v, *this, std::forward(args)...); + return Visitor::visit_relaxed(*this, std::forward(args)...); } }; @@ -1787,7 +1795,7 @@ struct relaxed_descent_pos template decltype(auto) visit(Visitor v, Args&& ...args) { - return visit_relaxed(v, *this, std::forward(args)...); + return Visitor::visit_relaxed(*this, std::forward(args)...); } }; diff --git a/src/immer/detail/rbts/rbtree.hpp b/src/immer/detail/rbts/rbtree.hpp index c6b39aa160e3..aab5bd9fe38a 100644 --- a/src/immer/detail/rbts/rbtree.hpp +++ b/src/immer/detail/rbts/rbtree.hpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include #include @@ -56,8 +58,10 @@ struct rbtree return result; } - template - static auto from_range(Iter first, Iter last) + template , bool> = true> + static auto from_range(Iter first, Sent last) { auto e = owner_t{}; auto result = rbtree{empty()}; diff --git a/src/immer/detail/rbts/rbtree_iterator.hpp b/src/immer/detail/rbts/rbtree_iterator.hpp index f1890dc053a1..672484eae3ea 100644 --- a/src/immer/detail/rbts/rbtree_iterator.hpp +++ b/src/immer/detail/rbts/rbtree_iterator.hpp @@ -20,7 +20,9 @@ struct rbtree_iterator : iterator_facade, std::random_access_iterator_tag, T, - const T&> + const T&, + std::ptrdiff_t, + const T*> { using tree_t = rbtree; diff --git a/src/immer/detail/rbts/rrbtree.hpp b/src/immer/detail/rbts/rrbtree.hpp index f18de3528e4d..7d26e50a66f6 100644 --- a/src/immer/detail/rbts/rrbtree.hpp +++ b/src/immer/detail/rbts/rrbtree.hpp @@ -13,6 +13,8 @@ #include #include +#include + #include #include #include @@ -63,8 +65,10 @@ struct rrbtree return result; } - template - static auto from_range(Iter first, Iter last) + template , bool> = true> + static auto from_range(Iter first, Sent last) { auto e = owner_t{}; auto result = rrbtree{empty()}; diff --git a/src/immer/detail/rbts/rrbtree_iterator.hpp b/src/immer/detail/rbts/rrbtree_iterator.hpp index 36222f976839..a40f7f486de0 100644 --- a/src/immer/detail/rbts/rrbtree_iterator.hpp +++ b/src/immer/detail/rbts/rrbtree_iterator.hpp @@ -20,7 +20,9 @@ struct rrbtree_iterator : iterator_facade, std::random_access_iterator_tag, T, - const T&> + const T&, + std::ptrdiff_t, + const T*> { using tree_t = rrbtree; using region_t = std::tuple; diff --git a/src/immer/detail/rbts/visitor.hpp b/src/immer/detail/rbts/visitor.hpp index d85f96d865ed..eaccfcdca474 100644 --- a/src/immer/detail/rbts/visitor.hpp +++ b/src/immer/detail/rbts/visitor.hpp @@ -8,6 +8,8 @@ #pragma once +#include + #include #include @@ -15,68 +17,39 @@ namespace immer { namespace detail { namespace rbts { -struct visitor_tag {}; - -template -using fn_visitor = std::tuple; - -template -auto make_visitor(Fns&& ...fns) -{ - return std::make_tuple(visitor_tag{}, std::forward(fns)...); -} - -template -decltype(auto) visit_relaxed(fn_visitor v, Args&& ...args) -{ return std::get<1>(v)(v, std::forward(args)...); } - -template -decltype(auto) visit_regular(fn_visitor v, Args&& ...args) -{ return std::get<2>(v)(v, std::forward(args)...); } - -template -decltype(auto) visit_leaf(fn_visitor v, Args&& ...args) -{ return std::get<3>(v)(v, std::forward(args)...); } - -template -decltype(auto) visit_inner(fn_visitor v, Args&& ...args) -{ return std::get<1>(v)(v, std::forward(args)...); } - -template -decltype(auto) visit_leaf(fn_visitor v, Args&& ...args) -{ return std::get<2>(v)(v, std::forward(args)...); } - -template -decltype(auto) visit_node(fn_visitor v, Args&& ...args) -{ return std::get<1>(v)(v, std::forward(args)...); } - -template -decltype(auto) visit_relaxed(Visitor&& v, Args&& ...args) -{ - return visit_inner(std::forward(v), - std::forward(args)...); -} - -template -decltype(auto) visit_regular(Visitor&& v, Args&& ...args) -{ - return visit_inner(std::forward(v), - std::forward(args)...); -} - -template -decltype(auto) visit_inner(Visitor&& v, Args&& ...args) -{ - return visit_node(std::forward(v), - std::forward(args)...); -} - -template -decltype(auto) visit_leaf(Visitor&& v, Args&& ...args) +template +struct visitor_base { - return visit_node(std::forward(v), - std::forward(args)...); -} + template + static decltype(auto) visit_node(Args&& ...args) + { + IMMER_UNREACHABLE; + } + + template + static decltype(auto) visit_relaxed(Args&& ...args) + { + return Deriv::visit_inner(std::forward(args)...); + } + + template + static decltype(auto) visit_regular(Args&& ...args) + { + return Deriv::visit_inner(std::forward(args)...); + } + + template + static decltype(auto) visit_inner(Args&& ...args) + { + return Deriv::visit_node(std::forward(args)...); + } + + template + static decltype(auto) visit_leaf(Args&& ...args) + { + return Deriv::visit_node(std::forward(args)...); + } +}; } // namespace rbts } // namespace detail diff --git a/src/immer/detail/type_traits.hpp b/src/immer/detail/type_traits.hpp new file mode 100644 index 000000000000..7820e75d7a56 --- /dev/null +++ b/src/immer/detail/type_traits.hpp @@ -0,0 +1,191 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace immer { +namespace detail { + +template +struct make_void { using type = void; }; + +template +using void_t = typename make_void::type; + +template +struct is_dereferenceable : std::false_type {}; + +template +struct is_dereferenceable()))>> : + std::true_type {}; + +template +constexpr bool is_dereferenceable_v = is_dereferenceable::value; + +template +struct is_equality_comparable : std::false_type {}; + +template +struct is_equality_comparable +() == std::declval())>::value>> : + std::true_type {}; + +template +constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +template +struct is_inequality_comparable : std::false_type {}; + +template +struct is_inequality_comparable +() != std::declval())>::value>> : + std::true_type {}; + +template +constexpr bool is_inequality_comparable_v = + is_inequality_comparable::value; + +template +struct is_preincrementable : std::false_type {}; + +template +struct is_preincrementable +()))>::value>> : + std::true_type {}; + +template +constexpr bool is_preincrementable_v = is_preincrementable::value; + +template +struct is_subtractable : std::false_type {}; + +template +struct is_subtractable +() - std::declval())>> : + std::true_type {}; + +template +constexpr bool is_subtractable_v = is_subtractable::value; + +namespace swappable { + +using std::swap; + +template +struct with : std::false_type {}; + +// Does not account for non-referenceable types +template +struct with +(), std::declval())), + decltype(swap(std::declval(), std::declval()))>> : + std::true_type {}; + +} + +template +using is_swappable_with = swappable::with; + +template +using is_swappable = is_swappable_with; + +template +constexpr bool is_swappable_v = is_swappable_with::value; + +template +struct is_iterator : std::false_type {}; + +// See http://en.cppreference.com/w/cpp/concept/Iterator +template +struct is_iterator + + && is_dereferenceable_v + // accounts for non-referenceable types + && std::is_copy_constructible::value + && std::is_copy_assignable::value + && std::is_destructible::value + && is_swappable_v>, + typename std::iterator_traits::value_type, + typename std::iterator_traits::difference_type, + typename std::iterator_traits::reference, + typename std::iterator_traits::pointer, + typename std::iterator_traits::iterator_category>> : + std::true_type {}; + +template +constexpr bool is_iterator_v = is_iterator::value; + +template +struct compatible_sentinel : std::false_type {}; + +template +struct compatible_sentinel + + && is_equality_comparable_v + && is_inequality_comparable_v>> : + std::true_type {}; + +template +constexpr bool compatible_sentinel_v = compatible_sentinel::value; + +template +struct is_forward_iterator : std::false_type {}; + +template +struct is_forward_iterator + && + std::is_base_of + ::iterator_category>::value>> : + std::true_type {}; + +template +constexpr bool is_forward_iterator_v = is_forward_iterator::value; + +template +struct std_distance_supports : std::false_type {}; + +template +struct std_distance_supports +(), std::declval()))>> : + std::true_type {}; + +template +constexpr bool std_distance_supports_v = std_distance_supports::value; + +template +struct std_uninitialized_copy_supports : std::false_type {}; + +template +struct std_uninitialized_copy_supports +(), + std::declval(), + std::declval()))>> : + std::true_type {}; + +template +constexpr bool std_uninitialized_copy_supports_v = + std_uninitialized_copy_supports::value; + +} +} diff --git a/src/immer/detail/util.hpp b/src/immer/detail/util.hpp index 7490357dbf60..854a263d9e68 100644 --- a/src/immer/detail/util.hpp +++ b/src/immer/detail/util.hpp @@ -15,6 +15,12 @@ #include #include +#include + +#if defined(_MSC_VER) +#include // for __lzcnt* +#endif + namespace immer { namespace detail { @@ -74,26 +80,32 @@ struct exact_t template 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 inline constexpr T log2_aux(T x, T r = 0) { - return x <= 1 ? r : log2(x >> 1, r + 1); + return x <= 1 ? r : log2_aux(x >> 1, r + 1); } template inline constexpr auto log2(T x) - -> std::enable_if_t{}, T> + -> std::enable_if_t::value, T> { return x == 0 ? 0 : sizeof(std::size_t) * 8 - 1 - clz_(x); } template inline constexpr auto log2(T x) - -> std::enable_if_t{}, T> + -> std::enable_if_t::value, T> { return log2_aux(x); } @@ -119,5 +131,95 @@ struct constantly T operator() (Args&&...) const { return value; } }; +/*! + * An alias to `std::distance` + */ +template , bool> = true> +typename std::iterator_traits::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 ) + && detail::is_forward_iterator_v + && detail::compatible_sentinel_v + && (!detail::is_subtractable_v), bool> = true> +typename std::iterator_traits::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 ) + && detail::is_forward_iterator_v + && detail::compatible_sentinel_v + && detail::is_subtractable_v, bool> = true> +typename std::iterator_traits::difference_type +distance(Iterator first, Sentinel last) +{ + return last - first; +} + + + +/*! + * An alias to `std::uninitialized_copy` + */ +template , 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 ) + && detail::compatible_sentinel_v + && detail::is_forward_iterator_v, 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::value_type; + for (;d_first != current; ++d_first){ + d_first->~Value(); + } + throw; + } + return current; +} + } // namespace detail } // namespace immer diff --git a/src/immer/flex_vector.hpp b/src/immer/flex_vector.hpp index e2ddea24e9a7..7ab8fcd31b16 100644 --- a/src/immer/flex_vector.hpp +++ b/src/immer/flex_vector.hpp @@ -89,18 +89,20 @@ class flex_vector flex_vector() = default; /*! - * Constructs a vector containing the elements in `values`. + * Constructs a flex_vector containing the elements in `values`. */ flex_vector(std::initializer_list values) : impl_{impl_t::from_initializer_list(values)} {} /*! - * Constructs a vector containing the elements in the range - * defined by the input iterators `first` and `last`. + * Constructs a flex_vector containing the elements in the range + * defined by the input iterator `first` and range sentinel `last`. */ - template - flex_vector(Iter first, Iter last) + template , bool> = true> + flex_vector(Iter first, Sent last) : impl_{impl_t::from_range(first, last)} {} diff --git a/src/immer/heap/debug_size_heap.hpp b/src/immer/heap/debug_size_heap.hpp index 124beb81b419..4fbc665c69b6 100644 --- a/src/immer/heap/debug_size_heap.hpp +++ b/src/immer/heap/debug_size_heap.hpp @@ -8,9 +8,10 @@ #pragma once +#include +#include #include #include -#include namespace immer { @@ -23,20 +24,22 @@ namespace immer { template struct debug_size_heap { + constexpr static auto extra_size = alignof(std::max_align_t); + template static void* allocate(std::size_t size, Tags... tags) { - auto p = (std::size_t*) Base::allocate(size + sizeof(std::size_t), tags...); + auto p = (std::size_t*) Base::allocate(size + extra_size, tags...); *p = size; - return p + 1; + return ((char*)p) + extra_size; } template static void deallocate(std::size_t size, void* data, Tags... tags) { - auto p = ((std::size_t*) data) - 1; + auto p = (std::size_t*) (((char*) data) - extra_size); assert(*p == size); - Base::deallocate(size + sizeof(std::size_t), p, tags...); + Base::deallocate(size + extra_size, p, tags...); } }; diff --git a/src/immer/map.hpp b/src/immer/map.hpp index 53d450b6835f..99e483a2d40e 100644 --- a/src/immer/map.hpp +++ b/src/immer/map.hpp @@ -57,9 +57,9 @@ class map_transient; */ template , - typename Equal = std::equal_to, - typename MemoryPolicy = default_memory_policy, + typename Hash = std::hash, + typename Equal = std::equal_to, + typename MemoryPolicy = default_memory_policy, detail::hamts::bits_t B = default_bits> class map { diff --git a/src/immer/refcount/no_refcount_policy.hpp b/src/immer/refcount/no_refcount_policy.hpp index 188fb9f5594e..95663368f112 100644 --- a/src/immer/refcount/no_refcount_policy.hpp +++ b/src/immer/refcount/no_refcount_policy.hpp @@ -12,12 +12,26 @@ 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) {} diff --git a/src/immer/refcount/refcount_policy.hpp b/src/immer/refcount/refcount_policy.hpp index b143cbf7e43a..68ba209c8c76 100644 --- a/src/immer/refcount/refcount_policy.hpp +++ b/src/immer/refcount/refcount_policy.hpp @@ -13,15 +13,74 @@ #include #include #include +#include + +// 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 refcount; refcount_policy() : refcount{1} {}; diff --git a/src/immer/refcount/unsafe_refcount_policy.hpp b/src/immer/refcount/unsafe_refcount_policy.hpp index 9a77f06a512a..2a170b7573cc 100644 --- a/src/immer/refcount/unsafe_refcount_policy.hpp +++ b/src/immer/refcount/unsafe_refcount_policy.hpp @@ -21,6 +21,8 @@ namespace immer { */ struct unsafe_refcount_policy { + using spinlock_type = no_spinlock; + mutable int refcount; unsafe_refcount_policy() : refcount{1} {}; diff --git a/src/immer/set.hpp b/src/immer/set.hpp index 9f2b0b301df4..5d9eed8ca6d3 100644 --- a/src/immer/set.hpp +++ b/src/immer/set.hpp @@ -54,9 +54,9 @@ class set_transient; * */ template , - typename Equal = std::equal_to, - typename MemoryPolicy = default_memory_policy, + typename Hash = std::hash, + typename Equal = std::equal_to, + typename MemoryPolicy = default_memory_policy, detail::hamts::bits_t B = default_bits> class set { diff --git a/src/immer/vector.hpp b/src/immer/vector.hpp index 296431fb0ae6..b9c9ef48f6c8 100644 --- a/src/immer/vector.hpp +++ b/src/immer/vector.hpp @@ -111,10 +111,12 @@ class vector /*! * Constructs a vector containing the elements in the range - * defined by the input iterators `first` and `last`. + * defined by the input iterator `first` and range sentinel `last`. */ - template - vector(Iter first, Iter last) + template , bool> = true> + vector(Iter first, Sent last) : impl_{impl_t::from_range(first, last)} {} From 1572f7c6db8c2204480794491e0d20377f953099 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 1 Apr 2019 10:23:18 +0200 Subject: [PATCH 2/2] Temporary fix for alignof(std::max_align_t) on MinGW 32bit builds See https://github.com/arximboldi/immer/issues/78 --- src/immer/heap/debug_size_heap.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/immer/heap/debug_size_heap.hpp b/src/immer/heap/debug_size_heap.hpp index 4fbc665c69b6..5cfb74ca8f65 100644 --- a/src/immer/heap/debug_size_heap.hpp +++ b/src/immer/heap/debug_size_heap.hpp @@ -24,7 +24,8 @@ namespace immer { template struct debug_size_heap { - constexpr static auto extra_size = alignof(std::max_align_t); + // temporary fix until https://github.com/arximboldi/immer/issues/78 is fixed + constexpr static auto extra_size = sizeof(void*) * 2; //alignof(std::max_align_t); template static void* allocate(std::size_t size, Tags... tags)