Coverage Report

Created: 2025-07-11 06:44

/src/immer/immer/detail/hamts/champ.hpp
Line
Count
Source (jump to first uncovered line)
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
340k
    {
143
340k
        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
340k
        return empty_->inc();
150
340k
    }
151
152
    champ(node_t* r, size_t sz = 0) noexcept
153
        : root{r}
154
        , size{sz}
155
679k
    {
156
679k
    }
157
158
    champ(const champ& other) noexcept
159
        : champ{other.root, other.size}
160
16.1k
    {
161
16.1k
        inc();
162
16.1k
    }
163
164
    champ(champ&& other) noexcept
165
        : champ{empty()}
166
330k
    {
167
330k
        swap(*this, other);
168
330k
    }
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
381k
    {
179
381k
        swap(*this, other);
180
381k
        return *this;
181
381k
    }
182
183
    friend void swap(champ& x, champ& y) noexcept
184
711k
    {
185
711k
        using std::swap;
186
711k
        swap(x.root, y.root);
187
711k
        swap(x.size, y.size);
188
711k
    }
189
190
679k
    ~champ() { dec(); }
191
192
16.1k
    void inc() const { root->inc(); }
193
194
    void dec() const
195
679k
    {
196
679k
        if (root->dec())
197
327k
            node_t::delete_deep(root, 0);
198
679k
    }
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
8.69M
    {
205
8.69M
        auto result = std::size_t{};
206
8.69M
        if (depth < max_depth<hash_t, B>) {
207
8.57M
            auto nodemap = node->nodemap();
208
8.57M
            if (nodemap) {
209
4.98M
                auto fst = node->children();
210
44.8M
                for (auto idx = std::size_t{}; idx < branches<B>; ++idx) {
211
39.8M
                    if (nodemap & (1 << idx)) {
212
8.55M
                        auto child = *fst++;
213
8.55M
                        result +=
214
8.55M
                            do_check_champ(child,
215
8.55M
                                           depth + 1,
216
8.55M
                                           path_hash | (idx << (B * depth)),
217
8.55M
                                           (hash_mask << B) | mask<hash_t, B>);
218
8.55M
                    }
219
39.8M
                }
220
4.98M
            }
221
8.57M
            auto datamap = node->datamap();
222
8.57M
            if (datamap) {
223
5.61M
                auto fst = node->values();
224
50.5M
                for (auto idx = std::size_t{}; idx < branches<B>; ++idx) {
225
44.9M
                    if (datamap & (1 << idx)) {
226
13.1M
                        auto hash  = Hash{}(*fst++);
227
13.1M
                        auto check = (hash & hash_mask) ==
228
13.1M
                                     (path_hash | (idx << (B * depth)));
229
                        // assert(check);
230
13.1M
                        result += !!check;
231
13.1M
                    }
232
44.9M
                }
233
5.61M
            }
234
8.57M
        } else {
235
117k
            auto fst = node->collisions();
236
117k
            auto lst = fst + node->collision_count();
237
427k
            for (; fst != lst; ++fst) {
238
309k
                auto hash  = Hash{}(*fst);
239
309k
                auto check = hash == path_hash;
240
                // assert(check);
241
309k
                result += !!check;
242
309k
            }
243
117k
        }
244
8.69M
        return result;
245
8.69M
    }
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
142k
    {
252
142k
        auto r = do_check_champ(root, 0, 0, mask<hash_t, B>);
253
        // assert(r == size);
254
142k
        return r == size;
255
142k
    }
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
187k
    {
321
187k
        if (depth < max_depth<hash_t, B>) {
322
182k
            auto datamap = node->datamap();
323
182k
            if (datamap)
324
97.1k
                fn(node->values(), node->values() + node->data_count());
325
182k
            auto nodemap = node->nodemap();
326
182k
            if (nodemap) {
327
92.4k
                auto fst = node->children();
328
92.4k
                auto lst = fst + node->children_count();
329
190k
                for (; fst != lst; ++fst)
330
98.4k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
92.4k
            }
332
182k
        } else {
333
5.19k
            fn(node->collisions(),
334
5.19k
               node->collisions() + node->collision_count());
335
5.19k
        }
336
187k
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::for_each_chunk_traversal<immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> 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
826
    {
321
826
        if (depth < max_depth<hash_t, B>) {
322
826
            auto datamap = node->datamap();
323
826
            if (datamap)
324
0
                fn(node->values(), node->values() + node->data_count());
325
826
            auto nodemap = node->nodemap();
326
826
            if (nodemap) {
327
826
                auto fst = node->children();
328
826
                auto lst = fst + node->children_count();
329
1.65k
                for (; fst != lst; ++fst)
330
826
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
826
            }
332
826
        } else {
333
0
            fn(node->collisions(),
334
0
               node->collisions() + node->collision_count());
335
0
        }
336
826
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::for_each_chunk_traversal<immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> 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
15.5k
    {
321
15.5k
        if (depth < max_depth<hash_t, B>) {
322
15.1k
            auto datamap = node->datamap();
323
15.1k
            if (datamap)
324
1.13k
                fn(node->values(), node->values() + node->data_count());
325
15.1k
            auto nodemap = node->nodemap();
326
15.1k
            if (nodemap) {
327
14.1k
                auto fst = node->children();
328
14.1k
                auto lst = fst + node->children_count();
329
28.8k
                for (; fst != lst; ++fst)
330
14.6k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
14.1k
            }
332
15.1k
        } else {
333
369
            fn(node->collisions(),
334
369
               node->collisions() + node->collision_count());
335
369
        }
336
15.5k
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::for_each_chunk_traversal<immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> 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.08k
    {
321
1.08k
        if (depth < max_depth<hash_t, B>) {
322
1.08k
            auto datamap = node->datamap();
323
1.08k
            if (datamap)
324
0
                fn(node->values(), node->values() + node->data_count());
325
1.08k
            auto nodemap = node->nodemap();
326
1.08k
            if (nodemap) {
327
1.08k
                auto fst = node->children();
328
1.08k
                auto lst = fst + node->children_count();
329
2.17k
                for (; fst != lst; ++fst)
330
1.08k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
1.08k
            }
332
1.08k
        } else {
333
0
            fn(node->collisions(),
334
0
               node->collisions() + node->collision_count());
335
0
        }
336
1.08k
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::for_each_chunk_traversal<immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> 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
20.4k
    {
321
20.4k
        if (depth < max_depth<hash_t, B>) {
322
19.9k
            auto datamap = node->datamap();
323
19.9k
            if (datamap)
324
1.42k
                fn(node->values(), node->values() + node->data_count());
325
19.9k
            auto nodemap = node->nodemap();
326
19.9k
            if (nodemap) {
327
18.8k
                auto fst = node->children();
328
18.8k
                auto lst = fst + node->children_count();
329
38.2k
                for (; fst != lst; ++fst)
330
19.3k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
18.8k
            }
332
19.9k
        } else {
333
486
            fn(node->collisions(),
334
486
               node->collisions() + node->collision_count());
335
486
        }
336
20.4k
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::for_each_chunk_traversal<immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> 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
7.01k
    {
321
7.01k
        if (depth < max_depth<hash_t, B>) {
322
6.82k
            auto datamap = node->datamap();
323
6.82k
            if (datamap)
324
5.50k
                fn(node->values(), node->values() + node->data_count());
325
6.82k
            auto nodemap = node->nodemap();
326
6.82k
            if (nodemap) {
327
2.31k
                auto fst = node->children();
328
2.31k
                auto lst = fst + node->children_count();
329
5.66k
                for (; fst != lst; ++fst)
330
3.35k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
2.31k
            }
332
6.82k
        } else {
333
194
            fn(node->collisions(),
334
194
               node->collisions() + node->collision_count());
335
194
        }
336
7.01k
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::for_each_chunk_traversal<immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> 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
6.28k
    {
321
6.28k
        if (depth < max_depth<hash_t, B>) {
322
5.75k
            auto datamap = node->datamap();
323
5.75k
            if (datamap)
324
3.54k
                fn(node->values(), node->values() + node->data_count());
325
5.75k
            auto nodemap = node->nodemap();
326
5.75k
            if (nodemap) {
327
2.58k
                auto fst = node->children();
328
2.58k
                auto lst = fst + node->children_count();
329
5.51k
                for (; fst != lst; ++fst)
330
2.93k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
2.58k
            }
332
5.75k
        } else {
333
537
            fn(node->collisions(),
334
537
               node->collisions() + node->collision_count());
335
537
        }
336
6.28k
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::for_each_chunk_traversal<immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> 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
6.00k
    {
321
6.00k
        if (depth < max_depth<hash_t, B>) {
322
5.80k
            auto datamap = node->datamap();
323
5.80k
            if (datamap)
324
4.30k
                fn(node->values(), node->values() + node->data_count());
325
5.80k
            auto nodemap = node->nodemap();
326
5.80k
            if (nodemap) {
327
2.01k
                auto fst = node->children();
328
2.01k
                auto lst = fst + node->children_count();
329
4.53k
                for (; fst != lst; ++fst)
330
2.52k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
2.01k
            }
332
5.80k
        } else {
333
194
            fn(node->collisions(),
334
194
               node->collisions() + node->collision_count());
335
194
        }
336
6.00k
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::for_each_chunk_traversal<immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> 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
5.23k
    {
321
5.23k
        if (depth < max_depth<hash_t, B>) {
322
4.75k
            auto datamap = node->datamap();
323
4.75k
            if (datamap)
324
2.65k
                fn(node->values(), node->values() + node->data_count());
325
4.75k
            auto nodemap = node->nodemap();
326
4.75k
            if (nodemap) {
327
2.33k
                auto fst = node->children();
328
2.33k
                auto lst = fst + node->children_count();
329
5.04k
                for (; fst != lst; ++fst)
330
2.70k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
2.33k
            }
332
4.75k
        } else {
333
481
            fn(node->collisions(),
334
481
               node->collisions() + node->collision_count());
335
481
        }
336
5.23k
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::for_each_chunk_traversal<immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff_data_node<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> const*, immer::detail::hamts::node, unsigned char, 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
38.6k
    {
321
38.6k
        if (depth < max_depth<hash_t, B>) {
322
38.2k
            auto datamap = node->datamap();
323
38.2k
            if (datamap)
324
32.1k
                fn(node->values(), node->values() + node->data_count());
325
38.2k
            auto nodemap = node->nodemap();
326
38.2k
            if (nodemap) {
327
8.15k
                auto fst = node->children();
328
8.15k
                auto lst = fst + node->children_count();
329
17.2k
                for (; fst != lst; ++fst)
330
9.13k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
8.15k
            }
332
38.2k
        } else {
333
352
            fn(node->collisions(),
334
352
               node->collisions() + node->collision_count());
335
352
        }
336
38.6k
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::for_each_chunk_traversal<immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff_data_node<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> const*, immer::detail::hamts::node, unsigned char, 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.9k
    {
321
27.9k
        if (depth < max_depth<hash_t, B>) {
322
26.6k
            auto datamap = node->datamap();
323
26.6k
            if (datamap)
324
9.68k
                fn(node->values(), node->values() + node->data_count());
325
26.6k
            auto nodemap = node->nodemap();
326
26.6k
            if (nodemap) {
327
17.9k
                auto fst = node->children();
328
17.9k
                auto lst = fst + node->children_count();
329
36.8k
                for (; fst != lst; ++fst)
330
18.8k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
17.9k
            }
332
26.6k
        } else {
333
1.26k
            fn(node->collisions(),
334
1.26k
               node->collisions() + node->collision_count());
335
1.26k
        }
336
27.9k
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::for_each_chunk_traversal<immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff_node_data<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> const*, immer::detail::hamts::node, unsigned char, 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
35.7k
    {
321
35.7k
        if (depth < max_depth<hash_t, B>) {
322
35.4k
            auto datamap = node->datamap();
323
35.4k
            if (datamap)
324
29.4k
                fn(node->values(), node->values() + node->data_count());
325
35.4k
            auto nodemap = node->nodemap();
326
35.4k
            if (nodemap) {
327
7.13k
                auto fst = node->children();
328
7.13k
                auto lst = fst + node->children_count();
329
14.6k
                for (; fst != lst; ++fst)
330
7.49k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
7.13k
            }
332
35.4k
        } else {
333
316
            fn(node->collisions(),
334
316
               node->collisions() + node->collision_count());
335
316
        }
336
35.7k
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::for_each_chunk_traversal<immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff_node_data<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> const*, immer::detail::hamts::node, unsigned char, 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
22.9k
    {
321
22.9k
        if (depth < max_depth<hash_t, B>) {
322
21.9k
            auto datamap = node->datamap();
323
21.9k
            if (datamap)
324
7.33k
                fn(node->values(), node->values() + node->data_count());
325
21.9k
            auto nodemap = node->nodemap();
326
21.9k
            if (nodemap) {
327
14.9k
                auto fst = node->children();
328
14.9k
                auto lst = fst + node->children_count();
329
30.4k
                for (; fst != lst; ++fst)
330
15.5k
                    for_each_chunk_traversal(*fst, depth + 1, fn);
331
14.9k
            }
332
21.9k
        } else {
333
999
            fn(node->collisions(),
334
999
               node->collisions() + node->collision_count());
335
999
        }
336
22.9k
    }
337
338
    template <typename EqualValue, typename Differ>
339
    void diff(const champ& new_champ, Differ&& differ) const
340
23.0k
    {
341
23.0k
        diff<EqualValue>(root, new_champ.root, 0, std::forward<Differ>(differ));
342
23.0k
    }
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
1.02M
    {
350
1.02M
        if (old_node == new_node)
351
212k
            return;
352
812k
        if (depth < max_depth<hash_t, B>) {
353
803k
            auto old_nodemap = old_node->nodemap();
354
803k
            auto new_nodemap = new_node->nodemap();
355
803k
            auto old_datamap = old_node->datamap();
356
803k
            auto new_datamap = new_node->datamap();
357
803k
            auto old_bits    = old_nodemap | old_datamap;
358
803k
            auto new_bits    = new_nodemap | new_datamap;
359
803k
            auto changes     = old_bits ^ new_bits;
360
361
            // added bits
362
803k
            for (auto bit : set_bits_range<bitmap_t>(new_bits & changes)) {
363
67.1k
                if (new_nodemap & bit) {
364
7.84k
                    auto offset = new_node->children_count(bit);
365
7.84k
                    auto child  = new_node->children()[offset];
366
7.84k
                    for_each_chunk_traversal(
367
7.84k
                        child,
368
7.84k
                        depth + 1,
369
11.2k
                        [&](auto const& begin, auto const& end) {
370
35.5k
                            for (auto it = begin; it != end; it++)
371
24.2k
                                differ.added(*it);
372
11.2k
                        });
set-st-str-conflict.cpp:auto immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> 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()<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*, {lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}>(immer::detail::hamts::node<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const* const) const
Line
Count
Source
369
1.50k
                        [&](auto const& begin, auto const& end) {
370
4.80k
                            for (auto it = begin; it != end; it++)
371
3.30k
                                differ.added(*it);
372
1.50k
                        });
set-st-str-conflict.cpp:auto immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> 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()<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*, {lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#1}>(immer::detail::hamts::node<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const* const) const
Line
Count
Source
369
9.77k
                        [&](auto const& begin, auto const& end) {
370
30.7k
                            for (auto it = begin; it != end; it++)
371
20.9k
                                differ.added(*it);
372
9.77k
                        });
373
59.2k
                } else if (new_datamap & bit) {
374
59.2k
                    auto offset       = new_node->data_count(bit);
375
59.2k
                    auto const& value = new_node->values()[offset];
376
59.2k
                    differ.added(value);
377
59.2k
                }
378
67.1k
            }
379
380
            // removed bits
381
803k
            for (auto bit : set_bits_range<bitmap_t>(old_bits & changes)) {
382
62.4k
                if (old_nodemap & bit) {
383
7.08k
                    auto offset = old_node->children_count(bit);
384
7.08k
                    auto child  = old_node->children()[offset];
385
7.08k
                    for_each_chunk_traversal(
386
7.08k
                        child,
387
7.08k
                        depth + 1,
388
9.54k
                        [&](auto const& begin, auto const& end) {
389
29.6k
                            for (auto it = begin; it != end; it++)
390
20.0k
                                differ.removed(*it);
391
9.54k
                        });
set-st-str-conflict.cpp:auto immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> 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()<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*, {lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#2}>(immer::detail::hamts::node<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const* const) const
Line
Count
Source
388
1.91k
                        [&](auto const& begin, auto const& end) {
389
5.97k
                            for (auto it = begin; it != end; it++)
390
4.06k
                                differ.removed(*it);
391
1.91k
                        });
set-st-str-conflict.cpp:auto immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> 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()<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*, {lambda(auto:1 const&, {lambda(auto:1&&)#2} const&)#2}>(immer::detail::hamts::node<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const* const) const
Line
Count
Source
388
7.63k
                        [&](auto const& begin, auto const& end) {
389
23.6k
                            for (auto it = begin; it != end; it++)
390
15.9k
                                differ.removed(*it);
391
7.63k
                        });
392
55.3k
                } else if (old_datamap & bit) {
393
55.3k
                    auto offset       = old_node->data_count(bit);
394
55.3k
                    auto const& value = old_node->values()[offset];
395
55.3k
                    differ.removed(value);
396
55.3k
                }
397
62.4k
            }
398
399
            // bits in both nodes
400
2.01M
            for (auto bit : set_bits_range<bitmap_t>(old_bits & new_bits)) {
401
2.01M
                if ((old_nodemap & bit) && (new_nodemap & bit)) {
402
1.00M
                    auto old_offset = old_node->children_count(bit);
403
1.00M
                    auto new_offset = new_node->children_count(bit);
404
1.00M
                    auto old_child  = old_node->children()[old_offset];
405
1.00M
                    auto new_child  = new_node->children()[new_offset];
406
1.00M
                    diff<EqualValue>(old_child, new_child, depth + 1, differ);
407
1.00M
                } else if ((old_datamap & bit) && (new_nodemap & bit)) {
408
38.6k
                    diff_data_node<EqualValue>(
409
38.6k
                        old_node, new_node, bit, depth, differ);
410
969k
                } else if ((old_nodemap & bit) && (new_datamap & bit)) {
411
35.7k
                    diff_node_data<EqualValue>(
412
35.7k
                        old_node, new_node, bit, depth, differ);
413
933k
                } else if ((old_datamap & bit) && (new_datamap & bit)) {
414
933k
                    diff_data_data<EqualValue>(old_node, new_node, bit, differ);
415
933k
                }
416
2.01M
            }
417
803k
        } else {
418
9.40k
            diff_collisions<EqualValue>(old_node, new_node, differ);
419
9.40k
        }
420
812k
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const
Line
Count
Source
349
23.0k
    {
350
23.0k
        if (old_node == new_node)
351
1.10k
            return;
352
21.9k
        if (depth < max_depth<hash_t, B>) {
353
21.9k
            auto old_nodemap = old_node->nodemap();
354
21.9k
            auto new_nodemap = new_node->nodemap();
355
21.9k
            auto old_datamap = old_node->datamap();
356
21.9k
            auto new_datamap = new_node->datamap();
357
21.9k
            auto old_bits    = old_nodemap | old_datamap;
358
21.9k
            auto new_bits    = new_nodemap | new_datamap;
359
21.9k
            auto changes     = old_bits ^ new_bits;
360
361
            // added bits
362
21.9k
            for (auto bit : set_bits_range<bitmap_t>(new_bits & changes)) {
363
1.03k
                if (new_nodemap & bit) {
364
826
                    auto offset = new_node->children_count(bit);
365
826
                    auto child  = new_node->children()[offset];
366
826
                    for_each_chunk_traversal(
367
826
                        child,
368
826
                        depth + 1,
369
826
                        [&](auto const& begin, auto const& end) {
370
826
                            for (auto it = begin; it != end; it++)
371
826
                                differ.added(*it);
372
826
                        });
373
826
                } else if (new_datamap & bit) {
374
206
                    auto offset       = new_node->data_count(bit);
375
206
                    auto const& value = new_node->values()[offset];
376
206
                    differ.added(value);
377
206
                }
378
1.03k
            }
379
380
            // removed bits
381
21.9k
            for (auto bit : set_bits_range<bitmap_t>(old_bits & changes)) {
382
1.33k
                if (old_nodemap & bit) {
383
1.08k
                    auto offset = old_node->children_count(bit);
384
1.08k
                    auto child  = old_node->children()[offset];
385
1.08k
                    for_each_chunk_traversal(
386
1.08k
                        child,
387
1.08k
                        depth + 1,
388
1.08k
                        [&](auto const& begin, auto const& end) {
389
1.08k
                            for (auto it = begin; it != end; it++)
390
1.08k
                                differ.removed(*it);
391
1.08k
                        });
392
1.08k
                } else if (old_datamap & bit) {
393
247
                    auto offset       = old_node->data_count(bit);
394
247
                    auto const& value = old_node->values()[offset];
395
247
                    differ.removed(value);
396
247
                }
397
1.33k
            }
398
399
            // bits in both nodes
400
21.9k
            for (auto bit : set_bits_range<bitmap_t>(old_bits & new_bits)) {
401
19.5k
                if ((old_nodemap & bit) && (new_nodemap & bit)) {
402
17.5k
                    auto old_offset = old_node->children_count(bit);
403
17.5k
                    auto new_offset = new_node->children_count(bit);
404
17.5k
                    auto old_child  = old_node->children()[old_offset];
405
17.5k
                    auto new_child  = new_node->children()[new_offset];
406
17.5k
                    diff<EqualValue>(old_child, new_child, depth + 1, differ);
407
17.5k
                } else if ((old_datamap & bit) && (new_nodemap & bit)) {
408
907
                    diff_data_node<EqualValue>(
409
907
                        old_node, new_node, bit, depth, differ);
410
1.10k
                } else if ((old_nodemap & bit) && (new_datamap & bit)) {
411
815
                    diff_node_data<EqualValue>(
412
815
                        old_node, new_node, bit, depth, differ);
413
815
                } else if ((old_datamap & bit) && (new_datamap & bit)) {
414
291
                    diff_data_data<EqualValue>(old_node, new_node, bit, differ);
415
291
                }
416
19.5k
            }
417
21.9k
        } else {
418
0
            diff_collisions<EqualValue>(old_node, new_node, differ);
419
0
        }
420
21.9k
    }
set-st-str-conflict.cpp:void immer::detail::hamts::champ<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u>::diff<std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 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>, 3u> const*, immer::detail::hamts::node, unsigned int, LLVMFuzzerTestOneInput::$_1::operator()<fuzzer_input>(fuzzer_input&) const::{lambda(auto:1&&)#2}) const
Line
Count
Source
349
1.00M
    {
350
1.00M
        if (old_node == new_node)
351
211k
            return;
352
790k
        if (depth < max_depth<hash_t, B>) {
353
781k
            auto old_nodemap = old_node->nodemap();
354
781k
            auto new_nodemap = new_node->nodemap();
355
781k
            auto old_datamap = old_node->datamap();
356
781k
            auto new_datamap = new_node->datamap();
357
781k
            auto old_bits    = old_nodemap | old_datamap;
358
781k
            auto new_bits    = new_nodemap | new_datamap;
359
781k
            auto changes     = old_bits ^ new_bits;
360
361
            // added bits
362
781k
            for (auto bit : set_bits_range<bitmap_t>(new_bits & changes)) {
363
66.1k
                if (new_nodemap & bit) {
364
7.01k
                    auto offset = new_node->children_count(bit);
365
7.01k
                    auto child  = new_node->children()[offset];
366
7.01k
                    for_each_chunk_traversal(
367
7.01k
                        child,
368
7.01k
                        depth + 1,
369
7.01k
                        [&](auto const& begin, auto const& end) {
370
7.01k
                            for (auto it = begin; it != end; it++)
371
7.01k
                                differ.added(*it);
372
7.01k
                        });
373
59.0k
                } else if (new_datamap & bit) {
374
59.0k
                    auto offset       = new_node->data_count(bit);
375
59.0k
                    auto const& value = new_node->values()[offset];
376
59.0k
                    differ.added(value);
377
59.0k
                }
378
66.1k
            }
379
380
            // removed bits
381
781k
            for (auto bit : set_bits_range<bitmap_t>(old_bits & changes)) {
382
61.0k
                if (old_nodemap & bit) {
383
6.00k
                    auto offset = old_node->children_count(bit);
384
6.00k
                    auto child  = old_node->children()[offset];
385
6.00k
                    for_each_chunk_traversal(
386
6.00k
                        child,
387
6.00k
                        depth + 1,
388
6.00k
                        [&](auto const& begin, auto const& end) {
389
6.00k
                            for (auto it = begin; it != end; it++)
390
6.00k
                                differ.removed(*it);
391
6.00k
                        });
392
55.0k
                } else if (old_datamap & bit) {
393
55.0k
                    auto offset       = old_node->data_count(bit);
394
55.0k
                    auto const& value = old_node->values()[offset];
395
55.0k
                    differ.removed(value);
396
55.0k
                }
397
61.0k
            }
398
399
            // bits in both nodes
400
1.99M
            for (auto bit : set_bits_range<bitmap_t>(old_bits & new_bits)) {
401
1.99M
                if ((old_nodemap & bit) && (new_nodemap & bit)) {
402
985k
                    auto old_offset = old_node->children_count(bit);
403
985k
                    auto new_offset = new_node->children_count(bit);
404
985k
                    auto old_child  = old_node->children()[old_offset];
405
985k
                    auto new_child  = new_node->children()[new_offset];
406
985k
                    diff<EqualValue>(old_child, new_child, depth + 1, differ);
407
1.00M
                } else if ((old_datamap & bit) && (new_nodemap & bit)) {
408
37.7k
                    diff_data_node<EqualValue>(
409
37.7k
                        old_node, new_node, bit, depth, differ);
410
968k
                } else if ((old_nodemap & bit) && (new_datamap & bit)) {
411
34.9k
                    diff_node_data<EqualValue>(
412
34.9k
                        old_node, new_node, bit, depth, differ);
413
933k
                } else if ((old_datamap & bit) && (new_datamap & bit)) {
414
933k
                    diff_data_data<EqualValue>(old_node, new_node, bit, differ);
415
933k
                }
416
1.99M
            }
417
781k
        } else {
418
9.40k
            diff_collisions<EqualValue>(old_node, new_node, differ);
419
9.40k
        }
420
790k
    }
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
38.6k
    {
429
38.6k
        auto old_offset       = old_node->data_count(bit);
430
38.6k
        auto const& old_value = old_node->values()[old_offset];
431
38.6k
        auto new_offset       = new_node->children_count(bit);
432
38.6k
        auto new_child        = new_node->children()[new_offset];
433
434
38.6k
        bool found = false;
435
38.6k
        for_each_chunk_traversal(
436
43.4k
            new_child, depth + 1, [&](auto const& begin, auto const& end) {
437
132k
                for (auto it = begin; it != end; it++) {
438
88.6k
                    if (Equal{}(old_value, *it)) {
439
36.0k
                        if (!EqualValue{}(old_value, *it))
440
0
                            differ.changed(old_value, *it);
441
36.0k
                        found = true;
442
52.6k
                    } else {
443
52.6k
                        differ.added(*it);
444
52.6k
                    }
445
88.6k
                }
446
43.4k
            });
447
38.6k
        if (!found)
448
2.62k
            differ.removed(old_value);
449
38.6k
    }
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
35.7k
    {
458
35.7k
        auto old_offset       = old_node->children_count(bit);
459
35.7k
        auto old_child        = old_node->children()[old_offset];
460
35.7k
        auto new_offset       = new_node->data_count(bit);
461
35.7k
        auto const& new_value = new_node->values()[new_offset];
462
463
35.7k
        bool found = false;
464
35.7k
        for_each_chunk_traversal(
465
38.1k
            old_child, depth + 1, [&](auto const& begin, auto const& end) {
466
116k
                for (auto it = begin; it != end; it++) {
467
78.0k
                    if (Equal{}(*it, new_value)) {
468
33.9k
                        if (!EqualValue{}(*it, new_value))
469
0
                            differ.changed(*it, new_value);
470
33.9k
                        found = true;
471
44.0k
                    } else {
472
44.0k
                        differ.removed(*it);
473
44.0k
                    }
474
78.0k
                }
475
38.1k
            });
476
35.7k
        if (!found)
477
1.73k
            differ.added(new_value);
478
35.7k
    }
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
933k
    {
486
933k
        auto old_offset       = old_node->data_count(bit);
487
933k
        auto new_offset       = new_node->data_count(bit);
488
933k
        auto const& old_value = old_node->values()[old_offset];
489
933k
        auto const& new_value = new_node->values()[new_offset];
490
933k
        if (!Equal{}(old_value, new_value)) {
491
7.13k
            differ.removed(old_value);
492
7.13k
            differ.added(new_value);
493
926k
        } else {
494
926k
            if (!EqualValue{}(old_value, new_value))
495
0
                differ.changed(old_value, new_value);
496
926k
        }
497
933k
    }
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
9.40k
    {
504
9.40k
        auto old_begin = old_node->collisions();
505
9.40k
        auto old_end   = old_node->collisions() + old_node->collision_count();
506
9.40k
        auto new_begin = new_node->collisions();
507
9.40k
        auto new_end   = new_node->collisions() + new_node->collision_count();
508
        // search changes and removals
509
35.7k
        for (auto old_it = old_begin; old_it != old_end; old_it++) {
510
26.3k
            bool found = false;
511
56.9k
            for (auto new_it = new_begin; new_it != new_end; new_it++) {
512
55.4k
                if (Equal{}(*old_it, *new_it)) {
513
24.9k
                    if (!EqualValue{}(*old_it, *new_it))
514
0
                        differ.changed(*old_it, *new_it);
515
24.9k
                    found = true;
516
24.9k
                    break;
517
24.9k
                }
518
55.4k
            }
519
26.3k
            if (!found)
520
1.45k
                differ.removed(*old_it);
521
26.3k
        }
522
        // search new entries
523
35.7k
        for (auto new_it = new_begin; new_it != new_end; new_it++) {
524
26.3k
            bool found = false;
525
56.7k
            for (auto old_it = old_begin; old_it != old_end; old_it++) {
526
55.3k
                if (Equal{}(*old_it, *new_it)) {
527
24.9k
                    found = true;
528
24.9k
                    break;
529
24.9k
                }
530
55.3k
            }
531
26.3k
            if (!found)
532
1.41k
                differ.added(*new_it);
533
26.3k
        }
534
9.40k
    }
535
536
    template <typename Project, typename Default, typename K>
537
    decltype(auto) get(const K& k) const
538
554k
    {
539
554k
        auto node = root;
540
554k
        auto hash = Hash{}(k);
541
10.1M
        for (auto i = count_t{}; i < max_depth<hash_t, B>; ++i) {
542
10.1M
            auto bit = bitmap_t{1u} << (hash & mask<hash_t, B>);
543
10.1M
            if (node->nodemap() & bit) {
544
9.61M
                auto offset = node->children_count(bit);
545
9.61M
                node        = node->children()[offset];
546
9.61M
                hash        = hash >> B;
547
9.61M
            } else if (node->datamap() & bit) {
548
377k
                auto offset = node->data_count(bit);
549
377k
                auto val    = node->values() + offset;
550
377k
                if (Equal{}(*val, k))
551
265k
                    return Project{}(*val);
552
111k
                else
553
111k
                    return Default{}();
554
377k
            } else {
555
162k
                return Default{}();
556
162k
            }
557
10.1M
        }
558
14.7k
        auto fst = node->collisions();
559
14.7k
        auto lst = fst + node->collision_count();
560
29.6k
        for (; fst != lst; ++fst)
561
26.7k
            if (Equal{}(*fst, k))
562
11.8k
                return Project{}(*fst);
563
2.88k
        return Default{}();
564
14.7k
    }
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
6.10M
    {
574
6.10M
        assert(node);
575
6.10M
        if (shift == max_shift<hash_t, B>) {
576
13.0k
            auto fst = node->collisions();
577
13.0k
            auto lst = fst + node->collision_count();
578
32.3k
            for (; fst != lst; ++fst)
579
30.9k
                if (Equal{}(*fst, v))
580
11.6k
                    return {
581
11.6k
                        node_t::copy_collision_replace(node, fst, std::move(v)),
582
11.6k
                        false};
583
1.44k
            return {node_t::copy_collision_insert(node, std::move(v)), true};
584
6.09M
        } else {
585
6.09M
            auto idx = (hash & (mask<hash_t, B> << shift)) >> shift;
586
6.09M
            auto bit = bitmap_t{1u} << idx;
587
6.09M
            if (node->nodemap() & bit) {
588
5.78M
                auto offset = node->children_count(bit);
589
5.78M
                assert(node->children()[offset]);
590
0
                auto result = do_add(
591
5.78M
                    node->children()[offset], std::move(v), hash, shift + B);
592
5.78M
                IMMER_TRY {
593
5.78M
                    result.node =
594
5.78M
                        node_t::copy_inner_replace(node, offset, result.node);
595
5.78M
                    return result;
596
5.78M
                }
597
5.78M
                IMMER_CATCH (...) {
598
0
                    node_t::delete_deep_shift(result.node, shift + B);
599
0
                    IMMER_RETHROW;
600
0
                }
601
5.78M
            } else if (node->datamap() & bit) {
602
293k
                auto offset = node->data_count(bit);
603
293k
                auto val    = node->values() + offset;
604
293k
                if (Equal{}(*val, v))
605
276k
                    return {node_t::copy_inner_replace_value(
606
276k
                                node, offset, std::move(v)),
607
276k
                            false};
608
16.8k
                else {
609
16.8k
                    auto child = node_t::make_merged(
610
16.8k
                        shift + B, std::move(v), hash, *val, Hash{}(*val));
611
16.8k
                    IMMER_TRY {
612
16.8k
                        return {node_t::copy_inner_replace_merged(
613
16.8k
                                    node, bit, offset, child),
614
16.8k
                                true};
615
16.8k
                    }
616
16.8k
                    IMMER_CATCH (...) {
617
0
                        node_t::delete_deep_shift(child, shift + B);
618
0
                        IMMER_RETHROW;
619
0
                    }
620
16.8k
                }
621
293k
            } else {
622
19.8k
                return {
623
19.8k
                    node_t::copy_inner_insert_value(node, bit, std::move(v)),
624
19.8k
                    true};
625
19.8k
            }
626
6.09M
        }
627
6.10M
    }
628
629
    champ add(T v) const
630
319k
    {
631
319k
        auto hash     = Hash{}(v);
632
319k
        auto res      = do_add(root, std::move(v), hash, 0);
633
319k
        auto new_size = size + (res.added ? 1 : 0);
634
319k
        return {res.node, new_size};
635
319k
    }
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
545k
    {
647
545k
        assert(node);
648
545k
        if (shift == max_shift<hash_t, B>) {
649
3.17k
            auto fst = node->collisions();
650
3.17k
            auto lst = fst + node->collision_count();
651
7.05k
            for (; fst != lst; ++fst)
652
6.15k
                if (Equal{}(*fst, v)) {
653
2.27k
                    if (node->can_mutate(e)) {
654
1.81k
                        *fst = std::move(v);
655
1.81k
                        return {node, false, true};
656
1.81k
                    } else {
657
459
                        auto r = node_t::copy_collision_replace(
658
459
                            node, fst, std::move(v));
659
459
                        return {node_t::owned(r, e), false, false};
660
459
                    }
661
2.27k
                }
662
904
            auto mutate = node->can_mutate(e);
663
904
            auto r = mutate ? node_t::move_collision_insert(node, std::move(v))
664
904
                            : node_t::copy_collision_insert(node, std::move(v));
665
904
            return {node_t::owned(r, e), true, mutate};
666
542k
        } else {
667
542k
            auto idx = (hash & (mask<hash_t, B> << shift)) >> shift;
668
542k
            auto bit = bitmap_t{1u} << idx;
669
542k
            if (node->nodemap() & bit) {
670
519k
                auto offset = node->children_count(bit);
671
519k
                auto child  = node->children()[offset];
672
519k
                if (node->can_mutate(e)) {
673
511k
                    auto result =
674
511k
                        do_add_mut(e, child, std::move(v), hash, shift + B);
675
511k
                    node->children()[offset] = result.node;
676
511k
                    if (!result.mutated && child->dec())
677
0
                        node_t::delete_deep_shift(child, shift + B);
678
511k
                    return {node, result.added, true};
679
511k
                } else {
680
7.35k
                    assert(node->children()[offset]);
681
0
                    auto result = do_add(child, std::move(v), hash, shift + B);
682
7.35k
                    IMMER_TRY {
683
7.35k
                        result.node = node_t::copy_inner_replace(
684
7.35k
                            node, offset, result.node);
685
7.35k
                        node_t::owned(result.node, e);
686
7.35k
                        return {result.node, result.added, false};
687
7.35k
                    }
688
7.35k
                    IMMER_CATCH (...) {
689
0
                        node_t::delete_deep_shift(result.node, shift + B);
690
0
                        IMMER_RETHROW;
691
0
                    }
692
7.35k
                }
693
519k
            } else if (node->datamap() & bit) {
694
14.6k
                auto offset = node->data_count(bit);
695
14.6k
                auto val    = node->values() + offset;
696
14.6k
                if (Equal{}(*val, v)) {
697
7.27k
                    if (node->can_mutate(e)) {
698
5.42k
                        auto vals    = node->ensure_mutable_values(e);
699
5.42k
                        vals[offset] = std::move(v);
700
5.42k
                        return {node, false, true};
701
5.42k
                    } else {
702
1.85k
                        auto r = node_t::copy_inner_replace_value(
703
1.85k
                            node, offset, std::move(v));
704
1.85k
                        return {node_t::owned_values(r, e), false, false};
705
1.85k
                    }
706
7.33k
                } else {
707
7.33k
                    auto mutate        = node->can_mutate(e);
708
7.33k
                    auto mutate_values = mutate && node->can_mutate_values(e);
709
7.33k
                    auto hash2         = Hash{}(*val);
710
7.33k
                    auto child         = node_t::make_merged_e(
711
7.33k
                        e,
712
7.33k
                        shift + B,
713
7.33k
                        std::move(v),
714
7.33k
                        hash,
715
7.33k
                        mutate_values ? std::move(*val) : *val,
716
7.33k
                        hash2);
717
7.33k
                    IMMER_TRY {
718
7.33k
                        auto r = mutate ? node_t::move_inner_replace_merged(
719
5.43k
                                              e, node, bit, offset, child)
720
7.33k
                                        : node_t::copy_inner_replace_merged(
721
1.89k
                                              node, bit, offset, child);
722
7.33k
                        return {node_t::owned_values_safe(r, e), true, mutate};
723
7.33k
                    }
724
7.33k
                    IMMER_CATCH (...) {
725
0
                        node_t::delete_deep_shift(child, shift + B);
726
0
                        IMMER_RETHROW;
727
0
                    }
728
7.33k
                }
729
14.6k
            } else {
730
9.04k
                auto mutate = node->can_mutate(e);
731
9.04k
                auto r      = mutate ? node_t::move_inner_insert_value(
732
4.98k
                                      e, node, bit, std::move(v))
733
9.04k
                                     : node_t::copy_inner_insert_value(
734
4.06k
                                      node, bit, std::move(v));
735
9.04k
                return {node_t::owned_values(r, e), true, mutate};
736
9.04k
            }
737
542k
        }
738
545k
    }
739
740
    void add_mut(edit_t e, T v)
741
34.1k
    {
742
34.1k
        auto hash = Hash{}(v);
743
34.1k
        auto res  = do_add_mut(e, root, std::move(v), hash, 0);
744
34.1k
        if (!res.mutated && root->dec())
745
0
            node_t::delete_deep(root, 0);
746
34.1k
        root = res.node;
747
34.1k
        size += res.added ? 1 : 0;
748
34.1k
    }
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
126k
            : kind{nothing} {};
1192
        sub_result(T* x)
1193
            : kind{singleton}
1194
2.67k
        {
1195
2.67k
            data.singleton = x;
1196
2.67k
        };
1197
        sub_result(node_t* x)
1198
            : kind{tree}
1199
88.9k
        {
1200
88.9k
            data.tree = x;
1201
88.9k
        };
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
225k
    {
1208
225k
        if (shift == max_shift<hash_t, B>) {
1209
1.53k
            auto fst = node->collisions();
1210
1.53k
            auto lst = fst + node->collision_count();
1211
3.32k
            for (auto cur = fst; cur != lst; ++cur)
1212
2.86k
                if (Equal{}(*cur, k))
1213
1.07k
                    return node->collision_count() > 2
1214
1.07k
                               ? node_t::copy_collision_remove(node, cur)
1215
1.07k
                               : sub_result{fst + (cur == fst)};
1216
462
#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
462
#endif
1222
            // Apparently GCC is generating this warning sometimes when
1223
            // compiling the benchmarks. It makes however no sense at all.
1224
462
            return {};
1225
1.53k
#if !defined(_MSC_VER)
1226
#if defined(__GNUC__) && !defined(__clang__)
1227
#pragma GCC diagnostic pop
1228
#endif
1229
1.53k
#endif
1230
223k
        } else {
1231
223k
            auto idx = (hash & (mask<hash_t, B> << shift)) >> shift;
1232
223k
            auto bit = bitmap_t{1u} << idx;
1233
223k
            if (node->nodemap() & bit) {
1234
210k
                auto offset = node->children_count(bit);
1235
210k
                auto result =
1236
210k
                    do_sub(node->children()[offset], k, hash, shift + B);
1237
210k
                switch (result.kind) {
1238
117k
                case sub_result::nothing:
1239
117k
                    return {};
1240
9.64k
                case sub_result::singleton:
1241
9.64k
                    return node->datamap() == 0 &&
1242
9.64k
                                   node->children_count() == 1 && shift > 0
1243
9.64k
                               ? result
1244
9.64k
                               : node_t::copy_inner_replace_inline(
1245
2.08k
                                     node, bit, offset, *result.data.singleton);
1246
83.5k
                case sub_result::tree:
1247
83.5k
                    IMMER_TRY {
1248
83.5k
                        return node_t::copy_inner_replace(
1249
83.5k
                            node, offset, result.data.tree);
1250
83.5k
                    }
1251
83.5k
                    IMMER_CATCH (...) {
1252
0
                        node_t::delete_deep_shift(result.data.tree, shift + B);
1253
0
                        IMMER_RETHROW;
1254
0
                    }
1255
210k
                }
1256
210k
            } else if (node->datamap() & bit) {
1257
8.40k
                auto offset = node->data_count(bit);
1258
8.40k
                auto val    = node->values() + offset;
1259
8.40k
                if (Equal{}(*val, k)) {
1260
4.94k
                    auto nv = node->data_count();
1261
4.94k
                    if (node->nodemap() || nv > 2)
1262
2.59k
                        return node_t::copy_inner_remove_value(
1263
2.59k
                            node, bit, offset);
1264
2.35k
                    else if (nv == 2) {
1265
2.15k
                        return shift > 0 ? sub_result{node->values() + !offset}
1266
2.15k
                                         : node_t::make_inner_n(
1267
0
                                               0,
1268
0
                                               node->datamap() & ~bit,
1269
0
                                               node->values()[!offset]);
1270
2.15k
                    } else {
1271
202
                        assert(shift == 0);
1272
0
                        return empty();
1273
202
                    }
1274
4.94k
                }
1275
8.40k
            }
1276
8.68k
            return {};
1277
223k
        }
1278
225k
    }
1279
1280
    template <typename K>
1281
    champ sub(const K& k) const
1282
11.6k
    {
1283
11.6k
        auto hash = Hash{}(k);
1284
11.6k
        auto res  = do_sub(root, k, hash, 0);
1285
11.6k
        switch (res.kind) {
1286
6.86k
        case sub_result::nothing:
1287
6.86k
            return *this;
1288
4.78k
        case sub_result::tree:
1289
4.78k
            return {res.data.tree, size - 1};
1290
0
        default:
1291
0
            IMMER_UNREACHABLE;
1292
11.6k
        }
1293
11.6k
    }
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
            : kind{a.kind}
1307
            , data{a.data}
1308
            , owned{false}
1309
            , mutated{false}
1310
3.52k
        {
1311
3.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
            : kind{kind_t::nothing}
1321
123k
            , mutated{false} {};
1322
        sub_result_mut(T* x, bool m)
1323
            : kind{kind_t::singleton}
1324
            , owned{m}
1325
            , mutated{m}
1326
2.54k
        {
1327
2.54k
            data.singleton = x;
1328
2.54k
        };
1329
        sub_result_mut(T* x, bool o, bool m)
1330
            : kind{kind_t::singleton}
1331
            , owned{o}
1332
            , mutated{m}
1333
17.7k
        {
1334
17.7k
            data.singleton = x;
1335
17.7k
        };
1336
        sub_result_mut(node_t* x, bool m)
1337
            : kind{kind_t::tree}
1338
            , owned{false}
1339
            , mutated{m}
1340
116k
        {
1341
116k
            data.tree = x;
1342
116k
        };
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
260k
    {
1353
260k
        auto mutate = node->can_mutate(e);
1354
260k
        if (shift == max_shift<hash_t, B>) {
1355
3.16k
            auto fst = node->collisions();
1356
3.16k
            auto lst = fst + node->collision_count();
1357
6.50k
            for (auto cur = fst; cur != lst; ++cur) {
1358
6.02k
                if (Equal{}(*cur, k)) {
1359
2.67k
                    if (node->collision_count() <= 2) {
1360
1.21k
                        if (mutate) {
1361
984
                            auto r = new (store)
1362
984
                                T{std::move(node->collisions()[cur == fst])};
1363
984
                            node_t::delete_collision(node);
1364
984
                            return sub_result_mut{r, true};
1365
984
                        } else {
1366
231
                            return sub_result_mut{fst + (cur == fst), false};
1367
231
                        }
1368
1.46k
                    } else {
1369
1.46k
                        auto r = mutate
1370
1.46k
                                     ? node_t::move_collision_remove(node, cur)
1371
1.46k
                                     : node_t::copy_collision_remove(node, cur);
1372
1.46k
                        return {node_t::owned(r, e), mutate};
1373
1.46k
                    }
1374
2.67k
                }
1375
6.02k
            }
1376
483
            return {};
1377
257k
        } else {
1378
257k
            auto idx = (hash & (mask<hash_t, B> << shift)) >> shift;
1379
257k
            auto bit = bitmap_t{1u} << idx;
1380
257k
            if (node->nodemap() & bit) {
1381
247k
                auto offset   = node->children_count(bit);
1382
247k
                auto children = node->children();
1383
247k
                auto child    = children[offset];
1384
247k
                auto result =
1385
247k
                    mutate ? do_sub_mut(e, child, k, hash, shift + B, store)
1386
247k
                           : do_sub(child, k, hash, shift + B);
1387
247k
                switch (result.kind) {
1388
117k
                case sub_result::nothing:
1389
117k
                    return {};
1390
20.9k
                case sub_result::singleton:
1391
20.9k
                    if (node->datamap() == 0 && node->children_count() == 1 &&
1392
20.9k
                        shift > 0) {
1393
17.7k
                        if (mutate) {
1394
17.5k
                            node_t::delete_inner(node);
1395
17.5k
                            if (!result.mutated && child->dec())
1396
0
                                node_t::delete_deep_shift(child, shift + B);
1397
17.5k
                        }
1398
17.7k
                        return {result.data.singleton, result.owned, mutate};
1399
17.7k
                    } else {
1400
3.13k
                        auto r =
1401
3.13k
                            mutate ? node_t::move_inner_replace_inline(
1402
2.77k
                                         e,
1403
2.77k
                                         node,
1404
2.77k
                                         bit,
1405
2.77k
                                         offset,
1406
2.77k
                                         result.owned
1407
2.77k
                                             ? std::move(*result.data.singleton)
1408
2.77k
                                             : *result.data.singleton)
1409
3.13k
                                   : node_t::copy_inner_replace_inline(
1410
367
                                         node,
1411
367
                                         bit,
1412
367
                                         offset,
1413
367
                                         *result.data.singleton);
1414
3.13k
                        if (result.owned)
1415
2.09k
                            detail::destroy_at(result.data.singleton);
1416
3.13k
                        if (!result.mutated && mutate && child->dec())
1417
0
                            node_t::delete_deep_shift(child, shift + B);
1418
3.13k
                        return {node_t::owned_values(r, e), mutate};
1419
3.13k
                    }
1420
109k
                case sub_result::tree:
1421
109k
                    if (mutate) {
1422
108k
                        children[offset] = result.data.tree;
1423
108k
                        if (!result.mutated && child->dec())
1424
0
                            node_t::delete_deep_shift(child, shift + B);
1425
108k
                        return {node, true};
1426
108k
                    } else {
1427
649
                        IMMER_TRY {
1428
649
                            auto r = node_t::copy_inner_replace(
1429
649
                                node, offset, result.data.tree);
1430
649
                            return {node_t::owned(r, e), false};
1431
649
                        }
1432
649
                        IMMER_CATCH (...) {
1433
0
                            node_t::delete_deep_shift(result.data.tree,
1434
0
                                                      shift + B);
1435
0
                            IMMER_RETHROW;
1436
0
                        }
1437
649
                    }
1438
247k
                }
1439
247k
            } else if (node->datamap() & bit) {
1440
6.42k
                auto offset        = node->data_count(bit);
1441
6.42k
                auto val           = node->values() + offset;
1442
6.42k
                auto mutate_values = mutate && node->can_mutate_values(e);
1443
6.42k
                if (Equal{}(*val, k)) {
1444
3.92k
                    auto nv = node->data_count();
1445
3.92k
                    if (node->nodemap() || nv > 2) {
1446
1.87k
                        auto r = mutate ? node_t::move_inner_remove_value(
1447
1.55k
                                              e, node, bit, offset)
1448
1.87k
                                        : node_t::copy_inner_remove_value(
1449
325
                                              node, bit, offset);
1450
1.87k
                        return {node_t::owned_values_safe(r, e), mutate};
1451
2.04k
                    } else if (nv == 2) {
1452
1.33k
                        if (shift > 0) {
1453
1.33k
                            if (mutate_values) {
1454
1.11k
                                auto r = new (store)
1455
1.11k
                                    T{std::move(node->values()[!offset])};
1456
1.11k
                                node_t::delete_inner(node);
1457
1.11k
                                return {r, true};
1458
1.11k
                            } else {
1459
222
                                return {node->values() + !offset, false};
1460
222
                            }
1461
1.33k
                        } else {
1462
0
                            auto& v = node->values()[!offset];
1463
0
                            auto r  = node_t::make_inner_n(
1464
0
                                0,
1465
0
                                node->datamap() & ~bit,
1466
0
                                mutate_values ? std::move(v) : v);
1467
0
                            assert(!node->nodemap());
1468
0
                            if (mutate)
1469
0
                                node_t::delete_inner(node);
1470
0
                            return {node_t::owned_values(r, e), mutate};
1471
0
                        }
1472
1.33k
                    } else {
1473
715
                        assert(shift == 0);
1474
715
                        if (mutate)
1475
520
                            node_t::delete_inner(node);
1476
715
                        return {empty(), mutate};
1477
715
                    }
1478
3.92k
                }
1479
6.42k
            }
1480
5.74k
            return {};
1481
257k
        }
1482
260k
    }
1483
1484
    template <typename K>
1485
    void sub_mut(edit_t e, const K& k)
1486
16.3k
    {
1487
16.3k
        auto store = aligned_storage_for<T>{};
1488
16.3k
        auto hash  = Hash{}(k);
1489
16.3k
        auto res   = do_sub_mut(e, root, k, hash, 0, &store);
1490
16.3k
        switch (res.kind) {
1491
8.51k
        case sub_result::nothing:
1492
8.51k
            break;
1493
7.84k
        case sub_result::tree:
1494
7.84k
            if (!res.mutated && root->dec())
1495
0
                node_t::delete_deep(root, 0);
1496
7.84k
            root = res.data.tree;
1497
7.84k
            --size;
1498
7.84k
            break;
1499
0
        default:
1500
0
            IMMER_UNREACHABLE;
1501
16.3k
        }
1502
16.3k
    }
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