about summary refs log tree commit diff
path: root/third_party/immer/immer/detail/hamts
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/immer/immer/detail/hamts')
-rw-r--r--third_party/immer/immer/detail/hamts/bits.hpp108
-rw-r--r--third_party/immer/immer/detail/hamts/champ.hpp473
-rw-r--r--third_party/immer/immer/detail/hamts/champ_iterator.hpp148
-rw-r--r--third_party/immer/immer/detail/hamts/node.hpp717
4 files changed, 0 insertions, 1446 deletions
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 b02caf7706..0000000000
--- 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 e3b55d397c..0000000000
--- 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 72673b41be..0000000000
--- 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 216e82b787..0000000000
--- 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