/src/immer/immer/detail/rbts/rrbtree.hpp
Line | Count | Source |
1 | | // |
2 | | // immer: immutable data structures for C++ |
3 | | // Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente |
4 | | // |
5 | | // This software is distributed under the Boost Software License, Version 1.0. |
6 | | // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt |
7 | | // |
8 | | |
9 | | #pragma once |
10 | | |
11 | | #include <immer/config.hpp> |
12 | | #include <immer/detail/rbts/node.hpp> |
13 | | #include <immer/detail/rbts/operations.hpp> |
14 | | #include <immer/detail/rbts/position.hpp> |
15 | | |
16 | | #include <immer/detail/type_traits.hpp> |
17 | | |
18 | | #include <cassert> |
19 | | #include <limits> |
20 | | #include <memory> |
21 | | #include <numeric> |
22 | | #include <stdexcept> |
23 | | |
24 | | namespace immer { |
25 | | namespace detail { |
26 | | namespace rbts { |
27 | | |
28 | | #if IMMER_THROW_ON_INVALID_STATE |
29 | | struct invalid_tree : std::exception |
30 | | {}; |
31 | | #define IMMER_INVALID_STATE_ASSERT(expr) \ |
32 | 3.75M | if (!(expr)) \ |
33 | 3.75M | IMMER_THROW(invalid_tree{}) |
34 | | #else |
35 | | #define IMMER_INVALID_STATE_ASSERT(expr) assert(expr) |
36 | | #endif |
37 | | |
38 | | template <typename T, typename MemoryPolicy, bits_t B, bits_t BL> |
39 | | struct rrbtree_iterator; |
40 | | |
41 | | template <typename T, typename MemoryPolicy, bits_t B, bits_t BL> |
42 | | struct rrbtree |
43 | | { |
44 | | using node_t = node<T, MemoryPolicy, B, BL>; |
45 | | using edit_t = typename node_t::edit_t; |
46 | | using owner_t = typename MemoryPolicy::transience_t::owner; |
47 | | |
48 | | size_t size; |
49 | | shift_t shift; |
50 | | node_t* root; |
51 | | node_t* tail; |
52 | | |
53 | | constexpr static size_t max_size() |
54 | 2.00M | { |
55 | 2.00M | auto S = sizeof(size_t) * 8; |
56 | 2.00M | return ((size_t{1} << BL) - std::min(size_t{BL}, size_t{2})) * |
57 | 2.00M | ipow((size_t{1} << B) - 2, (S - BL) / B); |
58 | 2.00M | } |
59 | | |
60 | | static node_t* empty_root() |
61 | 384k | { |
62 | 384k | static const auto empty_ = [] { |
63 | 1 | constexpr auto size = node_t::sizeof_inner_n(0); |
64 | 1 | static std::aligned_storage_t<size, alignof(std::max_align_t)> |
65 | 1 | storage; |
66 | 1 | return node_t::make_inner_n_into(&storage, size, 0u); |
67 | 1 | }(); |
68 | 384k | return empty_->inc(); |
69 | 384k | } |
70 | | |
71 | | static node_t* empty_tail() |
72 | 373k | { |
73 | 373k | static const auto empty_ = [] { |
74 | 1 | constexpr auto size = node_t::sizeof_leaf_n(0); |
75 | 1 | static std::aligned_storage_t<size, alignof(std::max_align_t)> |
76 | 1 | storage; |
77 | 1 | return node_t::make_leaf_n_into(&storage, size, 0u); |
78 | 1 | }(); |
79 | 373k | return empty_->inc(); |
80 | 373k | } |
81 | | |
82 | | template <typename U> |
83 | | static auto from_initializer_list(std::initializer_list<U> values) |
84 | | { |
85 | | auto e = owner_t{}; |
86 | | auto result = rrbtree{}; |
87 | | for (auto&& v : values) |
88 | | result.push_back_mut(e, v); |
89 | | return result; |
90 | | } |
91 | | |
92 | | template <typename Iter, |
93 | | typename Sent, |
94 | | std::enable_if_t<compatible_sentinel_v<Iter, Sent>, bool> = true> |
95 | | static auto from_range(Iter first, Sent last) |
96 | | { |
97 | | auto e = owner_t{}; |
98 | | auto result = rrbtree{}; |
99 | | for (; first != last; ++first) |
100 | | result.push_back_mut(e, *first); |
101 | | return result; |
102 | | } |
103 | | |
104 | | static auto from_fill(size_t n, T v) |
105 | | { |
106 | | auto e = owner_t{}; |
107 | | auto result = rrbtree{}; |
108 | | while (n-- > 0) |
109 | | result.push_back_mut(e, v); |
110 | | return result; |
111 | | } |
112 | | |
113 | | rrbtree() noexcept |
114 | 369k | : size{0} |
115 | 369k | , shift{BL} |
116 | 369k | , root{empty_root()} |
117 | 369k | , tail{empty_tail()} |
118 | 369k | { |
119 | 369k | assert(check_tree()); |
120 | 369k | } |
121 | | |
122 | | rrbtree(size_t sz, shift_t sh, node_t* r, node_t* t) |
123 | | #if IMMER_THROW_ON_INVALID_STATE |
124 | | #else |
125 | | noexcept |
126 | | #endif |
127 | 305k | : size{sz} |
128 | 305k | , shift{sh} |
129 | 305k | , root{r} |
130 | 305k | , tail{t} |
131 | 305k | { |
132 | 305k | #if IMMER_THROW_ON_INVALID_STATE |
133 | | // assert only happens in the Debug build, but when |
134 | | // IMMER_THROW_ON_INVALID_STATE is activated, we want to just check_tree |
135 | | // even in Release. |
136 | 305k | try { |
137 | 305k | check_tree(); |
138 | 305k | } catch (...) { |
139 | | // This not fully constructed rrbtree owns the nodes already, have |
140 | | // to dec them. |
141 | 0 | if (r && t) |
142 | 0 | dec(); |
143 | 0 | throw; |
144 | 0 | } |
145 | | #else |
146 | | assert(check_tree()); |
147 | | #endif |
148 | 305k | } |
149 | | |
150 | | rrbtree(const rrbtree& other) noexcept |
151 | 137k | : rrbtree{other.size, other.shift, other.root, other.tail} |
152 | 137k | { |
153 | 137k | inc(); |
154 | 137k | } |
155 | | |
156 | | rrbtree(rrbtree&& other) noexcept |
157 | 293k | : rrbtree{} |
158 | 293k | { |
159 | 293k | swap(*this, other); |
160 | 293k | } |
161 | | |
162 | | rrbtree& operator=(const rrbtree& other) |
163 | 6.34k | { |
164 | 6.34k | auto next{other}; |
165 | 6.34k | swap(*this, next); |
166 | 6.34k | return *this; |
167 | 6.34k | } |
168 | | |
169 | | rrbtree& operator=(rrbtree&& other) noexcept |
170 | 310k | { |
171 | 310k | swap(*this, other); |
172 | 310k | return *this; |
173 | 310k | } |
174 | | |
175 | | friend void swap(rrbtree& x, rrbtree& y) noexcept |
176 | 610k | { |
177 | 610k | using std::swap; |
178 | 610k | swap(x.size, y.size); |
179 | 610k | swap(x.shift, y.shift); |
180 | 610k | swap(x.root, y.root); |
181 | 610k | swap(x.tail, y.tail); |
182 | 610k | } |
183 | | |
184 | 675k | ~rrbtree() { dec(); } |
185 | | |
186 | | void inc() const |
187 | 137k | { |
188 | 137k | root->inc(); |
189 | 137k | tail->inc(); |
190 | 137k | } |
191 | | |
192 | 675k | void dec() const { traverse(dec_visitor()); } |
193 | | |
194 | 1.06M | auto tail_size() const { return size - tail_offset(); } |
195 | | |
196 | | auto tail_offset() const |
197 | 7.00M | { |
198 | 7.00M | auto r = root->relaxed(); |
199 | 7.00M | assert(r == nullptr || r->d.count); |
200 | 7.00M | return r ? r->d.sizes[r->d.count - 1] |
201 | 7.00M | : size ? (size - 1) & ~mask<BL> |
202 | | /* otherwise */ |
203 | 4.55M | : 0; |
204 | 7.00M | } |
205 | | |
206 | | template <typename Visitor, typename... Args> |
207 | | void traverse(Visitor v, Args&&... args) const |
208 | 675k | { |
209 | 675k | auto tail_off = tail_offset(); |
210 | 675k | auto tail_size = size - tail_off; |
211 | | |
212 | 675k | if (tail_off) |
213 | 247k | visit_maybe_relaxed_sub(root, shift, tail_off, v, args...); |
214 | 427k | else |
215 | 427k | make_empty_regular_pos(root).visit(v, args...); |
216 | | |
217 | 675k | if (tail_size) |
218 | 293k | make_leaf_sub_pos(tail, tail_size).visit(v, args...); |
219 | 381k | else |
220 | 381k | make_empty_leaf_pos(tail).visit(v, args...); |
221 | 675k | } |
222 | | |
223 | | template <typename Visitor, typename... Args> |
224 | | void traverse(Visitor v, size_t first, size_t last, Args&&... args) const |
225 | | { |
226 | | auto tail_off = tail_offset(); |
227 | | auto tail_size = size - tail_off; |
228 | | |
229 | | if (first < tail_off) |
230 | | visit_maybe_relaxed_sub(root, |
231 | | shift, |
232 | | tail_off, |
233 | | v, |
234 | | first, |
235 | | last < tail_off ? last : tail_off, |
236 | | args...); |
237 | | if (last > tail_off) |
238 | | make_leaf_sub_pos(tail, tail_size) |
239 | | .visit(v, |
240 | | first > tail_off ? first - tail_off : 0, |
241 | | last - tail_off, |
242 | | args...); |
243 | | } |
244 | | |
245 | | template <typename Visitor, typename... Args> |
246 | | bool traverse_p(Visitor v, Args&&... args) const |
247 | | { |
248 | | auto tail_off = tail_offset(); |
249 | | auto tail_size = size - tail_off; |
250 | | return (tail_off |
251 | | ? visit_maybe_relaxed_sub(root, shift, tail_off, v, args...) |
252 | | : make_empty_regular_pos(root).visit(v, args...)) && |
253 | | (tail_size ? make_leaf_sub_pos(tail, tail_size).visit(v, args...) |
254 | | : make_empty_leaf_pos(tail).visit(v, args...)); |
255 | | } |
256 | | |
257 | | template <typename Visitor, typename... Args> |
258 | | bool traverse_p(Visitor v, size_t first, size_t last, Args&&... args) const |
259 | | { |
260 | | auto tail_off = tail_offset(); |
261 | | auto tail_size = size - tail_off; |
262 | | return (first < tail_off |
263 | | ? visit_maybe_relaxed_sub(root, |
264 | | shift, |
265 | | tail_off, |
266 | | v, |
267 | | first, |
268 | | last < tail_off ? last : tail_off, |
269 | | args...) |
270 | | : true) && |
271 | | (last > tail_off |
272 | | ? make_leaf_sub_pos(tail, tail_size) |
273 | | .visit(v, |
274 | | first > tail_off ? first - tail_off : 0, |
275 | | last - tail_off, |
276 | | args...) |
277 | | : true); |
278 | | } |
279 | | |
280 | | template <typename Visitor> |
281 | | decltype(auto) descend(Visitor v, size_t idx) const |
282 | | { |
283 | | auto tail_off = tail_offset(); |
284 | | return idx >= tail_off |
285 | | ? make_leaf_descent_pos(tail).visit(v, idx - tail_off) |
286 | | : visit_maybe_relaxed_descent(root, shift, v, idx); |
287 | | } |
288 | | |
289 | | template <typename Fn> |
290 | | void for_each_chunk(Fn&& fn) const |
291 | | { |
292 | | traverse(for_each_chunk_visitor{}, std::forward<Fn>(fn)); |
293 | | } |
294 | | |
295 | | template <typename Fn> |
296 | | void for_each_chunk(size_t first, size_t last, Fn&& fn) const |
297 | | { |
298 | | traverse(for_each_chunk_i_visitor{}, first, last, std::forward<Fn>(fn)); |
299 | | } |
300 | | |
301 | | template <typename Fn> |
302 | | bool for_each_chunk_p(Fn&& fn) const |
303 | | { |
304 | | return traverse_p(for_each_chunk_p_visitor{}, std::forward<Fn>(fn)); |
305 | | } |
306 | | |
307 | | template <typename Fn> |
308 | | bool for_each_chunk_p(size_t first, size_t last, Fn&& fn) const |
309 | | { |
310 | | return traverse_p( |
311 | | for_each_chunk_p_i_visitor{}, first, last, std::forward<Fn>(fn)); |
312 | | } |
313 | | |
314 | | bool equals(const rrbtree& other) const |
315 | | { |
316 | | using iter_t = rrbtree_iterator<T, MemoryPolicy, B, BL>; |
317 | | if (size != other.size) |
318 | | return false; |
319 | | if (size == 0) |
320 | | return true; |
321 | | auto tail_off = tail_offset(); |
322 | | auto tail_off_other = other.tail_offset(); |
323 | | // compare trees |
324 | | if (tail_off > 0 && tail_off_other > 0) { |
325 | | // other.shift != shift is a theoretical possibility for |
326 | | // relaxed trees that sadly we haven't managed to exercise |
327 | | // in tests yet... |
328 | | if (other.shift >= shift) { |
329 | | if (!visit_maybe_relaxed_sub(other.root, |
330 | | other.shift, |
331 | | tail_off_other, |
332 | | equals_visitor::rrb{}, |
333 | | iter_t{other}, |
334 | | root, |
335 | | shift, |
336 | | tail_off)) |
337 | | return false; |
338 | | } else { |
339 | | if (!visit_maybe_relaxed_sub(root, |
340 | | shift, |
341 | | tail_off, |
342 | | equals_visitor::rrb{}, |
343 | | iter_t{*this}, |
344 | | other.root, |
345 | | other.shift, |
346 | | tail_off_other)) |
347 | | return false; |
348 | | } |
349 | | } |
350 | | return tail_off == tail_off_other |
351 | | ? make_leaf_sub_pos(tail, tail_size()) |
352 | | .visit(equals_visitor{}, other.tail) |
353 | | : tail_off > tail_off_other |
354 | | ? std::equal(tail->leaf(), |
355 | | tail->leaf() + (size - tail_off), |
356 | | other.tail->leaf() + |
357 | | (tail_off - tail_off_other)) |
358 | | /* otherwise */ |
359 | | : std::equal(tail->leaf(), |
360 | | tail->leaf() + (size - tail_off), |
361 | | iter_t{other} + tail_off); |
362 | | } |
363 | | |
364 | | std::tuple<shift_t, node_t*> push_tail(node_t* root, |
365 | | shift_t shift, |
366 | | size_t size, |
367 | | node_t* tail, |
368 | | count_t tail_size) const |
369 | 25.9k | { |
370 | 25.9k | if (auto r = root->relaxed()) { |
371 | 13.5k | auto new_root = |
372 | 13.5k | make_relaxed_pos(root, shift, r) |
373 | 13.5k | .visit(push_tail_visitor<node_t>{}, tail, tail_size); |
374 | 13.5k | if (new_root) |
375 | 12.9k | return std::make_tuple(shift, new_root); |
376 | 608 | else { |
377 | 608 | auto new_root = node_t::make_inner_r_n(2); |
378 | 608 | IMMER_TRY { |
379 | 608 | auto new_path = node_t::make_path(shift, tail); |
380 | 608 | new_root->inner()[0] = root->inc(); |
381 | 608 | new_root->inner()[1] = new_path; |
382 | 608 | new_root->relaxed()->d.sizes[0] = size; |
383 | 608 | new_root->relaxed()->d.sizes[1] = size + tail_size; |
384 | 608 | assert(size); |
385 | 608 | assert(tail_size); |
386 | 608 | new_root->relaxed()->d.count = 2u; |
387 | 608 | } |
388 | 608 | IMMER_CATCH (...) { |
389 | 0 | node_t::delete_inner_r(new_root, 2); |
390 | 0 | IMMER_RETHROW; |
391 | 0 | } |
392 | 608 | return std::make_tuple(shift + B, new_root); |
393 | 608 | } |
394 | 13.5k | } else if (size == size_t{branches<B>} << shift) { |
395 | 1.04k | auto new_root = node_t::make_inner_n(2); |
396 | 1.04k | IMMER_TRY { |
397 | 1.04k | auto new_path = node_t::make_path(shift, tail); |
398 | 1.04k | new_root->inner()[0] = root->inc(); |
399 | 1.04k | new_root->inner()[1] = new_path; |
400 | 1.04k | } |
401 | 1.04k | IMMER_CATCH (...) { |
402 | 0 | node_t::delete_inner(new_root, 2); |
403 | 0 | IMMER_RETHROW; |
404 | 0 | } |
405 | 1.04k | return std::make_tuple(shift + B, new_root); |
406 | 11.3k | } else if (size) { |
407 | 5.31k | auto new_root = make_regular_sub_pos(root, shift, size) |
408 | 5.31k | .visit(push_tail_visitor<node_t>{}, tail); |
409 | 5.31k | return std::make_tuple(shift, new_root); |
410 | 6.04k | } else { |
411 | 6.04k | return std::make_tuple(shift, node_t::make_path(shift, tail)); |
412 | 6.04k | } |
413 | 25.9k | } |
414 | | |
415 | | void |
416 | | push_tail_mut(edit_t e, size_t tail_off, node_t* tail, count_t tail_size) |
417 | 1.73M | { |
418 | 1.73M | if (auto r = root->relaxed()) { |
419 | 665k | auto new_root = |
420 | 665k | make_relaxed_pos(root, shift, r) |
421 | 665k | .visit(push_tail_mut_visitor<node_t>{}, e, tail, tail_size); |
422 | 665k | if (new_root) { |
423 | 663k | root = new_root; |
424 | 663k | } else { |
425 | 1.62k | auto new_root = node_t::make_inner_r_e(e); |
426 | 1.62k | IMMER_TRY { |
427 | 1.62k | auto new_path = node_t::make_path_e(e, shift, tail); |
428 | 1.62k | new_root->inner()[0] = root; |
429 | 1.62k | new_root->inner()[1] = new_path; |
430 | 1.62k | new_root->relaxed()->d.sizes[0] = tail_off; |
431 | 1.62k | new_root->relaxed()->d.sizes[1] = tail_off + tail_size; |
432 | 1.62k | assert(tail_off); |
433 | 1.62k | assert(tail_size); |
434 | 1.62k | new_root->relaxed()->d.count = 2u; |
435 | 1.62k | root = new_root; |
436 | 1.62k | shift += B; |
437 | 1.62k | } |
438 | 1.62k | IMMER_CATCH (...) { |
439 | 0 | node_t::delete_inner_r_e(new_root); |
440 | 0 | IMMER_RETHROW; |
441 | 0 | } |
442 | 1.62k | } |
443 | 1.07M | } else if (tail_off == size_t{branches<B>} << shift) { |
444 | 5.13k | auto new_root = node_t::make_inner_e(e); |
445 | 5.13k | IMMER_TRY { |
446 | 5.13k | auto new_path = node_t::make_path_e(e, shift, tail); |
447 | 5.13k | new_root->inner()[0] = root; |
448 | 5.13k | new_root->inner()[1] = new_path; |
449 | 5.13k | root = new_root; |
450 | 5.13k | shift += B; |
451 | 5.13k | } |
452 | 5.13k | IMMER_CATCH (...) { |
453 | 0 | node_t::delete_inner_e(new_root); |
454 | 0 | IMMER_RETHROW; |
455 | 0 | } |
456 | 1.06M | } else if (tail_off) { |
457 | 1.06M | auto new_root = |
458 | 1.06M | make_regular_sub_pos(root, shift, tail_off) |
459 | 1.06M | .visit(push_tail_mut_visitor<node_t>{}, e, tail); |
460 | 1.06M | root = new_root; |
461 | 1.06M | } else { |
462 | 5.16k | auto new_root = node_t::make_path_e(e, shift, tail); |
463 | 5.16k | dec_empty_regular(root); |
464 | 5.16k | root = new_root; |
465 | 5.16k | } |
466 | 1.73M | } |
467 | | |
468 | | void ensure_mutable_tail(edit_t e, count_t n) |
469 | 1.43M | { |
470 | 1.43M | if (!tail->can_mutate(e)) { |
471 | 22.6k | auto new_tail = node_t::copy_leaf_e(e, tail, n); |
472 | 22.6k | dec_leaf(tail, n); |
473 | 22.6k | tail = new_tail; |
474 | 22.6k | } |
475 | 1.43M | } |
476 | | |
477 | | void push_back_mut(edit_t e, T value) |
478 | 57.1k | { |
479 | 57.1k | auto ts = tail_size(); |
480 | 57.1k | if (ts < branches<BL>) { |
481 | 37.2k | ensure_mutable_tail(e, ts); |
482 | 37.2k | new (&tail->leaf()[ts]) T{std::move(value)}; |
483 | 37.2k | } else { |
484 | 19.9k | using std::get; |
485 | 19.9k | auto new_tail = node_t::make_leaf_e(e, std::move(value)); |
486 | 19.9k | auto tail_off = tail_offset(); |
487 | 19.9k | IMMER_TRY { |
488 | 19.9k | push_tail_mut(e, tail_off, tail, ts); |
489 | 19.9k | tail = new_tail; |
490 | 19.9k | } |
491 | 19.9k | IMMER_CATCH (...) { |
492 | 0 | node_t::delete_leaf(new_tail, 1u); |
493 | 0 | IMMER_RETHROW; |
494 | 0 | } |
495 | 19.9k | } |
496 | 57.1k | ++size; |
497 | 57.1k | } |
498 | | |
499 | | rrbtree push_back(T value) const |
500 | 69.7k | { |
501 | 69.7k | auto ts = tail_size(); |
502 | 69.7k | if (ts < branches<BL>) { |
503 | 51.8k | auto new_tail = |
504 | 51.8k | node_t::copy_leaf_emplace(tail, ts, std::move(value)); |
505 | 51.8k | return {size + 1, shift, root->inc(), new_tail}; |
506 | 51.8k | } else { |
507 | 17.9k | using std::get; |
508 | 17.9k | auto new_tail = node_t::make_leaf_n(1u, std::move(value)); |
509 | 17.9k | auto tail_off = tail_offset(); |
510 | 17.9k | IMMER_TRY { |
511 | 17.9k | auto new_root = |
512 | 17.9k | push_tail(root, shift, tail_off, tail, size - tail_off); |
513 | 17.9k | tail->inc(); |
514 | 17.9k | return {size + 1, get<0>(new_root), get<1>(new_root), new_tail}; |
515 | 17.9k | } |
516 | 17.9k | IMMER_CATCH (...) { |
517 | 0 | node_t::delete_leaf(new_tail, 1u); |
518 | 0 | IMMER_RETHROW; |
519 | 0 | } |
520 | 17.9k | } |
521 | 69.7k | } |
522 | | |
523 | | std::tuple<const T*, size_t, size_t> region_for(size_t idx) const |
524 | | { |
525 | | using std::get; |
526 | | auto tail_off = tail_offset(); |
527 | | if (idx >= tail_off) { |
528 | | return std::make_tuple(tail->leaf(), tail_off, size); |
529 | | } else { |
530 | | auto subs = visit_maybe_relaxed_sub( |
531 | | root, shift, tail_off, region_for_visitor<T>(), idx); |
532 | | auto first = idx - get<1>(subs); |
533 | | auto end = first + get<2>(subs); |
534 | | return std::make_tuple(get<0>(subs), first, end); |
535 | | } |
536 | | } |
537 | | |
538 | | T& get_mut(edit_t e, size_t idx) |
539 | 11.8k | { |
540 | 11.8k | auto tail_off = tail_offset(); |
541 | 11.8k | if (idx >= tail_off) { |
542 | 663 | ensure_mutable_tail(e, size - tail_off); |
543 | 663 | return tail->leaf()[(idx - tail_off) & mask<BL>]; |
544 | 11.1k | } else { |
545 | 11.1k | return visit_maybe_relaxed_sub(root, |
546 | 11.1k | shift, |
547 | 11.1k | tail_off, |
548 | 11.1k | get_mut_visitor<node_t>{}, |
549 | 11.1k | idx, |
550 | 11.1k | e, |
551 | 11.1k | &root); |
552 | 11.1k | } |
553 | 11.8k | } |
554 | | |
555 | | const T& get(size_t index) const |
556 | | { |
557 | | return descend(get_visitor<T>(), index); |
558 | | } |
559 | | |
560 | | const T& get_check(size_t index) const |
561 | | { |
562 | | if (index >= size) |
563 | | IMMER_THROW(std::out_of_range{"out of range"}); |
564 | | return descend(get_visitor<T>(), index); |
565 | | } |
566 | | |
567 | | const T& front() const { return get(0); } |
568 | | |
569 | | const T& back() const { return get(size - 1); } |
570 | | |
571 | | template <typename FnT> |
572 | | void update_mut(edit_t e, size_t idx, FnT&& fn) |
573 | 11.8k | { |
574 | 11.8k | auto& elem = get_mut(e, idx); |
575 | 11.8k | elem = std::forward<FnT>(fn)(std::move(elem)); |
576 | 11.8k | } |
577 | | |
578 | | template <typename FnT> |
579 | | rrbtree update(size_t idx, FnT&& fn) const |
580 | 5.60k | { |
581 | 5.60k | auto tail_off = tail_offset(); |
582 | 5.60k | if (idx >= tail_off) { |
583 | 489 | auto tail_size = size - tail_off; |
584 | 489 | auto new_tail = |
585 | 489 | make_leaf_sub_pos(tail, tail_size) |
586 | 489 | .visit(update_visitor<node_t>{}, idx - tail_off, fn); |
587 | 489 | return {size, shift, root->inc(), new_tail}; |
588 | 5.11k | } else { |
589 | 5.11k | auto new_root = visit_maybe_relaxed_sub( |
590 | 5.11k | root, shift, tail_off, update_visitor<node_t>{}, idx, fn); |
591 | 5.11k | return {size, shift, new_root, tail->inc()}; |
592 | 5.11k | } |
593 | 5.60k | } |
594 | | |
595 | | void assoc_mut(edit_t e, size_t idx, T value) |
596 | | { |
597 | | update_mut(e, idx, [&](auto&&) { return std::move(value); }); |
598 | | } |
599 | | |
600 | | rrbtree assoc(size_t idx, T value) const |
601 | | { |
602 | | return update(idx, [&](auto&&) { return std::move(value); }); |
603 | | } |
604 | | |
605 | | void take_mut(edit_t e, size_t new_size) |
606 | 39.5k | { |
607 | 39.5k | auto tail_off = tail_offset(); |
608 | 39.5k | if (new_size == 0) { |
609 | 789 | *this = {}; |
610 | 38.7k | } else if (new_size >= size) { |
611 | 2.65k | return; |
612 | 36.0k | } else if (new_size > tail_off) { |
613 | 647 | auto ts = size - tail_off; |
614 | 647 | auto newts = new_size - tail_off; |
615 | 647 | if (tail->can_mutate(e)) { |
616 | 418 | detail::destroy_n(tail->leaf() + newts, ts - newts); |
617 | 418 | } else { |
618 | 229 | auto new_tail = node_t::copy_leaf_e(e, tail, newts); |
619 | 229 | dec_leaf(tail, ts); |
620 | 229 | tail = new_tail; |
621 | 229 | } |
622 | 647 | size = new_size; |
623 | 647 | return; |
624 | 35.4k | } else { |
625 | 35.4k | using std::get; |
626 | 35.4k | auto l = new_size - 1; |
627 | 35.4k | auto v = slice_right_mut_visitor<node_t>(); |
628 | 35.4k | auto r = visit_maybe_relaxed_sub(root, shift, tail_off, v, l, e); |
629 | 35.4k | auto new_shift = get<0>(r); |
630 | 35.4k | auto new_root = get<1>(r); |
631 | 35.4k | auto new_tail = get<3>(r); |
632 | 35.4k | if (new_root) { |
633 | 27.4k | root = new_root; |
634 | 27.4k | shift = new_shift; |
635 | 27.4k | } else { |
636 | 8.00k | root = empty_root(); |
637 | 8.00k | shift = BL; |
638 | 8.00k | } |
639 | 35.4k | dec_leaf(tail, size - tail_off); |
640 | 35.4k | size = new_size; |
641 | 35.4k | tail = new_tail; |
642 | 35.4k | return; |
643 | 35.4k | } |
644 | 39.5k | } |
645 | | |
646 | | rrbtree take(size_t new_size) const |
647 | 12.1k | { |
648 | 12.1k | auto tail_off = tail_offset(); |
649 | 12.1k | if (new_size == 0) { |
650 | 1.49k | return {}; |
651 | 10.6k | } else if (new_size >= size) { |
652 | 329 | return *this; |
653 | 10.3k | } else if (new_size > tail_off) { |
654 | 223 | auto new_tail = node_t::copy_leaf(tail, new_size - tail_off); |
655 | 223 | return {new_size, shift, root->inc(), new_tail}; |
656 | 10.1k | } else { |
657 | 10.1k | using std::get; |
658 | 10.1k | auto l = new_size - 1; |
659 | 10.1k | auto v = slice_right_visitor<node_t>(); |
660 | 10.1k | auto r = visit_maybe_relaxed_sub(root, shift, tail_off, v, l); |
661 | 10.1k | auto new_shift = get<0>(r); |
662 | 10.1k | auto new_root = get<1>(r); |
663 | 10.1k | auto new_tail = get<3>(r); |
664 | 10.1k | if (new_root) { |
665 | 7.80k | IMMER_ASSERT_TAGGED(new_root->compute_shift() == get<0>(r)); |
666 | 7.80k | assert(new_root->check(new_shift, new_size - get<2>(r))); |
667 | 7.80k | return {new_size, new_shift, new_root, new_tail}; |
668 | 7.80k | } else { |
669 | 2.29k | return {new_size, BL, empty_root(), new_tail}; |
670 | 2.29k | } |
671 | 10.1k | } |
672 | 12.1k | } |
673 | | |
674 | | void drop_mut(edit_t e, size_t elems) |
675 | | { |
676 | | using std::get; |
677 | | auto tail_off = tail_offset(); |
678 | | if (elems == 0) { |
679 | | return; |
680 | | } else if (elems >= size) { |
681 | | *this = {}; |
682 | | } else if (elems == tail_off) { |
683 | | dec_inner(root, shift, tail_off); |
684 | | shift = BL; |
685 | | root = empty_root(); |
686 | | size -= elems; |
687 | | return; |
688 | | } else if (elems > tail_off) { |
689 | | auto v = slice_left_mut_visitor<node_t>(); |
690 | | tail = get<1>(make_leaf_sub_pos(tail, size - tail_off) |
691 | | .visit(v, elems - tail_off, e)); |
692 | | if (tail_off) { |
693 | | dec_inner(root, shift, tail_off); |
694 | | shift = BL; |
695 | | root = empty_root(); |
696 | | } |
697 | | size -= elems; |
698 | | return; |
699 | | } else { |
700 | | auto v = slice_left_mut_visitor<node_t>(); |
701 | | auto r = |
702 | | visit_maybe_relaxed_sub(root, shift, tail_off, v, elems, e); |
703 | | shift = get<0>(r); |
704 | | root = get<1>(r); |
705 | | size -= elems; |
706 | | return; |
707 | | } |
708 | | } |
709 | | |
710 | | rrbtree drop(size_t elems) const |
711 | 18.1k | { |
712 | 18.1k | if (elems == 0) { |
713 | 1.26k | return *this; |
714 | 16.9k | } else if (elems >= size) { |
715 | 212 | return {}; |
716 | 16.7k | } else if (elems == tail_offset()) { |
717 | 200 | return {size - elems, BL, empty_root(), tail->inc()}; |
718 | 16.5k | } else if (elems > tail_offset()) { |
719 | 583 | auto tail_off = tail_offset(); |
720 | 583 | auto new_tail = |
721 | 583 | node_t::copy_leaf(tail, elems - tail_off, size - tail_off); |
722 | 583 | return {size - elems, BL, empty_root(), new_tail}; |
723 | 15.9k | } else { |
724 | 15.9k | using std::get; |
725 | 15.9k | auto v = slice_left_visitor<node_t>(); |
726 | 15.9k | auto r = |
727 | 15.9k | visit_maybe_relaxed_sub(root, shift, tail_offset(), v, elems); |
728 | 15.9k | auto new_root = get<1>(r); |
729 | 15.9k | auto new_shift = get<0>(r); |
730 | 15.9k | return {size - elems, new_shift, new_root, tail->inc()}; |
731 | 15.9k | } |
732 | 18.1k | } |
733 | | |
734 | | rrbtree concat(const rrbtree& r) const |
735 | 59.6k | { |
736 | 59.6k | assert(r.size + size <= max_size()); |
737 | 59.6k | using std::get; |
738 | 59.6k | if (size == 0) |
739 | 717 | return r; |
740 | 58.9k | else if (r.size == 0) |
741 | 392 | return *this; |
742 | 58.5k | else if (r.tail_offset() == 0) { |
743 | | // just concat the tail, similar to push_back |
744 | 6.65k | auto tail_offst = tail_offset(); |
745 | 6.65k | auto tail_size = size - tail_offst; |
746 | 6.65k | if (tail_size == branches<BL>) { |
747 | 2.40k | auto new_root = |
748 | 2.40k | push_tail(root, shift, tail_offst, tail, tail_size); |
749 | 2.40k | tail->inc(); |
750 | 2.40k | return {size + r.size, |
751 | 2.40k | get<0>(new_root), |
752 | 2.40k | get<1>(new_root), |
753 | 2.40k | r.tail->inc()}; |
754 | 4.24k | } else if (tail_size + r.size <= branches<BL>) { |
755 | 2.69k | auto new_tail = |
756 | 2.69k | node_t::copy_leaf(tail, tail_size, r.tail, r.size); |
757 | 2.69k | return {size + r.size, shift, root->inc(), new_tail}; |
758 | 2.69k | } else { |
759 | 1.54k | auto remaining = branches<BL> - tail_size; |
760 | 1.54k | auto add_tail = |
761 | 1.54k | node_t::copy_leaf(tail, tail_size, r.tail, remaining); |
762 | 1.54k | IMMER_TRY { |
763 | 1.54k | auto new_tail = |
764 | 1.54k | node_t::copy_leaf(r.tail, remaining, r.size); |
765 | 1.54k | IMMER_TRY { |
766 | 1.54k | auto new_root = push_tail( |
767 | 1.54k | root, shift, tail_offst, add_tail, branches<BL>); |
768 | 1.54k | return {size + r.size, |
769 | 1.54k | get<0>(new_root), |
770 | 1.54k | get<1>(new_root), |
771 | 1.54k | new_tail}; |
772 | 1.54k | } |
773 | 1.54k | IMMER_CATCH (...) { |
774 | 0 | node_t::delete_leaf(new_tail, r.size - remaining); |
775 | 0 | IMMER_RETHROW; |
776 | 0 | } |
777 | 1.54k | } |
778 | 1.54k | IMMER_CATCH (...) { |
779 | 0 | node_t::delete_leaf(add_tail, branches<BL>); |
780 | 0 | IMMER_RETHROW; |
781 | 0 | } |
782 | 1.54k | } |
783 | 51.8k | } else if (tail_offset() == 0) { |
784 | 2.93k | auto tail_offst = tail_offset(); |
785 | 2.93k | auto tail_size = size - tail_offst; |
786 | 2.93k | auto concated = |
787 | 2.93k | concat_trees(tail, tail_size, r.root, r.shift, r.tail_offset()); |
788 | 2.93k | auto new_shift = concated.shift(); |
789 | 2.93k | auto new_root = concated.node(); |
790 | 2.93k | IMMER_ASSERT_TAGGED(new_shift == new_root->compute_shift()); |
791 | 2.93k | assert(new_root->check(new_shift, size + r.tail_offset())); |
792 | 2.93k | return {size + r.size, new_shift, new_root, r.tail->inc()}; |
793 | 48.9k | } else { |
794 | 48.9k | auto tail_offst = tail_offset(); |
795 | 48.9k | auto tail_size = size - tail_offst; |
796 | 48.9k | auto concated = concat_trees(root, |
797 | 48.9k | shift, |
798 | 48.9k | tail_offst, |
799 | 48.9k | tail, |
800 | 48.9k | tail_size, |
801 | 48.9k | r.root, |
802 | 48.9k | r.shift, |
803 | 48.9k | r.tail_offset()); |
804 | 48.9k | auto new_shift = concated.shift(); |
805 | 48.9k | auto new_root = concated.node(); |
806 | 48.9k | IMMER_ASSERT_TAGGED(new_shift == new_root->compute_shift()); |
807 | 48.9k | assert(new_root->check(new_shift, size + r.tail_offset())); |
808 | 48.9k | return {size + r.size, new_shift, new_root, r.tail->inc()}; |
809 | 48.9k | } |
810 | 59.6k | } |
811 | | |
812 | | constexpr static bool supports_transient_concat = |
813 | | !std::is_empty<edit_t>::value; |
814 | | |
815 | | friend void concat_mut_l(rrbtree& l, edit_t el, const rrbtree& r) |
816 | 1.81M | { |
817 | 1.81M | assert(&l != &r); |
818 | 1.81M | assert(r.size < (std::numeric_limits<size_t>::max() - l.size)); |
819 | 1.81M | using std::get; |
820 | 1.81M | if (l.size == 0) |
821 | 2.34k | l = r; |
822 | 1.81M | else if (r.size == 0) |
823 | 12.4k | return; |
824 | 1.80M | else if (r.tail_offset() == 0) { |
825 | | // just concat the tail, similar to push_back |
826 | 1.72M | auto tail_offst = l.tail_offset(); |
827 | 1.72M | auto tail_size = l.size - tail_offst; |
828 | 1.72M | if (tail_size == branches<BL>) { |
829 | 329k | l.push_tail_mut(el, tail_offst, l.tail, tail_size); |
830 | 329k | l.tail = r.tail->inc(); |
831 | 329k | l.size += r.size; |
832 | 329k | return; |
833 | 1.39M | } else if (tail_size + r.size <= branches<BL>) { |
834 | 3.04k | l.ensure_mutable_tail(el, tail_size); |
835 | 3.04k | detail::uninitialized_copy(r.tail->leaf(), |
836 | 3.04k | r.tail->leaf() + r.size, |
837 | 3.04k | l.tail->leaf() + tail_size); |
838 | 3.04k | l.size += r.size; |
839 | 3.04k | return; |
840 | 1.38M | } else { |
841 | 1.38M | auto remaining = branches<BL> - tail_size; |
842 | 1.38M | l.ensure_mutable_tail(el, tail_size); |
843 | 1.38M | detail::uninitialized_copy(r.tail->leaf(), |
844 | 1.38M | r.tail->leaf() + remaining, |
845 | 1.38M | l.tail->leaf() + tail_size); |
846 | 1.38M | IMMER_TRY { |
847 | 1.38M | auto new_tail = |
848 | 1.38M | node_t::copy_leaf_e(el, r.tail, remaining, r.size); |
849 | 1.38M | IMMER_TRY { |
850 | 1.38M | l.push_tail_mut(el, tail_offst, l.tail, branches<BL>); |
851 | 1.38M | l.tail = new_tail; |
852 | 1.38M | l.size += r.size; |
853 | 1.38M | return; |
854 | 1.38M | } |
855 | 1.38M | IMMER_CATCH (...) { |
856 | 0 | node_t::delete_leaf(new_tail, r.size - remaining); |
857 | 0 | IMMER_RETHROW; |
858 | 0 | } |
859 | 1.38M | } |
860 | 1.38M | IMMER_CATCH (...) { |
861 | 0 | detail::destroy_n(r.tail->leaf() + tail_size, remaining); |
862 | 0 | IMMER_RETHROW; |
863 | 0 | } |
864 | 1.38M | } |
865 | 1.72M | } else if (l.tail_offset() == 0) { |
866 | 3.20k | if (supports_transient_concat) { |
867 | 3.20k | auto tail_offst = l.tail_offset(); |
868 | 3.20k | auto tail_size = l.size - tail_offst; |
869 | 3.20k | auto concated = |
870 | 3.20k | concat_trees_mut(el, |
871 | 3.20k | el, |
872 | 3.20k | l.tail, |
873 | 3.20k | tail_size, |
874 | 3.20k | MemoryPolicy::transience_t::noone, |
875 | 3.20k | r.root, |
876 | 3.20k | r.shift, |
877 | 3.20k | r.tail_offset()); |
878 | 3.20k | IMMER_ASSERT_TAGGED(concated.shift() == |
879 | 3.20k | concated.node()->compute_shift()); |
880 | 3.20k | l.size += r.size; |
881 | 3.20k | l.shift = concated.shift(); |
882 | 3.20k | l.root = concated.node(); |
883 | 3.20k | l.tail = r.tail; |
884 | 3.20k | assert(l.check_tree()); |
885 | 3.20k | } else { |
886 | 0 | auto tail_offst = l.tail_offset(); |
887 | 0 | auto tail_size = l.size - tail_offst; |
888 | 0 | auto concated = concat_trees( |
889 | 0 | l.tail, tail_size, r.root, r.shift, r.tail_offset()); |
890 | 0 | l = {l.size + r.size, |
891 | 0 | concated.shift(), |
892 | 0 | concated.node(), |
893 | 0 | r.tail->inc()}; |
894 | 0 | assert(l.check_tree()); |
895 | 0 | return; |
896 | 0 | } |
897 | 77.4k | } else { |
898 | 77.4k | if (supports_transient_concat) { |
899 | 77.4k | auto tail_offst = l.tail_offset(); |
900 | 77.4k | auto tail_size = l.size - tail_offst; |
901 | 77.4k | assert(l.check_tree()); |
902 | 77.4k | assert(r.check_tree()); |
903 | 77.4k | auto concated = |
904 | 77.4k | concat_trees_mut(el, |
905 | 77.4k | el, |
906 | 77.4k | l.root, |
907 | 77.4k | l.shift, |
908 | 77.4k | tail_offst, |
909 | 77.4k | l.tail, |
910 | 77.4k | tail_size, |
911 | 77.4k | MemoryPolicy::transience_t::noone, |
912 | 77.4k | r.root, |
913 | 77.4k | r.shift, |
914 | 77.4k | r.tail_offset()); |
915 | 77.4k | IMMER_ASSERT_TAGGED(concated.shift() == |
916 | 77.4k | concated.node()->compute_shift()); |
917 | 77.4k | l.size += r.size; |
918 | 77.4k | l.shift = concated.shift(); |
919 | 77.4k | l.root = concated.node(); |
920 | 77.4k | l.tail = r.tail; |
921 | 77.4k | assert(l.check_tree()); |
922 | 77.4k | } else { |
923 | 0 | auto tail_offst = l.tail_offset(); |
924 | 0 | auto tail_size = l.size - tail_offst; |
925 | 0 | auto concated = concat_trees(l.root, |
926 | 0 | l.shift, |
927 | 0 | tail_offst, |
928 | 0 | l.tail, |
929 | 0 | tail_size, |
930 | 0 | r.root, |
931 | 0 | r.shift, |
932 | 0 | r.tail_offset()); |
933 | 0 | l = {l.size + r.size, |
934 | 0 | concated.shift(), |
935 | 0 | concated.node(), |
936 | 0 | r.tail->inc()}; |
937 | 0 | } |
938 | 77.4k | } |
939 | 1.81M | } |
940 | | |
941 | | friend void concat_mut_r(const rrbtree& l, rrbtree& r, edit_t er) |
942 | 33.1k | { |
943 | 33.1k | assert(&l != &r); |
944 | 33.1k | assert(r.size < (std::numeric_limits<size_t>::max() - l.size)); |
945 | 33.1k | using std::get; |
946 | 33.1k | if (r.size == 0) |
947 | 3.26k | r = std::move(l); |
948 | 29.8k | else if (l.size == 0) |
949 | 445 | return; |
950 | 29.4k | else if (r.tail_offset() == 0) { |
951 | | // just concat the tail, similar to push_back |
952 | 6.27k | auto tail_offst = l.tail_offset(); |
953 | 6.27k | auto tail_size = l.size - tail_offst; |
954 | 6.27k | if (tail_size == branches<BL>) { |
955 | | // this could be improved by making sure that the |
956 | | // newly created nodes as part of the `push_tail()` |
957 | | // are tagged with `er` |
958 | 1.27k | auto res = |
959 | 1.27k | l.push_tail(l.root, l.shift, tail_offst, l.tail, tail_size); |
960 | 1.27k | l.tail->inc(); // note: leak if mutably concatenated |
961 | | // with itself, but this is forbidden |
962 | | // by the interface |
963 | 1.27k | r = {l.size + r.size, get<0>(res), get<1>(res), r.tail->inc()}; |
964 | 1.27k | return; |
965 | 5.00k | } else if (tail_size + r.size <= branches<BL>) { |
966 | | // doing this in a exception way mutating way is very |
967 | | // tricky while potential performance gains are |
968 | | // minimal (we need to move every element of the right |
969 | | // tail anyways to make space for the left tail) |
970 | | // |
971 | | // we could however improve this by at least moving the |
972 | | // elements of the right tail... |
973 | 2.80k | auto new_tail = |
974 | 2.80k | node_t::copy_leaf(l.tail, tail_size, r.tail, r.size); |
975 | 2.80k | r = {l.size + r.size, l.shift, l.root->inc(), new_tail}; |
976 | 2.80k | return; |
977 | 2.80k | } else { |
978 | | // like the immutable version |
979 | 2.20k | auto remaining = branches<BL> - tail_size; |
980 | 2.20k | auto add_tail = node_t::copy_leaf_e( |
981 | 2.20k | er, l.tail, tail_size, r.tail, remaining); |
982 | 2.20k | IMMER_TRY { |
983 | 2.20k | auto new_tail = |
984 | 2.20k | node_t::copy_leaf_e(er, r.tail, remaining, r.size); |
985 | 2.20k | IMMER_TRY { |
986 | | // this could be improved by making sure that the |
987 | | // newly created nodes as part of the `push_tail()` |
988 | | // are tagged with `er` |
989 | 2.20k | auto new_root = l.push_tail(l.root, |
990 | 2.20k | l.shift, |
991 | 2.20k | tail_offst, |
992 | 2.20k | add_tail, |
993 | 2.20k | branches<BL>); |
994 | 2.20k | r = {l.size + r.size, |
995 | 2.20k | get<0>(new_root), |
996 | 2.20k | get<1>(new_root), |
997 | 2.20k | new_tail}; |
998 | 2.20k | return; |
999 | 2.20k | } |
1000 | 2.20k | IMMER_CATCH (...) { |
1001 | 0 | node_t::delete_leaf(new_tail, r.size - remaining); |
1002 | 0 | IMMER_RETHROW; |
1003 | 0 | } |
1004 | 2.20k | } |
1005 | 2.20k | IMMER_CATCH (...) { |
1006 | 0 | node_t::delete_leaf(add_tail, branches<BL>); |
1007 | 0 | IMMER_RETHROW; |
1008 | 0 | } |
1009 | 2.20k | } |
1010 | 23.1k | } else if (l.tail_offset() == 0) { |
1011 | 2.84k | if (supports_transient_concat) { |
1012 | 2.84k | auto tail_offst = l.tail_offset(); |
1013 | 2.84k | auto tail_size = l.size - tail_offst; |
1014 | 2.84k | auto concated = |
1015 | 2.84k | concat_trees_mut(er, |
1016 | 2.84k | MemoryPolicy::transience_t::noone, |
1017 | 2.84k | l.tail, |
1018 | 2.84k | tail_size, |
1019 | 2.84k | er, |
1020 | 2.84k | r.root, |
1021 | 2.84k | r.shift, |
1022 | 2.84k | r.tail_offset()); |
1023 | 2.84k | IMMER_ASSERT_TAGGED(concated.shift() == |
1024 | 2.84k | concated.node()->compute_shift()); |
1025 | 2.84k | r.size += l.size; |
1026 | 2.84k | r.shift = concated.shift(); |
1027 | 2.84k | r.root = concated.node(); |
1028 | 2.84k | assert(r.check_tree()); |
1029 | 2.84k | } else { |
1030 | 0 | auto tail_offst = l.tail_offset(); |
1031 | 0 | auto tail_size = l.size - tail_offst; |
1032 | 0 | auto concated = concat_trees( |
1033 | 0 | l.tail, tail_size, r.root, r.shift, r.tail_offset()); |
1034 | 0 | r = {l.size + r.size, |
1035 | 0 | concated.shift(), |
1036 | 0 | concated.node(), |
1037 | 0 | r.tail->inc()}; |
1038 | 0 | return; |
1039 | 0 | } |
1040 | 20.3k | } else { |
1041 | 20.3k | if (supports_transient_concat) { |
1042 | 20.3k | auto tail_offst = l.tail_offset(); |
1043 | 20.3k | auto tail_size = l.size - tail_offst; |
1044 | 20.3k | auto concated = |
1045 | 20.3k | concat_trees_mut(er, |
1046 | 20.3k | MemoryPolicy::transience_t::noone, |
1047 | 20.3k | l.root, |
1048 | 20.3k | l.shift, |
1049 | 20.3k | tail_offst, |
1050 | 20.3k | l.tail, |
1051 | 20.3k | tail_size, |
1052 | 20.3k | er, |
1053 | 20.3k | r.root, |
1054 | 20.3k | r.shift, |
1055 | 20.3k | r.tail_offset()); |
1056 | 20.3k | IMMER_ASSERT_TAGGED(concated.shift() == |
1057 | 20.3k | concated.node()->compute_shift()); |
1058 | 20.3k | r.size += l.size; |
1059 | 20.3k | r.shift = concated.shift(); |
1060 | 20.3k | r.root = concated.node(); |
1061 | 20.3k | assert(r.check_tree()); |
1062 | 20.3k | return; |
1063 | 20.3k | } else { |
1064 | 0 | auto tail_offst = l.tail_offset(); |
1065 | 0 | auto tail_size = l.size - tail_offst; |
1066 | 0 | auto concated = concat_trees(l.root, |
1067 | 0 | l.shift, |
1068 | 0 | tail_offst, |
1069 | 0 | l.tail, |
1070 | 0 | tail_size, |
1071 | 0 | r.root, |
1072 | 0 | r.shift, |
1073 | 0 | r.tail_offset()); |
1074 | 0 | r = {l.size + r.size, |
1075 | 0 | concated.shift(), |
1076 | 0 | concated.node(), |
1077 | 0 | r.tail->inc()}; |
1078 | 0 | return; |
1079 | 0 | } |
1080 | 20.3k | } |
1081 | 33.1k | } |
1082 | | |
1083 | | friend void concat_mut_lr_l(rrbtree& l, edit_t el, rrbtree& r, edit_t er) |
1084 | 5.07k | { |
1085 | 5.07k | assert(&l != &r); |
1086 | 5.07k | assert(r.size < (std::numeric_limits<size_t>::max() - l.size)); |
1087 | 5.07k | using std::get; |
1088 | 5.07k | if (l.size == 0) |
1089 | 414 | l = r; |
1090 | 4.65k | else if (r.size == 0) |
1091 | 357 | return; |
1092 | 4.30k | else if (r.tail_offset() == 0) { |
1093 | | // just concat the tail, similar to push_back |
1094 | 2.37k | auto tail_offst = l.tail_offset(); |
1095 | 2.37k | auto tail_size = l.size - tail_offst; |
1096 | 2.37k | if (tail_size == branches<BL>) { |
1097 | 434 | l.push_tail_mut(el, tail_offst, l.tail, tail_size); |
1098 | 434 | l.tail = r.tail->inc(); |
1099 | 434 | l.size += r.size; |
1100 | 434 | return; |
1101 | 1.94k | } else if (tail_size + r.size <= branches<BL>) { |
1102 | 1.19k | l.ensure_mutable_tail(el, tail_size); |
1103 | 1.19k | if (r.tail->can_mutate(er)) |
1104 | 808 | detail::uninitialized_move(r.tail->leaf(), |
1105 | 808 | r.tail->leaf() + r.size, |
1106 | 808 | l.tail->leaf() + tail_size); |
1107 | 389 | else |
1108 | 389 | detail::uninitialized_copy(r.tail->leaf(), |
1109 | 389 | r.tail->leaf() + r.size, |
1110 | 389 | l.tail->leaf() + tail_size); |
1111 | 1.19k | l.size += r.size; |
1112 | 1.19k | return; |
1113 | 1.19k | } else { |
1114 | 744 | auto remaining = branches<BL> - tail_size; |
1115 | 744 | l.ensure_mutable_tail(el, tail_size); |
1116 | 744 | if (r.tail->can_mutate(er)) |
1117 | 199 | detail::uninitialized_move(r.tail->leaf(), |
1118 | 199 | r.tail->leaf() + remaining, |
1119 | 199 | l.tail->leaf() + tail_size); |
1120 | 545 | else |
1121 | 545 | detail::uninitialized_copy(r.tail->leaf(), |
1122 | 545 | r.tail->leaf() + remaining, |
1123 | 545 | l.tail->leaf() + tail_size); |
1124 | 744 | IMMER_TRY { |
1125 | 744 | auto new_tail = |
1126 | 744 | node_t::copy_leaf_e(el, r.tail, remaining, r.size); |
1127 | 744 | IMMER_TRY { |
1128 | 744 | l.push_tail_mut(el, tail_offst, l.tail, branches<BL>); |
1129 | 744 | l.tail = new_tail; |
1130 | 744 | l.size += r.size; |
1131 | 744 | return; |
1132 | 744 | } |
1133 | 744 | IMMER_CATCH (...) { |
1134 | 0 | node_t::delete_leaf(new_tail, r.size - remaining); |
1135 | 0 | IMMER_RETHROW; |
1136 | 0 | } |
1137 | 744 | } |
1138 | 744 | IMMER_CATCH (...) { |
1139 | 0 | detail::destroy_n(r.tail->leaf() + tail_size, remaining); |
1140 | 0 | IMMER_RETHROW; |
1141 | 0 | } |
1142 | 744 | } |
1143 | 2.37k | } else if (l.tail_offset() == 0) { |
1144 | 657 | if (supports_transient_concat) { |
1145 | 657 | auto tail_offst = l.tail_offset(); |
1146 | 657 | auto tail_size = l.size - tail_offst; |
1147 | 657 | auto concated = concat_trees_mut(el, |
1148 | 657 | el, |
1149 | 657 | l.tail, |
1150 | 657 | tail_size, |
1151 | 657 | er, |
1152 | 657 | r.root, |
1153 | 657 | r.shift, |
1154 | 657 | r.tail_offset()); |
1155 | 657 | IMMER_ASSERT_TAGGED(concated.shift() == |
1156 | 657 | concated.node()->compute_shift()); |
1157 | 657 | l.size += r.size; |
1158 | 657 | l.shift = concated.shift(); |
1159 | 657 | l.root = concated.node(); |
1160 | 657 | l.tail = r.tail; |
1161 | 657 | assert(l.check_tree()); |
1162 | 657 | r.hard_reset(); |
1163 | 657 | return; |
1164 | 657 | } else { |
1165 | 0 | auto tail_offst = l.tail_offset(); |
1166 | 0 | auto tail_size = l.size - tail_offst; |
1167 | 0 | auto concated = concat_trees( |
1168 | 0 | l.tail, tail_size, r.root, r.shift, r.tail_offset()); |
1169 | 0 | l = {l.size + r.size, |
1170 | 0 | concated.shift(), |
1171 | 0 | concated.node(), |
1172 | 0 | r.tail->inc()}; |
1173 | 0 | return; |
1174 | 0 | } |
1175 | 1.27k | } else { |
1176 | 1.27k | if (supports_transient_concat) { |
1177 | 1.27k | auto tail_offst = l.tail_offset(); |
1178 | 1.27k | auto tail_size = l.size - tail_offst; |
1179 | 1.27k | auto concated = concat_trees_mut(el, |
1180 | 1.27k | el, |
1181 | 1.27k | l.root, |
1182 | 1.27k | l.shift, |
1183 | 1.27k | tail_offst, |
1184 | 1.27k | l.tail, |
1185 | 1.27k | tail_size, |
1186 | 1.27k | er, |
1187 | 1.27k | r.root, |
1188 | 1.27k | r.shift, |
1189 | 1.27k | r.tail_offset()); |
1190 | 1.27k | IMMER_ASSERT_TAGGED(concated.shift() == |
1191 | 1.27k | concated.node()->compute_shift()); |
1192 | 1.27k | l.size += r.size; |
1193 | 1.27k | l.shift = concated.shift(); |
1194 | 1.27k | l.root = concated.node(); |
1195 | 1.27k | l.tail = r.tail; |
1196 | 1.27k | assert(l.check_tree()); |
1197 | 1.27k | r.hard_reset(); |
1198 | 1.27k | return; |
1199 | 1.27k | } else { |
1200 | 0 | auto tail_offst = l.tail_offset(); |
1201 | 0 | auto tail_size = l.size - tail_offst; |
1202 | 0 | auto concated = concat_trees(l.root, |
1203 | 0 | l.shift, |
1204 | 0 | tail_offst, |
1205 | 0 | l.tail, |
1206 | 0 | tail_size, |
1207 | 0 | r.root, |
1208 | 0 | r.shift, |
1209 | 0 | r.tail_offset()); |
1210 | 0 | l = {l.size + r.size, |
1211 | 0 | concated.shift(), |
1212 | 0 | concated.node(), |
1213 | 0 | r.tail->inc()}; |
1214 | 0 | return; |
1215 | 0 | } |
1216 | 1.27k | } |
1217 | 5.07k | } |
1218 | | |
1219 | | friend void concat_mut_lr_r(rrbtree& l, edit_t el, rrbtree& r, edit_t er) |
1220 | 3.96k | { |
1221 | 3.96k | assert(&l != &r); |
1222 | 3.96k | assert(r.size < (std::numeric_limits<size_t>::max() - l.size)); |
1223 | 3.96k | using std::get; |
1224 | 3.96k | if (r.size == 0) |
1225 | 312 | r = l; |
1226 | 3.65k | else if (l.size == 0) |
1227 | 301 | return; |
1228 | 3.35k | else if (r.tail_offset() == 0) { |
1229 | | // just concat the tail, similar to push_back |
1230 | 1.14k | auto tail_offst = l.tail_offset(); |
1231 | 1.14k | auto tail_size = l.size - tail_offst; |
1232 | 1.14k | if (tail_size == branches<BL>) { |
1233 | | // this could be improved by making sure that the |
1234 | | // newly created nodes as part of the `push_tail()` |
1235 | | // are tagged with `er` |
1236 | 319 | auto res = |
1237 | 319 | l.push_tail(l.root, l.shift, tail_offst, l.tail, tail_size); |
1238 | 319 | r = {l.size + r.size, get<0>(res), get<1>(res), r.tail->inc()}; |
1239 | 319 | return; |
1240 | 822 | } else if (tail_size + r.size <= branches<BL>) { |
1241 | | // doing this in a exception way mutating way is very |
1242 | | // tricky while potential performance gains are |
1243 | | // minimal (we need to move every element of the right |
1244 | | // tail anyways to make space for the left tail) |
1245 | | // |
1246 | | // we could however improve this by at least moving the |
1247 | | // elements of the mutable tails... |
1248 | 574 | auto new_tail = |
1249 | 574 | node_t::copy_leaf(l.tail, tail_size, r.tail, r.size); |
1250 | 574 | r = {l.size + r.size, l.shift, l.root->inc(), new_tail}; |
1251 | 574 | return; |
1252 | 574 | } else { |
1253 | | // like the immutable version. |
1254 | | // we could improve this also by moving elements |
1255 | | // instead of just copying them |
1256 | 248 | auto remaining = branches<BL> - tail_size; |
1257 | 248 | auto add_tail = node_t::copy_leaf_e( |
1258 | 248 | er, l.tail, tail_size, r.tail, remaining); |
1259 | 248 | IMMER_TRY { |
1260 | 248 | auto new_tail = |
1261 | 248 | node_t::copy_leaf_e(er, r.tail, remaining, r.size); |
1262 | 248 | IMMER_TRY { |
1263 | | // this could be improved by making sure that the |
1264 | | // newly created nodes as part of the `push_tail()` |
1265 | | // are tagged with `er` |
1266 | 248 | auto new_root = l.push_tail(l.root, |
1267 | 248 | l.shift, |
1268 | 248 | tail_offst, |
1269 | 248 | add_tail, |
1270 | 248 | branches<BL>); |
1271 | 248 | r = {l.size + r.size, |
1272 | 248 | get<0>(new_root), |
1273 | 248 | get<1>(new_root), |
1274 | 248 | new_tail}; |
1275 | 248 | return; |
1276 | 248 | } |
1277 | 248 | IMMER_CATCH (...) { |
1278 | 0 | node_t::delete_leaf(new_tail, r.size - remaining); |
1279 | 0 | IMMER_RETHROW; |
1280 | 0 | } |
1281 | 248 | } |
1282 | 248 | IMMER_CATCH (...) { |
1283 | 0 | node_t::delete_leaf(add_tail, branches<BL>); |
1284 | 0 | IMMER_RETHROW; |
1285 | 0 | } |
1286 | 248 | } |
1287 | 2.21k | } else if (l.tail_offset() == 0) { |
1288 | 669 | if (supports_transient_concat) { |
1289 | 669 | auto tail_offst = l.tail_offset(); |
1290 | 669 | auto tail_size = l.size - tail_offst; |
1291 | 669 | auto concated = concat_trees_mut(er, |
1292 | 669 | el, |
1293 | 669 | l.tail, |
1294 | 669 | tail_size, |
1295 | 669 | er, |
1296 | 669 | r.root, |
1297 | 669 | r.shift, |
1298 | 669 | r.tail_offset()); |
1299 | 669 | IMMER_ASSERT_TAGGED(concated.shift() == |
1300 | 669 | concated.node()->compute_shift()); |
1301 | 669 | assert(concated.node()->check(concated.shift(), |
1302 | 669 | l.size + r.tail_offset())); |
1303 | 669 | r.size += l.size; |
1304 | 669 | r.shift = concated.shift(); |
1305 | 669 | r.root = concated.node(); |
1306 | 669 | assert(r.check_tree()); |
1307 | 669 | l.hard_reset(); |
1308 | 669 | } else { |
1309 | 0 | auto tail_offst = l.tail_offset(); |
1310 | 0 | auto tail_size = l.size - tail_offst; |
1311 | 0 | auto concated = concat_trees( |
1312 | 0 | l.tail, tail_size, r.root, r.shift, r.tail_offset()); |
1313 | 0 | r = {l.size + r.size, |
1314 | 0 | concated.shift(), |
1315 | 0 | concated.node(), |
1316 | 0 | r.tail->inc()}; |
1317 | 0 | return; |
1318 | 0 | } |
1319 | 1.54k | } else { |
1320 | 1.54k | if (supports_transient_concat) { |
1321 | 1.54k | auto tail_offst = l.tail_offset(); |
1322 | 1.54k | auto tail_size = l.size - tail_offst; |
1323 | 1.54k | auto concated = concat_trees_mut(er, |
1324 | 1.54k | el, |
1325 | 1.54k | l.root, |
1326 | 1.54k | l.shift, |
1327 | 1.54k | tail_offst, |
1328 | 1.54k | l.tail, |
1329 | 1.54k | tail_size, |
1330 | 1.54k | er, |
1331 | 1.54k | r.root, |
1332 | 1.54k | r.shift, |
1333 | 1.54k | r.tail_offset()); |
1334 | 1.54k | IMMER_ASSERT_TAGGED(concated.shift() == |
1335 | 1.54k | concated.node()->compute_shift()); |
1336 | 1.54k | assert(concated.node()->check(concated.shift(), |
1337 | 1.54k | l.size + r.tail_offset())); |
1338 | 1.54k | r.size += l.size; |
1339 | 1.54k | r.shift = concated.shift(); |
1340 | 1.54k | r.root = concated.node(); |
1341 | 1.54k | assert(r.check_tree()); |
1342 | 1.54k | l.hard_reset(); |
1343 | 1.54k | } else { |
1344 | 0 | auto tail_offst = l.tail_offset(); |
1345 | 0 | auto tail_size = l.size - tail_offst; |
1346 | 0 | auto concated = concat_trees(l.root, |
1347 | 0 | l.shift, |
1348 | 0 | tail_offst, |
1349 | 0 | l.tail, |
1350 | 0 | tail_size, |
1351 | 0 | r.root, |
1352 | 0 | r.shift, |
1353 | 0 | r.tail_offset()); |
1354 | 0 | r = {l.size + r.size, |
1355 | 0 | concated.shift(), |
1356 | 0 | concated.node(), |
1357 | 0 | r.tail->inc()}; |
1358 | 0 | } |
1359 | 1.54k | } |
1360 | 3.96k | } |
1361 | | |
1362 | | void hard_reset() |
1363 | 4.13k | { |
1364 | 4.13k | assert(supports_transient_concat); |
1365 | 4.13k | size = 0; |
1366 | 4.13k | shift = BL; |
1367 | 4.13k | root = empty_root(); |
1368 | 4.13k | tail = empty_tail(); |
1369 | 4.13k | } |
1370 | | |
1371 | | bool check_tree() const |
1372 | 938k | { |
1373 | 938k | IMMER_INVALID_STATE_ASSERT(shift <= sizeof(size_t) * 8 - BL); |
1374 | 938k | IMMER_INVALID_STATE_ASSERT(shift >= BL); |
1375 | 938k | IMMER_INVALID_STATE_ASSERT(tail_offset() <= size); |
1376 | 938k | IMMER_INVALID_STATE_ASSERT(tail_size() <= branches<BL>); |
1377 | | #if IMMER_DEBUG_DEEP_CHECK |
1378 | | IMMER_INVALID_STATE_ASSERT(check_root()); |
1379 | | IMMER_INVALID_STATE_ASSERT(check_tail()); |
1380 | | #endif |
1381 | 938k | return true; |
1382 | 938k | } |
1383 | | |
1384 | | bool check_tail() const |
1385 | | { |
1386 | | #if IMMER_DEBUG_DEEP_CHECK |
1387 | | if (tail_size() > 0) |
1388 | | assert(tail->check(endshift<B, BL>, tail_size())); |
1389 | | #endif |
1390 | | return true; |
1391 | | } |
1392 | | |
1393 | | bool check_root() const |
1394 | | { |
1395 | | #if IMMER_DEBUG_DEEP_CHECK |
1396 | | if (tail_offset() > 0) |
1397 | | assert(root->check(shift, tail_offset())); |
1398 | | else { |
1399 | | IMMER_ASSERT_TAGGED(root->kind() == node_t::kind_t::inner); |
1400 | | assert(shift == BL); |
1401 | | } |
1402 | | #endif |
1403 | | return true; |
1404 | | } |
1405 | | |
1406 | | #if IMMER_DEBUG_PRINT |
1407 | | void debug_print(std::ostream& out) const |
1408 | | { |
1409 | | out << "--" << std::endl |
1410 | | << "{" << std::endl |
1411 | | << " size = " << size << std::endl |
1412 | | << " shift = " << shift << std::endl |
1413 | | << " root = " << std::endl; |
1414 | | debug_print_node(out, root, shift, tail_offset()); |
1415 | | out << " tail = " << std::endl; |
1416 | | debug_print_node(out, tail, endshift<B, BL>, tail_size()); |
1417 | | out << "}" << std::endl; |
1418 | | } |
1419 | | |
1420 | | void debug_print_indent(std::ostream& out, unsigned indent) const |
1421 | | { |
1422 | | while (indent-- > 0) |
1423 | | out << ' '; |
1424 | | } |
1425 | | |
1426 | | void debug_print_node(std::ostream& out, |
1427 | | node_t* node, |
1428 | | shift_t shift, |
1429 | | size_t size, |
1430 | | unsigned indent = 8) const |
1431 | | { |
1432 | | const auto indent_step = 4; |
1433 | | |
1434 | | if (shift == endshift<B, BL>) { |
1435 | | debug_print_indent(out, indent); |
1436 | | out << "- {" << size << "} " |
1437 | | << pretty_print_array(node->leaf(), size) << std::endl; |
1438 | | } else if (auto r = node->relaxed()) { |
1439 | | auto count = r->d.count; |
1440 | | debug_print_indent(out, indent); |
1441 | | out << "# {" << size << "} " |
1442 | | << pretty_print_array(r->d.sizes, r->d.count) << std::endl; |
1443 | | auto last_size = size_t{}; |
1444 | | for (auto i = count_t{}; i < count; ++i) { |
1445 | | debug_print_node(out, |
1446 | | node->inner()[i], |
1447 | | shift - B, |
1448 | | r->d.sizes[i] - last_size, |
1449 | | indent + indent_step); |
1450 | | last_size = r->d.sizes[i]; |
1451 | | } |
1452 | | } else { |
1453 | | debug_print_indent(out, indent); |
1454 | | out << "+ {" << size << "}" << std::endl; |
1455 | | auto count = |
1456 | | (size >> shift) + (size - ((size >> shift) << shift) > 0); |
1457 | | if (count) { |
1458 | | for (auto i = count_t{}; i < count - 1; ++i) |
1459 | | debug_print_node(out, |
1460 | | node->inner()[i], |
1461 | | shift - B, |
1462 | | 1 << shift, |
1463 | | indent + indent_step); |
1464 | | debug_print_node(out, |
1465 | | node->inner()[count - 1], |
1466 | | shift - B, |
1467 | | size - ((count - 1) << shift), |
1468 | | indent + indent_step); |
1469 | | } |
1470 | | } |
1471 | | } |
1472 | | #endif // IMMER_DEBUG_PRINT |
1473 | | }; |
1474 | | |
1475 | | } // namespace rbts |
1476 | | } // namespace detail |
1477 | | } // namespace immer |