Coverage Report

Created: 2025-11-24 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/immer/immer/detail/hamts/champ.hpp
Line
Count
Source
1
//
2
// immer: immutable data structures for C++
3
// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
4
//
5
// This software is distributed under the Boost Software License, Version 1.0.
6
// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
7
//
8
9
#pragma once
10
11
#include <immer/config.hpp>
12
#include <immer/detail/hamts/node.hpp>
13
14
#include <algorithm>
15
16
namespace immer {
17
namespace detail {
18
namespace hamts {
19
20
#if IMMER_DEBUG_STATS
21
struct champ_debug_stats
22
{
23
    std::size_t bits       = {};
24
    std::size_t value_size = {};
25
    std::size_t child_size = sizeof(void*);
26
27
    std::size_t inner_node_count         = {};
28
    std::size_t inner_node_w_value_count = {};
29
    std::size_t inner_node_w_child_count = {};
30
    std::size_t collision_node_count     = {};
31
32
    std::size_t child_count     = {};
33
    std::size_t value_count     = {};
34
    std::size_t collision_count = {};
35
36
    friend champ_debug_stats operator+(champ_debug_stats a, champ_debug_stats b)
37
    {
38
        if (a.bits != b.bits || a.value_size != b.value_size ||
39
            a.child_size != b.child_size)
40
            throw std::runtime_error{"accumulating incompatible stats"};
41
        return {
42
            a.bits,
43
            a.value_size,
44
            a.child_size,
45
            a.inner_node_count + b.inner_node_count,
46
            a.inner_node_w_value_count + b.inner_node_w_value_count,
47
            a.inner_node_w_child_count + b.inner_node_w_child_count,
48
            a.collision_node_count + b.collision_node_count,
49
            a.child_count + b.child_count,
50
            a.value_count + b.value_count,
51
            a.collision_count + b.collision_count,
52
        };
53
    }
54
55
    struct summary
56
    {
57
        double collision_ratio;
58
59
        double utilization;
60
        double child_utilization;
61
        double value_utilization;
62
63
        double dense_utilization;
64
        double dense_value_utilization;
65
        double dense_child_utilization;
66
67
        friend std::ostream& operator<<(std::ostream& os, const summary& s)
68
        {
69
            os << "---\n";
70
            os << "collisions\n"
71
               << "  ratio = " << s.collision_ratio << " %\n";
72
            os << "utilization\n"
73
               << "  total    = " << s.utilization << " %\n"
74
               << "  children = " << s.child_utilization << " %\n"
75
               << "  values   = " << s.value_utilization << " %\n";
76
            os << "utilization (dense)\n"
77
               << "  total    = " << s.dense_utilization << " %\n"
78
               << "  children = " << s.dense_child_utilization << " %\n"
79
               << "  values   = " << s.dense_value_utilization << " %\n";
80
            return os;
81
        }
82
    };
83
84
    summary get_summary() const
85
    {
86
        auto m = std::size_t{1} << bits;
87
88
        auto collision_ratio = 100. * collision_count / value_count;
89
90
        auto capacity          = m * inner_node_count;
91
        auto child_utilization = 100. * child_count / capacity;
92
        auto value_utilization = 100. * value_count / capacity;
93
        auto utilization =
94
            100. * (value_count * value_size + child_count * child_size) /
95
            (capacity * value_size + capacity * child_size);
96
97
        auto value_capacity = m * inner_node_w_value_count;
98
        auto child_capacity = m * inner_node_w_child_count;
99
        auto dense_child_utilization =
100
            child_capacity == 0 ? 100. : 100. * child_count / child_capacity;
101
        auto dense_value_utilization =
102
            value_capacity == 0 ? 100. : 100. * value_count / value_capacity;
103
        auto dense_utilization =
104
            value_capacity + child_capacity == 0
105
                ? 100.
106
                : 100. * (value_count * value_size + child_count * child_size) /
107
                      (value_capacity * value_size +
108
                       child_capacity * child_size);
109
110
        return {collision_ratio,
111
                utilization,
112
                child_utilization,
113
                value_utilization,
114
                dense_utilization,
115
                dense_value_utilization,
116
                dense_child_utilization};
117
    }
118
};
119
#endif
120
121
template <typename T,
122
          typename Hash,
123
          typename Equal,
124
          typename MemoryPolicy,
125
          bits_t B>
126
struct champ
127
{
128
    static constexpr auto bits = B;
129
130
    using node_t   = node<T, Hash, Equal, MemoryPolicy, B>;
131
    using edit_t   = typename MemoryPolicy::transience_t::edit;
132
    using owner_t  = typename MemoryPolicy::transience_t::owner;
133
    using bitmap_t = typename get_bitmap_type<B>::type;
134
    using hash_t   = typename node_t::hash_t;
135
136
    static_assert(branches<B> <= sizeof(bitmap_t) * 8, "");
137
138
    node_t* root;
139
    size_t size;
140
141
    static node_t* empty()
142
397k
    {
143
397k
        static const auto empty_ = [] {
144
1
            constexpr auto size = node_t::sizeof_inner_n(0);
145
1
            static std::aligned_storage_t<size, alignof(std::max_align_t)>
146
1
                storage;
147
1
            return node_t::make_inner_n_into(&storage, size, 0u);
148
1
        }();
149
397k
        return empty_->inc();
150
397k
    }
151
152
    champ(node_t* r, size_t sz = 0) noexcept
153
784k
        : root{r}
154
784k
        , size{sz}
155
784k
    {
156
784k
    }
157
158
    champ(const champ& other) noexcept
159
17.2k
        : champ{other.root, other.size}
160
17.2k
    {
161
17.2k
        inc();
162
17.2k
    }
163
164
    champ(champ&& other) noexcept
165
379k
        : champ{empty()}
166
379k
    {
167
379k
        swap(*this, other);
168
379k
    }
169
170
    champ& operator=(const champ& other)
171
    {
172
        auto next = other;
173
        swap(*this, next);
174
        return *this;
175
    }
176
177
    champ& operator=(champ&& other) noexcept
178
414k
    {
179
414k
        swap(*this, other);
180
414k
        return *this;
181
414k
    }
182
183
    friend void swap(champ& x, champ& y) noexcept
184
793k
    {
185
793k
        using std::swap;
186
793k
        swap(x.root, y.root);
187
793k
        swap(x.size, y.size);
188
793k
    }
189
190
784k
    ~champ() { dec(); }
191
192
17.2k
    void inc() const { root->inc(); }
193
194
    void dec() const
195
784k
    {
196
784k
        if (root->dec())
197
373k
            node_t::delete_deep(root, 0);
198
784k
    }
199
200
    std::size_t do_check_champ(node_t* node,
201
                               count_t depth,
202
                               size_t path_hash,
203
                               size_t hash_mask) const
204
    {
205
        auto result = std::size_t{};
206
        if (depth < max_depth<hash_t, B>) {
207
            auto nodemap = node->nodemap();
208
            if (nodemap) {
209
                auto fst = node->children();
210
                for (auto idx = std::size_t{}; idx < branches<B>; ++idx) {
211
                    if (nodemap & (1 << idx)) {
212
                        auto child = *fst++;
213
                        result +=
214
                            do_check_champ(child,
215
                                           depth + 1,
216
                                           path_hash | (idx << (B * depth)),
217
                                           (hash_mask << B) | mask<hash_t, B>);
218
                    }
219
                }
220
            }
221
            auto datamap = node->datamap();
222
            if (datamap) {
223
                auto fst = node->values();
224
                for (auto idx = std::size_t{}; idx < branches<B>; ++idx) {
225
                    if (datamap & (1 << idx)) {
226
                        auto hash  = Hash{}(*fst++);
227
                        auto check = (hash & hash_mask) ==
228
                                     (path_hash | (idx << (B * depth)));
229
                        // assert(check);
230
                        result += !!check;
231
                    }
232
                }
233
            }
234
        } else {
235
            auto fst = node->collisions();
236
            auto lst = fst + node->collision_count();
237
            for (; fst != lst; ++fst) {
238
                auto hash  = Hash{}(*fst);
239
                auto check = hash == path_hash;
240
                // assert(check);
241
                result += !!check;
242
            }
243
        }
244
        return result;
245
    }
246
247
    // Checks that the hashes of the values correspond with what has actually
248
    // been inserted.  If it doesn't it can mean that corruption has happened
249
    // due some value being moved out of the champ when it should have not.
250
    bool check_champ() const
251
    {
252
        auto r = do_check_champ(root, 0, 0, mask<hash_t, B>);
253
        // assert(r == size);
254
        return r == size;
255
    }
256
257
#if IMMER_DEBUG_STATS
258
    void do_get_debug_stats(champ_debug_stats& stats,
259
                            node_t* node,
260
                            count_t depth) const
261
    {
262
        if (depth < max_depth<hash_t, B>) {
263
            ++stats.inner_node_count;
264
            stats.inner_node_w_value_count += node->data_count() > 0;
265
            stats.inner_node_w_child_count += node->children_count() > 0;
266
            stats.value_count += node->data_count();
267
            stats.child_count += node->children_count();
268
            auto nodemap = node->nodemap();
269
            if (nodemap) {
270
                auto fst = node->children();
271
                auto lst = fst + node->children_count();
272
                for (; fst != lst; ++fst)
273
                    do_get_debug_stats(stats, *fst, depth + 1);
274
            }
275
        } else {
276
            ++stats.collision_node_count;
277
            stats.collision_count += node->collision_count();
278
        }
279
    }
280
281
    champ_debug_stats get_debug_stats() const
282
    {
283
        auto stats = champ_debug_stats{B, sizeof(T)};
284
        do_get_debug_stats(stats, root, 0);
285
        return stats;
286
    }
287
#endif
288
289
    template <typename U>
290
    static auto from_initializer_list(std::initializer_list<U> values)
291
    {
292
        auto e      = owner_t{};
293
        auto result = champ{empty()};
294
        for (auto&& v : values)
295
            result.add_mut(e, v);
296
        return result;
297
    }
298
299
    template <typename Iter,
300
              typename Sent,
301
              std::enable_if_t<compatible_sentinel_v<Iter, Sent>, bool> = true>
302
    static auto from_range(Iter first, Sent last)
303
    {
304
        auto e      = owner_t{};
305
        auto result = champ{empty()};
306
        for (; first != last; ++first)
307
            result.add_mut(e, *first);
308
        return result;
309
    }
310
311
    template <typename Fn>
312
    void for_each_chunk(Fn&& fn) const
313
    {
314
        for_each_chunk_traversal(root, 0, fn);
315
    }
316
317
    template <typename Fn>
318
    void
319
    for_each_chunk_traversal(const node_t* node, count_t depth, Fn&& fn) const
320
124k
    {
321
124k
        if (depth < max_depth<hash_t, B>) {
322
119k
            auto datamap = node->datamap();
323
119k
            if (datamap)
324
44.8k
                fn(node->values(), node->values() + node->data_count());
325
119k
            auto nodemap = node->nodemap();
326
119k
            if (nodemap) {
327
80.8k
                auto fst = node->children();
328
80.8k
                auto lst = fst + node->children_count();
329
173k
                for (; fst != lst; ++fst)
330
92.3k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
80.8k
            }
332
119k
        } else {
333
5.03k
            fn(node->collisions(),
334
5.03k
               node->collisions() + node->collision_count());
335
5.03k
        }
336
124k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::for_each_chunk_traversal<immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}> >(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}>(immer::detail::hamts::node, unsigned int, fuzzer_input&&) const
Line
Count
Source
320
1.27k
    {
321
1.27k
        if (depth < max_depth<hash_t, B>) {
322
1.27k
            auto datamap = node->datamap();
323
1.27k
            if (datamap)
324
420
                fn(node->values(), node->values() + node->data_count());
325
1.27k
            auto nodemap = node->nodemap();
326
1.27k
            if (nodemap) {
327
930
                auto fst = node->children();
328
930
                auto lst = fst + node->children_count();
329
2.86k
                for (; fst != lst; ++fst)
330
1.93k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
930
            }
332
1.27k
        } else {
333
0
            fn(node->collisions(),
334
0
               node->collisions() + node->collision_count());
335
0
        }
336
1.27k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::for_each_chunk_traversal<immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}> >(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}&>(immer::detail::hamts::node, unsigned int, fuzzer_input&&) const
Line
Count
Source
320
9.26k
    {
321
9.26k
        if (depth < max_depth<hash_t, B>) {
322
8.97k
            auto datamap = node->datamap();
323
8.97k
            if (datamap)
324
3.12k
                fn(node->values(), node->values() + node->data_count());
325
8.97k
            auto nodemap = node->nodemap();
326
8.97k
            if (nodemap) {
327
6.26k
                auto fst = node->children();
328
6.26k
                auto lst = fst + node->children_count();
329
13.5k
                for (; fst != lst; ++fst)
330
7.33k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
6.26k
            }
332
8.97k
        } else {
333
291
            fn(node->collisions(),
334
291
               node->collisions() + node->collision_count());
335
291
        }
336
9.26k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::for_each_chunk_traversal<immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}> >(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#2}>(immer::detail::hamts::node, unsigned int, fuzzer_input&&) const
Line
Count
Source
320
1.34k
    {
321
1.34k
        if (depth < max_depth<hash_t, B>) {
322
1.34k
            auto datamap = node->datamap();
323
1.34k
            if (datamap)
324
484
                fn(node->values(), node->values() + node->data_count());
325
1.34k
            auto nodemap = node->nodemap();
326
1.34k
            if (nodemap) {
327
1.02k
                auto fst = node->children();
328
1.02k
                auto lst = fst + node->children_count();
329
3.17k
                for (; fst != lst; ++fst)
330
2.15k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
1.02k
            }
332
1.34k
        } else {
333
0
            fn(node->collisions(),
334
0
               node->collisions() + node->collision_count());
335
0
        }
336
1.34k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::for_each_chunk_traversal<immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}> >(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#2}&>(immer::detail::hamts::node, unsigned int, fuzzer_input&&) const
Line
Count
Source
320
10.5k
    {
321
10.5k
        if (depth < max_depth<hash_t, B>) {
322
10.0k
            auto datamap = node->datamap();
323
10.0k
            if (datamap)
324
3.24k
                fn(node->values(), node->values() + node->data_count());
325
10.0k
            auto nodemap = node->nodemap();
326
10.0k
            if (nodemap) {
327
7.46k
                auto fst = node->children();
328
7.46k
                auto lst = fst + node->children_count();
329
15.8k
                for (; fst != lst; ++fst)
330
8.38k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
7.46k
            }
332
10.0k
        } else {
333
487
            fn(node->collisions(),
334
487
               node->collisions() + node->collision_count());
335
487
        }
336
10.5k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::for_each_chunk_traversal<immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}>&>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}>(immer::detail::hamts::node, unsigned int, fuzzer_input&&) const
Line
Count
Source
320
3.64k
    {
321
3.64k
        if (depth < max_depth<hash_t, B>) {
322
3.35k
            auto datamap = node->datamap();
323
3.35k
            if (datamap)
324
1.70k
                fn(node->values(), node->values() + node->data_count());
325
3.35k
            auto nodemap = node->nodemap();
326
3.35k
            if (nodemap) {
327
1.85k
                auto fst = node->children();
328
1.85k
                auto lst = fst + node->children_count();
329
4.56k
                for (; fst != lst; ++fst)
330
2.70k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
1.85k
            }
332
3.35k
        } else {
333
290
            fn(node->collisions(),
334
290
               node->collisions() + node->collision_count());
335
290
        }
336
3.64k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::for_each_chunk_traversal<immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}>&>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}&>(immer::detail::hamts::node, unsigned int, fuzzer_input&&) const
Line
Count
Source
320
10.6k
    {
321
10.6k
        if (depth < max_depth<hash_t, B>) {
322
10.3k
            auto datamap = node->datamap();
323
10.3k
            if (datamap)
324
3.60k
                fn(node->values(), node->values() + node->data_count());
325
10.3k
            auto nodemap = node->nodemap();
326
10.3k
            if (nodemap) {
327
7.02k
                auto fst = node->children();
328
7.02k
                auto lst = fst + node->children_count();
329
14.9k
                for (; fst != lst; ++fst)
330
7.94k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
7.02k
            }
332
10.3k
        } else {
333
331
            fn(node->collisions(),
334
331
               node->collisions() + node->collision_count());
335
331
        }
336
10.6k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::for_each_chunk_traversal<immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}>&>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#2}>(immer::detail::hamts::node, unsigned int, fuzzer_input&&) const
Line
Count
Source
320
3.44k
    {
321
3.44k
        if (depth < max_depth<hash_t, B>) {
322
3.15k
            auto datamap = node->datamap();
323
3.15k
            if (datamap)
324
1.63k
                fn(node->values(), node->values() + node->data_count());
325
3.15k
            auto nodemap = node->nodemap();
326
3.15k
            if (nodemap) {
327
1.68k
                auto fst = node->children();
328
1.68k
                auto lst = fst + node->children_count();
329
4.26k
                for (; fst != lst; ++fst)
330
2.57k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
1.68k
            }
332
3.15k
        } else {
333
288
            fn(node->collisions(),
334
288
               node->collisions() + node->collision_count());
335
288
        }
336
3.44k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::for_each_chunk_traversal<immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}>&>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#2}&>(immer::detail::hamts::node, unsigned int, fuzzer_input&&) const
Line
Count
Source
320
10.7k
    {
321
10.7k
        if (depth < max_depth<hash_t, B>) {
322
10.3k
            auto datamap = node->datamap();
323
10.3k
            if (datamap)
324
3.51k
                fn(node->values(), node->values() + node->data_count());
325
10.3k
            auto nodemap = node->nodemap();
326
10.3k
            if (nodemap) {
327
7.10k
                auto fst = node->children();
328
7.10k
                auto lst = fst + node->children_count();
329
15.2k
                for (; fst != lst; ++fst)
330
8.15k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
7.10k
            }
332
10.3k
        } else {
333
380
            fn(node->collisions(),
334
380
               node->collisions() + node->collision_count());
335
380
        }
336
10.7k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::for_each_chunk_traversal<immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff_data_node<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}>&>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}>(immer::detail::hamts::node, unsigned int, fuzzer_input&&) const
Line
Count
Source
320
10.7k
    {
321
10.7k
        if (depth < max_depth<hash_t, B>) {
322
10.4k
            auto datamap = node->datamap();
323
10.4k
            if (datamap)
324
5.46k
                fn(node->values(), node->values() + node->data_count());
325
10.4k
            auto nodemap = node->nodemap();
326
10.4k
            if (nodemap) {
327
5.70k
                auto fst = node->children();
328
5.70k
                auto lst = fst + node->children_count();
329
12.3k
                for (; fst != lst; ++fst)
330
6.62k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
5.70k
            }
332
10.4k
        } else {
333
304
            fn(node->collisions(),
334
304
               node->collisions() + node->collision_count());
335
304
        }
336
10.7k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::for_each_chunk_traversal<immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff_data_node<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}>&>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}&>(immer::detail::hamts::node, unsigned int, fuzzer_input&&) const
Line
Count
Source
320
23.7k
    {
321
23.7k
        if (depth < max_depth<hash_t, B>) {
322
22.6k
            auto datamap = node->datamap();
323
22.6k
            if (datamap)
324
7.15k
                fn(node->values(), node->values() + node->data_count());
325
22.6k
            auto nodemap = node->nodemap();
326
22.6k
            if (nodemap) {
327
16.4k
                auto fst = node->children();
328
16.4k
                auto lst = fst + node->children_count();
329
33.5k
                for (; fst != lst; ++fst)
330
17.1k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
16.4k
            }
332
22.6k
        } else {
333
1.10k
            fn(node->collisions(),
334
1.10k
               node->collisions() + node->collision_count());
335
1.10k
        }
336
23.7k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::for_each_chunk_traversal<immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff_node_data<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}>&>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}>(immer::detail::hamts::node, unsigned int, fuzzer_input&&) const
Line
Count
Source
320
12.1k
    {
321
12.1k
        if (depth < max_depth<hash_t, B>) {
322
11.8k
            auto datamap = node->datamap();
323
11.8k
            if (datamap)
324
5.90k
                fn(node->values(), node->values() + node->data_count());
325
11.8k
            auto nodemap = node->nodemap();
326
11.8k
            if (nodemap) {
327
6.68k
                auto fst = node->children();
328
6.68k
                auto lst = fst + node->children_count();
329
14.4k
                for (; fst != lst; ++fst)
330
7.73k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
6.68k
            }
332
11.8k
        } else {
333
314
            fn(node->collisions(),
334
314
               node->collisions() + node->collision_count());
335
314
        }
336
12.1k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::for_each_chunk_traversal<immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff_node_data<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}>&>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}&>(immer::detail::hamts::node, unsigned int, fuzzer_input&&) const
Line
Count
Source
320
27.3k
    {
321
27.3k
        if (depth < max_depth<hash_t, B>) {
322
26.1k
            auto datamap = node->datamap();
323
26.1k
            if (datamap)
324
8.58k
                fn(node->values(), node->values() + node->data_count());
325
26.1k
            auto nodemap = node->nodemap();
326
26.1k
            if (nodemap) {
327
18.6k
                auto fst = node->children();
328
18.6k
                auto lst = fst + node->children_count();
329
38.3k
                for (; fst != lst; ++fst)
330
19.6k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
18.6k
            }
332
26.1k
        } else {
333
1.23k
            fn(node->collisions(),
334
1.23k
               node->collisions() + node->collision_count());
335
1.23k
        }
336
27.3k
    }
337
338
    template <typename EqualValue, typename Differ>
339
    void diff(const champ& new_champ, Differ&& differ) const
340
15.3k
    {
341
15.3k
        diff<EqualValue>(root, new_champ.root, 0, std::forward<Differ>(differ));
342
15.3k
    }
343
344
    template <typename EqualValue, typename Differ>
345
    void diff(const node_t* old_node,
346
              const node_t* new_node,
347
              count_t depth,
348
              Differ&& differ) const
349
345k
    {
350
345k
        if (old_node == new_node)
351
100k
            return;
352
245k
        if (depth < max_depth<hash_t, B>) {
353
241k
            auto old_nodemap = old_node->nodemap();
354
241k
            auto new_nodemap = new_node->nodemap();
355
241k
            auto old_datamap = old_node->datamap();
356
241k
            auto new_datamap = new_node->datamap();
357
241k
            auto old_bits    = old_nodemap | old_datamap;
358
241k
            auto new_bits    = new_nodemap | new_datamap;
359
241k
            auto changes     = old_bits ^ new_bits;
360
361
            // added bits
362
241k
            for (auto bit : set_bits_range<bitmap_t>(new_bits & changes)) {
363
25.6k
                if (new_nodemap & bit) {
364
4.91k
                    auto offset = new_node->children_count(bit);
365
4.91k
                    auto child  = new_node->children()[offset];
366
4.91k
                    for_each_chunk_traversal(
367
4.91k
                        child,
368
4.91k
                        depth + 1,
369
9.77k
                        [&](auto const& begin, auto const& end) {
370
31.8k
                            for (auto it = begin; it != end; it++)
371
22.0k
                                differ.added(*it);
372
9.77k
                        });
set-st.cpp:auto immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}> >(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}::operator()<unsigned long const*, {lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const, unsigned long const* const) const
Line
Count
Source
369
3.83k
                        [&](auto const& begin, auto const& end) {
370
12.9k
                            for (auto it = begin; it != end; it++)
371
9.06k
                                differ.added(*it);
372
3.83k
                        });
set-st.cpp:auto immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}>&>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}::operator()<unsigned long const*, {lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const, unsigned long const* const) const
Line
Count
Source
369
5.93k
                        [&](auto const& begin, auto const& end) {
370
18.9k
                            for (auto it = begin; it != end; it++)
371
12.9k
                                differ.added(*it);
372
5.93k
                        });
373
20.7k
                } else if (new_datamap & bit) {
374
20.7k
                    auto offset       = new_node->data_count(bit);
375
20.7k
                    auto const& value = new_node->values()[offset];
376
20.7k
                    differ.added(value);
377
20.7k
                }
378
25.6k
            }
379
380
            // removed bits
381
241k
            for (auto bit : set_bits_range<bitmap_t>(old_bits & changes)) {
382
28.4k
                if (old_nodemap & bit) {
383
4.78k
                    auto offset = old_node->children_count(bit);
384
4.78k
                    auto child  = old_node->children()[offset];
385
4.78k
                    for_each_chunk_traversal(
386
4.78k
                        child,
387
4.78k
                        depth + 1,
388
10.0k
                        [&](auto const& begin, auto const& end) {
389
33.3k
                            for (auto it = begin; it != end; it++)
390
23.3k
                                differ.removed(*it);
391
10.0k
                        });
set-st.cpp:auto immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}> >(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#2}::operator()<unsigned long const*, {lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#2}>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const, unsigned long const* const) const
Line
Count
Source
388
4.21k
                        [&](auto const& begin, auto const& end) {
389
14.1k
                            for (auto it = begin; it != end; it++)
390
9.92k
                                differ.removed(*it);
391
4.21k
                        });
set-st.cpp:auto immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}>&>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const::{lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#2}::operator()<unsigned long const*, {lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#2}>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const, unsigned long const* const) const
Line
Count
Source
388
5.81k
                        [&](auto const& begin, auto const& end) {
389
19.1k
                            for (auto it = begin; it != end; it++)
390
13.3k
                                differ.removed(*it);
391
5.81k
                        });
392
23.6k
                } else if (old_datamap & bit) {
393
23.6k
                    auto offset       = old_node->data_count(bit);
394
23.6k
                    auto const& value = old_node->values()[offset];
395
23.6k
                    differ.removed(value);
396
23.6k
                }
397
28.4k
            }
398
399
            // bits in both nodes
400
681k
            for (auto bit : set_bits_range<bitmap_t>(old_bits & new_bits)) {
401
681k
                if ((old_nodemap & bit) && (new_nodemap & bit)) {
402
329k
                    auto old_offset = old_node->children_count(bit);
403
329k
                    auto new_offset = new_node->children_count(bit);
404
329k
                    auto old_child  = old_node->children()[old_offset];
405
329k
                    auto new_child  = new_node->children()[new_offset];
406
329k
                    diff<EqualValue>(old_child, new_child, depth + 1, differ);
407
352k
                } else if ((old_datamap & bit) && (new_nodemap & bit)) {
408
10.7k
                    diff_data_node<EqualValue>(
409
10.7k
                        old_node, new_node, bit, depth, differ);
410
341k
                } else if ((old_nodemap & bit) && (new_datamap & bit)) {
411
12.1k
                    diff_node_data<EqualValue>(
412
12.1k
                        old_node, new_node, bit, depth, differ);
413
329k
                } else if ((old_datamap & bit) && (new_datamap & bit)) {
414
329k
                    diff_data_data<EqualValue>(old_node, new_node, bit, differ);
415
329k
                }
416
681k
            }
417
241k
        } else {
418
3.18k
            diff_collisions<EqualValue>(old_node, new_node, differ);
419
3.18k
        }
420
245k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}> >(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const
Line
Count
Source
349
15.3k
    {
350
15.3k
        if (old_node == new_node)
351
1.08k
            return;
352
14.3k
        if (depth < max_depth<hash_t, B>) {
353
14.3k
            auto old_nodemap = old_node->nodemap();
354
14.3k
            auto new_nodemap = new_node->nodemap();
355
14.3k
            auto old_datamap = old_node->datamap();
356
14.3k
            auto new_datamap = new_node->datamap();
357
14.3k
            auto old_bits    = old_nodemap | old_datamap;
358
14.3k
            auto new_bits    = new_nodemap | new_datamap;
359
14.3k
            auto changes     = old_bits ^ new_bits;
360
361
            // added bits
362
14.3k
            for (auto bit : set_bits_range<bitmap_t>(new_bits & changes)) {
363
2.01k
                if (new_nodemap & bit) {
364
1.27k
                    auto offset = new_node->children_count(bit);
365
1.27k
                    auto child  = new_node->children()[offset];
366
1.27k
                    for_each_chunk_traversal(
367
1.27k
                        child,
368
1.27k
                        depth + 1,
369
1.27k
                        [&](auto const& begin, auto const& end) {
370
1.27k
                            for (auto it = begin; it != end; it++)
371
1.27k
                                differ.added(*it);
372
1.27k
                        });
373
1.27k
                } else if (new_datamap & bit) {
374
740
                    auto offset       = new_node->data_count(bit);
375
740
                    auto const& value = new_node->values()[offset];
376
740
                    differ.added(value);
377
740
                }
378
2.01k
            }
379
380
            // removed bits
381
14.3k
            for (auto bit : set_bits_range<bitmap_t>(old_bits & changes)) {
382
2.11k
                if (old_nodemap & bit) {
383
1.34k
                    auto offset = old_node->children_count(bit);
384
1.34k
                    auto child  = old_node->children()[offset];
385
1.34k
                    for_each_chunk_traversal(
386
1.34k
                        child,
387
1.34k
                        depth + 1,
388
1.34k
                        [&](auto const& begin, auto const& end) {
389
1.34k
                            for (auto it = begin; it != end; it++)
390
1.34k
                                differ.removed(*it);
391
1.34k
                        });
392
1.34k
                } else if (old_datamap & bit) {
393
771
                    auto offset       = old_node->data_count(bit);
394
771
                    auto const& value = old_node->values()[offset];
395
771
                    differ.removed(value);
396
771
                }
397
2.11k
            }
398
399
            // bits in both nodes
400
16.9k
            for (auto bit : set_bits_range<bitmap_t>(old_bits & new_bits)) {
401
16.9k
                if ((old_nodemap & bit) && (new_nodemap & bit)) {
402
13.1k
                    auto old_offset = old_node->children_count(bit);
403
13.1k
                    auto new_offset = new_node->children_count(bit);
404
13.1k
                    auto old_child  = old_node->children()[old_offset];
405
13.1k
                    auto new_child  = new_node->children()[new_offset];
406
13.1k
                    diff<EqualValue>(old_child, new_child, depth + 1, differ);
407
13.1k
                } else if ((old_datamap & bit) && (new_nodemap & bit)) {
408
1.43k
                    diff_data_node<EqualValue>(
409
1.43k
                        old_node, new_node, bit, depth, differ);
410
2.30k
                } else if ((old_nodemap & bit) && (new_datamap & bit)) {
411
1.37k
                    diff_node_data<EqualValue>(
412
1.37k
                        old_node, new_node, bit, depth, differ);
413
1.37k
                } else if ((old_datamap & bit) && (new_datamap & bit)) {
414
928
                    diff_data_data<EqualValue>(old_node, new_node, bit, differ);
415
928
                }
416
16.9k
            }
417
14.3k
        } else {
418
0
            diff_collisions<EqualValue>(old_node, new_node, differ);
419
0
        }
420
14.3k
    }
set-st.cpp:void immer::detail::hamts::champ<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u>::diff<std::__1::equal_to<unsigned long>, immer::differ<LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#1}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&, auto:2&&)#1}>&>(immer::detail::hamts::node<unsigned long, colliding_hash_t, std::__1::equal_to<void>, immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy, immer::no_lock_policy, immer::no_transience_policy, false, true>, 5u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const
Line
Count
Source
349
329k
    {
350
329k
        if (old_node == new_node)
351
99.0k
            return;
352
230k
        if (depth < max_depth<hash_t, B>) {
353
227k
            auto old_nodemap = old_node->nodemap();
354
227k
            auto new_nodemap = new_node->nodemap();
355
227k
            auto old_datamap = old_node->datamap();
356
227k
            auto new_datamap = new_node->datamap();
357
227k
            auto old_bits    = old_nodemap | old_datamap;
358
227k
            auto new_bits    = new_nodemap | new_datamap;
359
227k
            auto changes     = old_bits ^ new_bits;
360
361
            // added bits
362
227k
            for (auto bit : set_bits_range<bitmap_t>(new_bits & changes)) {
363
23.6k
                if (new_nodemap & bit) {
364
3.64k
                    auto offset = new_node->children_count(bit);
365
3.64k
                    auto child  = new_node->children()[offset];
366
3.64k
                    for_each_chunk_traversal(
367
3.64k
                        child,
368
3.64k
                        depth + 1,
369
3.64k
                        [&](auto const& begin, auto const& end) {
370
3.64k
                            for (auto it = begin; it != end; it++)
371
3.64k
                                differ.added(*it);
372
3.64k
                        });
373
20.0k
                } else if (new_datamap & bit) {
374
20.0k
                    auto offset       = new_node->data_count(bit);
375
20.0k
                    auto const& value = new_node->values()[offset];
376
20.0k
                    differ.added(value);
377
20.0k
                }
378
23.6k
            }
379
380
            // removed bits
381
227k
            for (auto bit : set_bits_range<bitmap_t>(old_bits & changes)) {
382
26.3k
                if (old_nodemap & bit) {
383
3.44k
                    auto offset = old_node->children_count(bit);
384
3.44k
                    auto child  = old_node->children()[offset];
385
3.44k
                    for_each_chunk_traversal(
386
3.44k
                        child,
387
3.44k
                        depth + 1,
388
3.44k
                        [&](auto const& begin, auto const& end) {
389
3.44k
                            for (auto it = begin; it != end; it++)
390
3.44k
                                differ.removed(*it);
391
3.44k
                        });
392
22.9k
                } else if (old_datamap & bit) {
393
22.9k
                    auto offset       = old_node->data_count(bit);
394
22.9k
                    auto const& value = old_node->values()[offset];
395
22.9k
                    differ.removed(value);
396
22.9k
                }
397
26.3k
            }
398
399
            // bits in both nodes
400
664k
            for (auto bit : set_bits_range<bitmap_t>(old_bits & new_bits)) {
401
664k
                if ((old_nodemap & bit) && (new_nodemap & bit)) {
402
316k
                    auto old_offset = old_node->children_count(bit);
403
316k
                    auto new_offset = new_node->children_count(bit);
404
316k
                    auto old_child  = old_node->children()[old_offset];
405
316k
                    auto new_child  = new_node->children()[new_offset];
406
316k
                    diff<EqualValue>(old_child, new_child, depth + 1, differ);
407
348k
                } else if ((old_datamap & bit) && (new_nodemap & bit)) {
408
9.30k
                    diff_data_node<EqualValue>(
409
9.30k
                        old_node, new_node, bit, depth, differ);
410
339k
                } else if ((old_nodemap & bit) && (new_datamap & bit)) {
411
10.7k
                    diff_node_data<EqualValue>(
412
10.7k
                        old_node, new_node, bit, depth, differ);
413
328k
                } else if ((old_datamap & bit) && (new_datamap & bit)) {
414
328k
                    diff_data_data<EqualValue>(old_node, new_node, bit, differ);
415
328k
                }
416
664k
            }
417
227k
        } else {
418
3.18k
            diff_collisions<EqualValue>(old_node, new_node, differ);
419
3.18k
        }
420
230k
    }
421
422
    template <typename EqualValue, typename Differ>
423
    void diff_data_node(const node_t* old_node,
424
                        const node_t* new_node,
425
                        bitmap_t bit,
426
                        count_t depth,
427
                        Differ&& differ) const
428
10.7k
    {
429
10.7k
        auto old_offset       = old_node->data_count(bit);
430
10.7k
        auto const& old_value = old_node->values()[old_offset];
431
10.7k
        auto new_offset       = new_node->children_count(bit);
432
10.7k
        auto new_child        = new_node->children()[new_offset];
433
434
10.7k
        bool found = false;
435
10.7k
        for_each_chunk_traversal(
436
14.0k
            new_child, depth + 1, [&](auto const& begin, auto const& end) {
437
43.8k
                for (auto it = begin; it != end; it++) {
438
29.8k
                    if (Equal{}(old_value, *it)) {
439
9.96k
                        if (!EqualValue{}(old_value, *it))
440
0
                            differ.changed(old_value, *it);
441
9.96k
                        found = true;
442
19.8k
                    } else {
443
19.8k
                        differ.added(*it);
444
19.8k
                    }
445
29.8k
                }
446
14.0k
            });
447
10.7k
        if (!found)
448
775
            differ.removed(old_value);
449
10.7k
    }
450
451
    template <typename EqualValue, typename Differ>
452
    void diff_node_data(const node_t* old_node,
453
                        const node_t* const new_node,
454
                        bitmap_t bit,
455
                        count_t depth,
456
                        Differ&& differ) const
457
12.1k
    {
458
12.1k
        auto old_offset       = old_node->children_count(bit);
459
12.1k
        auto old_child        = old_node->children()[old_offset];
460
12.1k
        auto new_offset       = new_node->data_count(bit);
461
12.1k
        auto const& new_value = new_node->values()[new_offset];
462
463
12.1k
        bool found = false;
464
12.1k
        for_each_chunk_traversal(
465
16.0k
            old_child, depth + 1, [&](auto const& begin, auto const& end) {
466
49.8k
                for (auto it = begin; it != end; it++) {
467
33.8k
                    if (Equal{}(*it, new_value)) {
468
11.4k
                        if (!EqualValue{}(*it, new_value))
469
0
                            differ.changed(*it, new_value);
470
11.4k
                        found = true;
471
22.3k
                    } else {
472
22.3k
                        differ.removed(*it);
473
22.3k
                    }
474
33.8k
                }
475
16.0k
            });
476
12.1k
        if (!found)
477
700
            differ.added(new_value);
478
12.1k
    }
479
480
    template <typename EqualValue, typename Differ>
481
    void diff_data_data(const node_t* old_node,
482
                        const node_t* new_node,
483
                        bitmap_t bit,
484
                        Differ&& differ) const
485
329k
    {
486
329k
        auto old_offset       = old_node->data_count(bit);
487
329k
        auto new_offset       = new_node->data_count(bit);
488
329k
        auto const& old_value = old_node->values()[old_offset];
489
329k
        auto const& new_value = new_node->values()[new_offset];
490
329k
        if (!Equal{}(old_value, new_value)) {
491
1.34k
            differ.removed(old_value);
492
1.34k
            differ.added(new_value);
493
327k
        } else {
494
327k
            if (!EqualValue{}(old_value, new_value))
495
0
                differ.changed(old_value, new_value);
496
327k
        }
497
329k
    }
498
499
    template <typename EqualValue, typename Differ>
500
    void diff_collisions(const node_t* old_node,
501
                         const node_t* new_node,
502
                         Differ&& differ) const
503
3.18k
    {
504
3.18k
        auto old_begin = old_node->collisions();
505
3.18k
        auto old_end   = old_node->collisions() + old_node->collision_count();
506
3.18k
        auto new_begin = new_node->collisions();
507
3.18k
        auto new_end   = new_node->collisions() + new_node->collision_count();
508
        // search changes and removals
509
11.2k
        for (auto old_it = old_begin; old_it != old_end; old_it++) {
510
8.06k
            bool found = false;
511
16.7k
            for (auto new_it = new_begin; new_it != new_end; new_it++) {
512
16.0k
                if (Equal{}(*old_it, *new_it)) {
513
7.34k
                    if (!EqualValue{}(*old_it, *new_it))
514
0
                        differ.changed(*old_it, *new_it);
515
7.34k
                    found = true;
516
7.34k
                    break;
517
7.34k
                }
518
16.0k
            }
519
8.06k
            if (!found)
520
719
                differ.removed(*old_it);
521
8.06k
        }
522
        // search new entries
523
11.1k
        for (auto new_it = new_begin; new_it != new_end; new_it++) {
524
7.93k
            bool found = false;
525
16.6k
            for (auto old_it = old_begin; old_it != old_end; old_it++) {
526
16.0k
                if (Equal{}(*old_it, *new_it)) {
527
7.34k
                    found = true;
528
7.34k
                    break;
529
7.34k
                }
530
16.0k
            }
531
7.93k
            if (!found)
532
594
                differ.added(*new_it);
533
7.93k
        }
534
3.18k
    }
535
536
    template <typename Project, typename Default, typename K>
537
    decltype(auto) get(const K& k) const
538
275k
    {
539
275k
        auto node = root;
540
275k
        auto hash = Hash{}(k);
541
1.40M
        for (auto i = count_t{}; i < max_depth<hash_t, B>; ++i) {
542
1.39M
            auto bit = bitmap_t{1u} << (hash & mask<hash_t, B>);
543
1.39M
            if (node->nodemap() & bit) {
544
1.12M
                auto offset = node->children_count(bit);
545
1.12M
                node        = node->children()[offset];
546
1.12M
                hash        = hash >> B;
547
1.12M
            } else if (node->datamap() & bit) {
548
172k
                auto offset = node->data_count(bit);
549
172k
                auto val    = node->values() + offset;
550
172k
                if (Equal{}(*val, k))
551
126k
                    return Project{}(*val);
552
45.3k
                else
553
45.3k
                    return Default{}();
554
172k
            } else {
555
90.8k
                return Default{}();
556
90.8k
            }
557
1.39M
        }
558
12.0k
        auto fst = node->collisions();
559
12.0k
        auto lst = fst + node->collision_count();
560
24.8k
        for (; fst != lst; ++fst)
561
23.5k
            if (Equal{}(*fst, k))
562
10.6k
                return Project{}(*fst);
563
1.34k
        return Default{}();
564
12.0k
    }
565
566
    struct add_result
567
    {
568
        node_t* node;
569
        bool added;
570
    };
571
572
    add_result do_add(node_t* node, T v, hash_t hash, shift_t shift) const
573
2.16M
    {
574
2.16M
        assert(node);
575
2.16M
        if (shift == max_shift<hash_t, B>) {
576
12.7k
            auto fst = node->collisions();
577
12.7k
            auto lst = fst + node->collision_count();
578
25.6k
            for (; fst != lst; ++fst)
579
24.4k
                if (Equal{}(*fst, v))
580
11.4k
                    return {
581
11.4k
                        node_t::copy_collision_replace(node, fst, std::move(v)),
582
11.4k
                        false};
583
1.26k
            return {node_t::copy_collision_insert(node, std::move(v)), true};
584
2.15M
        } else {
585
2.15M
            auto idx = (hash & (mask<hash_t, B> << shift)) >> shift;
586
2.15M
            auto bit = bitmap_t{1u} << idx;
587
2.15M
            if (node->nodemap() & bit) {
588
1.79M
                auto offset = node->children_count(bit);
589
1.79M
                assert(node->children()[offset]);
590
1.79M
                auto result = do_add(
591
1.79M
                    node->children()[offset], std::move(v), hash, shift + B);
592
1.79M
                IMMER_TRY {
593
1.79M
                    result.node =
594
1.79M
                        node_t::copy_inner_replace(node, offset, result.node);
595
1.79M
                    return result;
596
1.79M
                }
597
1.79M
                IMMER_CATCH (...) {
598
0
                    node_t::delete_deep_shift(result.node, shift + B);
599
0
                    IMMER_RETHROW;
600
0
                }
601
1.79M
            } else if (node->datamap() & bit) {
602
301k
                auto offset = node->data_count(bit);
603
301k
                auto val    = node->values() + offset;
604
301k
                if (Equal{}(*val, v))
605
266k
                    return {node_t::copy_inner_replace_value(
606
266k
                                node, offset, std::move(v)),
607
266k
                            false};
608
34.9k
                else {
609
34.9k
                    auto child = node_t::make_merged(
610
34.9k
                        shift + B, std::move(v), hash, *val, Hash{}(*val));
611
34.9k
                    IMMER_TRY {
612
34.9k
                        return {node_t::copy_inner_replace_merged(
613
34.9k
                                    node, bit, offset, child),
614
34.9k
                                true};
615
34.9k
                    }
616
34.9k
                    IMMER_CATCH (...) {
617
0
                        node_t::delete_deep_shift(child, shift + B);
618
0
                        IMMER_RETHROW;
619
0
                    }
620
34.9k
                }
621
301k
            } else {
622
57.6k
                return {
623
57.6k
                    node_t::copy_inner_insert_value(node, bit, std::move(v)),
624
57.6k
                    true};
625
57.6k
            }
626
2.15M
        }
627
2.16M
    }
628
629
    champ add(T v) const
630
366k
    {
631
366k
        auto hash     = Hash{}(v);
632
366k
        auto res      = do_add(root, std::move(v), hash, 0);
633
366k
        auto new_size = size + (res.added ? 1 : 0);
634
366k
        return {res.node, new_size};
635
366k
    }
636
637
    struct add_mut_result
638
    {
639
        node_t* node;
640
        bool added;
641
        bool mutated;
642
    };
643
644
    add_mut_result
645
    do_add_mut(edit_t e, node_t* node, T v, hash_t hash, shift_t shift) const
646
95.9k
    {
647
95.9k
        assert(node);
648
95.9k
        if (shift == max_shift<hash_t, B>) {
649
1.47k
            auto fst = node->collisions();
650
1.47k
            auto lst = fst + node->collision_count();
651
3.62k
            for (; fst != lst; ++fst)
652
3.12k
                if (Equal{}(*fst, v)) {
653
980
                    if (node->can_mutate(e)) {
654
762
                        *fst = std::move(v);
655
762
                        return {node, false, true};
656
762
                    } else {
657
218
                        auto r = node_t::copy_collision_replace(
658
218
                            node, fst, std::move(v));
659
218
                        return {node_t::owned(r, e), false, false};
660
218
                    }
661
980
                }
662
499
            auto mutate = node->can_mutate(e);
663
499
            auto r = mutate ? node_t::move_collision_insert(node, std::move(v))
664
499
                            : node_t::copy_collision_insert(node, std::move(v));
665
499
            return {node_t::owned(r, e), true, mutate};
666
94.5k
        } else {
667
94.5k
            auto idx = (hash & (mask<hash_t, B> << shift)) >> shift;
668
94.5k
            auto bit = bitmap_t{1u} << idx;
669
94.5k
            if (node->nodemap() & bit) {
670
78.6k
                auto offset = node->children_count(bit);
671
78.6k
                auto child  = node->children()[offset];
672
78.6k
                if (node->can_mutate(e)) {
673
73.2k
                    auto result =
674
73.2k
                        do_add_mut(e, child, std::move(v), hash, shift + B);
675
73.2k
                    node->children()[offset] = result.node;
676
73.2k
                    if (!result.mutated && child->dec())
677
0
                        node_t::delete_deep_shift(child, shift + B);
678
73.2k
                    return {node, result.added, true};
679
73.2k
                } else {
680
5.36k
                    assert(node->children()[offset]);
681
5.36k
                    auto result = do_add(child, std::move(v), hash, shift + B);
682
5.36k
                    IMMER_TRY {
683
5.36k
                        result.node = node_t::copy_inner_replace(
684
5.36k
                            node, offset, result.node);
685
5.36k
                        node_t::owned(result.node, e);
686
5.36k
                        return {result.node, result.added, false};
687
5.36k
                    }
688
5.36k
                    IMMER_CATCH (...) {
689
0
                        node_t::delete_deep_shift(result.node, shift + B);
690
0
                        IMMER_RETHROW;
691
0
                    }
692
5.36k
                }
693
78.6k
            } else if (node->datamap() & bit) {
694
9.70k
                auto offset = node->data_count(bit);
695
9.70k
                auto val    = node->values() + offset;
696
9.70k
                if (Equal{}(*val, v)) {
697
4.87k
                    if (node->can_mutate(e)) {
698
4.06k
                        auto vals    = node->ensure_mutable_values(e);
699
4.06k
                        vals[offset] = std::move(v);
700
4.06k
                        return {node, false, true};
701
4.06k
                    } else {
702
805
                        auto r = node_t::copy_inner_replace_value(
703
805
                            node, offset, std::move(v));
704
805
                        return {node_t::owned_values(r, e), false, false};
705
805
                    }
706
4.87k
                } else {
707
4.83k
                    auto mutate        = node->can_mutate(e);
708
4.83k
                    auto mutate_values = mutate && node->can_mutate_values(e);
709
4.83k
                    auto hash2         = Hash{}(*val);
710
4.83k
                    auto child         = node_t::make_merged_e(
711
4.83k
                        e,
712
4.83k
                        shift + B,
713
4.83k
                        std::move(v),
714
4.83k
                        hash,
715
4.83k
                        mutate_values ? std::move(*val) : *val,
716
4.83k
                        hash2);
717
4.83k
                    IMMER_TRY {
718
4.83k
                        auto r = mutate ? node_t::move_inner_replace_merged(
719
4.10k
                                              e, node, bit, offset, child)
720
4.83k
                                        : node_t::copy_inner_replace_merged(
721
726
                                              node, bit, offset, child);
722
4.83k
                        return {node_t::owned_values_safe(r, e), true, mutate};
723
4.83k
                    }
724
4.83k
                    IMMER_CATCH (...) {
725
0
                        node_t::delete_deep_shift(child, shift + B);
726
0
                        IMMER_RETHROW;
727
0
                    }
728
4.83k
                }
729
9.70k
            } else {
730
6.19k
                auto mutate = node->can_mutate(e);
731
6.19k
                auto r      = mutate ? node_t::move_inner_insert_value(
732
4.52k
                                      e, node, bit, std::move(v))
733
6.19k
                                     : node_t::copy_inner_insert_value(
734
1.66k
                                      node, bit, std::move(v));
735
6.19k
                return {node_t::owned_values(r, e), true, mutate};
736
6.19k
            }
737
94.5k
        }
738
95.9k
    }
739
740
    void add_mut(edit_t e, T v)
741
22.7k
    {
742
22.7k
        auto hash = Hash{}(v);
743
22.7k
        auto res  = do_add_mut(e, root, std::move(v), hash, 0);
744
22.7k
        if (!res.mutated && root->dec())
745
0
            node_t::delete_deep(root, 0);
746
22.7k
        root = res.node;
747
22.7k
        size += res.added ? 1 : 0;
748
22.7k
    }
749
750
    using update_result = add_result;
751
752
    template <typename Project,
753
              typename Default,
754
              typename Combine,
755
              typename K,
756
              typename Fn>
757
    update_result
758
    do_update(node_t* node, K&& k, Fn&& fn, hash_t hash, shift_t shift) const
759
    {
760
        if (shift == max_shift<hash_t, B>) {
761
            auto fst = node->collisions();
762
            auto lst = fst + node->collision_count();
763
            for (; fst != lst; ++fst)
764
                if (Equal{}(*fst, k))
765
                    return {node_t::copy_collision_replace(
766
                                node,
767
                                fst,
768
                                Combine{}(std::forward<K>(k),
769
                                          std::forward<Fn>(fn)(Project{}(
770
                                              detail::as_const(*fst))))),
771
                            false};
772
            return {node_t::copy_collision_insert(
773
                        node,
774
                        Combine{}(std::forward<K>(k),
775
                                  std::forward<Fn>(fn)(Default{}()))),
776
                    true};
777
        } else {
778
            auto idx = (hash & (mask<hash_t, B> << shift)) >> shift;
779
            auto bit = bitmap_t{1u} << idx;
780
            if (node->nodemap() & bit) {
781
                auto offset = node->children_count(bit);
782
                auto result = do_update<Project, Default, Combine>(
783
                    node->children()[offset],
784
                    k,
785
                    std::forward<Fn>(fn),
786
                    hash,
787
                    shift + B);
788
                IMMER_TRY {
789
                    result.node =
790
                        node_t::copy_inner_replace(node, offset, result.node);
791
                    return result;
792
                }
793
                IMMER_CATCH (...) {
794
                    node_t::delete_deep_shift(result.node, shift + B);
795
                    IMMER_RETHROW;
796
                }
797
            } else if (node->datamap() & bit) {
798
                auto offset = node->data_count(bit);
799
                auto val    = node->values() + offset;
800
                if (Equal{}(*val, k))
801
                    return {node_t::copy_inner_replace_value(
802
                                node,
803
                                offset,
804
                                Combine{}(std::forward<K>(k),
805
                                          std::forward<Fn>(fn)(Project{}(
806
                                              detail::as_const(*val))))),
807
                            false};
808
                else {
809
                    auto child = node_t::make_merged(
810
                        shift + B,
811
                        Combine{}(std::forward<K>(k),
812
                                  std::forward<Fn>(fn)(Default{}())),
813
                        hash,
814
                        *val,
815
                        Hash{}(*val));
816
                    IMMER_TRY {
817
                        return {node_t::copy_inner_replace_merged(
818
                                    node, bit, offset, child),
819
                                true};
820
                    }
821
                    IMMER_CATCH (...) {
822
                        node_t::delete_deep_shift(child, shift + B);
823
                        IMMER_RETHROW;
824
                    }
825
                }
826
            } else {
827
                return {node_t::copy_inner_insert_value(
828
                            node,
829
                            bit,
830
                            Combine{}(std::forward<K>(k),
831
                                      std::forward<Fn>(fn)(Default{}()))),
832
                        true};
833
            }
834
        }
835
    }
836
837
    template <typename Project,
838
              typename Default,
839
              typename Combine,
840
              typename K,
841
              typename Fn>
842
    champ update(const K& k, Fn&& fn) const
843
    {
844
        auto hash = Hash{}(k);
845
        auto res  = do_update<Project, Default, Combine>(
846
            root, k, std::forward<Fn>(fn), hash, 0);
847
        auto new_size = size + (res.added ? 1 : 0);
848
        return {res.node, new_size};
849
    }
850
851
    template <typename Project, typename Combine, typename K, typename Fn>
852
    node_t* do_update_if_exists(
853
        node_t* node, K&& k, Fn&& fn, hash_t hash, shift_t shift) const
854
    {
855
        if (shift == max_shift<hash_t, B>) {
856
            auto fst = node->collisions();
857
            auto lst = fst + node->collision_count();
858
            for (; fst != lst; ++fst)
859
                if (Equal{}(*fst, k))
860
                    return node_t::copy_collision_replace(
861
                        node,
862
                        fst,
863
                        Combine{}(std::forward<K>(k),
864
                                  std::forward<Fn>(fn)(
865
                                      Project{}(detail::as_const(*fst)))));
866
            return nullptr;
867
        } else {
868
            auto idx = (hash & (mask<hash_t, B> << shift)) >> shift;
869
            auto bit = bitmap_t{1u} << idx;
870
            if (node->nodemap() & bit) {
871
                auto offset = node->children_count(bit);
872
                auto result = do_update_if_exists<Project, Combine>(
873
                    node->children()[offset],
874
                    k,
875
                    std::forward<Fn>(fn),
876
                    hash,
877
                    shift + B);
878
                IMMER_TRY {
879
                    return result ? node_t::copy_inner_replace(
880
                                        node, offset, result)
881
                                  : nullptr;
882
                }
883
                IMMER_CATCH (...) {
884
                    node_t::delete_deep_shift(result, shift + B);
885
                    IMMER_RETHROW;
886
                }
887
            } else if (node->datamap() & bit) {
888
                auto offset = node->data_count(bit);
889
                auto val    = node->values() + offset;
890
                if (Equal{}(*val, k))
891
                    return node_t::copy_inner_replace_value(
892
                        node,
893
                        offset,
894
                        Combine{}(std::forward<K>(k),
895
                                  std::forward<Fn>(fn)(
896
                                      Project{}(detail::as_const(*val)))));
897
                else {
898
                    return nullptr;
899
                }
900
            } else {
901
                return nullptr;
902
            }
903
        }
904
    }
905
906
    template <typename Project, typename Combine, typename K, typename Fn>
907
    champ update_if_exists(const K& k, Fn&& fn) const
908
    {
909
        auto hash = Hash{}(k);
910
        auto res  = do_update_if_exists<Project, Combine>(
911
            root, k, std::forward<Fn>(fn), hash, 0);
912
        if (res) {
913
            return {res, size};
914
        } else {
915
            return {root->inc(), size};
916
        };
917
    }
918
919
    using update_mut_result = add_mut_result;
920
921
    template <typename Project,
922
              typename Default,
923
              typename Combine,
924
              typename K,
925
              typename Fn>
926
    update_mut_result do_update_mut(edit_t e,
927
                                    node_t* node,
928
                                    K&& k,
929
                                    Fn&& fn,
930
                                    hash_t hash,
931
                                    shift_t shift) const
932
    {
933
        if (shift == max_shift<hash_t, B>) {
934
            auto fst = node->collisions();
935
            auto lst = fst + node->collision_count();
936
            for (; fst != lst; ++fst)
937
                if (Equal{}(*fst, k)) {
938
                    if (node->can_mutate(e)) {
939
                        *fst = Combine{}(
940
                            std::forward<K>(k),
941
                            std::forward<Fn>(fn)(Project{}(std::move(*fst))));
942
                        return {node, false, true};
943
                    } else {
944
                        auto r = node_t::copy_collision_replace(
945
                            node,
946
                            fst,
947
                            Combine{}(std::forward<K>(k),
948
                                      std::forward<Fn>(fn)(
949
                                          Project{}(detail::as_const(*fst)))));
950
                        return {node_t::owned(r, e), false, false};
951
                    }
952
                }
953
            auto v      = Combine{}(std::forward<K>(k),
954
                               std::forward<Fn>(fn)(Default{}()));
955
            auto mutate = node->can_mutate(e);
956
            auto r = mutate ? node_t::move_collision_insert(node, std::move(v))
957
                            : node_t::copy_collision_insert(node, std::move(v));
958
            return {node_t::owned(r, e), true, mutate};
959
        } else {
960
            auto idx = (hash & (mask<hash_t, B> << shift)) >> shift;
961
            auto bit = bitmap_t{1u} << idx;
962
            if (node->nodemap() & bit) {
963
                auto offset = node->children_count(bit);
964
                auto child  = node->children()[offset];
965
                if (node->can_mutate(e)) {
966
                    auto result = do_update_mut<Project, Default, Combine>(
967
                        e, child, k, std::forward<Fn>(fn), hash, shift + B);
968
                    node->children()[offset] = result.node;
969
                    if (!result.mutated && child->dec())
970
                        node_t::delete_deep_shift(child, shift + B);
971
                    return {node, result.added, true};
972
                } else {
973
                    auto result = do_update<Project, Default, Combine>(
974
                        child, k, std::forward<Fn>(fn), hash, shift + B);
975
                    IMMER_TRY {
976
                        result.node = node_t::copy_inner_replace(
977
                            node, offset, result.node);
978
                        node_t::owned(result.node, e);
979
                        return {result.node, result.added, false};
980
                    }
981
                    IMMER_CATCH (...) {
982
                        node_t::delete_deep_shift(result.node, shift + B);
983
                        IMMER_RETHROW;
984
                    }
985
                }
986
            } else if (node->datamap() & bit) {
987
                auto offset = node->data_count(bit);
988
                auto val    = node->values() + offset;
989
                if (Equal{}(*val, k)) {
990
                    if (node->can_mutate(e)) {
991
                        auto vals    = node->ensure_mutable_values(e);
992
                        vals[offset] = Combine{}(std::forward<K>(k),
993
                                                 std::forward<Fn>(fn)(Project{}(
994
                                                     std::move(vals[offset]))));
995
                        return {node, false, true};
996
                    } else {
997
                        auto r = node_t::copy_inner_replace_value(
998
                            node,
999
                            offset,
1000
                            Combine{}(std::forward<K>(k),
1001
                                      std::forward<Fn>(fn)(
1002
                                          Project{}(detail::as_const(*val)))));
1003
                        return {node_t::owned_values(r, e), false, false};
1004
                    }
1005
                } else {
1006
                    auto mutate        = node->can_mutate(e);
1007
                    auto mutate_values = mutate && node->can_mutate_values(e);
1008
                    auto hash2         = Hash{}(*val);
1009
                    auto child         = node_t::make_merged_e(
1010
                        e,
1011
                        shift + B,
1012
                        Combine{}(std::forward<K>(k),
1013
                                  std::forward<Fn>(fn)(Default{}())),
1014
                        hash,
1015
                        mutate_values ? std::move(*val) : *val,
1016
                        hash2);
1017
                    IMMER_TRY {
1018
                        auto r = mutate ? node_t::move_inner_replace_merged(
1019
                                              e, node, bit, offset, child)
1020
                                        : node_t::copy_inner_replace_merged(
1021
                                              node, bit, offset, child);
1022
                        return {node_t::owned_values_safe(r, e), true, mutate};
1023
                    }
1024
                    IMMER_CATCH (...) {
1025
                        node_t::delete_deep_shift(child, shift + B);
1026
                        IMMER_RETHROW;
1027
                    }
1028
                }
1029
            } else {
1030
                auto mutate = node->can_mutate(e);
1031
                auto v      = Combine{}(std::forward<K>(k),
1032
                                   std::forward<Fn>(fn)(Default{}()));
1033
                auto r      = mutate ? node_t::move_inner_insert_value(
1034
                                      e, node, bit, std::move(v))
1035
                                     : node_t::copy_inner_insert_value(
1036
                                      node, bit, std::move(v));
1037
                return {node_t::owned_values(r, e), true, mutate};
1038
            }
1039
        }
1040
    }
1041
1042
    template <typename Project,
1043
              typename Default,
1044
              typename Combine,
1045
              typename K,
1046
              typename Fn>
1047
    void update_mut(edit_t e, const K& k, Fn&& fn)
1048
    {
1049
        auto hash = Hash{}(k);
1050
        auto res  = do_update_mut<Project, Default, Combine>(
1051
            e, root, k, std::forward<Fn>(fn), hash, 0);
1052
        if (!res.mutated && root->dec())
1053
            node_t::delete_deep(root, 0);
1054
        root = res.node;
1055
        size += res.added ? 1 : 0;
1056
    }
1057
1058
    struct update_if_exists_mut_result
1059
    {
1060
        node_t* node;
1061
        bool mutated;
1062
    };
1063
1064
    template <typename Project, typename Combine, typename K, typename Fn>
1065
    update_if_exists_mut_result do_update_if_exists_mut(edit_t e,
1066
                                                        node_t* node,
1067
                                                        K&& k,
1068
                                                        Fn&& fn,
1069
                                                        hash_t hash,
1070
                                                        shift_t shift) const
1071
    {
1072
        if (shift == max_shift<hash_t, B>) {
1073
            auto fst = node->collisions();
1074
            auto lst = fst + node->collision_count();
1075
            for (; fst != lst; ++fst)
1076
                if (Equal{}(*fst, k)) {
1077
                    if (node->can_mutate(e)) {
1078
                        *fst = Combine{}(
1079
                            std::forward<K>(k),
1080
                            std::forward<Fn>(fn)(Project{}(std::move(*fst))));
1081
                        return {node, true};
1082
                    } else {
1083
                        auto r = node_t::copy_collision_replace(
1084
                            node,
1085
                            fst,
1086
                            Combine{}(std::forward<K>(k),
1087
                                      std::forward<Fn>(fn)(
1088
                                          Project{}(detail::as_const(*fst)))));
1089
                        return {node_t::owned(r, e), false};
1090
                    }
1091
                }
1092
            return {nullptr, false};
1093
        } else {
1094
            auto idx = (hash & (mask<hash_t, B> << shift)) >> shift;
1095
            auto bit = bitmap_t{1u} << idx;
1096
            if (node->nodemap() & bit) {
1097
                auto offset = node->children_count(bit);
1098
                auto child  = node->children()[offset];
1099
                if (node->can_mutate(e)) {
1100
                    auto result = do_update_if_exists_mut<Project, Combine>(
1101
                        e, child, k, std::forward<Fn>(fn), hash, shift + B);
1102
                    if (result.node) {
1103
                        node->children()[offset] = result.node;
1104
                        if (!result.mutated && child->dec())
1105
                            node_t::delete_deep_shift(child, shift + B);
1106
                        return {node, true};
1107
                    } else {
1108
                        return {nullptr, false};
1109
                    }
1110
                } else {
1111
                    auto result = do_update_if_exists<Project, Combine>(
1112
                        child, k, std::forward<Fn>(fn), hash, shift + B);
1113
                    IMMER_TRY {
1114
                        if (result) {
1115
                            result = node_t::copy_inner_replace(
1116
                                node, offset, result);
1117
                            node_t::owned(result, e);
1118
                            return {result, false};
1119
                        } else {
1120
                            return {nullptr, false};
1121
                        }
1122
                    }
1123
                    IMMER_CATCH (...) {
1124
                        node_t::delete_deep_shift(result, shift + B);
1125
                        IMMER_RETHROW;
1126
                    }
1127
                }
1128
            } else if (node->datamap() & bit) {
1129
                auto offset = node->data_count(bit);
1130
                auto val    = node->values() + offset;
1131
                if (Equal{}(*val, k)) {
1132
                    if (node->can_mutate(e)) {
1133
                        auto vals    = node->ensure_mutable_values(e);
1134
                        vals[offset] = Combine{}(std::forward<K>(k),
1135
                                                 std::forward<Fn>(fn)(Project{}(
1136
                                                     std::move(vals[offset]))));
1137
                        return {node, true};
1138
                    } else {
1139
                        auto r = node_t::copy_inner_replace_value(
1140
                            node,
1141
                            offset,
1142
                            Combine{}(std::forward<K>(k),
1143
                                      std::forward<Fn>(fn)(
1144
                                          Project{}(detail::as_const(*val)))));
1145
                        return {node_t::owned_values(r, e), false};
1146
                    }
1147
                } else {
1148
                    return {nullptr, false};
1149
                }
1150
            } else {
1151
                return {nullptr, false};
1152
            }
1153
        }
1154
    }
1155
1156
    template <typename Project, typename Combine, typename K, typename Fn>
1157
    void update_if_exists_mut(edit_t e, const K& k, Fn&& fn)
1158
    {
1159
        auto hash = Hash{}(k);
1160
        auto res  = do_update_if_exists_mut<Project, Combine>(
1161
            e, root, k, std::forward<Fn>(fn), hash, 0);
1162
        if (res.node) {
1163
            if (!res.mutated && root->dec())
1164
                node_t::delete_deep(root, 0);
1165
            root = res.node;
1166
        }
1167
    }
1168
1169
    // basically:
1170
    //      variant<monostate_t, T*, node_t*>
1171
    // boo bad we are not using... C++17 :'(
1172
    struct sub_result
1173
    {
1174
        enum kind_t
1175
        {
1176
            nothing,
1177
            singleton,
1178
            tree
1179
        };
1180
1181
        union data_t
1182
        {
1183
            T* singleton;
1184
            node_t* tree;
1185
        };
1186
1187
        kind_t kind;
1188
        data_t data;
1189
1190
        sub_result()
1191
41.1k
            : kind{nothing} {};
1192
        sub_result(T* x)
1193
3.11k
            : kind{singleton}
1194
3.11k
        {
1195
3.11k
            data.singleton = x;
1196
3.11k
        };
1197
        sub_result(node_t* x)
1198
27.8k
            : kind{tree}
1199
27.8k
        {
1200
27.8k
            data.tree = x;
1201
27.8k
        };
1202
    };
1203
1204
    template <typename K>
1205
    sub_result
1206
    do_sub(node_t* node, const K& k, hash_t hash, shift_t shift) const
1207
81.4k
    {
1208
81.4k
        if (shift == max_shift<hash_t, B>) {
1209
1.42k
            auto fst = node->collisions();
1210
1.42k
            auto lst = fst + node->collision_count();
1211
2.91k
            for (auto cur = fst; cur != lst; ++cur)
1212
2.61k
                if (Equal{}(*cur, k))
1213
1.12k
                    return node->collision_count() > 2
1214
1.12k
                               ? node_t::copy_collision_remove(node, cur)
1215
1.12k
                               : sub_result{fst + (cur == fst)};
1216
294
#if !defined(_MSC_VER)
1217
#if defined(__GNUC__) && !defined(__clang__)
1218
#pragma GCC diagnostic push
1219
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
1220
#endif
1221
294
#endif
1222
            // Apparently GCC is generating this warning sometimes when
1223
            // compiling the benchmarks. It makes however no sense at all.
1224
294
            return {};
1225
1.42k
#if !defined(_MSC_VER)
1226
#if defined(__GNUC__) && !defined(__clang__)
1227
#pragma GCC diagnostic pop
1228
#endif
1229
1.42k
#endif
1230
80.0k
        } else {
1231
80.0k
            auto idx = (hash & (mask<hash_t, B> << shift)) >> shift;
1232
80.0k
            auto bit = bitmap_t{1u} << idx;
1233
80.0k
            if (node->nodemap() & bit) {
1234
65.4k
                auto offset = node->children_count(bit);
1235
65.4k
                auto result =
1236
65.4k
                    do_sub(node->children()[offset], k, hash, shift + B);
1237
65.4k
                switch (result.kind) {
1238
31.0k
                case sub_result::nothing:
1239
31.0k
                    return {};
1240
11.7k
                case sub_result::singleton:
1241
11.7k
                    return node->datamap() == 0 &&
1242
10.2k
                                   node->children_count() == 1 && shift > 0
1243
11.7k
                               ? result
1244
11.7k
                               : node_t::copy_inner_replace_inline(
1245
2.36k
                                     node, bit, offset, *result.data.singleton);
1246
22.6k
                case sub_result::tree:
1247
22.6k
                    IMMER_TRY {
1248
22.6k
                        return node_t::copy_inner_replace(
1249
22.6k
                            node, offset, result.data.tree);
1250
22.6k
                    }
1251
22.6k
                    IMMER_CATCH (...) {
1252
0
                        node_t::delete_deep_shift(result.data.tree, shift + B);
1253
0
                        IMMER_RETHROW;
1254
0
                    }
1255
65.4k
                }
1256
65.4k
            } else if (node->datamap() & bit) {
1257
9.36k
                auto offset = node->data_count(bit);
1258
9.36k
                auto val    = node->values() + offset;
1259
9.36k
                if (Equal{}(*val, k)) {
1260
4.79k
                    auto nv = node->data_count();
1261
4.79k
                    if (node->nodemap() || nv > 2)
1262
1.55k
                        return node_t::copy_inner_remove_value(
1263
1.55k
                            node, bit, offset);
1264
3.24k
                    else if (nv == 2) {
1265
3.04k
                        return shift > 0 ? sub_result{node->values() + !offset}
1266
3.04k
                                         : node_t::make_inner_n(
1267
498
                                               0,
1268
498
                                               node->datamap() & ~bit,
1269
498
                                               node->values()[!offset]);
1270
3.04k
                    } else {
1271
204
                        assert(shift == 0);
1272
204
                        return empty();
1273
204
                    }
1274
4.79k
                }
1275
9.36k
            }
1276
9.81k
            return {};
1277
80.0k
        }
1278
81.4k
    }
1279
1280
    template <typename K>
1281
    champ sub(const K& k) const
1282
13.5k
    {
1283
13.5k
        auto hash = Hash{}(k);
1284
13.5k
        auto res  = do_sub(root, k, hash, 0);
1285
13.5k
        switch (res.kind) {
1286
8.74k
        case sub_result::nothing:
1287
8.74k
            return *this;
1288
4.76k
        case sub_result::tree:
1289
4.76k
            return {res.data.tree, size - 1};
1290
0
        default:
1291
0
            IMMER_UNREACHABLE;
1292
13.5k
        }
1293
13.5k
    }
1294
1295
    struct sub_result_mut
1296
    {
1297
        using kind_t = typename sub_result::kind_t;
1298
        using data_t = typename sub_result::data_t;
1299
1300
        kind_t kind;
1301
        data_t data;
1302
        bool owned;
1303
        bool mutated;
1304
1305
        sub_result_mut(sub_result a)
1306
2.52k
            : kind{a.kind}
1307
2.52k
            , data{a.data}
1308
2.52k
            , owned{false}
1309
2.52k
            , mutated{false}
1310
2.52k
        {
1311
2.52k
        }
1312
        sub_result_mut(sub_result a, bool m)
1313
            : kind{a.kind}
1314
            , data{a.data}
1315
            , owned{false}
1316
            , mutated{m}
1317
        {
1318
        }
1319
        sub_result_mut()
1320
19.6k
            : kind{kind_t::nothing}
1321
19.6k
            , mutated{false} {};
1322
        sub_result_mut(T* x, bool m)
1323
2.43k
            : kind{kind_t::singleton}
1324
2.43k
            , owned{m}
1325
2.43k
            , mutated{m}
1326
2.43k
        {
1327
2.43k
            data.singleton = x;
1328
2.43k
        };
1329
        sub_result_mut(T* x, bool o, bool m)
1330
8.76k
            : kind{kind_t::singleton}
1331
8.76k
            , owned{o}
1332
8.76k
            , mutated{m}
1333
8.76k
        {
1334
8.76k
            data.singleton = x;
1335
8.76k
        };
1336
        sub_result_mut(node_t* x, bool m)
1337
42.6k
            : kind{kind_t::tree}
1338
42.6k
            , owned{false}
1339
42.6k
            , mutated{m}
1340
42.6k
        {
1341
42.6k
            data.tree = x;
1342
42.6k
        };
1343
    };
1344
1345
    template <typename K>
1346
    sub_result_mut do_sub_mut(edit_t e,
1347
                              node_t* node,
1348
                              const K& k,
1349
                              hash_t hash,
1350
                              shift_t shift,
1351
                              void* store) const
1352
73.4k
    {
1353
73.4k
        auto mutate = node->can_mutate(e);
1354
73.4k
        if (shift == max_shift<hash_t, B>) {
1355
1.71k
            auto fst = node->collisions();
1356
1.71k
            auto lst = fst + node->collision_count();
1357
3.20k
            for (auto cur = fst; cur != lst; ++cur) {
1358
2.92k
                if (Equal{}(*cur, k)) {
1359
1.43k
                    if (node->collision_count() <= 2) {
1360
773
                        if (mutate) {
1361
577
                            auto r = new (store)
1362
577
                                T{std::move(node->collisions()[cur == fst])};
1363
577
                            node_t::delete_collision(node);
1364
577
                            return sub_result_mut{r, true};
1365
577
                        } else {
1366
196
                            return sub_result_mut{fst + (cur == fst), false};
1367
196
                        }
1368
773
                    } else {
1369
659
                        auto r = mutate
1370
659
                                     ? node_t::move_collision_remove(node, cur)
1371
659
                                     : node_t::copy_collision_remove(node, cur);
1372
659
                        return {node_t::owned(r, e), mutate};
1373
659
                    }
1374
1.43k
                }
1375
2.92k
            }
1376
280
            return {};
1377
71.7k
        } else {
1378
71.7k
            auto idx = (hash & (mask<hash_t, B> << shift)) >> shift;
1379
71.7k
            auto bit = bitmap_t{1u} << idx;
1380
71.7k
            if (node->nodemap() & bit) {
1381
63.9k
                auto offset   = node->children_count(bit);
1382
63.9k
                auto children = node->children();
1383
63.9k
                auto child    = children[offset];
1384
63.9k
                auto result =
1385
63.9k
                    mutate ? do_sub_mut(e, child, k, hash, shift + B, store)
1386
63.9k
                           : do_sub(child, k, hash, shift + B);
1387
63.9k
                switch (result.kind) {
1388
15.8k
                case sub_result::nothing:
1389
15.8k
                    return {};
1390
11.9k
                case sub_result::singleton:
1391
11.9k
                    if (node->datamap() == 0 && node->children_count() == 1 &&
1392
9.33k
                        shift > 0) {
1393
8.76k
                        if (mutate) {
1394
8.36k
                            node_t::delete_inner(node);
1395
8.36k
                            if (!result.mutated && child->dec())
1396
0
                                node_t::delete_deep_shift(child, shift + B);
1397
8.36k
                        }
1398
8.76k
                        return {result.data.singleton, result.owned, mutate};
1399
8.76k
                    } else {
1400
3.18k
                        auto r =
1401
3.18k
                            mutate ? node_t::move_inner_replace_inline(
1402
2.84k
                                         e,
1403
2.84k
                                         node,
1404
2.84k
                                         bit,
1405
2.84k
                                         offset,
1406
2.84k
                                         result.owned
1407
2.84k
                                             ? std::move(*result.data.singleton)
1408
2.84k
                                             : *result.data.singleton)
1409
3.18k
                                   : node_t::copy_inner_replace_inline(
1410
342
                                         node,
1411
342
                                         bit,
1412
342
                                         offset,
1413
342
                                         *result.data.singleton);
1414
3.18k
                        if (result.owned)
1415
1.61k
                            detail::destroy_at(result.data.singleton);
1416
3.18k
                        if (!result.mutated && mutate && child->dec())
1417
0
                            node_t::delete_deep_shift(child, shift + B);
1418
3.18k
                        return {node_t::owned_values(r, e), mutate};
1419
3.18k
                    }
1420
36.1k
                case sub_result::tree:
1421
36.1k
                    if (mutate) {
1422
35.7k
                        children[offset] = result.data.tree;
1423
35.7k
                        if (!result.mutated && child->dec())
1424
0
                            node_t::delete_deep_shift(child, shift + B);
1425
35.7k
                        return {node, true};
1426
35.7k
                    } else {
1427
412
                        IMMER_TRY {
1428
412
                            auto r = node_t::copy_inner_replace(
1429
412
                                node, offset, result.data.tree);
1430
412
                            return {node_t::owned(r, e), false};
1431
412
                        }
1432
412
                        IMMER_CATCH (...) {
1433
0
                            node_t::delete_deep_shift(result.data.tree,
1434
0
                                                      shift + B);
1435
0
                            IMMER_RETHROW;
1436
0
                        }
1437
412
                    }
1438
63.9k
                }
1439
63.9k
            } else if (node->datamap() & bit) {
1440
5.86k
                auto offset        = node->data_count(bit);
1441
5.86k
                auto val           = node->values() + offset;
1442
5.86k
                auto mutate_values = mutate && node->can_mutate_values(e);
1443
5.86k
                if (Equal{}(*val, k)) {
1444
4.32k
                    auto nv = node->data_count();
1445
4.32k
                    if (node->nodemap() || nv > 2) {
1446
1.73k
                        auto r = mutate ? node_t::move_inner_remove_value(
1447
1.53k
                                              e, node, bit, offset)
1448
1.73k
                                        : node_t::copy_inner_remove_value(
1449
202
                                              node, bit, offset);
1450
1.73k
                        return {node_t::owned_values_safe(r, e), mutate};
1451
2.59k
                    } else if (nv == 2) {
1452
2.05k
                        if (shift > 0) {
1453
1.66k
                            if (mutate_values) {
1454
1.03k
                                auto r = new (store)
1455
1.03k
                                    T{std::move(node->values()[!offset])};
1456
1.03k
                                node_t::delete_inner(node);
1457
1.03k
                                return {r, true};
1458
1.03k
                            } else {
1459
625
                                return {node->values() + !offset, false};
1460
625
                            }
1461
1.66k
                        } else {
1462
388
                            auto& v = node->values()[!offset];
1463
388
                            auto r  = node_t::make_inner_n(
1464
388
                                0,
1465
388
                                node->datamap() & ~bit,
1466
388
                                mutate_values ? std::move(v) : v);
1467
388
                            assert(!node->nodemap());
1468
388
                            if (mutate)
1469
194
                                node_t::delete_inner(node);
1470
388
                            return {node_t::owned_values(r, e), mutate};
1471
388
                        }
1472
2.05k
                    } else {
1473
539
                        assert(shift == 0);
1474
539
                        if (mutate)
1475
344
                            node_t::delete_inner(node);
1476
539
                        return {empty(), mutate};
1477
539
                    }
1478
4.32k
                }
1479
5.86k
            }
1480
3.44k
            return {};
1481
71.7k
        }
1482
73.4k
    }
1483
1484
    template <typename K>
1485
    void sub_mut(edit_t e, const K& k)
1486
12.0k
    {
1487
12.0k
        auto store = aligned_storage_for<T>{};
1488
12.0k
        auto hash  = Hash{}(k);
1489
12.0k
        auto res   = do_sub_mut(e, root, k, hash, 0, &store);
1490
12.0k
        switch (res.kind) {
1491
5.08k
        case sub_result::nothing:
1492
5.08k
            break;
1493
6.91k
        case sub_result::tree:
1494
6.91k
            if (!res.mutated && root->dec())
1495
0
                node_t::delete_deep(root, 0);
1496
6.91k
            root = res.data.tree;
1497
6.91k
            --size;
1498
6.91k
            break;
1499
0
        default:
1500
0
            IMMER_UNREACHABLE;
1501
12.0k
        }
1502
12.0k
    }
1503
1504
    template <typename Eq = Equal>
1505
    bool equals(const champ& other) const
1506
    {
1507
        return size == other.size && equals_tree<Eq>(root, other.root, 0);
1508
    }
1509
1510
    template <typename Eq>
1511
    static bool equals_tree(const node_t* a, const node_t* b, count_t depth)
1512
    {
1513
        if (a == b)
1514
            return true;
1515
        else if (depth == max_depth<hash_t, B>) {
1516
            auto nv = a->collision_count();
1517
            return nv == b->collision_count() &&
1518
                   equals_collisions<Eq>(a->collisions(), b->collisions(), nv);
1519
        } else {
1520
            if (a->nodemap() != b->nodemap() || a->datamap() != b->datamap())
1521
                return false;
1522
            auto n = a->children_count();
1523
            for (auto i = count_t{}; i < n; ++i)
1524
                if (!equals_tree<Eq>(
1525
                        a->children()[i], b->children()[i], depth + 1))
1526
                    return false;
1527
            auto nv = a->data_count();
1528
            return !nv || equals_values<Eq>(a->values(), b->values(), nv);
1529
        }
1530
    }
1531
1532
    template <typename Eq>
1533
    static bool equals_values(const T* a, const T* b, count_t n)
1534
    {
1535
        return std::equal(a, a + n, b, Eq{});
1536
    }
1537
1538
    template <typename Eq>
1539
    static bool equals_collisions(const T* a, const T* b, count_t n)
1540
    {
1541
        auto ae = a + n;
1542
        auto be = b + n;
1543
        for (; a != ae; ++a) {
1544
            for (auto fst = b; fst != be; ++fst)
1545
                if (Eq{}(*a, *fst))
1546
                    goto good;
1547
            return false;
1548
        good:
1549
            continue;
1550
        }
1551
        return true;
1552
    }
1553
};
1554
1555
} // namespace hamts
1556
} // namespace detail
1557
} // namespace immer