Coverage Report

Created: 2025-08-25 06:15

/src/immer/immer/detail/hamts/node.hpp
Line
Count
Source (jump to first uncovered line)
1
//
2
// immer: immutable data structures for C++
3
// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
4
//
5
// This software is distributed under the Boost Software License, Version 1.0.
6
// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
7
//
8
9
#pragma once
10
11
#include <immer/config.hpp>
12
#include <immer/detail/combine_standard_layout.hpp>
13
#include <immer/detail/hamts/bits.hpp>
14
#include <immer/detail/util.hpp>
15
16
#include <cassert>
17
#include <cstddef>
18
19
namespace immer {
20
namespace detail {
21
namespace hamts {
22
23
// For C++14 support.
24
// Calling the destructor inline breaks MSVC in some obscure
25
// corner cases.
26
template <typename T>
27
constexpr void destroy_at(T* p)
28
0
{
29
0
    p->~T();
30
0
}
31
32
template <typename T,
33
          typename Hash,
34
          typename Equal,
35
          typename MemoryPolicy,
36
          bits_t B>
37
struct node
38
{
39
    using node_t = node;
40
41
    using memory      = MemoryPolicy;
42
    using heap_policy = typename memory::heap;
43
    using heap        = typename heap_policy::type;
44
    using transience  = typename memory::transience_t;
45
    using refs_t      = typename memory::refcount;
46
    using ownee_t     = typename transience::ownee;
47
    using edit_t      = typename transience::edit;
48
    using value_t     = T;
49
    using bitmap_t    = typename get_bitmap_type<B>::type;
50
    using hash_t      = decltype(Hash{}(std::declval<const T&>()));
51
52
    enum class kind_t
53
    {
54
        collision,
55
        inner
56
    };
57
58
    struct collision_t
59
    {
60
        count_t count;
61
        aligned_storage_for<T> buffer;
62
    };
63
64
    struct values_data_t
65
    {
66
        aligned_storage_for<T> buffer;
67
    };
68
69
    using values_t = combine_standard_layout_t<values_data_t, refs_t, ownee_t>;
70
71
    struct inner_t
72
    {
73
        bitmap_t nodemap;
74
        bitmap_t datamap;
75
        values_t* values;
76
        aligned_storage_for<node_t*> buffer;
77
    };
78
79
    union data_t
80
    {
81
        inner_t inner;
82
        collision_t collision;
83
    };
84
85
    struct impl_data_t
86
    {
87
#if IMMER_TAGGED_NODE
88
        kind_t kind;
89
#endif
90
        data_t data;
91
    };
92
93
    using impl_t = combine_standard_layout_t<impl_data_t, refs_t, ownee_t>;
94
95
    impl_t impl;
96
97
    constexpr static std::size_t sizeof_values_n(count_t count)
98
497k
    {
99
497k
        return std::max(sizeof(values_t),
100
497k
                        immer_offsetof(values_t, d.buffer) +
101
497k
                            sizeof(values_data_t::buffer) * count);
102
497k
    }
103
104
    constexpr static std::size_t sizeof_collision_n(count_t count)
105
12.1k
    {
106
12.1k
        return immer_offsetof(impl_t, d.data.collision.buffer) +
107
12.1k
               sizeof(collision_t::buffer) * count;
108
12.1k
    }
109
110
    constexpr static std::size_t sizeof_inner_n(count_t count)
111
9.18M
    {
112
9.18M
        return immer_offsetof(impl_t, d.data.inner.buffer) +
113
9.18M
               sizeof(inner_t::buffer) * count;
114
9.18M
    }
115
116
#if IMMER_TAGGED_NODE
117
85.4M
    kind_t kind() const { return impl.d.kind; }
118
#endif
119
120
    auto values()
121
2.84M
    {
122
2.84M
        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
123
2.84M
        assert(impl.d.data.inner.values);
124
2.84M
        return (T*) &impl.d.data.inner.values->d.buffer;
125
2.84M
    }
126
127
    auto values() const
128
4.36M
    {
129
4.36M
        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
130
4.36M
        assert(impl.d.data.inner.values);
131
4.36M
        return (const T*) &impl.d.data.inner.values->d.buffer;
132
4.36M
    }
133
134
    auto children()
135
14.4M
    {
136
14.4M
        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
137
14.4M
        return (node_t**) &impl.d.data.inner.buffer;
138
14.4M
    }
139
140
    auto children() const
141
4.17M
    {
142
4.17M
        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
143
4.17M
        return (const node_t* const*) &impl.d.data.inner.buffer;
144
4.17M
    }
145
146
    auto datamap() const
147
13.6M
    {
148
13.6M
        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
149
13.6M
        return impl.d.data.inner.datamap;
150
13.6M
    }
151
152
    auto nodemap() const
153
24.7M
    {
154
24.7M
        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
155
24.7M
        return impl.d.data.inner.nodemap;
156
24.7M
    }
157
158
    auto data_count() const
159
1.13M
    {
160
1.13M
        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
161
1.13M
        return popcount(datamap());
162
1.13M
    }
163
164
    auto data_count(bitmap_t bit) const
165
4.71M
    {
166
4.71M
        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
167
4.71M
        return popcount(static_cast<bitmap_t>(datamap() & (bit - 1)));
168
4.71M
    }
169
170
    auto children_count() const
171
3.44M
    {
172
3.44M
        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
173
3.44M
        return popcount(nodemap());
174
3.44M
    }
175
176
    auto children_count(bitmap_t bit) const
177
8.64M
    {
178
8.64M
        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
179
8.64M
        return popcount(static_cast<bitmap_t>(nodemap() & (bit - 1)));
180
8.64M
    }
181
182
    auto collision_count() const
183
81.2k
    {
184
81.2k
        IMMER_ASSERT_TAGGED(kind() == kind_t::collision);
185
81.2k
        return impl.d.data.collision.count;
186
81.2k
    }
187
188
    T* collisions()
189
50.1k
    {
190
50.1k
        IMMER_ASSERT_TAGGED(kind() == kind_t::collision);
191
50.1k
        return (T*) &impl.d.data.collision.buffer;
192
50.1k
    }
193
194
    const T* collisions() const
195
80.1k
    {
196
80.1k
        IMMER_ASSERT_TAGGED(kind() == kind_t::collision);
197
80.1k
        return (const T*) &impl.d.data.collision.buffer;
198
80.1k
    }
199
200
    static refs_t& refs(const values_t* x)
201
1.26M
    {
202
1.26M
        return auto_const_cast(get<refs_t>(*x));
203
1.26M
    }
204
    static const ownee_t& ownee(const values_t* x) { return get<ownee_t>(*x); }
205
    static ownee_t& ownee(values_t* x) { return get<ownee_t>(*x); }
206
    static bool can_mutate(values_t* x, edit_t e)
207
    {
208
        return refs(x).unique() || ownee(x).can_mutate(e);
209
    }
210
211
    static refs_t& refs(const node_t* x)
212
12.5M
    {
213
12.5M
        return auto_const_cast(get<refs_t>(x->impl));
214
12.5M
    }
215
    static const ownee_t& ownee(const node_t* x)
216
    {
217
        return get<ownee_t>(x->impl);
218
    }
219
    static ownee_t& ownee(node_t* x) { return get<ownee_t>(x->impl); }
220
221
    bool can_mutate(edit_t e) const
222
    {
223
        return refs(this).unique() || ownee(this).can_mutate(e);
224
    }
225
    bool can_mutate_values(edit_t e) const
226
    {
227
        return can_mutate(impl.d.data.inner.values, e);
228
    }
229
230
    static node_t* make_inner_n_into(void* buffer, std::size_t size, count_t n)
231
3.06M
    {
232
3.06M
        assert(n <= branches<B>);
233
3.06M
        assert(size >= sizeof_inner_n(n));
234
3.06M
        auto p = new (buffer) node_t;
235
3.06M
#if IMMER_TAGGED_NODE
236
3.06M
        p->impl.d.kind = node_t::kind_t::inner;
237
3.06M
#endif
238
3.06M
        p->impl.d.data.inner.nodemap = 0;
239
3.06M
        p->impl.d.data.inner.datamap = 0;
240
3.06M
        p->impl.d.data.inner.values  = nullptr;
241
3.06M
        return p;
242
3.06M
    }
243
244
    static node_t* make_inner_n(count_t n)
245
3.06M
    {
246
3.06M
        assert(n <= branches<B>);
247
3.06M
        auto m = heap::allocate(sizeof_inner_n(n));
248
3.06M
        return make_inner_n_into(m, sizeof_inner_n(n), n);
249
3.06M
    }
250
251
    static node_t* make_inner_n(count_t n, values_t* values)
252
2.50M
    {
253
2.50M
        auto p = make_inner_n(n);
254
2.50M
        if (values) {
255
1.26M
            p->impl.d.data.inner.values = values;
256
1.26M
            refs(values).inc();
257
1.26M
        }
258
2.50M
        return p;
259
2.50M
    }
260
261
    static node_t* make_inner_n(count_t n, count_t nv)
262
504k
    {
263
504k
        assert(nv <= branches<B>);
264
504k
        auto p = make_inner_n(n);
265
504k
        if (nv) {
266
497k
            IMMER_TRY {
267
497k
                p->impl.d.data.inner.values =
268
497k
                    new (heap::allocate(sizeof_values_n(nv))) values_t{};
269
497k
            }
270
497k
            IMMER_CATCH (...) {
271
0
                deallocate_inner(p, n);
272
0
                IMMER_RETHROW;
273
0
            }
274
497k
        }
275
504k
        return p;
276
504k
    }
277
278
    static node_t* make_inner_n(count_t n, count_t idx, node_t* child)
279
48.2k
    {
280
48.2k
        assert(n >= 1);
281
48.2k
        auto p                       = make_inner_n(n);
282
48.2k
        p->impl.d.data.inner.nodemap = bitmap_t{1u} << idx;
283
48.2k
        p->children()[0]             = child;
284
48.2k
        return p;
285
48.2k
    }
286
287
    static node_t* make_inner_n(count_t n, bitmap_t bitmap, T x)
288
195
    {
289
195
        auto p                       = make_inner_n(n, 1);
290
195
        p->impl.d.data.inner.datamap = bitmap;
291
195
        IMMER_TRY {
292
195
            new (p->values()) T{std::move(x)};
293
195
        }
294
195
        IMMER_CATCH (...) {
295
0
            deallocate_inner(p, n, 1);
296
0
            IMMER_RETHROW;
297
0
        }
298
0
        return p;
299
195
    }
300
301
    static node_t*
302
    make_inner_n(count_t n, count_t idx1, T x1, count_t idx2, T x2)
303
29.8k
    {
304
29.8k
        assert(idx1 != idx2);
305
29.8k
        auto p = make_inner_n(n, 2);
306
29.8k
        p->impl.d.data.inner.datamap =
307
29.8k
            (bitmap_t{1u} << idx1) | (bitmap_t{1u} << idx2);
308
29.8k
        auto assign = [&](auto&& x1, auto&& x2) {
309
29.8k
            auto vp = p->values();
310
29.8k
            IMMER_TRY {
311
29.8k
                new (vp) T{std::move(x1)};
312
29.8k
                IMMER_TRY {
313
29.8k
                    new (vp + 1) T{std::move(x2)};
314
29.8k
                }
315
29.8k
                IMMER_CATCH (...) {
316
0
                    immer::detail::hamts::destroy_at(vp);
317
0
                    IMMER_RETHROW;
318
0
                }
319
29.8k
            }
320
29.8k
            IMMER_CATCH (...) {
321
0
                deallocate_inner(p, n, 2);
322
0
                IMMER_RETHROW;
323
0
            }
324
29.8k
        };
325
29.8k
        if (idx1 < idx2)
326
10.6k
            assign(x1, x2);
327
19.2k
        else
328
19.2k
            assign(x2, x1);
329
29.8k
        return p;
330
29.8k
    }
331
332
    static node_t* make_collision_n(count_t n)
333
10.4k
    {
334
10.4k
        auto m = heap::allocate(sizeof_collision_n(n));
335
10.4k
        auto p = new (m) node_t;
336
10.4k
#if IMMER_TAGGED_NODE
337
10.4k
        p->impl.d.kind = node_t::kind_t::collision;
338
10.4k
#endif
339
10.4k
        p->impl.d.data.collision.count = n;
340
10.4k
        return p;
341
10.4k
    }
342
343
    static node_t* make_collision(T v1, T v2)
344
1.72k
    {
345
1.72k
        auto m = heap::allocate(sizeof_collision_n(2));
346
1.72k
        auto p = new (m) node_t;
347
1.72k
#if IMMER_TAGGED_NODE
348
1.72k
        p->impl.d.kind = node_t::kind_t::collision;
349
1.72k
#endif
350
1.72k
        p->impl.d.data.collision.count = 2;
351
1.72k
        auto cols                      = p->collisions();
352
1.72k
        IMMER_TRY {
353
1.72k
            new (cols) T{std::move(v1)};
354
1.72k
            IMMER_TRY {
355
1.72k
                new (cols + 1) T{std::move(v2)};
356
1.72k
            }
357
1.72k
            IMMER_CATCH (...) {
358
0
                cols->~T();
359
0
                IMMER_RETHROW;
360
0
            }
361
1.72k
        }
362
1.72k
        IMMER_CATCH (...) {
363
0
            deallocate_collision(p, 2);
364
0
            IMMER_RETHROW;
365
0
        }
366
0
        return p;
367
1.72k
    }
368
369
    T* ensure_mutable_values(edit_t e)
370
    {
371
        assert(can_mutate(e));
372
        auto old = impl.d.data.inner.values;
373
        if (node_t::can_mutate(old, e))
374
            return values();
375
        else {
376
            auto nv    = data_count();
377
            auto nxt   = new (heap::allocate(sizeof_values_n(nv))) values_t{};
378
            auto dst   = (T*) &nxt->d.buffer;
379
            auto src   = values();
380
            ownee(nxt) = e;
381
            IMMER_TRY {
382
                detail::uninitialized_copy(src, src + nv, dst);
383
            }
384
            IMMER_CATCH (...) {
385
                deallocate_values(nxt, nv);
386
                IMMER_RETHROW;
387
            }
388
            impl.d.data.inner.values = nxt;
389
            if (refs(old).dec())
390
                delete_values(old, nv);
391
            return dst;
392
        }
393
    }
394
395
    static node_t* copy_collision_insert(node_t* src, T v)
396
703
    {
397
703
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision);
398
703
        auto n    = src->collision_count();
399
703
        auto dst  = make_collision_n(n + 1);
400
703
        auto srcp = src->collisions();
401
703
        auto dstp = dst->collisions();
402
703
        IMMER_TRY {
403
703
            new (dstp) T{std::move(v)};
404
703
            IMMER_TRY {
405
703
                detail::uninitialized_copy(srcp, srcp + n, dstp + 1);
406
703
            }
407
703
            IMMER_CATCH (...) {
408
0
                dstp->~T();
409
0
                IMMER_RETHROW;
410
0
            }
411
703
        }
412
703
        IMMER_CATCH (...) {
413
0
            deallocate_collision(dst, n + 1);
414
0
            IMMER_RETHROW;
415
0
        }
416
0
        return dst;
417
703
    }
418
419
    static node_t* move_collision_insert(node_t* src, T v)
420
    {
421
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision);
422
        auto n    = src->collision_count();
423
        auto dst  = make_collision_n(n + 1);
424
        auto srcp = src->collisions();
425
        auto dstp = dst->collisions();
426
        IMMER_TRY {
427
            new (dstp) T{std::move(v)};
428
            IMMER_TRY {
429
                detail::uninitialized_move(srcp, srcp + n, dstp + 1);
430
            }
431
            IMMER_CATCH (...) {
432
                dstp->~T();
433
                IMMER_RETHROW;
434
            }
435
        }
436
        IMMER_CATCH (...) {
437
            deallocate_collision(dst, n + 1);
438
            IMMER_RETHROW;
439
        }
440
        delete_collision(src);
441
        return dst;
442
    }
443
444
    static node_t* copy_collision_remove(node_t* src, T* v)
445
1.21k
    {
446
1.21k
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision);
447
1.21k
        assert(src->collision_count() > 1);
448
1.21k
        auto n    = src->collision_count();
449
1.21k
        auto dst  = make_collision_n(n - 1);
450
1.21k
        auto srcp = src->collisions();
451
1.21k
        auto dstp = dst->collisions();
452
1.21k
        IMMER_TRY {
453
1.21k
            dstp = detail::uninitialized_copy(srcp, v, dstp);
454
1.21k
            IMMER_TRY {
455
1.21k
                detail::uninitialized_copy(v + 1, srcp + n, dstp);
456
1.21k
            }
457
1.21k
            IMMER_CATCH (...) {
458
0
                detail::destroy(dst->collisions(), dstp);
459
0
                IMMER_RETHROW;
460
0
            }
461
1.21k
        }
462
1.21k
        IMMER_CATCH (...) {
463
0
            deallocate_collision(dst, n - 1);
464
0
            IMMER_RETHROW;
465
0
        }
466
0
        return dst;
467
1.21k
    }
468
469
    static node_t* move_collision_remove(node_t* src, T* v)
470
    {
471
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision);
472
        assert(src->collision_count() > 1);
473
        auto n    = src->collision_count();
474
        auto dst  = make_collision_n(n - 1);
475
        auto srcp = src->collisions();
476
        auto dstp = dst->collisions();
477
        IMMER_TRY {
478
            dstp = detail::uninitialized_move(srcp, v, dstp);
479
            IMMER_TRY {
480
                detail::uninitialized_move(v + 1, srcp + n, dstp);
481
            }
482
            IMMER_CATCH (...) {
483
                detail::destroy(dst->collisions(), dstp);
484
                IMMER_RETHROW;
485
            }
486
        }
487
        IMMER_CATCH (...) {
488
            deallocate_collision(dst, n - 1);
489
            IMMER_RETHROW;
490
        }
491
        delete_collision(src);
492
        return dst;
493
    }
494
495
    static node_t* copy_collision_replace(node_t* src, T* pos, T v)
496
8.53k
    {
497
8.53k
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision);
498
8.53k
        auto n    = src->collision_count();
499
8.53k
        auto dst  = make_collision_n(n);
500
8.53k
        auto srcp = src->collisions();
501
8.53k
        auto dstp = dst->collisions();
502
8.53k
        assert(pos >= srcp && pos < srcp + n);
503
8.53k
        IMMER_TRY {
504
8.53k
            new (dstp) T{std::move(v)};
505
8.53k
            IMMER_TRY {
506
8.53k
                dstp = detail::uninitialized_copy(srcp, pos, dstp + 1);
507
8.53k
                IMMER_TRY {
508
8.53k
                    detail::uninitialized_copy(pos + 1, srcp + n, dstp);
509
8.53k
                }
510
8.53k
                IMMER_CATCH (...) {
511
0
                    detail::destroy(dst->collisions(), dstp);
512
0
                    IMMER_RETHROW;
513
0
                }
514
8.53k
            }
515
8.53k
            IMMER_CATCH (...) {
516
0
                dst->collisions()->~T();
517
0
                IMMER_RETHROW;
518
0
            }
519
8.53k
        }
520
8.53k
        IMMER_CATCH (...) {
521
0
            deallocate_collision(dst, n);
522
0
            IMMER_RETHROW;
523
0
        }
524
0
        return dst;
525
8.53k
    }
526
527
    static node_t*
528
    copy_inner_replace(node_t* src, count_t offset, node_t* child)
529
2.50M
    {
530
2.50M
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
531
2.50M
        auto n    = src->children_count();
532
2.50M
        auto dst  = make_inner_n(n, src->impl.d.data.inner.values);
533
2.50M
        auto srcp = src->children();
534
2.50M
        auto dstp = dst->children();
535
2.50M
        dst->impl.d.data.inner.datamap = src->datamap();
536
2.50M
        dst->impl.d.data.inner.nodemap = src->nodemap();
537
2.50M
        std::copy(srcp, srcp + n, dstp);
538
2.50M
        inc_nodes(srcp, offset);
539
2.50M
        inc_nodes(srcp + offset + 1, n - offset - 1);
540
2.50M
        dstp[offset] = child;
541
2.50M
        return dst;
542
2.50M
    }
543
544
    static node_t* owned(node_t* n, edit_t e)
545
    {
546
        ownee(n) = e;
547
        return n;
548
    }
549
550
    static node_t* owned_values(node_t* n, edit_t e)
551
    {
552
        ownee(n)                           = e;
553
        ownee(n->impl.d.data.inner.values) = e;
554
        return n;
555
    }
556
557
    static node_t* owned_values_safe(node_t* n, edit_t e)
558
    {
559
        ownee(n) = e;
560
        if (n->impl.d.data.inner.values)
561
            ownee(n->impl.d.data.inner.values) = e;
562
        return n;
563
    }
564
565
    static node_t* copy_inner_replace_value(node_t* src, count_t offset, T v)
566
390k
    {
567
390k
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
568
390k
        assert(offset < src->data_count());
569
390k
        auto n                         = src->children_count();
570
390k
        auto nv                        = src->data_count();
571
390k
        auto dst                       = make_inner_n(n, nv);
572
390k
        dst->impl.d.data.inner.datamap = src->datamap();
573
390k
        dst->impl.d.data.inner.nodemap = src->nodemap();
574
390k
        IMMER_TRY {
575
390k
            detail::uninitialized_copy(
576
390k
                src->values(), src->values() + nv, dst->values());
577
390k
            IMMER_TRY {
578
390k
                dst->values()[offset] = std::move(v);
579
390k
            }
580
390k
            IMMER_CATCH (...) {
581
0
                detail::destroy_n(dst->values(), nv);
582
0
                IMMER_RETHROW;
583
0
            }
584
390k
        }
585
390k
        IMMER_CATCH (...) {
586
0
            deallocate_inner(dst, n, nv);
587
0
            IMMER_RETHROW;
588
0
        }
589
0
        inc_nodes(src->children(), n);
590
0
        std::copy(src->children(), src->children() + n, dst->children());
591
0
        return dst;
592
390k
    }
593
594
    static node_t* copy_inner_replace_merged(node_t* src,
595
                                             bitmap_t bit,
596
                                             count_t voffset,
597
                                             node_t* node)
598
31.5k
    {
599
31.5k
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
600
31.5k
        assert(!(src->nodemap() & bit));
601
31.5k
        assert(src->datamap() & bit);
602
31.5k
        assert(voffset == src->data_count(bit));
603
31.5k
        auto n                         = src->children_count();
604
31.5k
        auto nv                        = src->data_count();
605
31.5k
        auto dst                       = make_inner_n(n + 1, nv - 1);
606
31.5k
        auto noffset                   = src->children_count(bit);
607
31.5k
        dst->impl.d.data.inner.datamap = src->datamap() & ~bit;
608
31.5k
        dst->impl.d.data.inner.nodemap = src->nodemap() | bit;
609
31.5k
        if (nv > 1) {
610
24.5k
            IMMER_TRY {
611
24.5k
                detail::uninitialized_copy(
612
24.5k
                    src->values(), src->values() + voffset, dst->values());
613
24.5k
                IMMER_TRY {
614
24.5k
                    detail::uninitialized_copy(src->values() + voffset + 1,
615
24.5k
                                               src->values() + nv,
616
24.5k
                                               dst->values() + voffset);
617
24.5k
                }
618
24.5k
                IMMER_CATCH (...) {
619
0
                    detail::destroy_n(dst->values(), voffset);
620
0
                    IMMER_RETHROW;
621
0
                }
622
24.5k
            }
623
24.5k
            IMMER_CATCH (...) {
624
0
                deallocate_inner(dst, n + 1, nv - 1);
625
0
                IMMER_RETHROW;
626
0
            }
627
24.5k
        }
628
7.03k
        inc_nodes(src->children(), n);
629
7.03k
        std::copy(src->children(), src->children() + noffset, dst->children());
630
7.03k
        std::copy(src->children() + noffset,
631
7.03k
                  src->children() + n,
632
7.03k
                  dst->children() + noffset + 1);
633
7.03k
        dst->children()[noffset] = node;
634
7.03k
        return dst;
635
31.5k
    }
636
637
    static node_t* move_inner_replace_merged(
638
        edit_t e, node_t* src, bitmap_t bit, count_t voffset, node_t* node)
639
    {
640
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
641
        assert(!(src->nodemap() & bit));
642
        assert(src->datamap() & bit);
643
        assert(voffset == src->data_count(bit));
644
        auto n                         = src->children_count();
645
        auto nv                        = src->data_count();
646
        auto dst                       = make_inner_n(n + 1, nv - 1);
647
        auto noffset                   = src->children_count(bit);
648
        dst->impl.d.data.inner.datamap = src->datamap() & ~bit;
649
        dst->impl.d.data.inner.nodemap = src->nodemap() | bit;
650
        if (nv > 1) {
651
            auto mutate_values = can_mutate(src->impl.d.data.inner.values, e);
652
            IMMER_TRY {
653
                if (mutate_values)
654
                    detail::uninitialized_move(
655
                        src->values(), src->values() + voffset, dst->values());
656
                else
657
                    detail::uninitialized_copy(
658
                        src->values(), src->values() + voffset, dst->values());
659
                IMMER_TRY {
660
                    if (mutate_values)
661
                        detail::uninitialized_move(src->values() + voffset + 1,
662
                                                   src->values() + nv,
663
                                                   dst->values() + voffset);
664
                    else
665
                        detail::uninitialized_copy(src->values() + voffset + 1,
666
                                                   src->values() + nv,
667
                                                   dst->values() + voffset);
668
                }
669
                IMMER_CATCH (...) {
670
                    detail::destroy_n(dst->values(), voffset);
671
                    IMMER_RETHROW;
672
                }
673
            }
674
            IMMER_CATCH (...) {
675
                deallocate_inner(dst, n + 1, nv - 1);
676
                IMMER_RETHROW;
677
            }
678
        }
679
        // inc_nodes(src->children(), n);
680
        std::copy(src->children(), src->children() + noffset, dst->children());
681
        std::copy(src->children() + noffset,
682
                  src->children() + n,
683
                  dst->children() + noffset + 1);
684
        dst->children()[noffset] = node;
685
        delete_inner(src);
686
        return dst;
687
    }
688
689
    static node_t* copy_inner_replace_inline(node_t* src,
690
                                             bitmap_t bit,
691
                                             count_t noffset,
692
                                             T value)
693
2.16k
    {
694
2.16k
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
695
2.16k
        assert(!(src->datamap() & bit));
696
2.16k
        assert(src->nodemap() & bit);
697
2.16k
        assert(noffset == src->children_count(bit));
698
2.16k
        auto n                         = src->children_count();
699
2.16k
        auto nv                        = src->data_count();
700
2.16k
        auto dst                       = make_inner_n(n - 1, nv + 1);
701
2.16k
        auto voffset                   = src->data_count(bit);
702
2.16k
        dst->impl.d.data.inner.nodemap = src->nodemap() & ~bit;
703
2.16k
        dst->impl.d.data.inner.datamap = src->datamap() | bit;
704
2.16k
        IMMER_TRY {
705
2.16k
            if (nv)
706
1.15k
                detail::uninitialized_copy(
707
1.15k
                    src->values(), src->values() + voffset, dst->values());
708
2.16k
            IMMER_TRY {
709
2.16k
                new (dst->values() + voffset) T{std::move(value)};
710
2.16k
                IMMER_TRY {
711
2.16k
                    if (nv)
712
1.15k
                        detail::uninitialized_copy(src->values() + voffset,
713
1.15k
                                                   src->values() + nv,
714
1.15k
                                                   dst->values() + voffset + 1);
715
2.16k
                }
716
2.16k
                IMMER_CATCH (...) {
717
0
                    dst->values()[voffset].~T();
718
0
                    IMMER_RETHROW;
719
0
                }
720
2.16k
            }
721
2.16k
            IMMER_CATCH (...) {
722
0
                detail::destroy_n(dst->values(), voffset);
723
0
                IMMER_RETHROW;
724
0
            }
725
2.16k
        }
726
2.16k
        IMMER_CATCH (...) {
727
0
            deallocate_inner(dst, n - 1, nv + 1);
728
0
            IMMER_RETHROW;
729
0
        }
730
0
        inc_nodes(src->children(), noffset);
731
0
        inc_nodes(src->children() + noffset + 1, n - noffset - 1);
732
0
        std::copy(src->children(), src->children() + noffset, dst->children());
733
0
        std::copy(src->children() + noffset + 1,
734
0
                  src->children() + n,
735
0
                  dst->children() + noffset);
736
0
        return dst;
737
2.16k
    }
738
739
    static node_t* move_inner_replace_inline(
740
        edit_t e, node_t* src, bitmap_t bit, count_t noffset, T value)
741
    {
742
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
743
        assert(!(src->datamap() & bit));
744
        assert(src->nodemap() & bit);
745
        assert(noffset == src->children_count(bit));
746
        auto n                         = src->children_count();
747
        auto nv                        = src->data_count();
748
        auto dst                       = make_inner_n(n - 1, nv + 1);
749
        auto voffset                   = src->data_count(bit);
750
        dst->impl.d.data.inner.nodemap = src->nodemap() & ~bit;
751
        dst->impl.d.data.inner.datamap = src->datamap() | bit;
752
        IMMER_TRY {
753
            auto mutate_values =
754
                nv && can_mutate(src->impl.d.data.inner.values, e);
755
            if (nv) {
756
                if (mutate_values)
757
                    detail::uninitialized_move(
758
                        src->values(), src->values() + voffset, dst->values());
759
                else
760
                    detail::uninitialized_copy(
761
                        src->values(), src->values() + voffset, dst->values());
762
            }
763
            IMMER_TRY {
764
                new (dst->values() + voffset) T{std::move(value)};
765
                IMMER_TRY {
766
                    if (nv) {
767
                        if (mutate_values)
768
                            detail::uninitialized_move(src->values() + voffset,
769
                                                       src->values() + nv,
770
                                                       dst->values() + voffset +
771
                                                           1);
772
                        else
773
                            detail::uninitialized_copy(src->values() + voffset,
774
                                                       src->values() + nv,
775
                                                       dst->values() + voffset +
776
                                                           1);
777
                    }
778
                }
779
                IMMER_CATCH (...) {
780
                    dst->values()[voffset].~T();
781
                    IMMER_RETHROW;
782
                }
783
            }
784
            IMMER_CATCH (...) {
785
                detail::destroy_n(dst->values(), voffset);
786
                IMMER_RETHROW;
787
            }
788
        }
789
        IMMER_CATCH (...) {
790
            deallocate_inner(dst, n - 1, nv + 1);
791
            IMMER_RETHROW;
792
        }
793
        std::copy(src->children(), src->children() + noffset, dst->children());
794
        std::copy(src->children() + noffset + 1,
795
                  src->children() + n,
796
                  dst->children() + noffset);
797
        delete_inner(src);
798
        return dst;
799
    }
800
801
    static node_t*
802
    copy_inner_remove_value(node_t* src, bitmap_t bit, count_t voffset)
803
979
    {
804
979
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
805
979
        assert(!(src->nodemap() & bit));
806
979
        assert(src->datamap() & bit);
807
979
        assert(voffset == src->data_count(bit));
808
979
        auto n                         = src->children_count();
809
979
        auto nv                        = src->data_count();
810
979
        auto dst                       = make_inner_n(n, nv - 1);
811
979
        dst->impl.d.data.inner.datamap = src->datamap() & ~bit;
812
979
        dst->impl.d.data.inner.nodemap = src->nodemap();
813
979
        if (nv > 1) {
814
726
            IMMER_TRY {
815
726
                detail::uninitialized_copy(
816
726
                    src->values(), src->values() + voffset, dst->values());
817
726
                IMMER_TRY {
818
726
                    detail::uninitialized_copy(src->values() + voffset + 1,
819
726
                                               src->values() + nv,
820
726
                                               dst->values() + voffset);
821
726
                }
822
726
                IMMER_CATCH (...) {
823
0
                    detail::destroy_n(dst->values(), voffset);
824
0
                    IMMER_RETHROW;
825
0
                }
826
726
            }
827
726
            IMMER_CATCH (...) {
828
0
                deallocate_inner(dst, n, nv - 1);
829
0
                IMMER_RETHROW;
830
0
            }
831
726
        }
832
253
        inc_nodes(src->children(), n);
833
253
        std::copy(src->children(), src->children() + n, dst->children());
834
253
        return dst;
835
979
    }
836
837
    static node_t* move_inner_remove_value(edit_t e,
838
                                           node_t* src,
839
                                           bitmap_t bit,
840
                                           count_t voffset)
841
    {
842
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
843
        assert(!(src->nodemap() & bit));
844
        assert(src->datamap() & bit);
845
        assert(voffset == src->data_count(bit));
846
        auto n                         = src->children_count();
847
        auto nv                        = src->data_count();
848
        auto dst                       = make_inner_n(n, nv - 1);
849
        dst->impl.d.data.inner.datamap = src->datamap() & ~bit;
850
        dst->impl.d.data.inner.nodemap = src->nodemap();
851
        if (nv > 1) {
852
            auto mutate_values = can_mutate(src->impl.d.data.inner.values, e);
853
            if (mutate_values) {
854
                IMMER_TRY {
855
                    detail::uninitialized_move(
856
                        src->values(), src->values() + voffset, dst->values());
857
                    IMMER_TRY {
858
                        detail::uninitialized_move(src->values() + voffset + 1,
859
                                                   src->values() + nv,
860
                                                   dst->values() + voffset);
861
                    }
862
                    IMMER_CATCH (...) {
863
                        detail::destroy_n(dst->values(), voffset);
864
                        IMMER_RETHROW;
865
                    }
866
                }
867
                IMMER_CATCH (...) {
868
                    deallocate_inner(dst, n, nv - 1);
869
                    IMMER_RETHROW;
870
                }
871
            } else {
872
                IMMER_TRY {
873
                    detail::uninitialized_copy(
874
                        src->values(), src->values() + voffset, dst->values());
875
                    IMMER_TRY {
876
                        detail::uninitialized_copy(src->values() + voffset + 1,
877
                                                   src->values() + nv,
878
                                                   dst->values() + voffset);
879
                    }
880
                    IMMER_CATCH (...) {
881
                        detail::destroy_n(dst->values(), voffset);
882
                        IMMER_RETHROW;
883
                    }
884
                }
885
                IMMER_CATCH (...) {
886
                    deallocate_inner(dst, n, nv - 1);
887
                    IMMER_RETHROW;
888
                }
889
            }
890
        }
891
        std::copy(src->children(), src->children() + n, dst->children());
892
        delete_inner(src);
893
        return dst;
894
    }
895
896
    static node_t* copy_inner_insert_value(node_t* src, bitmap_t bit, T v)
897
49.8k
    {
898
49.8k
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
899
49.8k
        auto n                         = src->children_count();
900
49.8k
        auto nv                        = src->data_count();
901
49.8k
        auto offset                    = src->data_count(bit);
902
49.8k
        auto dst                       = make_inner_n(n, nv + 1);
903
49.8k
        dst->impl.d.data.inner.datamap = src->datamap() | bit;
904
49.8k
        dst->impl.d.data.inner.nodemap = src->nodemap();
905
49.8k
        IMMER_TRY {
906
49.8k
            if (nv)
907
39.9k
                detail::uninitialized_copy(
908
39.9k
                    src->values(), src->values() + offset, dst->values());
909
49.8k
            IMMER_TRY {
910
49.8k
                new (dst->values() + offset) T{std::move(v)};
911
49.8k
                IMMER_TRY {
912
49.8k
                    if (nv)
913
39.9k
                        detail::uninitialized_copy(src->values() + offset,
914
39.9k
                                                   src->values() + nv,
915
39.9k
                                                   dst->values() + offset + 1);
916
49.8k
                }
917
49.8k
                IMMER_CATCH (...) {
918
0
                    dst->values()[offset].~T();
919
0
                    IMMER_RETHROW;
920
0
                }
921
49.8k
            }
922
49.8k
            IMMER_CATCH (...) {
923
0
                detail::destroy_n(dst->values(), offset);
924
0
                IMMER_RETHROW;
925
0
            }
926
49.8k
        }
927
49.8k
        IMMER_CATCH (...) {
928
0
            deallocate_inner(dst, n, nv + 1);
929
0
            IMMER_RETHROW;
930
0
        }
931
0
        inc_nodes(src->children(), n);
932
0
        std::copy(src->children(), src->children() + n, dst->children());
933
0
        return dst;
934
49.8k
    }
935
936
    static node_t*
937
    move_inner_insert_value(edit_t e, node_t* src, bitmap_t bit, T v)
938
    {
939
        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
940
        auto n                         = src->children_count();
941
        auto nv                        = src->data_count();
942
        auto offset                    = src->data_count(bit);
943
        auto dst                       = make_inner_n(n, nv + 1);
944
        dst->impl.d.data.inner.datamap = src->datamap() | bit;
945
        dst->impl.d.data.inner.nodemap = src->nodemap();
946
        IMMER_TRY {
947
            auto mutate_values =
948
                nv && can_mutate(src->impl.d.data.inner.values, e);
949
            if (nv) {
950
                if (mutate_values)
951
                    detail::uninitialized_move(
952
                        src->values(), src->values() + offset, dst->values());
953
                else
954
                    detail::uninitialized_copy(
955
                        src->values(), src->values() + offset, dst->values());
956
            }
957
            IMMER_TRY {
958
                new (dst->values() + offset) T{std::move(v)};
959
                IMMER_TRY {
960
                    if (nv) {
961
                        if (mutate_values)
962
                            detail::uninitialized_move(src->values() + offset,
963
                                                       src->values() + nv,
964
                                                       dst->values() + offset +
965
                                                           1);
966
                        else
967
                            detail::uninitialized_copy(src->values() + offset,
968
                                                       src->values() + nv,
969
                                                       dst->values() + offset +
970
                                                           1);
971
                    }
972
                }
973
                IMMER_CATCH (...) {
974
                    dst->values()[offset].~T();
975
                    IMMER_RETHROW;
976
                }
977
            }
978
            IMMER_CATCH (...) {
979
                detail::destroy_n(dst->values(), offset);
980
                IMMER_RETHROW;
981
            }
982
        }
983
        IMMER_CATCH (...) {
984
            deallocate_inner(dst, n, nv + 1);
985
            IMMER_RETHROW;
986
        }
987
        std::copy(src->children(), src->children() + n, dst->children());
988
        delete_inner(src);
989
        return dst;
990
    }
991
992
    static node_t*
993
    make_merged(shift_t shift, T v1, hash_t hash1, T v2, hash_t hash2)
994
79.8k
    {
995
79.8k
        if (shift < max_shift<hash_t, B>) {
996
78.1k
            auto idx1 = hash1 & (mask<hash_t, B> << shift);
997
78.1k
            auto idx2 = hash2 & (mask<hash_t, B> << shift);
998
78.1k
            if (idx1 == idx2) {
999
48.2k
                auto merged = make_merged(
1000
48.2k
                    shift + B, std::move(v1), hash1, std::move(v2), hash2);
1001
48.2k
                IMMER_TRY {
1002
48.2k
                    return make_inner_n(
1003
48.2k
                        1, static_cast<count_t>(idx1 >> shift), merged);
1004
48.2k
                }
1005
48.2k
                IMMER_CATCH (...) {
1006
0
                    delete_deep_shift(merged, shift + B);
1007
0
                    IMMER_RETHROW;
1008
0
                }
1009
48.2k
            } else {
1010
29.8k
                return make_inner_n(0,
1011
29.8k
                                    static_cast<count_t>(idx1 >> shift),
1012
29.8k
                                    std::move(v1),
1013
29.8k
                                    static_cast<count_t>(idx2 >> shift),
1014
29.8k
                                    std::move(v2));
1015
29.8k
            }
1016
78.1k
        } else {
1017
1.72k
            return make_collision(std::move(v1), std::move(v2));
1018
1.72k
        }
1019
79.8k
    }
1020
1021
    static node_t* make_merged_e(
1022
        edit_t e, shift_t shift, T v1, hash_t hash1, T v2, hash_t hash2)
1023
    {
1024
        if (shift < max_shift<hash_t, B>) {
1025
            auto idx1 = hash1 & (mask<hash_t, B> << shift);
1026
            auto idx2 = hash2 & (mask<hash_t, B> << shift);
1027
            if (idx1 == idx2) {
1028
                auto merged = make_merged_e(
1029
                    e, shift + B, std::move(v1), hash1, std::move(v2), hash2);
1030
                IMMER_TRY {
1031
                    return owned(
1032
                        make_inner_n(
1033
                            1, static_cast<count_t>(idx1 >> shift), merged),
1034
                        e);
1035
                }
1036
                IMMER_CATCH (...) {
1037
                    delete_deep_shift(merged, shift + B);
1038
                    IMMER_RETHROW;
1039
                }
1040
            } else {
1041
                auto r = make_inner_n(0,
1042
                                      static_cast<count_t>(idx1 >> shift),
1043
                                      std::move(v1),
1044
                                      static_cast<count_t>(idx2 >> shift),
1045
                                      std::move(v2));
1046
                return owned_values(r, e);
1047
            }
1048
        } else {
1049
            return owned(make_collision(std::move(v1), std::move(v2)), e);
1050
        }
1051
    }
1052
1053
    node_t* inc()
1054
508k
    {
1055
508k
        refs(this).inc();
1056
508k
        return this;
1057
508k
    }
1058
1059
    const node_t* inc() const
1060
    {
1061
        refs(this).inc();
1062
        return this;
1063
    }
1064
1065
993k
    bool dec() const { return refs(this).dec(); }
1066
1067
    static void inc_nodes(node_t** p, count_t n)
1068
5.49M
    {
1069
16.5M
        for (auto i = p, e = i + n; i != e; ++i)
1070
11.0M
            refs(*i).inc();
1071
5.49M
    }
1072
1073
    static void delete_values(values_t* p, count_t n)
1074
0
    {
1075
0
        assert(p);
1076
0
        detail::destroy_n((T*) &p->d.buffer, n);
1077
0
        deallocate_values(p, n);
1078
0
    }
1079
1080
    static void delete_inner(node_t* p)
1081
0
    {
1082
0
        assert(p);
1083
0
        IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
1084
0
        auto vp = p->impl.d.data.inner.values;
1085
0
        if (vp && refs(vp).dec())
1086
0
            delete_values(vp, p->data_count());
1087
0
        deallocate_inner(p, p->children_count());
1088
0
    }
1089
1090
    static void delete_collision(node_t* p)
1091
0
    {
1092
0
        assert(p);
1093
0
        IMMER_ASSERT_TAGGED(p->kind() == kind_t::collision);
1094
0
        auto n = p->collision_count();
1095
0
        detail::destroy_n(p->collisions(), n);
1096
0
        deallocate_collision(p, n);
1097
0
    }
1098
1099
    static void delete_deep(node_t* p, shift_t s)
1100
0
    {
1101
0
        if (s == max_depth<hash_t, B>)
1102
0
            delete_collision(p);
1103
0
        else {
1104
0
            auto fst = p->children();
1105
0
            auto lst = fst + p->children_count();
1106
0
            for (; fst != lst; ++fst)
1107
0
                if ((*fst)->dec())
1108
0
                    delete_deep(*fst, s + 1);
1109
0
            delete_inner(p);
1110
0
        }
1111
0
    }
1112
1113
    static void delete_deep_shift(node_t* p, shift_t s)
1114
0
    {
1115
0
        if (s == max_shift<hash_t, B>)
1116
0
            delete_collision(p);
1117
0
        else {
1118
0
            auto fst = p->children();
1119
0
            auto lst = fst + p->children_count();
1120
0
            for (; fst != lst; ++fst)
1121
0
                if ((*fst)->dec())
1122
0
                    delete_deep_shift(*fst, s + B);
1123
0
            delete_inner(p);
1124
0
        }
1125
0
    }
1126
1127
    static void deallocate_values(values_t* p, count_t n)
1128
0
    {
1129
0
        heap::deallocate(node_t::sizeof_values_n(n), p);
1130
0
    }
1131
1132
    static void deallocate_collision(node_t* p, count_t n)
1133
0
    {
1134
0
        heap::deallocate(node_t::sizeof_collision_n(n), p);
1135
0
    }
1136
1137
    static void deallocate_inner(node_t* p, count_t n)
1138
0
    {
1139
0
        heap::deallocate(node_t::sizeof_inner_n(n), p);
1140
0
    }
1141
1142
    static void deallocate_inner(node_t* p, count_t n, count_t nv)
1143
0
    {
1144
0
        assert(nv);
1145
0
        heap::deallocate(node_t::sizeof_values_n(nv),
1146
0
                         p->impl.d.data.inner.values);
1147
0
        heap::deallocate(node_t::sizeof_inner_n(n), p);
1148
0
    }
1149
};
1150
1151
} // namespace hamts
1152
} // namespace detail
1153
} // namespace immer