Coverage Report

Created: 2026-01-22 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
134k
    {
46
134k
        static const auto empty_ = node_t::make_inner_n(0u);
47
134k
        return empty_->inc();
48
134k
    }
49
50
    static node_t* empty_tail()
51
122k
    {
52
122k
        static const auto empty_ = node_t::make_leaf_n(0u);
53
122k
        return empty_->inc();
54
122k
    }
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
122k
        : size{0}
89
122k
        , shift{BL}
90
122k
        , root{empty_root()}
91
122k
        , tail{empty_tail()}
92
122k
    {
93
122k
        assert(check_tree());
94
122k
    }
95
96
    rbtree(size_t sz, shift_t sh, node_t* r, node_t* t)
97
96.4k
        : size{sz}
98
96.4k
        , shift{sh}
99
96.4k
        , root{r}
100
96.4k
        , tail{t}
101
96.4k
    {
102
96.4k
        assert(check_tree());
103
96.4k
    }
104
105
    rbtree(const rbtree& other)
106
63.9k
        : rbtree{other.size, other.shift, other.root, other.tail}
107
63.9k
    {
108
63.9k
        inc();
109
63.9k
    }
110
111
    rbtree(rbtree&& other)
112
98.3k
        : rbtree{}
113
98.3k
    {
114
98.3k
        swap(*this, other);
115
98.3k
    }
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
103k
    {
126
103k
        swap(*this, other);
127
103k
        return *this;
128
103k
    }
129
130
    friend void swap(rbtree& x, rbtree& y)
131
201k
    {
132
201k
        using std::swap;
133
201k
        swap(x.size, y.size);
134
201k
        swap(x.shift, y.shift);
135
201k
        swap(x.root, y.root);
136
201k
        swap(x.tail, y.tail);
137
201k
    }
138
139
219k
    ~rbtree() { dec(); }
140
141
    void inc() const
142
63.9k
    {
143
63.9k
        root->inc();
144
63.9k
        tail->inc();
145
63.9k
    }
146
147
219k
    void dec() const { traverse(dec_visitor()); }
148
149
    auto tail_size() const { return size ? ((size - 1) & mask<BL>) +1 : 0; }
150
151
2.00M
    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
219k
    {
156
219k
        auto tail_off  = tail_offset();
157
219k
        auto tail_size = size - tail_off;
158
159
219k
        if (tail_off)
160
79.5k
            make_regular_sub_pos(root, shift, tail_off).visit(v, args...);
161
139k
        else
162
139k
            make_empty_regular_pos(root).visit(v, args...);
163
164
219k
        make_leaf_sub_pos(tail, tail_size).visit(v, args...);
165
219k
    }
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
1.26M
    {
264
1.26M
        if (!tail->can_mutate(e)) {
265
11.7k
            auto new_tail = node_t::copy_leaf_e(e, tail, n);
266
11.7k
            dec_leaf(tail, n);
267
11.7k
            tail = new_tail;
268
11.7k
        }
269
1.26M
    }
270
271
    void push_back_mut(edit_t e, T value)
272
1.70M
    {
273
1.70M
        auto tail_off = tail_offset();
274
1.70M
        auto ts       = size - tail_off;
275
1.70M
        if (ts < branches<BL>) {
276
1.26M
            ensure_mutable_tail(e, ts);
277
1.26M
            new (&tail->leaf()[ts]) T(std::move(value));
278
1.26M
        } else {
279
431k
            auto new_tail = node_t::make_leaf_e(e, std::move(value));
280
431k
            IMMER_TRY {
281
431k
                if (tail_off == size_t{branches<B>} << shift) {
282
5.42k
                    auto new_root = node_t::make_inner_e(e);
283
5.42k
                    IMMER_TRY {
284
5.42k
                        auto path = node_t::make_path_e(e, shift, tail);
285
5.42k
                        new_root->inner()[0] = root;
286
5.42k
                        new_root->inner()[1] = path;
287
5.42k
                        root                 = new_root;
288
5.42k
                        tail                 = new_tail;
289
5.42k
                        shift += B;
290
5.42k
                    }
291
5.42k
                    IMMER_CATCH (...) {
292
0
                        node_t::delete_inner_e(new_root);
293
0
                        IMMER_RETHROW;
294
0
                    }
295
426k
                } else if (tail_off) {
296
421k
                    auto new_root =
297
421k
                        make_regular_sub_pos(root, shift, tail_off)
298
421k
                            .visit(push_tail_mut_visitor<node_t>{}, e, tail);
299
421k
                    root = new_root;
300
421k
                    tail = new_tail;
301
421k
                } else {
302
5.23k
                    auto new_root = node_t::make_path_e(e, shift, tail);
303
5.23k
                    assert(tail_off == 0);
304
5.23k
                    dec_empty_regular(root);
305
5.23k
                    root = new_root;
306
5.23k
                    tail = new_tail;
307
5.23k
                }
308
431k
            }
309
431k
            IMMER_CATCH (...) {
310
0
                node_t::delete_leaf(new_tail, 1);
311
0
                IMMER_RETHROW;
312
0
            }
313
431k
        }
314
1.70M
        ++size;
315
1.70M
    }
316
317
    rbtree push_back(T value) const
318
7.88k
    {
319
7.88k
        auto tail_off = tail_offset();
320
7.88k
        auto ts       = size - tail_off;
321
7.88k
        if (ts < branches<BL>) {
322
5.04k
            auto new_tail =
323
5.04k
                node_t::copy_leaf_emplace(tail, ts, std::move(value));
324
5.04k
            return {size + 1, shift, root->inc(), new_tail};
325
5.04k
        } else {
326
2.84k
            auto new_tail = node_t::make_leaf_n(1, std::move(value));
327
2.84k
            IMMER_TRY {
328
2.84k
                if (tail_off == size_t{branches<B>} << shift) {
329
392
                    auto new_root = node_t::make_inner_n(2);
330
392
                    IMMER_TRY {
331
392
                        auto path            = node_t::make_path(shift, tail);
332
392
                        new_root->inner()[0] = root;
333
392
                        new_root->inner()[1] = path;
334
392
                        root->inc();
335
392
                        tail->inc();
336
392
                        return {size + 1, shift + B, new_root, new_tail};
337
392
                    }
338
392
                    IMMER_CATCH (...) {
339
0
                        node_t::delete_inner(new_root, 2);
340
0
                        IMMER_RETHROW;
341
0
                    }
342
2.45k
                } else if (tail_off) {
343
2.16k
                    auto new_root =
344
2.16k
                        make_regular_sub_pos(root, shift, tail_off)
345
2.16k
                            .visit(push_tail_visitor<node_t>{}, tail);
346
2.16k
                    tail->inc();
347
2.16k
                    return {size + 1, shift, new_root, new_tail};
348
2.16k
                } else {
349
283
                    auto new_root = node_t::make_path(shift, tail);
350
283
                    tail->inc();
351
283
                    return {size + 1, shift, new_root, new_tail};
352
283
                }
353
2.84k
            }
354
2.84k
            IMMER_CATCH (...) {
355
0
                node_t::delete_leaf(new_tail, 1);
356
0
                IMMER_RETHROW;
357
0
            }
358
2.84k
        }
359
7.88k
    }
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
17.0k
    {
368
17.0k
        auto tail_off = tail_offset();
369
17.0k
        if (idx >= tail_off) {
370
1.37k
            ensure_mutable_tail(e, size - tail_off);
371
1.37k
            return tail->leaf()[idx & mask<BL>];
372
15.6k
        } else {
373
15.6k
            return make_regular_sub_pos(root, shift, tail_off)
374
15.6k
                .visit(get_mut_visitor<node_t>{}, idx, e, &root);
375
15.6k
        }
376
17.0k
    }
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
17.0k
    {
397
17.0k
        auto& elem = get_mut(e, idx);
398
17.0k
        elem       = std::forward<FnT>(fn)(std::move(elem));
399
17.0k
    }
400
401
    template <typename FnT>
402
    rbtree update(size_t idx, FnT&& fn) const
403
6.16k
    {
404
6.16k
        auto tail_off = tail_offset();
405
6.16k
        if (idx >= tail_off) {
406
938
            auto tail_size = size - tail_off;
407
938
            auto new_tail =
408
938
                make_leaf_sub_pos(tail, tail_size)
409
938
                    .visit(update_visitor<node_t>{}, idx - tail_off, fn);
410
938
            return {size, shift, root->inc(), new_tail};
411
5.22k
        } else {
412
5.22k
            auto new_root = make_regular_sub_pos(root, shift, tail_off)
413
5.22k
                                .visit(update_visitor<node_t>{}, idx, fn);
414
5.22k
            return {size, shift, new_root, tail->inc()};
415
5.22k
        }
416
6.16k
    }
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
21.5k
    {
430
21.5k
        auto tail_off = tail_offset();
431
21.5k
        if (new_size == 0) {
432
1.88k
            return {};
433
19.6k
        } else if (new_size >= size) {
434
1.22k
            return *this;
435
18.4k
        } else if (new_size > tail_off) {
436
436
            auto new_tail = node_t::copy_leaf(tail, new_size - tail_off);
437
436
            return {new_size, shift, root->inc(), new_tail};
438
18.0k
        } else {
439
18.0k
            using std::get;
440
18.0k
            auto l = new_size - 1;
441
18.0k
            auto v = slice_right_visitor<node_t>();
442
18.0k
            auto r = make_regular_sub_pos(root, shift, tail_off).visit(v, l);
443
18.0k
            auto new_shift = get<0>(r);
444
18.0k
            auto new_root  = get<1>(r);
445
18.0k
            auto new_tail  = get<3>(r);
446
18.0k
            if (new_root) {
447
15.3k
                IMMER_ASSERT_TAGGED(new_root->compute_shift() == get<0>(r));
448
15.3k
                assert(new_root->check(new_shift, new_size - get<2>(r)));
449
15.3k
                return {new_size, new_shift, new_root, new_tail};
450
15.3k
            } else {
451
2.70k
                return {new_size, BL, empty_root(), new_tail};
452
2.70k
            }
453
18.0k
        }
454
21.5k
    }
455
456
    void take_mut(edit_t e, size_t new_size)
457
35.4k
    {
458
35.4k
        auto tail_off = tail_offset();
459
35.4k
        if (new_size == 0) {
460
            // todo: more efficient?
461
4.74k
            *this = {};
462
30.7k
        } else if (new_size >= size) {
463
614
            return;
464
30.0k
        } else if (new_size > tail_off) {
465
1.19k
            auto ts    = size - tail_off;
466
1.19k
            auto newts = new_size - tail_off;
467
1.19k
            if (tail->can_mutate(e)) {
468
773
                detail::destroy_n(tail->leaf() + newts, ts - newts);
469
773
            } else {
470
418
                auto new_tail = node_t::copy_leaf_e(e, tail, newts);
471
418
                dec_leaf(tail, ts);
472
418
                tail = new_tail;
473
418
            }
474
1.19k
            size = new_size;
475
1.19k
            return;
476
28.8k
        } else {
477
28.8k
            using std::get;
478
28.8k
            auto l = new_size - 1;
479
28.8k
            auto v = slice_right_mut_visitor<node_t>();
480
28.8k
            auto r = make_regular_sub_pos(root, shift, tail_off).visit(v, l, e);
481
28.8k
            auto new_shift = get<0>(r);
482
28.8k
            auto new_root  = get<1>(r);
483
28.8k
            auto new_tail  = get<3>(r);
484
28.8k
            if (new_root) {
485
19.8k
                root  = new_root;
486
19.8k
                shift = new_shift;
487
19.8k
            } else {
488
9.09k
                root  = empty_root();
489
9.09k
                shift = BL;
490
9.09k
            }
491
28.8k
            dec_leaf(tail, size - tail_off);
492
28.8k
            size = new_size;
493
28.8k
            tail = new_tail;
494
28.8k
            return;
495
28.8k
        }
496
35.4k
    }
497
498
    bool check_tree() const
499
219k
    {
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
219k
        return true;
507
219k
    }
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