Coverage Report

Created: 2025-11-24 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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