/src/immer/immer/detail/rbts/rbtree.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 | | #include <immer/detail/type_traits.hpp> |
16 | | |
17 | | #include <cassert> |
18 | | #include <memory> |
19 | | #include <numeric> |
20 | | #include <stdexcept> |
21 | | |
22 | | namespace immer { |
23 | | namespace detail { |
24 | | namespace rbts { |
25 | | |
26 | | template <typename T, typename MemoryPolicy, bits_t B, bits_t BL> |
27 | | struct rbtree |
28 | | { |
29 | | using node_t = node<T, MemoryPolicy, B, BL>; |
30 | | using edit_t = typename node_t::edit_t; |
31 | | using owner_t = typename MemoryPolicy::transience_t::owner; |
32 | | |
33 | | size_t size; |
34 | | shift_t shift; |
35 | | node_t* root; |
36 | | node_t* tail; |
37 | | |
38 | | constexpr static size_t max_size() |
39 | | { |
40 | | auto S = sizeof(size_t) * 8; |
41 | | return (size_t{1} << BL) * ipow(size_t{1} << B, (S - BL) / B); |
42 | | } |
43 | | |
44 | | static node_t* empty_root() |
45 | 1.20M | { |
46 | 1.20M | static const auto empty_ = node_t::make_inner_n(0u); |
47 | 1.20M | return empty_->inc(); |
48 | 1.20M | } |
49 | | |
50 | | static node_t* empty_tail() |
51 | 1.19M | { |
52 | 1.19M | static const auto empty_ = node_t::make_leaf_n(0u); |
53 | 1.19M | return empty_->inc(); |
54 | 1.19M | } |
55 | | |
56 | | template <typename U> |
57 | | static auto from_initializer_list(std::initializer_list<U> values) |
58 | | { |
59 | | auto e = owner_t{}; |
60 | | auto result = rbtree{}; |
61 | | for (auto&& v : values) |
62 | | result.push_back_mut(e, v); |
63 | | return result; |
64 | | } |
65 | | |
66 | | template <typename Iter, |
67 | | typename Sent, |
68 | | std::enable_if_t<compatible_sentinel_v<Iter, Sent>, bool> = true> |
69 | | static auto from_range(Iter first, Sent last) |
70 | | { |
71 | | auto e = owner_t{}; |
72 | | auto result = rbtree{}; |
73 | | for (; first != last; ++first) |
74 | | result.push_back_mut(e, *first); |
75 | | return result; |
76 | | } |
77 | | |
78 | | static auto from_fill(size_t n, T v) |
79 | | { |
80 | | auto e = owner_t{}; |
81 | | auto result = rbtree{}; |
82 | | while (n-- > 0) |
83 | | result.push_back_mut(e, v); |
84 | | return result; |
85 | | } |
86 | | |
87 | | rbtree() |
88 | 1.19M | : size{0} |
89 | 1.19M | , shift{BL} |
90 | 1.19M | , root{empty_root()} |
91 | 1.19M | , tail{empty_tail()} |
92 | 1.19M | { |
93 | 1.19M | assert(check_tree()); |
94 | 1.19M | } |
95 | | |
96 | | rbtree(size_t sz, shift_t sh, node_t* r, node_t* t) |
97 | 1.17M | : size{sz} |
98 | 1.17M | , shift{sh} |
99 | 1.17M | , root{r} |
100 | 1.17M | , tail{t} |
101 | 1.17M | { |
102 | 1.17M | assert(check_tree()); |
103 | 1.17M | } |
104 | | |
105 | | rbtree(const rbtree& other) |
106 | 884 | : rbtree{other.size, other.shift, other.root, other.tail} |
107 | 884 | { |
108 | 884 | inc(); |
109 | 884 | } |
110 | | |
111 | | rbtree(rbtree&& other) |
112 | 1.17M | : rbtree{} |
113 | 1.17M | { |
114 | 1.17M | swap(*this, other); |
115 | 1.17M | } |
116 | | |
117 | | rbtree& operator=(const rbtree& other) |
118 | | { |
119 | | auto next = other; |
120 | | swap(*this, next); |
121 | | return *this; |
122 | | } |
123 | | |
124 | | rbtree& operator=(rbtree&& other) |
125 | 1.41M | { |
126 | 1.41M | swap(*this, other); |
127 | 1.41M | return *this; |
128 | 1.41M | } |
129 | | |
130 | | friend void swap(rbtree& x, rbtree& y) |
131 | 2.59M | { |
132 | 2.59M | using std::swap; |
133 | 2.59M | swap(x.size, y.size); |
134 | 2.59M | swap(x.shift, y.shift); |
135 | 2.59M | swap(x.root, y.root); |
136 | 2.59M | swap(x.tail, y.tail); |
137 | 2.59M | } |
138 | | |
139 | 2.36M | ~rbtree() { dec(); } |
140 | | |
141 | | void inc() const |
142 | 884 | { |
143 | 884 | root->inc(); |
144 | 884 | tail->inc(); |
145 | 884 | } |
146 | | |
147 | 2.36M | void dec() const { traverse(dec_visitor()); } |
148 | | |
149 | | auto tail_size() const { return size ? ((size - 1) & mask<BL>) +1 : 0; } |
150 | | |
151 | 3.78M | auto tail_offset() const { return size ? (size - 1) & ~mask<BL> : 0; } |
152 | | |
153 | | template <typename Visitor, typename... Args> |
154 | | void traverse(Visitor v, Args&&... args) const |
155 | 2.36M | { |
156 | 2.36M | auto tail_off = tail_offset(); |
157 | 2.36M | auto tail_size = size - tail_off; |
158 | | |
159 | 2.36M | if (tail_off) |
160 | 1.14M | make_regular_sub_pos(root, shift, tail_off).visit(v, args...); |
161 | 1.22M | else |
162 | 1.22M | make_empty_regular_pos(root).visit(v, args...); |
163 | | |
164 | 2.36M | make_leaf_sub_pos(tail, tail_size).visit(v, args...); |
165 | 2.36M | } |
166 | | |
167 | | template <typename Visitor, typename... Args> |
168 | | void traverse(Visitor v, size_t first, size_t last, Args&&... args) const |
169 | | { |
170 | | auto tail_off = tail_offset(); |
171 | | auto tail_size = size - tail_off; |
172 | | |
173 | | if (first < tail_off) |
174 | | make_regular_sub_pos(root, shift, tail_off) |
175 | | .visit(v, first, last < tail_off ? last : tail_off, args...); |
176 | | if (last > tail_off) |
177 | | make_leaf_sub_pos(tail, tail_size) |
178 | | .visit(v, |
179 | | first > tail_off ? first - tail_off : 0, |
180 | | last - tail_off, |
181 | | args...); |
182 | | } |
183 | | |
184 | | template <typename Visitor, typename... Args> |
185 | | bool traverse_p(Visitor v, Args&&... args) const |
186 | | { |
187 | | auto tail_off = tail_offset(); |
188 | | auto tail_size = size - tail_off; |
189 | | return (tail_off ? make_regular_sub_pos(root, shift, tail_off) |
190 | | .visit(v, args...) |
191 | | : make_empty_regular_pos(root).visit(v, args...)) && |
192 | | make_leaf_sub_pos(tail, tail_size).visit(v, args...); |
193 | | } |
194 | | |
195 | | template <typename Visitor, typename... Args> |
196 | | bool traverse_p(Visitor v, size_t first, size_t last, Args&&... args) const |
197 | | { |
198 | | auto tail_off = tail_offset(); |
199 | | auto tail_size = size - tail_off; |
200 | | |
201 | | return (first < tail_off ? make_regular_sub_pos(root, shift, tail_off) |
202 | | .visit(v, |
203 | | first, |
204 | | last < tail_off ? last : tail_off, |
205 | | args...) |
206 | | : true) && |
207 | | (last > tail_off |
208 | | ? make_leaf_sub_pos(tail, tail_size) |
209 | | .visit(v, |
210 | | first > tail_off ? first - tail_off : 0, |
211 | | last - tail_off, |
212 | | args...) |
213 | | : true); |
214 | | } |
215 | | |
216 | | template <typename Visitor> |
217 | | decltype(auto) descend(Visitor v, size_t idx) const |
218 | | { |
219 | | auto tail_off = tail_offset(); |
220 | | return idx >= tail_off ? make_leaf_descent_pos(tail).visit(v, idx) |
221 | | : visit_regular_descent(root, shift, v, idx); |
222 | | } |
223 | | |
224 | | template <typename Fn> |
225 | | void for_each_chunk(Fn&& fn) const |
226 | | { |
227 | | traverse(for_each_chunk_visitor{}, std::forward<Fn>(fn)); |
228 | | } |
229 | | |
230 | | template <typename Fn> |
231 | | void for_each_chunk(size_t first, size_t last, Fn&& fn) const |
232 | | { |
233 | | traverse(for_each_chunk_i_visitor{}, first, last, std::forward<Fn>(fn)); |
234 | | } |
235 | | |
236 | | template <typename Fn> |
237 | | bool for_each_chunk_p(Fn&& fn) const |
238 | | { |
239 | | return traverse_p(for_each_chunk_p_visitor{}, std::forward<Fn>(fn)); |
240 | | } |
241 | | |
242 | | template <typename Fn> |
243 | | bool for_each_chunk_p(size_t first, size_t last, Fn&& fn) const |
244 | | { |
245 | | return traverse_p( |
246 | | for_each_chunk_p_i_visitor{}, first, last, std::forward<Fn>(fn)); |
247 | | } |
248 | | |
249 | | bool equals(const rbtree& other) const |
250 | | { |
251 | | if (size != other.size) |
252 | | return false; |
253 | | if (size == 0) |
254 | | return true; |
255 | | return (size <= branches<BL> || |
256 | | make_regular_sub_pos(root, shift, tail_offset()) |
257 | | .visit(equals_visitor{}, other.root)) && |
258 | | make_leaf_sub_pos(tail, tail_size()) |
259 | | .visit(equals_visitor{}, other.tail); |
260 | | } |
261 | | |
262 | | void ensure_mutable_tail(edit_t e, count_t n) |
263 | 146k | { |
264 | 146k | if (!tail->can_mutate(e)) { |
265 | 2.32k | auto new_tail = node_t::copy_leaf_e(e, tail, n); |
266 | 2.32k | dec_leaf(tail, n); |
267 | 2.32k | tail = new_tail; |
268 | 2.32k | } |
269 | 146k | } |
270 | | |
271 | | void push_back_mut(edit_t e, T value) |
272 | 195k | { |
273 | 195k | auto tail_off = tail_offset(); |
274 | 195k | auto ts = size - tail_off; |
275 | 195k | if (ts < branches<BL>) { |
276 | 145k | ensure_mutable_tail(e, ts); |
277 | 145k | new (&tail->leaf()[ts]) T{std::move(value)}; |
278 | 145k | } else { |
279 | 50.5k | auto new_tail = node_t::make_leaf_e(e, std::move(value)); |
280 | 50.5k | IMMER_TRY { |
281 | 50.5k | if (tail_off == size_t{branches<B>} << shift) { |
282 | 800 | auto new_root = node_t::make_inner_e(e); |
283 | 800 | IMMER_TRY { |
284 | 800 | auto path = node_t::make_path_e(e, shift, tail); |
285 | 800 | new_root->inner()[0] = root; |
286 | 800 | new_root->inner()[1] = path; |
287 | 800 | root = new_root; |
288 | 800 | tail = new_tail; |
289 | 800 | shift += B; |
290 | 800 | } |
291 | 800 | IMMER_CATCH (...) { |
292 | 0 | node_t::delete_inner_e(new_root); |
293 | 0 | IMMER_RETHROW; |
294 | 0 | } |
295 | 49.7k | } else if (tail_off) { |
296 | 49.2k | auto new_root = |
297 | 49.2k | make_regular_sub_pos(root, shift, tail_off) |
298 | 49.2k | .visit(push_tail_mut_visitor<node_t>{}, e, tail); |
299 | 49.2k | root = new_root; |
300 | 49.2k | tail = new_tail; |
301 | 49.2k | } else { |
302 | 447 | auto new_root = node_t::make_path_e(e, shift, tail); |
303 | 447 | assert(tail_off == 0); |
304 | 447 | dec_empty_regular(root); |
305 | 447 | root = new_root; |
306 | 447 | tail = new_tail; |
307 | 447 | } |
308 | 50.5k | } |
309 | 50.5k | IMMER_CATCH (...) { |
310 | 0 | node_t::delete_leaf(new_tail, 1); |
311 | 0 | IMMER_RETHROW; |
312 | 0 | } |
313 | 50.5k | } |
314 | 195k | ++size; |
315 | 195k | } |
316 | | |
317 | | rbtree push_back(T value) const |
318 | 1.13M | { |
319 | 1.13M | auto tail_off = tail_offset(); |
320 | 1.13M | auto ts = size - tail_off; |
321 | 1.13M | if (ts < branches<BL>) { |
322 | 849k | auto new_tail = |
323 | 849k | node_t::copy_leaf_emplace(tail, ts, std::move(value)); |
324 | 849k | return {size + 1, shift, root->inc(), new_tail}; |
325 | 849k | } else { |
326 | 288k | auto new_tail = node_t::make_leaf_n(1, std::move(value)); |
327 | 288k | IMMER_TRY { |
328 | 288k | if (tail_off == size_t{branches<B>} << shift) { |
329 | 8.62k | auto new_root = node_t::make_inner_n(2); |
330 | 8.62k | IMMER_TRY { |
331 | 8.62k | auto path = node_t::make_path(shift, tail); |
332 | 8.62k | new_root->inner()[0] = root; |
333 | 8.62k | new_root->inner()[1] = path; |
334 | 8.62k | root->inc(); |
335 | 8.62k | tail->inc(); |
336 | 8.62k | return {size + 1, shift + B, new_root, new_tail}; |
337 | 8.62k | } |
338 | 8.62k | IMMER_CATCH (...) { |
339 | 0 | node_t::delete_inner(new_root, 2); |
340 | 0 | IMMER_RETHROW; |
341 | 0 | } |
342 | 279k | } else if (tail_off) { |
343 | 274k | auto new_root = |
344 | 274k | make_regular_sub_pos(root, shift, tail_off) |
345 | 274k | .visit(push_tail_visitor<node_t>{}, tail); |
346 | 274k | tail->inc(); |
347 | 274k | return {size + 1, shift, new_root, new_tail}; |
348 | 274k | } else { |
349 | 5.21k | auto new_root = node_t::make_path(shift, tail); |
350 | 5.21k | tail->inc(); |
351 | 5.21k | return {size + 1, shift, new_root, new_tail}; |
352 | 5.21k | } |
353 | 288k | } |
354 | 288k | IMMER_CATCH (...) { |
355 | 0 | node_t::delete_leaf(new_tail, 1); |
356 | 0 | IMMER_RETHROW; |
357 | 0 | } |
358 | 288k | } |
359 | 1.13M | } |
360 | | |
361 | | const T* array_for(size_t index) const |
362 | | { |
363 | | return descend(array_for_visitor<T>(), index); |
364 | | } |
365 | | |
366 | | T& get_mut(edit_t e, size_t idx) |
367 | 6.11k | { |
368 | 6.11k | auto tail_off = tail_offset(); |
369 | 6.11k | if (idx >= tail_off) { |
370 | 982 | ensure_mutable_tail(e, size - tail_off); |
371 | 982 | return tail->leaf()[idx & mask<BL>]; |
372 | 5.13k | } else { |
373 | 5.13k | return make_regular_sub_pos(root, shift, tail_off) |
374 | 5.13k | .visit(get_mut_visitor<node_t>{}, idx, e, &root); |
375 | 5.13k | } |
376 | 6.11k | } |
377 | | |
378 | | const T& get(size_t index) const |
379 | | { |
380 | | return descend(get_visitor<T>(), index); |
381 | | } |
382 | | |
383 | | const T& get_check(size_t index) const |
384 | | { |
385 | | if (index >= size) |
386 | | IMMER_THROW(std::out_of_range{"index out of range"}); |
387 | | return descend(get_visitor<T>(), index); |
388 | | } |
389 | | |
390 | | const T& front() const { return get(0); } |
391 | | |
392 | | const T& back() const { return tail->leaf()[(size - 1) & mask<BL>]; } |
393 | | |
394 | | template <typename FnT> |
395 | | void update_mut(edit_t e, size_t idx, FnT&& fn) |
396 | 6.11k | { |
397 | 6.11k | auto& elem = get_mut(e, idx); |
398 | 6.11k | elem = std::forward<FnT>(fn)(std::move(elem)); |
399 | 6.11k | } |
400 | | |
401 | | template <typename FnT> |
402 | | rbtree update(size_t idx, FnT&& fn) const |
403 | 22.9k | { |
404 | 22.9k | auto tail_off = tail_offset(); |
405 | 22.9k | if (idx >= tail_off) { |
406 | 1.20k | auto tail_size = size - tail_off; |
407 | 1.20k | auto new_tail = |
408 | 1.20k | make_leaf_sub_pos(tail, tail_size) |
409 | 1.20k | .visit(update_visitor<node_t>{}, idx - tail_off, fn); |
410 | 1.20k | return {size, shift, root->inc(), new_tail}; |
411 | 21.7k | } else { |
412 | 21.7k | auto new_root = make_regular_sub_pos(root, shift, tail_off) |
413 | 21.7k | .visit(update_visitor<node_t>{}, idx, fn); |
414 | 21.7k | return {size, shift, new_root, tail->inc()}; |
415 | 21.7k | } |
416 | 22.9k | } |
417 | | |
418 | | void assoc_mut(edit_t e, size_t idx, T value) |
419 | | { |
420 | | update_mut(e, idx, [&](auto&&) { return std::move(value); }); |
421 | | } |
422 | | |
423 | | rbtree assoc(size_t idx, T value) const |
424 | | { |
425 | | return update(idx, [&](auto&&) { return std::move(value); }); |
426 | | } |
427 | | |
428 | | rbtree take(size_t new_size) const |
429 | 17.7k | { |
430 | 17.7k | auto tail_off = tail_offset(); |
431 | 17.7k | if (new_size == 0) { |
432 | 3.38k | return {}; |
433 | 14.3k | } else if (new_size >= size) { |
434 | 884 | return *this; |
435 | 13.4k | } else if (new_size > tail_off) { |
436 | 2.24k | auto new_tail = node_t::copy_leaf(tail, new_size - tail_off); |
437 | 2.24k | return {new_size, shift, root->inc(), new_tail}; |
438 | 11.2k | } else { |
439 | 11.2k | using std::get; |
440 | 11.2k | auto l = new_size - 1; |
441 | 11.2k | auto v = slice_right_visitor<node_t>(); |
442 | 11.2k | auto r = make_regular_sub_pos(root, shift, tail_off).visit(v, l); |
443 | 11.2k | auto new_shift = get<0>(r); |
444 | 11.2k | auto new_root = get<1>(r); |
445 | 11.2k | auto new_tail = get<3>(r); |
446 | 11.2k | if (new_root) { |
447 | 9.15k | IMMER_ASSERT_TAGGED(new_root->compute_shift() == get<0>(r)); |
448 | 9.15k | assert(new_root->check(new_shift, new_size - get<2>(r))); |
449 | 9.15k | return {new_size, new_shift, new_root, new_tail}; |
450 | 9.15k | } else { |
451 | 2.06k | return {new_size, BL, empty_root(), new_tail}; |
452 | 2.06k | } |
453 | 11.2k | } |
454 | 17.7k | } |
455 | | |
456 | | void take_mut(edit_t e, size_t new_size) |
457 | 36.4k | { |
458 | 36.4k | auto tail_off = tail_offset(); |
459 | 36.4k | if (new_size == 0) { |
460 | | // todo: more efficient? |
461 | 1.04k | *this = {}; |
462 | 35.3k | } else if (new_size >= size) { |
463 | 755 | return; |
464 | 34.6k | } else if (new_size > tail_off) { |
465 | 1.50k | auto ts = size - tail_off; |
466 | 1.50k | auto newts = new_size - tail_off; |
467 | 1.50k | if (tail->can_mutate(e)) { |
468 | 1.13k | detail::destroy_n(tail->leaf() + newts, ts - newts); |
469 | 1.13k | } else { |
470 | 375 | auto new_tail = node_t::copy_leaf_e(e, tail, newts); |
471 | 375 | dec_leaf(tail, ts); |
472 | 375 | tail = new_tail; |
473 | 375 | } |
474 | 1.50k | size = new_size; |
475 | 1.50k | return; |
476 | 33.1k | } else { |
477 | 33.1k | using std::get; |
478 | 33.1k | auto l = new_size - 1; |
479 | 33.1k | auto v = slice_right_mut_visitor<node_t>(); |
480 | 33.1k | auto r = make_regular_sub_pos(root, shift, tail_off).visit(v, l, e); |
481 | 33.1k | auto new_shift = get<0>(r); |
482 | 33.1k | auto new_root = get<1>(r); |
483 | 33.1k | auto new_tail = get<3>(r); |
484 | 33.1k | if (new_root) { |
485 | 24.5k | root = new_root; |
486 | 24.5k | shift = new_shift; |
487 | 24.5k | } else { |
488 | 8.58k | root = empty_root(); |
489 | 8.58k | shift = BL; |
490 | 8.58k | } |
491 | 33.1k | dec_leaf(tail, size - tail_off); |
492 | 33.1k | size = new_size; |
493 | 33.1k | tail = new_tail; |
494 | 33.1k | return; |
495 | 33.1k | } |
496 | 36.4k | } |
497 | | |
498 | | bool check_tree() const |
499 | 2.36M | { |
500 | | #if IMMER_DEBUG_DEEP_CHECK |
501 | | assert(shift >= BL); |
502 | | assert(tail_offset() <= size); |
503 | | assert(check_root()); |
504 | | assert(check_tail()); |
505 | | #endif |
506 | 2.36M | return true; |
507 | 2.36M | } |
508 | | |
509 | | bool check_tail() const |
510 | | { |
511 | | #if IMMER_DEBUG_DEEP_CHECK |
512 | | if (tail_size() > 0) |
513 | | assert(tail->check(endshift<B, BL>, tail_size())); |
514 | | #endif |
515 | | return true; |
516 | | } |
517 | | |
518 | | bool check_root() const |
519 | | { |
520 | | #if IMMER_DEBUG_DEEP_CHECK |
521 | | if (tail_offset() > 0) |
522 | | assert(root->check(shift, tail_offset())); |
523 | | else { |
524 | | IMMER_ASSERT_TAGGED(root->kind() == node_t::kind_t::inner); |
525 | | assert(shift == BL); |
526 | | } |
527 | | #endif |
528 | | return true; |
529 | | } |
530 | | }; |
531 | | |
532 | | } // namespace rbts |
533 | | } // namespace detail |
534 | | } // namespace immer |