• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / github_pull_request_275914

25 Sep 2023 03:10PM UTC coverage: 92.915% (+1.7%) from 91.215%
github_pull_request_275914

Pull #6073

Evergreen

jedelbo
Merge tag 'v13.21.0' into next-major

"Feature/Bugfix release"
Pull Request #6073: Merge next-major

96928 of 177706 branches covered (0.0%)

8324 of 8714 new or added lines in 122 files covered. (95.52%)

181 existing lines in 28 files now uncovered.

247505 of 266379 relevant lines covered (92.91%)

7164945.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.39
/src/realm/bplustree.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2018 Realm Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 * http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 **************************************************************************/
18

19
#include <realm/bplustree.hpp>
20
#include <realm/impl/destroy_guard.hpp>
21
#include <realm/array_unsigned.hpp>
22

23
using namespace realm;
24

25
namespace realm {
26

27
bool BPlusTreeNode::get_context_flag() const noexcept
28
{
3,329,967✔
29
    auto ref = get_ref();
6,636,384✔
30
    MemRef mem(ref, m_tree->get_alloc());
6,636,384✔
31
    return Array::get_context_flag_from_header(mem.get_addr());
6,636,384✔
32
}
6,636,384✔
33

3,306,417✔
34
void BPlusTreeNode::set_context_flag(bool cf) noexcept
35
{
11,985✔
36
    auto ref = get_ref();
24,696✔
37
    MemRef mem(ref, m_tree->get_alloc());
24,696✔
38
    if (Array::get_context_flag_from_header(mem.get_addr()) != cf) {
24,696✔
39
        Array arr(m_tree->get_alloc());
12,972✔
40
        arr.init_from_mem(mem);
522✔
41
        arr.set_context_flag(cf);
522✔
42
        if (auto new_ref = arr.get_ref(); new_ref != ref) {
522✔
43
            init_from_ref(new_ref);
264✔
44
            update_parent();
6✔
45
        }
6✔
46
    }
264✔
47
}
12,246✔
48

12,711✔
49

50
/*****************************************************************************/
51
/* BPlusTreeInner                                                            */
52
/* All interior nodes is of this class                                       */
53
/*****************************************************************************/
54
class BPlusTreeInner : public BPlusTreeNode, private Array {
55
public:
56
    using Array::destroy_deep;
57

58
    BPlusTreeInner(BPlusTreeBase* tree);
59
    ~BPlusTreeInner() override;
60

61
    void init_from_mem(MemRef mem);
62
    void create(size_t elems_per_child);
63

64
    /******** Implementation of abstract interface *******/
65
    bool is_leaf() const override
66
    {
186,453✔
67
        return false;
399,570✔
68
    }
399,570✔
69
    bool is_compact() const override
213,117✔
70
    {
71
        return (Array::get(0) & 1) != 0;
×
72
    }
×
73
    ref_type get_ref() const override
74
    {
36,489✔
75
        return Array::get_ref();
74,457✔
76
    }
74,457✔
77

37,968✔
78
    void init_from_ref(ref_type ref) noexcept override
79
    {
3,668,505✔
80
        char* header = m_alloc.translate(ref);
7,424,922✔
81
        init_from_mem(MemRef(header, ref, m_alloc));
7,424,922✔
82
    }
7,424,922✔
83

3,756,417✔
84
    void bp_set_parent(ArrayParent* p, size_t n) override
85
    {
4,174,533✔
86
        Array::set_parent(p, n);
8,525,610✔
87
    }
8,525,610✔
88
    void update_parent() override
4,351,077✔
89
    {
6,033✔
90
        Array::update_parent();
15,807✔
91
    }
15,807✔
92

9,774✔
93
    size_t get_node_size() const override
94
    {
7,196,322✔
95
        return Array::size() - 2;
15,220,614✔
96
    }
15,220,614✔
97
    size_t get_tree_size() const override
8,024,292✔
98
    {
7,755,129✔
99
        return size_t(back()) >> 1;
16,121,412✔
100
    }
16,121,412✔
101

8,366,283✔
102
    ref_type bptree_insert(size_t n, State& state, InsertFunc) override;
103
    void bptree_access(size_t n, AccessFunc) override;
104
    size_t bptree_erase(size_t n, EraseFunc) override;
105
    bool bptree_traverse(TraverseFunc) override;
106
    void verify() const override;
107

108
    // Other modifiers
109

110
    void append_tree_size(size_t sz)
111
    {
6,033✔
112
        Array::add((sz << 1) + 1);
15,807✔
113
    }
15,807✔
114
    void add_bp_node_ref(ref_type ref, int64_t offset = 0)
9,774✔
115
    {
12,066✔
116
        Array::add(from_ref(ref));
31,638✔
117
        REALM_ASSERT_DEBUG(offset >= 0);
31,638✔
118
        if (offset && m_offsets.is_attached()) {
31,638✔
119
            m_offsets.add(offset);
19,635✔
120
        }
126✔
121
    }
12,129✔
122
    // Reset first (and only!) child ref and return the previous value
19,572✔
123
    ref_type clear_first_bp_node_ref()
124
    {
84✔
125
        REALM_ASSERT(get_node_size() == 1);
177✔
126
        ref_type old_ref = Array::get_as_ref(1);
177✔
127
        Array::set(1, 0);
177✔
128

177✔
129
        return old_ref;
84✔
130
    }
177✔
131
    void ensure_offsets();
93✔
132

133
private:
134
    ArrayUnsigned m_offsets;
135
    size_t m_my_offset;
136

137
    void move(BPlusTreeNode* new_node, size_t ndx, int64_t offset_adj) override;
138
    void set_offset(size_t offs)
139
    {
140
        m_my_offset = offs;
141
    }
142
    void set_tree_size(size_t sz)
143
    {
×
144
        Array::set(m_size - 1, (sz << 1) + 1);
×
145
    }
146
    size_t get_elems_per_child() const
147
    {
9,249,696✔
148
        // Only relevant when in compact form
9,249,696✔
149
        REALM_ASSERT_DEBUG(!m_offsets.is_attached());
9,249,696✔
150
        return size_t(Array::get(0)) >> 1;
19,040,046✔
151
    }
9,249,696✔
152
    // Should not be mixed up with Array::get_child_ref
9,790,350✔
153
    ref_type get_bp_node_ref(size_t ndx) const noexcept
9,790,350✔
154
    {
19,130,118✔
155
        return Array::get_as_ref(ndx + 1);
9,339,768✔
156
    }
9,339,768✔
157
    void insert_bp_node_ref(size_t ndx, ref_type ref)
10,161,900✔
158
    {
10,164,762✔
159
        Array::insert(ndx + 1, from_ref(ref));
10,164,762✔
160
    }
2,862✔
161
    BPlusTreeLeaf* cache_leaf(MemRef mem, size_t ndx, size_t offset);
2,802✔
162
    void erase_and_destroy_bp_node(size_t ndx);
2,802✔
163
    ref_type insert_bp_node(size_t child_ndx, ref_type new_sibling_ref, State& state);
2,802✔
164
    size_t get_bp_node_offset(size_t child_ndx) const
165
    {
65,127✔
166
        return (child_ndx) > 0 ? size_t(m_offsets.get(child_ndx - 1)) : 0;
65,127✔
167
    }
65,127✔
168
};
353,121✔
169
}
251,025✔
170

353,121✔
171
/****************************** BPlusTreeNode ********************************/
172

173
BPlusTreeNode::~BPlusTreeNode()
174
{
17,132,625✔
175
}
17,132,625✔
176

177
/****************************** BPlusTreeLeaf ********************************/
23,875,248✔
178

23,875,248✔
179
ref_type BPlusTreeLeaf::bptree_insert(size_t ndx, State& state, InsertFunc func)
180
{
32,748,273✔
181
    size_t leaf_size = get_node_size();
32,748,273✔
182
    REALM_ASSERT_DEBUG(leaf_size <= REALM_MAX_BPNODE_SIZE);
32,748,273✔
183
    if (ndx > leaf_size)
70,133,928✔
184
        ndx = leaf_size;
70,050,423✔
185
    if (REALM_LIKELY(leaf_size < REALM_MAX_BPNODE_SIZE)) {
70,133,928✔
186
        func(this, ndx);
70,009,014✔
187
        m_tree->adjust_leaf_bounds(1);
69,853,791✔
188
        return 0; // Leaf was not split
70,009,014✔
189
    }
69,913,716✔
190

37,415,271✔
191
    // Split leaf node
37,415,271✔
192
    auto new_leaf = m_tree->create_leaf_node();
37,415,271✔
193
    if (ndx == leaf_size) {
124,914✔
194
        func(new_leaf.get(), 0);
8,820✔
195
        state.split_offset = ndx;
104,118✔
196
    }
104,118✔
197
    else {
128,592✔
198
        move(new_leaf.get(), ndx, 0);
128,592✔
199
        func(this, ndx);
128,592✔
200
        state.split_offset = ndx + 1;
198,894✔
201
        // Invalidate cached leaf
198,894✔
202
        m_tree->invalidate_leaf_cache();
198,894✔
203
    }
198,894✔
204
    state.split_size = leaf_size + 1;
124,914✔
205

207,714✔
206
    return new_leaf->get_ref();
207,714✔
207
}
220,212✔
208

209
void BPlusTreeLeaf::bptree_access(size_t ndx, AccessFunc func)
95,298✔
210
{
26,891,358✔
211
    func(this, ndx);
26,796,060✔
212
}
26,796,060✔
213

26,957,643✔
214
size_t BPlusTreeLeaf::bptree_erase(size_t ndx, EraseFunc func)
26,957,643✔
215
{
27,969,768✔
216
    return func(this, ndx);
1,012,125✔
217
}
1,012,125✔
218

981,927✔
219
bool BPlusTreeLeaf::bptree_traverse(TraverseFunc func)
981,927✔
220
{
1,653,033✔
221
    return func(this, 0) == IteratorControl::Stop;
671,106✔
222
}
671,106✔
223

744,738✔
224
/****************************** BPlusTreeInner *******************************/
744,738✔
225

744,738✔
226
BPlusTreeInner::BPlusTreeInner(BPlusTreeBase* tree)
227
    : BPlusTreeNode(tree)
228
    , Array(tree->get_alloc())
229
    , m_offsets(tree->get_alloc())
3✔
230
    , m_my_offset(0)
3✔
231
{
3,572,175✔
232
    m_offsets.set_parent(this, 0);
3,572,172✔
233
}
3,572,175✔
234

3✔
235
void BPlusTreeInner::create(size_t elems_per_child)
236
{
6,036✔
237
    // Born only with room for number of elements per child
6,033✔
238
    uint64_t tagged = (elems_per_child << 1) + 1;
6,036✔
239
    Array::create(Array::type_InnerBptreeNode, false, 1, tagged);
6,066✔
240
}
6,063✔
241

30✔
242
BPlusTreeInner::~BPlusTreeInner()
30✔
243
{
3,604,395✔
244
}
3,604,365✔
245

30,000✔
246
void BPlusTreeInner::init_from_mem(MemRef mem)
30✔
247
{
3,668,346✔
248
    Array::init_from_mem(mem);
3,668,319✔
249
    auto rot = Array::get(0);
3,668,319✔
250
    if ((rot & 1) == 0) {
3,668,319✔
251
        // rot is a ref
4,560✔
252
        m_offsets.init_from_ref(to_ref(rot));
4,557✔
253
    }
4,557✔
254
    else {
3,663,759✔
255
        m_offsets.detach();
3,663,759✔
256
    }
3,663,762✔
257
}
3,668,316✔
258

259
void BPlusTreeInner::bptree_access(size_t n, AccessFunc func)
260
{
2,193,957✔
261
    size_t child_ndx;
2,193,957✔
262
    size_t child_offset;
2,193,957✔
263
    if (m_offsets.is_attached()) {
2,193,957✔
264
        child_ndx = m_offsets.upper_bound(n);
18,579✔
265
        child_offset = get_bp_node_offset(child_ndx);
3,761,223✔
266
        REALM_ASSERT_3(child_ndx, <, get_node_size());
3,761,223✔
267
    }
3,761,223✔
268
    else {
2,175,378✔
269
        auto elems_per_child = get_elems_per_child();
2,175,378✔
270
        child_ndx = n / elems_per_child;
2,185,152✔
271
        child_offset = child_ndx * elems_per_child;
2,175,378✔
272
    }
2,185,152✔
273

2,203,731✔
274
    ref_type child_ref = get_bp_node_ref(child_ndx);
2,203,731✔
275
    char* child_header = m_alloc.translate(child_ref);
2,193,957✔
276
    MemRef mem(child_header, child_ref, m_alloc);
2,193,957✔
277
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
5,937,657✔
278
    if (child_is_leaf) {
5,937,678✔
279
        auto leaf = cache_leaf(mem, child_ndx, child_offset + m_my_offset);
2,193,978✔
280
        func(leaf, n - child_offset);
2,193,978✔
281
    }
5,950,308✔
282
    else {
2,151,239,977✔
283
        BPlusTreeInner node(m_tree);
2,151,239,977✔
284
        node.set_parent(this, child_ndx + 1);
2,151,239,977✔
285
        node.init_from_mem(mem);
2,147,483,647✔
286
        node.set_offset(child_offset + m_my_offset);
2,147,542,201✔
287
        node.bptree_access(n - child_offset, func);
2,147,542,201✔
288
    }
2,151,181,423✔
289
}
5,891,733✔
290

3,697,776✔
291
ref_type BPlusTreeInner::bptree_insert(size_t ndx, State& state, InsertFunc func)
3,756,330✔
292
{
4,090,176✔
293
    size_t child_ndx;
4,090,176✔
294
    size_t child_offset;
6,356,388✔
295
    if (ndx != npos) {
6,356,388✔
296
        ensure_offsets();
2,275,161✔
297
        child_ndx = m_offsets.upper_bound(ndx);
2,275,161✔
298
        child_offset = get_bp_node_offset(child_ndx);
87,504✔
299
        ndx -= child_offset;
87,504✔
300
        REALM_ASSERT_3(child_ndx, <, get_node_size());
87,504✔
301
    }
87,504✔
302
    else {
6,268,884✔
303
        child_ndx = get_node_size() - 1;
6,268,884✔
304
        if (m_offsets.is_attached()) {
6,268,884✔
305
            child_offset = get_bp_node_offset(child_ndx);
2,187,657✔
306
            REALM_ASSERT_3(child_ndx, <, get_node_size());
2,187,657✔
307
        }
308
        else {
6,347,439✔
309
            auto elems_per_child = get_elems_per_child();
6,347,439✔
310
            child_offset = child_ndx * elems_per_child;
6,347,439✔
311
        }
6,347,439✔
312
    }
6,347,439✔
313

6,356,349✔
314
    ref_type child_ref = get_bp_node_ref(child_ndx);
6,356,349✔
315
    char* child_header = m_alloc.translate(child_ref);
6,356,349✔
316
    MemRef mem(child_header, child_ref, m_alloc);
4,090,215✔
317
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
4,090,215✔
318
    ref_type new_sibling_ref;
4,090,215✔
319
    if (child_is_leaf) {
4,090,215✔
320
        auto leaf = cache_leaf(mem, child_ndx, child_offset + m_my_offset);
4,082,226✔
321
        new_sibling_ref = leaf->bptree_insert(ndx, state, func);
4,082,226✔
322
    }
4,082,226✔
323
    else {
2,274,201✔
324
        BPlusTreeInner node(m_tree);
7,989✔
325
        node.set_parent(this, child_ndx + 1);
7,989✔
326
        node.init_from_mem(mem);
4,618,338✔
327
        node.set_offset(child_offset + m_my_offset);
4,618,338✔
328
        new_sibling_ref = node.bptree_insert(ndx, state, func);
4,618,338✔
329
    }
4,618,338✔
330

4,099,125✔
331
    if (!new_sibling_ref) {
4,099,125✔
332
        adjust(size() - 1, +2); // Throws
4,065,270✔
333
        if (m_offsets.is_attached()) {
4,065,270✔
334
            m_offsets.adjust(child_ndx, m_offsets.size(), 1);
17,886✔
335
        }
17,886✔
336
        return 0;
8,657,721✔
337
    }
8,657,721✔
338

4,635,255✔
339
    return insert_bp_node(child_ndx, new_sibling_ref, state);
33,855✔
340
}
33,855!
341

342
size_t BPlusTreeInner::bptree_erase(size_t n, EraseFunc func)
4,601,400✔
343
{
4,635,987✔
344
    ensure_offsets();
4,635,987✔
345

4,635,987✔
346
    size_t child_ndx = m_offsets.upper_bound(n);
4,635,987✔
347
    size_t child_offset = get_bp_node_offset(child_ndx);
34,587✔
348
    REALM_ASSERT_3(child_ndx, <, get_node_size());
4,644,936✔
349

4,644,936✔
350
    // Call bptree_erase recursively and ultimately call 'func' on leaf
4,644,936✔
351
    size_t erase_node_size;
4,644,936✔
352
    ref_type child_ref = get_bp_node_ref(child_ndx);
4,644,936✔
353
    char* child_header = m_alloc.translate(child_ref);
4,644,936✔
354
    MemRef mem(child_header, child_ref, m_alloc);
4,642,362✔
355
    BPlusTreeLeaf* leaf = nullptr;
4,642,362✔
356
    BPlusTreeInner node(m_tree);
4,642,362✔
357
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
37,161✔
358
    if (child_is_leaf) {
37,161✔
359
        leaf = cache_leaf(mem, child_ndx, child_offset + m_my_offset);
37,161✔
360
        erase_node_size = func(leaf, n - child_offset);
37,161✔
361
        if (erase_node_size == 0) {
37,161✔
362
            m_tree->invalidate_leaf_cache();
2,598✔
363
        }
2,598✔
364
        else {
34,563✔
365
            m_tree->adjust_leaf_bounds(-1);
4,644,912✔
366
        }
4,633,386✔
367
    }
4,633,410✔
368
    else {
8,937✔
369
        node.set_parent(this, child_ndx + 1);
8,937✔
370
        node.init_from_mem(mem);
4,598,823✔
371
        node.set_offset(child_offset + m_my_offset);
4,598,823✔
372
        erase_node_size = node.bptree_erase(n - child_offset, func);
373
    }
11,526✔
374

46,113✔
375
    adjust(size() - 1, -2);
34,587✔
376
    m_offsets.adjust(child_ndx, m_offsets.size(), -1);
34,587✔
377

123,180✔
378
    // Check if some nodes could be merged
123,180✔
379
    size_t num_children = get_node_size();
34,587✔
380
    if (erase_node_size == 0) {
123,180✔
381
        if (num_children == 1) {
88,617✔
382
            // Only child empty - delete this one too
88,593✔
383
            return 0;
384
        }
385
        // Child leaf is empty - destroy it!
88,617✔
386
        erase_and_destroy_bp_node(child_ndx);
88,617✔
387
        return num_children - 1;
88,617✔
388
    }
88,617✔
389
    else if (erase_node_size < REALM_MAX_BPNODE_SIZE / 2 && child_ndx < (num_children - 1)) {
123,156✔
390
        // Candidate for merge. First calculate if the combined size of current and
104,079✔
391
        // next sibling is small enough.
104,079✔
392
        size_t sibling_ndx = child_ndx + 1;
104,079✔
393
        ref_type sibling_ref = get_bp_node_ref(sibling_ndx);
104,079✔
394
        std::unique_ptr<BPlusTreeLeaf> sibling_leaf;
104,079✔
395
        BPlusTreeInner node2(m_tree);
104,079✔
396
        BPlusTreeNode* sibling_node;
15,570✔
397
        if (child_is_leaf) {
15,570✔
398
            sibling_leaf = m_tree->init_leaf_node(sibling_ref);
103,995✔
399
            sibling_node = sibling_leaf.get();
103,995✔
400
        }
103,995✔
401
        else {
88,593✔
402
            node2.init_from_ref(sibling_ref);
×
403
            sibling_node = &node2;
×
404
        }
×
405
        sibling_node->bp_set_parent(this, sibling_ndx + 1);
15,486✔
406

15,486✔
407
        size_t combined_size = sibling_node->get_node_size() + erase_node_size;
15,486✔
408

15,486✔
409
        if (combined_size < REALM_MAX_BPNODE_SIZE * 3 / 4) {
104,079✔
410
            // Combined size is small enough for the nodes to be merged
88,659✔
411
            // Move all content from the next sibling into current node
66✔
412
            int64_t offs_adj = 0;
66✔
413
            if (child_is_leaf) {
88,659✔
414
                REALM_ASSERT(leaf);
88,659✔
415
                if (sibling_ndx < m_offsets.size())
150✔
416
                    m_offsets.set(sibling_ndx - 1, m_offsets.get(sibling_ndx));
417
                sibling_node->move(leaf, 0, 0);
66✔
418
                // Invalidate cached leaf
66✔
419
                m_tree->invalidate_leaf_cache();
66✔
420
            }
150✔
421
            else {
84✔
422
                node.ensure_offsets();
84✔
423
                node2.ensure_offsets();
88,509✔
424
                // Offset adjustment is negative as the sibling has bigger offsets
425
                auto sibling_offs = m_offsets.get(sibling_ndx - 1);
426
                auto erase_node_offs = get_bp_node_offset(child_ndx);
15,486✔
427
                offs_adj = erase_node_offs - sibling_offs;
15,486✔
428
                if (sibling_ndx < m_offsets.size())
15,486✔
429
                    m_offsets.set(sibling_ndx - 1, m_offsets.get(sibling_ndx));
15,486✔
430

15,486✔
431
                size_t orig_size = node.get_tree_size();
15,486✔
432
                size_t size_of_moved = node2.get_tree_size();
15,486✔
433
                // Remove size field from node as the move operation will just add
15,486✔
434
                node.erase(node.size() - 1);
15,486✔
435
                node2.move(&node, 0, offs_adj);
×
436
                node.append_tree_size(orig_size + size_of_moved);
×
437
            }
×
438

66✔
439
            // Destroy sibling
15,552✔
440
            erase_and_destroy_bp_node(sibling_ndx);
66✔
441
            num_children--;
15,552✔
442
        }
66✔
443
    }
30,972✔
444

34,587✔
445
    return num_children;
34,587✔
446
}
34,653✔
447

66✔
448
bool BPlusTreeInner::bptree_traverse(TraverseFunc func)
66✔
449
{
3,019,710✔
450
    size_t sz = get_node_size();
3,019,644✔
451
    for (size_t i = 0; i < sz; i++) {
3,029,013✔
452
        size_t child_offset;
3,027,351✔
453
        if (m_offsets.is_attached()) {
3,027,417✔
454
            child_offset = get_bp_node_offset(i);
3,066✔
455
        }
3,000✔
456
        else {
3,024,351✔
457
            auto elems_per_child = get_elems_per_child();
3,024,351✔
458
            child_offset = i * elems_per_child;
3,024,351✔
459
        }
3,024,351✔
460

3,027,351✔
461
        bool done;
3,027,351✔
462
        ref_type child_ref = get_bp_node_ref(i);
3,027,351!
463
        char* child_header = m_alloc.translate(child_ref);
3,027,351✔
464
        MemRef mem(child_header, child_ref, m_alloc);
3,027,351✔
465
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
3,027,351✔
466
        if (child_is_leaf) {
3,027,351✔
467
            auto leaf = cache_leaf(mem, i, child_offset + m_my_offset);
3,027,348✔
468
            done = (func(leaf, child_offset + m_my_offset) == IteratorControl::Stop);
3,027,348✔
469
        }
3,027,348✔
470
        else {
3✔
471
            BPlusTreeInner node(m_tree);
3✔
472
            node.set_parent(this, i + 1);
3✔
473
            node.init_from_mem(mem);
3✔
474
            node.set_offset(child_offset + m_my_offset);
69✔
475
            // std::cout << "BPlusTreeInner offset: " << child_offset << std::endl;
69✔
476
            done = node.bptree_traverse(func);
69✔
477
        }
15,489✔
478
        if (done) {
3,027,351✔
479
            return true;
3,106,557✔
480
        }
3,106,641✔
481
    }
3,027,351✔
482
    return false;
3,019,644✔
483
}
6,093,294✔
484

3,073,650✔
485
void BPlusTreeInner::move(BPlusTreeNode* new_node, size_t ndx, int64_t adj)
3,202,968✔
486
{
3,201,372✔
487
    BPlusTreeInner* dst(static_cast<BPlusTreeInner*>(new_node));
3,201,372✔
488
    size_t sz = get_node_size();
177,012✔
489

177,012✔
490
    // Copy refs
3,024,360✔
491
    for (size_t i = ndx; i < sz; i++) {
3,024,360✔
492
        size_t offs = get_bp_node_offset(i);
3,024,360✔
493
        dst->add_bp_node_ref(get_bp_node_ref(i), offs - adj);
3,024,360✔
494
    }
495
    truncate(ndx + 1);
3,201,372✔
496
    if (ndx > 0)
3,201,372✔
497
        m_offsets.truncate(ndx - 1);
3,201,372✔
498
}
3,201,372✔
499

3,201,372✔
500
void BPlusTreeInner::ensure_offsets()
3,201,372✔
501
{
3,244,971✔
502
    if (!m_offsets.is_attached()) {
3,244,971✔
503
        size_t elems_per_child = get_elems_per_child();
3,201,471✔
504
        size_t sz = size();
99✔
505
        size_t num_offsets = (sz > 2) ? sz - 3 : 0;
99✔
506
        size_t offs = 0;
99✔
507

99✔
508
        m_offsets.create(num_offsets, num_offsets * elems_per_child);
99✔
509
        for (size_t i = 0; i != num_offsets; ++i) {
141✔
510
            offs += elems_per_child;
42✔
511
            m_offsets.set(i, offs);
42✔
512
        }
3,201,414✔
513
        Array::set_as_ref(0, m_offsets.get_ref());
3,072,153✔
514
    }
3,072,153✔
515
}
3,244,971✔
516

1,596✔
517
inline BPlusTreeLeaf* BPlusTreeInner::cache_leaf(MemRef mem, size_t ndx, size_t offset)
3,073,650✔
518
{
9,307,932✔
519
    BPlusTreeLeaf* leaf = m_tree->cache_leaf(mem);
9,307,932✔
520
    leaf->bp_set_parent(this, ndx + 1);
9,307,932✔
521
    size_t sz = leaf->get_node_size();
9,307,932✔
522
    m_tree->set_leaf_bounds(offset, offset + sz);
9,307,932✔
523

9,307,932✔
524
    return leaf;
9,307,932✔
525
}
9,307,932!
526

527
void BPlusTreeInner::erase_and_destroy_bp_node(size_t ndx)
528
{
90✔
529
    ref_type ref = get_bp_node_ref(ndx);
90✔
530
    erase(ndx + 1);
90!
531
    Array::destroy_deep(ref, m_tree->get_alloc());
90✔
532
    REALM_ASSERT_DEBUG(m_offsets.is_attached());
90✔
533
    size_t sz = m_offsets.size();
90✔
534
    if (sz) {
90✔
535
        // in this case there will always be an offset to erase
97,695✔
536
        if (ndx < sz) {
97,695✔
537
            m_offsets.erase(ndx);
108✔
538
        }
108✔
539
        else {
153✔
540
            m_offsets.erase(sz - 1);
198✔
541
        }
90✔
542
    }
198✔
543
    REALM_ASSERT_DEBUG(m_offsets.size() == get_node_size() - 1);
300✔
544
}
192✔
545

102✔
546
ref_type BPlusTreeInner::insert_bp_node(size_t child_ndx, ref_type new_sibling_ref, State& state)
102✔
547
{
2,970✔
548
    size_t new_ref_ndx = child_ndx + 1;
2,970✔
549

100,467✔
550
    size_t sz = get_node_size();
2,862✔
551
    if (sz < REALM_MAX_BPNODE_SIZE) {
2,862✔
552
        // Room in current node for the new child
2,862✔
553
        adjust(size() - 1, +2); // Throws
2,862✔
554
        if (m_offsets.is_attached()) {
2,862✔
555
            size_t elem_ndx_offset = get_bp_node_offset(child_ndx);
12✔
556
            m_offsets.insert(child_ndx, elem_ndx_offset + state.split_offset); // Throws
12✔
557
            m_offsets.adjust(child_ndx + 1, m_offsets.size(), +1);             // Throws
12✔
558
        }
12✔
559
        insert_bp_node_ref(new_ref_ndx, new_sibling_ref);
2,862✔
560
        return ref_type(0);
2,862!
561
    }
2,862✔
NEW
562

×
NEW
563
    // This node has to be split
×
NEW
564
    BPlusTreeInner new_sibling(m_tree);
×
NEW
565

×
NEW
566
    size_t elem_ndx_offset = 0;
×
NEW
567
    if (m_offsets.is_attached()) {
×
NEW
568
        new_sibling.create(0);
×
NEW
569
        new_sibling.ensure_offsets();
×
NEW
570
        elem_ndx_offset = get_bp_node_offset(child_ndx);
×
NEW
571
    }
×
NEW
572
    else {
×
NEW
573
        size_t elems_per_child = get_elems_per_child();
×
NEW
574
        elem_ndx_offset = child_ndx * elems_per_child;
×
NEW
575
        new_sibling.create(elems_per_child);
×
NEW
576
    }
×
NEW
577

×
NEW
578
    size_t new_split_offset;
×
NEW
579
    size_t new_split_size;
×
580
    if (new_ref_ndx == sz) {
581
        // Case 1/2: The split child was the last child of the parent
582
        // to be split. In this case the parent may or may not be on
10,150,755✔
583
        // the compact form.
10,150,755✔
584
        new_split_offset = size_t(elem_ndx_offset + state.split_offset);
10,150,755✔
585
        new_split_size = elem_ndx_offset + state.split_size;
10,150,755✔
586
        new_sibling.add_bp_node_ref(new_sibling_ref); // Throws
10,150,755✔
587
        set_tree_size(new_split_offset);             // Throws
588
    }
10,150,755✔
589
    else {
10,150,755✔
590
        // Case 2/2: The split child was not the last child of the
591
        // parent to be split. Since this is not possible during
592
        // 'append', we can safely assume that the parent node is on
150✔
593
        // the general form.
150✔
594
        new_split_offset = size_t(elem_ndx_offset + state.split_size);
150✔
595
        new_split_size = get_tree_size() + 1;
150✔
596

150✔
597
        move(&new_sibling, new_ref_ndx, (new_split_offset - 1));               // Strips off tree size
150✔
598
        add_bp_node_ref(new_sibling_ref, elem_ndx_offset + state.split_offset); // Throws
150✔
599
        append_tree_size(new_split_offset);                                    // Throws
600
    }
150✔
601

×
602
    new_sibling.append_tree_size(new_split_size - new_split_offset); // Throws
×
603

150✔
604
    state.split_offset = new_split_offset;
150✔
605
    state.split_size = new_split_size;
150✔
606

150✔
607
    return new_sibling.get_ref();
150✔
608
}
150✔
609

610
void BPlusTreeInner::verify() const
611
{
2,802✔
612
#ifdef REALM_DEBUG
2,802✔
613
    Array::verify();
614

2,802✔
615
    // This node must not be a leaf
2,802✔
616
    REALM_ASSERT_3(Array::get_type(), ==, Array::type_InnerBptreeNode);
617

2,802✔
618
    REALM_ASSERT_3(Array::size(), >=, 2);
2,802✔
619
    size_t num_children = get_node_size();
12✔
620

12✔
621
    // Verify invar:bptree-nonempty-inner
12✔
622
    REALM_ASSERT_3(num_children, >=, 1);
12✔
623

2,802✔
624
    size_t elems_per_child = 0;
2,802✔
625
    if (m_offsets.is_attached()) {
2,802✔
626
        REALM_ASSERT_DEBUG(m_offsets.size() == num_children - 1);
627
    }
628
    else {
×
629
        elems_per_child = get_elems_per_child();
630
    }
×
631

×
632
    size_t num_elems = 0;
×
633
    for (size_t i = 0; i < num_children; i++) {
×
634
        ref_type child_ref = get_bp_node_ref(i);
×
635
        char* child_header = m_alloc.translate(child_ref);
×
636
        MemRef mem(child_header, child_ref, m_alloc);
×
637
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
×
638
        size_t elems_in_child;
×
639
        if (child_is_leaf) {
×
640
            auto leaf = const_cast<BPlusTreeInner*>(this)->cache_leaf(mem, i, 0);
×
641
            elems_in_child = leaf->get_node_size();
642
            leaf->verify();
×
643
        }
×
644
        else {
×
645
            BPlusTreeInner node(m_tree);
646
            node.set_parent(const_cast<BPlusTreeInner*>(this), i + 1);
647
            node.init_from_mem(mem);
648
            node.verify();
×
649
            elems_in_child = node.get_tree_size();
×
650
        }
×
651
        num_elems += elems_in_child;
×
652
        if (m_offsets.is_attached()) {
×
653
            if (i < num_children - 1) {
×
654
                REALM_ASSERT_DEBUG(num_elems == m_offsets.get(i));
655
            }
656
        }
657
        else {
658
            if (i < num_children - 1) {
×
659
                REALM_ASSERT_DEBUG(elems_in_child == elems_per_child);
×
660
            }
661
            else {
×
662
                REALM_ASSERT_DEBUG(elems_in_child <= elems_per_child);
×
663
            }
×
664
        }
×
665
    }
666
    REALM_ASSERT_DEBUG(get_tree_size() == num_elems);
×
667
    m_tree->invalidate_leaf_cache();
668
#endif
×
669
}
×
670

671
/****************************** BPlusTreeBase ********************************/
672

673
BPlusTreeBase::~BPlusTreeBase()
674
{
8,623,713✔
675
}
8,623,713✔
676

677
void BPlusTreeBase::create()
678
{
1,095,861✔
679
    if (!m_root) {                   // Idempotent
1,122,615✔
680
        m_root = create_leaf_node(); // Throws
1,122,615!
681
        if (m_parent) {
1,122,615✔
682
            ref_type ref = get_ref();
281,136!
683
            _impl::DeepArrayRefDestroyGuard destroy_guard{ref, get_alloc()};
281,136✔
684
            m_parent->update_child_ref(m_ndx_in_parent, ref); // Throws
281,136✔
685
            destroy_guard.release();
281,136✔
686
        }
281,136!
687
        m_root->bp_set_parent(m_parent, m_ndx_in_parent);
1,122,615✔
688
    }
1,122,615✔
689
}
1,095,861!
690

×
691
void BPlusTreeBase::destroy()
692
{
1,020,600✔
693
    if (is_attached()) {
1,020,600✔
694
        ref_type ref = m_root->get_ref();
827,961✔
695
        Array::destroy_deep(ref, m_alloc);
827,961✔
696
        m_root = nullptr;
827,961✔
697
    }
827,961!
698
    invalidate_leaf_cache();
1,020,600✔
699
}
1,020,600✔
700

701
void BPlusTreeBase::replace_root(std::unique_ptr<BPlusTreeNode> new_root)
702
{
6,117✔
703
    // Maintain parent.
6,117!
704
    new_root->bp_set_parent(m_parent, m_ndx_in_parent);
6,117✔
705
    new_root->update_parent(); // Throws
6,117✔
706

6,117✔
707
    m_root = std::move(new_root);
6,117✔
708
}
6,117✔
709

710
void BPlusTreeBase::bptree_insert(size_t n, BPlusTreeNode::InsertFunc func)
711
{
32,732,793✔
712
    size_t bptree_size = m_root->get_tree_size();
32,732,793✔
713
    if (n == bptree_size) {
32,732,793✔
714
        n = npos;
5,040,786✔
715
    }
5,040,786✔
716
    BPlusTreeNode::State state;
32,732,793!
717
    ref_type new_sibling_ref = m_root->bptree_insert(n, state, func);
32,732,793!
718
    if (REALM_UNLIKELY(new_sibling_ref)) {
32,732,793!
719
        bool compact_form = (n == npos) && m_root->is_compact();
6,033✔
720
        auto new_root = std::make_unique<BPlusTreeInner>(this);
6,033✔
721
        if (!compact_form) {
6,033✔
722
            new_root->create(0);
63!
723
            new_root->ensure_offsets();
63!
724
        }
63✔
725
        else {
5,970✔
726
            new_root->create(size_t(state.split_offset));
5,970!
727
        }
5,970✔
728

6,033✔
729
        new_root->add_bp_node_ref(m_root->get_ref());                   // Throws
6,033✔
730
        new_root->add_bp_node_ref(new_sibling_ref, state.split_offset); // Throws
6,033!
731
        new_root->append_tree_size(state.split_size);
6,033✔
732
        replace_root(std::move(new_root));
6,033✔
733
    }
6,033✔
734
}
32,732,793✔
735

736
void BPlusTreeBase::bptree_erase(size_t n, BPlusTreeNode::EraseFunc func)
737
{
1,046,658✔
738
    size_t root_size = m_root->bptree_erase(n, func);
13,025,562✔
739
    while (!m_root->is_leaf() && root_size == 1) {
13,025,646✔
740
        BPlusTreeInner* node = static_cast<BPlusTreeInner*>(m_root.get());
84✔
741

84✔
742
        ref_type new_root_ref = node->clear_first_bp_node_ref();
1,110,960✔
743
        node->destroy_deep();
1,110,960✔
744

1,110,621✔
745
        auto new_root = create_root_from_ref(new_root_ref);
1,110,621✔
746

290,325✔
747
        replace_root(std::move(new_root));
290,325✔
748
        root_size = m_root->get_node_size();
290,325✔
749
    }
290,325✔
750
}
1,336,899✔
751

1,110,537✔
752
std::unique_ptr<BPlusTreeNode> BPlusTreeBase::create_root_from_ref(ref_type ref)
1,110,537✔
753
{
10,407,510✔
754
    char* header = m_alloc.translate(ref);
9,296,634✔
755
    bool is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
9,296,634✔
756
    bool reuse_root = m_root && m_root->is_leaf() == is_leaf;
10,370,961✔
757

10,370,961✔
758
    if (reuse_root) {
10,132,620✔
759
        m_root->init_from_ref(ref);
2,692,785✔
760
        return std::move(m_root);
2,692,785✔
761
    }
2,692,785✔
762

8,514,162✔
763
    if (is_leaf) {
8,514,162✔
764
        return init_leaf_node(ref);
3,929,094✔
765
    }
3,929,094✔
766
    else {
3,520,608✔
767
        std::unique_ptr<BPlusTreeNode> new_root = std::make_unique<BPlusTreeInner>(this);
3,510,741✔
768
        new_root->init_from_ref(ref);
3,520,608✔
769
        return new_root;
3,520,608✔
770
    }
3,510,741✔
771
}
7,449,702✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc