/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 |