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

realm / realm-core / github_pull_request_281750

30 Oct 2023 03:37PM UTC coverage: 90.528% (-1.0%) from 91.571%
github_pull_request_281750

Pull #6073

Evergreen

jedelbo
Log free space and history sizes when opening file
Pull Request #6073: Merge next-major

95488 of 175952 branches covered (0.0%)

8973 of 12277 new or added lines in 149 files covered. (73.09%)

622 existing lines in 51 files now uncovered.

233503 of 257934 relevant lines covered (90.53%)

6533720.56 hits per line

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

90.27
/src/realm/cluster_tree.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2020 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/cluster_tree.hpp"
20
#include "realm/group.hpp"
21
#include "realm/replication.hpp"
22
#include "realm/array_key.hpp"
23
#include "realm/array_integer.hpp"
24
#include "realm/array_backlink.hpp"
25
#include "realm/array_typed_link.hpp"
26
#include "realm/array_timestamp.hpp"
27
#include "realm/array_bool.hpp"
28
#include "realm/array_string.hpp"
29
#include "realm/array_mixed.hpp"
30
#include "realm/array_fixed_bytes.hpp"
31

32
#include <iostream>
33

34
/*
35
 * Node-splitting is done in the way that if the new element comes after all the
36
 * current elements, then the new element is added to the new node as the only
37
 * element and the old node is untouched. Here split key is the key of the new
38
 * element.
39
 * Otherwise the node is split so that the new element can be added to the old node.
40
 * So all elements that should come after the new element are moved to the new node.
41
 * Split key is the key of the first element that is moved. (First key that comes
42
 * after the new element).
43
 * Merging is done when a node is less than half full and the combined size will be
44
 * less than 3/4 of the max size.
45
 */
46

47
namespace realm {
48
/*
49
 * The inner nodes are organized in the way that the main array has a ref to the
50
 * (optional) key array in position 0 and the subtree depth in position 1. After
51
 * that follows refs to the subordinate nodes.
52
 */
53
class ClusterNodeInner : public ClusterNode {
54
public:
55
    ClusterNodeInner(Allocator& allocator, const ClusterTree& tree_top);
56
    ~ClusterNodeInner() override;
57

58
    void create(int sub_tree_depth);
59
    void init(MemRef mem) override;
60
    void update_from_parent() noexcept override;
61
    MemRef ensure_writeable(ObjKey k) override;
62
    void update_ref_in_parent(ObjKey, ref_type) override;
63

64
    bool is_leaf() const override
65
    {
44,587,716✔
66
        return false;
44,587,716✔
67
    }
44,587,716✔
68

69
    int get_sub_tree_depth() const override
70
    {
54✔
71
        return m_sub_tree_depth;
54✔
72
    }
54✔
73

74
    bool traverse(ClusterTree::TraverseFunction func, int64_t) const;
75
    void update(ClusterTree::UpdateFunction func, int64_t);
76

77
    size_t node_size() const override
78
    {
72,350,703✔
79
        return Array::size() - s_first_node_index;
72,350,703✔
80
    }
72,350,703✔
81
    size_t get_tree_size() const override
82
    {
42,185,472✔
83
        return size_t(Array::get(s_sub_tree_size)) >> 1;
42,185,472✔
84
    }
42,185,472✔
85
    void set_tree_size(size_t sub_tree_size)
86
    {
39,529,653✔
87
        Array::set(s_sub_tree_size, sub_tree_size << 1 | 1);
39,529,653✔
88
    }
39,529,653✔
89
    size_t update_sub_tree_size();
90

91
    int64_t get_last_key_value() const override;
92

93
    void ensure_general_form() override;
94
    void insert_column(ColKey col) override;
95
    void remove_column(ColKey col) override;
96
    size_t nb_columns() const override;
97
    ref_type insert(ObjKey k, const FieldValues& init_values, State& state) override;
98
    bool try_get(ObjKey k, State& state) const noexcept override;
99
    ObjKey get(size_t ndx, State& state) const override;
100
    size_t get_ndx(ObjKey key, size_t ndx) const noexcept override;
101
    size_t erase(ObjKey k, CascadeState& state) override;
102
    void nullify_incoming_links(ObjKey key, CascadeState& state) override;
103
    void add(ref_type ref, int64_t key_value = 0);
104

105
    // Reset first (and only!) child ref and return node based on the previous value
106
    std::unique_ptr<ClusterNode> return_and_clear_first_child()
107
    {
120✔
108
        REALM_ASSERT(node_size() == 1);
120✔
109
        auto new_root = m_tree_top.get_node(this, s_first_node_index);
120✔
110
        Array::set(s_first_node_index, 0); // The node is no longer belonging to this
120✔
111
        return new_root;
120✔
112
    }
120✔
113

114
    int64_t get_first_key_value()
115
    {
120✔
116
        return m_keys.is_attached() ? m_keys.get(0) : 0;
120✔
117
    }
120✔
118

119
    bool get_leaf(ObjKey key, ClusterNode::IteratorState& state) const noexcept;
120

121
    void dump_objects(int64_t key_offset, std::string lead) const override;
122

123
private:
124
    static constexpr size_t s_key_ref_index = 0;
125
    static constexpr size_t s_sub_tree_depth_index = 1;
126
    static constexpr size_t s_sub_tree_size = 2;
127
    static constexpr size_t s_first_node_index = 3;
128

129
    int m_sub_tree_depth = 0;
130
    int m_shift_factor = 0;
131

132
    struct ChildInfo {
133
        size_t ndx;
134
        uint64_t offset;
135
        ObjKey key;
136
        MemRef mem;
137
    };
138
    bool find_child(ObjKey key, ChildInfo& ret) const noexcept
139
    {
259,552,695✔
140
        if (m_keys.is_attached()) {
259,552,695✔
141
            auto upper = m_keys.upper_bound(uint64_t(key.value));
209,047,752✔
142
            // The first entry in m_keys will always be smaller than or equal
104,104,932✔
143
            // to all keys in this subtree. If you get zero returned here, the
104,104,932✔
144
            // key is not in the tree
104,104,932✔
145
            if (upper == 0) {
209,047,752✔
146
                return false;
×
147
            }
×
148
            ret.ndx = upper - 1;
209,047,752✔
149
            ret.offset = m_keys.get(ret.ndx);
209,047,752✔
150
        }
209,047,752✔
151
        else {
50,504,943✔
152
            size_t sz = node_size();
50,504,943✔
153
            REALM_ASSERT_DEBUG(sz > 0);
50,504,943✔
154
            size_t max_ndx = sz - 1;
50,504,943✔
155
            ret.ndx = std::min(size_t(key.value) >> m_shift_factor, max_ndx);
50,504,943✔
156
            ret.offset = ret.ndx << m_shift_factor;
50,504,943✔
157
        }
50,504,943✔
158
        ret.key = ObjKey(key.value - ret.offset);
259,552,695✔
159
        ref_type child_ref = _get_child_ref(ret.ndx);
259,552,695✔
160
        char* child_header = m_alloc.translate(child_ref);
259,552,695✔
161
        ret.mem = MemRef(child_header, child_ref, m_alloc);
259,552,695✔
162
        return true;
259,552,695✔
163
    }
259,552,695✔
164

165
    ref_type _get_child_ref(size_t ndx) const noexcept
166
    {
318,247,224✔
167
        return Array::get_as_ref(ndx + s_first_node_index);
318,247,224✔
168
    }
318,247,224✔
169
    void _insert_child_ref(size_t ndx, ref_type ref)
170
    {
85,494✔
171
        Array::insert(ndx + s_first_node_index, from_ref(ref));
85,494✔
172
    }
85,494✔
173
    void _erase_child_ref(size_t ndx)
174
    {
20,127✔
175
        Array::erase(ndx + s_first_node_index);
20,127✔
176
    }
20,127✔
177
    void move(size_t ndx, ClusterNode* new_node, int64_t key_adj) override;
178

179
    template <class T, class F>
180
    T recurse(ObjKey key, F func);
181

182
    template <class T, class F>
183
    T recurse(ChildInfo& child_info, F func);
184
    // Adjust key offset values by adding offset
185
    void adjust_keys(int64_t offset);
186
    // Make sure the first key offset value is 0
187
    // This is done by adjusting the child node by current first offset
188
    // and setting it to 0 thereafter
189
    void adjust_keys_first_child(int64_t adj);
190
};
191

192
/***************************** ClusterNodeInner ******************************/
193

194
ClusterNodeInner::ClusterNodeInner(Allocator& allocator, const ClusterTree& tree_top)
195
    : ClusterNode(0, allocator, tree_top)
196
{
97,180,320✔
197
}
97,180,320✔
198

199
ClusterNodeInner::~ClusterNodeInner() {}
97,173,348✔
200

201
void ClusterNodeInner::create(int sub_tree_depth)
202
{
3,252✔
203
    Array::create(Array::type_InnerBptreeNode, false, s_first_node_index);
3,252✔
204

1,620✔
205
    Array::set(s_key_ref_index, 0);
3,252✔
206

1,620✔
207
    Array::set(s_sub_tree_depth_index, RefOrTagged::make_tagged(sub_tree_depth));
3,252✔
208
    Array::set(s_sub_tree_size, 1); // sub_tree_size = 0 (as tagged value)
3,252✔
209
    m_sub_tree_depth = sub_tree_depth;
3,252✔
210
    m_shift_factor = m_sub_tree_depth * node_shift_factor;
3,252✔
211
}
3,252✔
212

213
void ClusterNodeInner::init(MemRef mem)
214
{
95,152,446✔
215
    Array::init_from_mem(mem);
95,152,446✔
216
    m_keys.set_parent(this, s_key_ref_index);
95,152,446✔
217
    ref_type ref = Array::get_as_ref(s_key_ref_index);
95,152,446✔
218
    if (ref) {
95,152,446✔
219
        m_keys.init_from_ref(ref);
81,570,399✔
220
    }
81,570,399✔
221
    else {
13,582,047✔
222
        m_keys.detach();
13,582,047✔
223
    }
13,582,047✔
224
    m_sub_tree_depth = int(Array::get(s_sub_tree_depth_index)) >> 1;
95,152,446✔
225
    m_shift_factor = m_sub_tree_depth * node_shift_factor;
95,152,446✔
226
}
95,152,446✔
227

228
void ClusterNodeInner::update_from_parent() noexcept
229
{
81,054✔
230
    Array::update_from_parent();
81,054✔
231
    ref_type ref = Array::get_as_ref(s_key_ref_index);
81,054✔
232
    if (ref) {
81,054✔
233
        m_keys.update_from_parent();
68,595✔
234
    }
68,595✔
235
    m_sub_tree_depth = int(Array::get(s_sub_tree_depth_index)) >> 1;
81,054✔
236
}
81,054✔
237

238
template <class T, class F>
239
T ClusterNodeInner::recurse(ObjKey key, F func)
240
{
47,681,799✔
241
    ChildInfo child_info;
47,681,799✔
242
    if (!find_child(key, child_info)) {
47,681,799!
243
        throw KeyNotFound("Child not found in recurse");
×
244
    }
×
245
    return recurse<T>(child_info, func);
47,681,799✔
246
}
47,681,799✔
247

248
template <class T, class F>
249
T ClusterNodeInner::recurse(ChildInfo& child_info, F func)
250
{
259,538,286✔
251
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_info.mem.get_addr());
259,538,286✔
252
    if (child_is_leaf) {
259,538,286!
253
        Cluster leaf(child_info.offset + m_offset, m_alloc, m_tree_top);
170,061,549✔
254
        leaf.set_parent(this, child_info.ndx + s_first_node_index);
170,061,549✔
255
        leaf.init(child_info.mem);
170,061,549✔
256
        return func(&leaf, child_info);
170,061,549✔
257
    }
170,061,549✔
258
    else {
89,476,737✔
259
        ClusterNodeInner node(m_alloc, m_tree_top);
89,476,737✔
260
        node.set_parent(this, child_info.ndx + s_first_node_index);
89,476,737✔
261
        node.init(child_info.mem);
89,476,737✔
262
        node.set_offset(child_info.offset + m_offset);
89,476,737✔
263
        return func(&node, child_info);
89,476,737✔
264
    }
89,476,737✔
265
}
259,538,286✔
266

267
MemRef ClusterNodeInner::ensure_writeable(ObjKey key)
268
{
×
269
    return recurse<MemRef>(key, [](ClusterNode* node, ChildInfo& child_info) {
×
270
        return node->ensure_writeable(child_info.key);
×
271
    });
×
272
}
×
273

274
void ClusterNodeInner::update_ref_in_parent(ObjKey key, ref_type ref)
275
{
486,549✔
276
    ChildInfo child_info;
486,549✔
277
    if (!find_child(key, child_info)) {
486,549✔
278
        throw KeyNotFound("Child not found in update_ref_in_parent");
×
279
    }
×
280
    if (this->m_sub_tree_depth == 1) {
486,549✔
281
        set(child_info.ndx + s_first_node_index, ref);
269,565✔
282
    }
269,565✔
283
    else {
216,984✔
284
        ClusterNodeInner node(m_alloc, m_tree_top);
216,984✔
285
        node.set_parent(this, child_info.ndx + s_first_node_index);
216,984✔
286
        node.init(child_info.mem);
216,984✔
287
        node.set_offset(child_info.offset + m_offset);
216,984✔
288
        node.update_ref_in_parent(child_info.key, ref);
216,984✔
289
    }
216,984✔
290
}
486,549✔
291

292
ref_type ClusterNodeInner::insert(ObjKey key, const FieldValues& init_values, ClusterNode::State& state)
293
{
31,413,213✔
294
    return recurse<ref_type>(key, [this, &state, &init_values](ClusterNode* node, ChildInfo& child_info) {
31,382,322✔
295
        ref_type new_sibling_ref = node->insert(child_info.key, init_values, state);
31,333,056✔
296

15,595,101✔
297
        set_tree_size(get_tree_size() + 1);
31,333,056✔
298

15,595,101✔
299
        if (!new_sibling_ref) {
31,333,056✔
300
            return ref_type(0);
31,279,089✔
301
        }
31,279,089✔
302

43,716✔
303
        size_t new_ref_ndx = child_info.ndx + 1;
53,967✔
304

43,716✔
305
        int64_t split_key_value = state.split_key + child_info.offset;
53,967✔
306
        uint64_t sz = node_size();
53,967✔
307
        if (sz < cluster_node_size) {
86,712✔
308
            if (m_keys.is_attached()) {
85,494✔
309
                m_keys.insert(new_ref_ndx, split_key_value);
81,639✔
310
            }
81,639✔
311
            else {
3,855✔
312
                if (uint64_t(split_key_value) != sz << m_shift_factor) {
3,855✔
313
                    ensure_general_form();
288✔
314
                    m_keys.insert(new_ref_ndx, split_key_value);
288✔
315
                }
288✔
316
            }
3,855✔
317
            _insert_child_ref(new_ref_ndx, new_sibling_ref);
85,494✔
318
            return ref_type(0);
85,494✔
319
        }
85,494✔
320

1,218✔
321
        ClusterNodeInner child(m_alloc, m_tree_top);
2,147,484,865✔
322
        child.create(m_sub_tree_depth);
2,147,484,865✔
323
        if (new_ref_ndx == sz) {
2,147,484,865✔
324
            child.add(new_sibling_ref);
168✔
325
            state.split_key = split_key_value;
168✔
326
        }
168✔
327
        else {
2,147,484,781✔
328
            int64_t first_key_value = m_keys.get(new_ref_ndx);
2,147,484,781✔
329
            child.ensure_general_form();
2,147,484,781✔
330
            move(new_ref_ndx, &child, first_key_value);
2,147,484,781✔
331
            add(new_sibling_ref, split_key_value); // Throws
2,147,484,781✔
332
            state.split_key = first_key_value;
2,147,484,781✔
333
        }
2,147,484,781✔
334

1,218✔
335
        // Some objects has been moved out of this tree - find out how many
1,218✔
336
        size_t child_sub_tree_size = child.update_sub_tree_size();
2,147,484,865✔
337
        set_tree_size(get_tree_size() - child_sub_tree_size);
2,147,484,865✔
338

1,218✔
339
        return child.get_ref();
2,147,484,865✔
340
    });
2,147,484,865✔
341
}
31,413,213✔
342

343
bool ClusterNodeInner::try_get(ObjKey key, ClusterNode::State& state) const noexcept
344
{
212,371,035✔
345
    ChildInfo child_info;
212,371,035✔
346
    if (!find_child(key, child_info)) {
212,371,035✔
347
        return false;
×
348
    }
×
349
    return const_cast<ClusterNodeInner*>(this)->recurse<bool>(child_info,
212,371,035✔
350
                                                              [&state](const ClusterNode* node, ChildInfo& info) {
211,749,555✔
351
                                                                  return node->try_get(info.key, state);
211,193,694✔
352
                                                              });
211,193,694✔
353
}
212,371,035✔
354

355
ObjKey ClusterNodeInner::get(size_t ndx, ClusterNode::State& state) const
356
{
5,242,812✔
357
    size_t sz = node_size();
5,242,812✔
358
    size_t child_ndx = 0;
5,242,812✔
359
    while (child_ndx < sz) {
53,783,760✔
360
        int64_t key_offset = m_keys.is_attached() ? m_keys.get(child_ndx) : (child_ndx << m_shift_factor);
53,747,535✔
361

26,427,618✔
362
        ref_type child_ref = _get_child_ref(child_ndx);
53,783,760✔
363
        char* child_header = m_alloc.translate(child_ref);
53,783,760✔
364
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
53,783,760✔
365
        size_t sub_tree_size;
53,783,760✔
366
        if (child_is_leaf) {
53,783,760✔
367
            sub_tree_size = Cluster::node_size_from_header(m_alloc, child_header);
51,464,604✔
368
            if (ndx < sub_tree_size) {
51,464,604✔
369
                Cluster leaf(key_offset + m_offset, m_alloc, m_tree_top);
2,647,245✔
370
                leaf.init(MemRef(child_header, child_ref, m_alloc));
2,647,245✔
371
                REALM_ASSERT(sub_tree_size == leaf.get_tree_size());
2,647,245✔
372
                return leaf.get(ndx, state);
2,647,245✔
373
            }
2,647,245✔
374
        }
2,319,156✔
375
        else {
2,319,156✔
376
            ClusterNodeInner node(m_alloc, m_tree_top);
2,319,156✔
377
            node.init(MemRef(child_header, child_ref, m_alloc));
2,319,156✔
378
            node.set_offset(key_offset + m_offset);
2,319,156✔
379
            sub_tree_size = node.get_tree_size();
2,319,156✔
380
            if (ndx < sub_tree_size) {
2,596,323✔
381
                return node.get(ndx, state);
2,596,323✔
382
            }
2,596,323✔
383
        }
48,540,192✔
384
        child_ndx++;
48,540,192✔
385
        ndx -= sub_tree_size;
48,540,192✔
386
    }
48,540,192✔
387
    return {};
4,294,967,294✔
388
}
5,242,812✔
389

390
size_t ClusterNodeInner::get_ndx(ObjKey key, size_t ndx) const noexcept
391
{
24,723✔
392
    ChildInfo child_info;
24,723✔
393
    if (!find_child(key, child_info)) {
24,723✔
394
        return realm::npos;
×
395
    }
×
396

12,360✔
397
    // First figure out how many objects there are in nodes before actual one
12,360✔
398
    // then descent in tree
12,360✔
399

12,360✔
400
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_info.mem.get_addr());
24,723✔
401
    if (child_is_leaf) {
24,723✔
402
        for (unsigned i = 0; i < child_info.ndx; i++) {
51,075✔
403
            ref_type ref = _get_child_ref(i);
26,352✔
404
            char* header = m_alloc.translate(ref);
26,352✔
405
            ndx += Cluster::node_size_from_header(m_alloc, header);
26,352✔
406
        }
26,352✔
407
        Cluster leaf(child_info.offset + m_offset, m_alloc, m_tree_top);
24,723✔
408
        leaf.init(child_info.mem);
24,723✔
409
        return leaf.get_ndx(child_info.key, ndx);
24,723✔
410
    }
24,723✔
411
    else {
×
412
        for (unsigned i = 0; i < child_info.ndx; i++) {
×
413
            char* header = m_alloc.translate(_get_child_ref(i));
×
414
            ndx += size_t(Array::get(header, s_sub_tree_size)) >> 1;
×
415
        }
×
416
        ClusterNodeInner node(m_alloc, m_tree_top);
×
417
        node.init(child_info.mem);
×
418
        node.set_offset(child_info.offset + m_offset);
×
419
        return node.get_ndx(child_info.key, ndx);
×
420
    }
×
421
}
24,723✔
422

423
void ClusterNodeInner::adjust_keys(int64_t adj)
424
{
30✔
425
    ensure_general_form();
30✔
426
    REALM_ASSERT(m_keys.get(0) == 0);
30✔
427
    m_keys.adjust(0, m_keys.size(), adj);
30✔
428

15✔
429
    // Now the first key offset value is 'adj' - it must be 0
15✔
430
    adjust_keys_first_child(adj);
30✔
431
}
30✔
432

433
void ClusterNodeInner::adjust_keys_first_child(int64_t adj)
434
{
12,060✔
435
    ref_type child_ref = _get_child_ref(0);
12,060✔
436
    char* child_header = m_alloc.translate(child_ref);
12,060✔
437
    auto mem = MemRef(child_header, child_ref, m_alloc);
12,060✔
438
    if (Array::get_is_inner_bptree_node_from_header(child_header)) {
12,060✔
439
        ClusterNodeInner node(m_alloc, m_tree_top);
30✔
440
        node.set_parent(this, s_first_node_index);
30✔
441
        node.init(mem);
30✔
442
        node.adjust_keys(adj);
30✔
443
    }
30✔
444
    else {
12,030✔
445
        Cluster node(0, m_alloc, m_tree_top);
12,030✔
446
        node.set_parent(this, s_first_node_index);
12,030✔
447
        node.init(mem);
12,030✔
448
        node.adjust_keys(adj);
12,030✔
449
    }
12,030✔
450
    m_keys.set(0, 0);
12,060✔
451
}
12,060✔
452

453
size_t ClusterNodeInner::erase(ObjKey key, CascadeState& state)
454
{
8,167,275✔
455
    return recurse<size_t>(key, [this, &state](ClusterNode* erase_node, ChildInfo& child_info) {
8,167,245✔
456
        size_t erase_node_size = erase_node->erase(child_info.key, state);
8,167,194✔
457
        bool is_leaf = erase_node->is_leaf();
8,167,194✔
458
        set_tree_size(get_tree_size() - 1);
8,167,194✔
459

4,083,822✔
460
        if (erase_node_size == 0) {
8,167,194✔
461
            erase_node->destroy_deep();
13,347✔
462

6,672✔
463
            ensure_general_form();
13,347✔
464
            _erase_child_ref(child_info.ndx);
13,347✔
465
            m_keys.erase(child_info.ndx);
13,347✔
466
            if (child_info.ndx == 0 && m_keys.size() > 0) {
13,347✔
467
                auto first_offset = m_keys.get(0);
12,030✔
468
                // Adjust all key values in new first node
6,015✔
469
                // We have to make sure that the first key offset value
6,015✔
470
                // in all inner nodes is 0
6,015✔
471
                adjust_keys_first_child(first_offset);
12,030✔
472
            }
12,030✔
473
        }
13,347✔
474
        else if (erase_node_size < cluster_node_size / 2 && child_info.ndx < (node_size() - 1)) {
8,153,847✔
475
            // Candidate for merge. First calculate if the combined size of current and
1,652,712✔
476
            // next sibling is small enough.
1,652,712✔
477
            size_t sibling_ndx = child_info.ndx + 1;
3,272,427✔
478
            Cluster l2(child_info.offset, m_alloc, m_tree_top);
3,272,427✔
479
            ClusterNodeInner n2(m_alloc, m_tree_top);
3,272,427✔
480
            ClusterNode* sibling_node = is_leaf ? static_cast<ClusterNode*>(&l2) : static_cast<ClusterNode*>(&n2);
2,689,443✔
481
            sibling_node->set_parent(this, sibling_ndx + s_first_node_index);
3,272,427✔
482
            sibling_node->init_from_parent();
3,272,427✔
483

1,652,712✔
484
            size_t combined_size = sibling_node->node_size() + erase_node_size;
3,272,427✔
485

1,652,712✔
486
            if (combined_size < cluster_node_size * 3 / 4) {
3,272,427✔
487
                // Calculate value that must be subtracted from the moved keys
3,357✔
488
                // (will be negative as the sibling has bigger keys)
3,357✔
489
                int64_t key_adj = m_keys.is_attached() ? (m_keys.get(child_info.ndx) - m_keys.get(sibling_ndx))
6,774✔
490
                                                       : 0 - (1 << m_shift_factor);
3,363✔
491
                // And then move all elements into current node
3,357✔
492
                sibling_node->ensure_general_form();
6,780✔
493
                erase_node->ensure_general_form();
6,780✔
494
                sibling_node->move(0, erase_node, key_adj);
6,780✔
495

3,357✔
496
                if (!erase_node->is_leaf()) {
6,780✔
497
                    static_cast<ClusterNodeInner*>(erase_node)->update_sub_tree_size();
36✔
498
                }
36✔
499

3,357✔
500
                // Destroy sibling
3,357✔
501
                sibling_node->destroy_deep();
6,780✔
502

3,357✔
503
                ensure_general_form();
6,780✔
504
                _erase_child_ref(sibling_ndx);
6,780✔
505
                m_keys.erase(sibling_ndx);
6,780✔
506
            }
6,780✔
507
        }
3,272,427✔
508

4,083,822✔
509
        return node_size();
8,167,194✔
510
    });
8,167,194✔
511
}
8,167,275✔
512

513
void ClusterNodeInner::nullify_incoming_links(ObjKey key, CascadeState& state)
514
{
8,096,838✔
515
    recurse<void>(key, [&state](ClusterNode* node, ChildInfo& child_info) {
8,096,838✔
516
        node->nullify_incoming_links(child_info.key, state);
8,096,811✔
517
    });
8,096,811✔
518
}
8,096,838✔
519

520
void ClusterNodeInner::ensure_general_form()
521
{
23,250✔
522
    if (!m_keys.is_attached()) {
23,250✔
523
        size_t current_size = node_size();
3,087✔
524
        m_keys.create(current_size, (current_size - 1) << m_shift_factor);
3,087✔
525
        m_keys.update_parent();
3,087✔
526
        for (size_t i = 0; i < current_size; i++) {
9,510✔
527
            m_keys.set(i, i << m_shift_factor);
6,423✔
528
        }
6,423✔
529
    }
3,087✔
530
}
23,250✔
531

532
void ClusterNodeInner::insert_column(ColKey col)
533
{
162✔
534
    size_t sz = node_size();
162✔
535
    for (size_t i = 0; i < sz; i++) {
24,252✔
536
        std::shared_ptr<ClusterNode> node = m_tree_top.get_node(this, i + s_first_node_index);
24,090✔
537
        node->insert_column(col);
24,090✔
538
    }
24,090✔
539
}
162✔
540

541
void ClusterNodeInner::remove_column(ColKey col)
542
{
6✔
543
    size_t sz = node_size();
6✔
544
    for (size_t i = 0; i < sz; i++) {
18✔
545
        std::shared_ptr<ClusterNode> node = m_tree_top.get_node(this, i + s_first_node_index);
12✔
546
        node->remove_column(col);
12✔
547
    }
12✔
548
}
6✔
549

550
size_t ClusterNodeInner::nb_columns() const
551
{
×
552
    ref_type ref = _get_child_ref(0);
×
553
    char* header = m_alloc.translate(ref);
×
554
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
×
555
    MemRef mem(header, ref, m_alloc);
×
556
    if (child_is_leaf) {
×
557
        Cluster leaf(0, m_alloc, m_tree_top);
×
558
        leaf.init(mem);
×
559
        return leaf.nb_columns();
×
560
    }
×
561
    else {
×
562
        ClusterNodeInner node(m_alloc, m_tree_top);
×
563
        node.init(mem);
×
564
        return node.nb_columns();
×
565
    }
×
566
}
×
567

568
void ClusterNodeInner::add(ref_type ref, int64_t key_value)
569
{
6,312✔
570
    if (m_keys.is_attached()) {
6,312✔
571
        m_keys.add(key_value);
24✔
572
    }
24✔
573
    else {
6,288✔
574
        if (uint64_t(key_value) != (uint64_t(node_size()) << m_shift_factor)) {
6,288✔
575
            ensure_general_form();
2,709✔
576
            m_keys.add(key_value);
2,709✔
577
        }
2,709✔
578
    }
6,288✔
579
    Array::add(from_ref(ref));
6,312✔
580
}
6,312✔
581

582
// Find leaf that contains the object identified by key. If this does not exist return the
583
// leaf that contains the next object
584
bool ClusterNodeInner::get_leaf(ObjKey key, ClusterNode::IteratorState& state) const noexcept
585
{
2,691,330✔
586
    size_t child_ndx;
2,691,330✔
587
    if (m_keys.is_attached()) {
2,691,330✔
588
        child_ndx = m_keys.upper_bound(uint64_t(key.value));
2,653,932✔
589
        if (child_ndx > 0)
2,653,932✔
590
            child_ndx--;
2,654,319✔
591
    }
2,653,932✔
592
    else {
37,398✔
593
        REALM_ASSERT_DEBUG(node_size() > 0);
37,398✔
594
        size_t max_ndx = node_size() - 1;
37,398✔
595
        if (key.value < 0)
37,398✔
596
            child_ndx = 0;
×
597
        else
37,398✔
598
            child_ndx = std::min(size_t(key.value) >> m_shift_factor, max_ndx);
37,398✔
599
    }
37,398✔
600

1,344,855✔
601
    size_t sz = node_size();
2,691,330✔
602
    while (child_ndx < sz) {
2,741,388✔
603
        uint64_t key_offset = m_keys.is_attached() ? m_keys.get(child_ndx) : (child_ndx << m_shift_factor);
2,715,210✔
604
        ObjKey new_key(key_offset < uint64_t(key.value) ? key.value - key_offset : 0);
2,695,818✔
605
        state.m_key_offset += key_offset;
2,733,888✔
606

1,365,108✔
607
        ref_type child_ref = _get_child_ref(child_ndx);
2,733,888✔
608
        char* child_header = m_alloc.translate(child_ref);
2,733,888✔
609
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
2,733,888✔
610
        if (child_is_leaf) {
2,733,888✔
611
            state.m_current_leaf.init(MemRef(child_header, child_ref, m_alloc));
1,529,184✔
612
            state.m_current_leaf.set_offset(state.m_key_offset);
1,529,184✔
613
            state.m_current_index = state.m_current_leaf.lower_bound_key(new_key);
1,529,184✔
614
            if (state.m_current_index < state.m_current_leaf.node_size())
1,529,184✔
615
                return true;
1,479,138✔
616
        }
1,204,704✔
617
        else {
1,204,704✔
618
            ClusterNodeInner node(m_alloc, m_tree_top);
1,204,704✔
619
            node.init(MemRef(child_header, child_ref, m_alloc));
1,204,704✔
620
            if (node.get_leaf(new_key, state))
1,204,704✔
621
                return true;
1,204,692✔
622
        }
50,058✔
623
        state.m_key_offset -= key_offset;
50,058✔
624
        child_ndx++;
50,058✔
625
    }
50,058✔
626
    return false;
1,348,443✔
627
}
2,691,330✔
628

629
// LCOV_EXCL_START
630
void ClusterNodeInner::dump_objects(int64_t key_offset, std::string lead) const
631
{
×
632
    std::cout << lead << "node" << std::endl;
×
633
    if (!m_keys.is_attached()) {
×
634
        std::cout << lead << "compact form" << std::endl;
×
635
    }
×
636
    size_t sz = node_size();
×
637
    for (unsigned i = 0; i < sz; i++) {
×
638
        int64_t key_value;
×
639
        if (m_keys.is_attached()) {
×
640
            key_value = m_keys.get(i) + key_offset;
×
641
        }
×
642
        else {
×
643
            key_value = (i << m_shift_factor) + key_offset;
×
644
        }
×
645
        std::cout << lead << std::hex << "split: " << key_value << std::dec << std::endl;
×
646
        m_tree_top.get_node(const_cast<ClusterNodeInner*>(this), i + s_first_node_index)
×
647
            ->dump_objects(key_value, lead + "   ");
×
648
    }
×
649
}
×
650
// LCOV_EXCL_STOP
651
void ClusterNodeInner::move(size_t ndx, ClusterNode* new_node, int64_t key_adj)
652
{
60✔
653
    auto new_cluster_node_inner = static_cast<ClusterNodeInner*>(new_node);
60✔
654
    for (size_t i = ndx; i < node_size(); i++) {
5,595✔
655
        new_cluster_node_inner->Array::add(_get_child_ref(i));
5,535✔
656
    }
5,535✔
657
    for (size_t i = ndx; i < m_keys.size(); i++) {
5,595✔
658
        new_cluster_node_inner->m_keys.add(m_keys.get(i) - key_adj);
5,535✔
659
    }
5,535✔
660
    truncate(ndx + s_first_node_index);
60✔
661
    if (m_keys.is_attached()) {
60✔
662
        m_keys.truncate(ndx);
60✔
663
    }
60✔
664
}
60✔
665

666
size_t ClusterNodeInner::update_sub_tree_size()
667
{
3,288✔
668
    size_t sub_tree_size = 0;
3,288✔
669
    auto sz = node_size();
3,288✔
670

1,641✔
671
    for (unsigned i = 0; i < sz; i++) {
18,531✔
672
        ref_type ref = _get_child_ref(i);
15,243✔
673
        char* header = m_alloc.translate(ref);
15,243✔
674
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
15,243✔
675
        if (child_is_leaf) {
15,243✔
676
            sub_tree_size += Cluster::node_size_from_header(m_alloc, header);
15,135✔
677
        }
15,135✔
678
        else {
108✔
679
            sub_tree_size += size_t(Array::get(header, s_sub_tree_size)) >> 1;
108✔
680
        }
108✔
681
    }
15,243✔
682
    set_tree_size(sub_tree_size);
3,288✔
683
    return sub_tree_size;
3,288✔
684
}
3,288✔
685

686
bool ClusterNodeInner::traverse(ClusterTree::TraverseFunction func, int64_t key_offset) const
687
{
1,463,103✔
688
    auto sz = node_size();
1,463,103✔
689

630,825✔
690
    for (unsigned i = 0; i < sz; i++) {
7,030,356✔
691
        ref_type ref = _get_child_ref(i);
5,596,383✔
692
        char* header = m_alloc.translate(ref);
5,596,383✔
693
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
5,596,383✔
694
        MemRef mem(header, ref, m_alloc);
5,596,383✔
695
        int64_t offs = (m_keys.is_attached() ? m_keys.get(i) : i << m_shift_factor) + key_offset;
2,150,727,172✔
696
        if (child_is_leaf) {
6,130,839✔
697
            Cluster leaf(offs, m_alloc, m_tree_top);
6,130,839✔
698
            leaf.init(mem);
6,130,839✔
699
            if (func(&leaf) == IteratorControl::Stop) {
6,130,839✔
700
                return true;
29,130✔
701
            }
29,130✔
702
        }
4,294,967,294✔
703
        else {
4,294,967,294✔
704
            ClusterNodeInner node(m_alloc, m_tree_top);
4,294,967,294✔
705
            node.init(mem);
4,294,967,294✔
706
            if (node.traverse(func, offs)) {
4,294,967,294✔
707
                return true;
×
708
            }
×
709
        }
4,294,967,294✔
710
    }
5,596,383✔
711
    return false;
1,448,538✔
712
}
1,463,103✔
713

714
void ClusterNodeInner::update(ClusterTree::UpdateFunction func, int64_t key_offset)
715
{
36✔
716
    auto sz = node_size();
36✔
717

18✔
718
    for (unsigned i = 0; i < sz; i++) {
468✔
719
        ref_type ref = _get_child_ref(i);
432✔
720
        char* header = m_alloc.translate(ref);
432✔
721
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
432✔
722
        MemRef mem(header, ref, m_alloc);
432✔
723
        int64_t offs = (m_keys.is_attached() ? m_keys.get(i) : i << m_shift_factor) + key_offset;
432✔
724
        if (child_is_leaf) {
432✔
725
            Cluster leaf(offs, m_alloc, m_tree_top);
432✔
726
            leaf.init(mem);
432✔
727
            leaf.set_parent(this, i + s_first_node_index);
432✔
728
            func(&leaf);
432✔
729
        }
432✔
730
        else {
×
731
            ClusterNodeInner node(m_alloc, m_tree_top);
×
732
            node.init(mem);
×
733
            node.set_parent(this, i + s_first_node_index);
×
734
            node.update(func, offs);
×
735
        }
×
736
    }
432✔
737
}
36✔
738

739
int64_t ClusterNodeInner::get_last_key_value() const
740
{
×
741
    auto last_ndx = node_size() - 1;
×
742

743
    ref_type ref = _get_child_ref(last_ndx);
×
744
    char* header = m_alloc.translate(ref);
×
745
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
×
746
    MemRef mem(header, ref, m_alloc);
×
747
    int64_t offset = m_keys.is_attached() ? m_keys.get(last_ndx) : last_ndx << m_shift_factor;
×
748
    if (child_is_leaf) {
×
749
        Cluster leaf(offset, m_alloc, m_tree_top);
×
750
        leaf.init(mem);
×
751
        return offset + leaf.get_last_key_value();
×
752
    }
×
753
    else {
×
754
        ClusterNodeInner node(m_alloc, m_tree_top);
×
755
        node.init(mem);
×
756
        return offset + node.get_last_key_value();
×
757
    }
×
758
}
×
759

760
ClusterTree::ClusterTree(Table* owner, Allocator& alloc, size_t top_position_for_cluster_tree)
761
    : m_alloc(alloc)
762
    , m_owner(owner)
763
    , m_top_position_for_cluster_tree(top_position_for_cluster_tree)
764
{
577,524✔
765
}
577,524✔
766

767
ClusterTree::~ClusterTree() {}
547,056✔
768

769
std::unique_ptr<ClusterNode> ClusterTree::create_root_from_parent(ArrayParent* parent, size_t ndx_in_parent)
770
{
6,628,779✔
771
    ref_type ref = parent->get_child_ref(ndx_in_parent);
6,628,779✔
772
    if (!ref)
6,628,779✔
773
        return nullptr;
×
774

3,778,158✔
775
    MemRef mem{m_alloc.translate(ref), ref, m_alloc};
6,628,779✔
776
    const char* header = mem.get_addr();
6,628,779✔
777
    bool is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
6,628,779✔
778

3,778,158✔
779
    bool can_reuse_root_accessor = m_root && m_root->is_leaf() == is_leaf;
6,628,779✔
780
    if (can_reuse_root_accessor) {
6,628,779✔
781
        m_root->init(mem);
5,991,672✔
782
        return std::move(m_root); // Same root will be reinstalled.
5,991,672✔
783
    }
5,991,672✔
784

329,166✔
785
    // Not reusing root note, allocating a new one.
329,166✔
786
    std::unique_ptr<ClusterNode> new_root;
637,107✔
787
    if (is_leaf) {
637,107✔
788
        new_root = std::make_unique<Cluster>(0, m_alloc, *this);
607,824✔
789
    }
607,824✔
790
    else {
29,283✔
791
        new_root = std::make_unique<ClusterNodeInner>(m_alloc, *this);
29,283✔
792
    }
29,283✔
793
    new_root->init(mem);
637,107✔
794
    new_root->set_parent(parent, ndx_in_parent);
637,107✔
795

329,166✔
796
    return new_root;
637,107✔
797
}
637,107✔
798

799
std::unique_ptr<ClusterNode> ClusterTree::get_node(ArrayParent* parent, size_t ndx_in_parent) const
800
{
24,222✔
801
    ref_type ref = parent->get_child_ref(ndx_in_parent);
24,222✔
802

12,111✔
803
    std::unique_ptr<ClusterNode> node;
24,222✔
804

12,111✔
805
    char* child_header = static_cast<char*>(m_alloc.translate(ref));
24,222✔
806
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
24,222✔
807
    if (child_is_leaf) {
24,222✔
808
        node = std::make_unique<Cluster>(0, m_alloc, *this);
24,108✔
809
    }
24,108✔
810
    else {
114✔
811
        node = std::make_unique<ClusterNodeInner>(m_alloc, *this);
114✔
812
    }
114✔
813
    node->init(MemRef(child_header, ref, m_alloc));
24,222✔
814
    node->set_parent(parent, ndx_in_parent);
24,222✔
815

12,111✔
816
    return node;
24,222✔
817
}
24,222✔
818

819
void ClusterTree::clear(CascadeState& state)
820
{
4,197✔
821
    m_owner->clear_indexes();
4,197✔
822

1,434✔
823
    if (state.m_group) {
4,197✔
824
        remove_all_links(state); // This will also delete objects loosing their last strong link
3,045✔
825
    }
3,045✔
826

1,434✔
827
    // We no longer have "clear table" instruction, so we have to report the removal of each
1,434✔
828
    // object individually
1,434✔
829
    if (Replication* repl = m_owner->get_repl()) {
4,197✔
830
        // Go through all clusters
756✔
831
        traverse([repl, this](const Cluster* cluster) {
1,881✔
832
            auto sz = cluster->node_size();
1,881✔
833
            for (size_t i = 0; i < sz; i++) {
103,080✔
834
                repl->remove_object(m_owner, cluster->get_real_key(i));
101,199✔
835
            }
101,199✔
836
            return IteratorControl::AdvanceToNext;
1,881✔
837
        });
1,881✔
838
    }
1,527✔
839

1,434✔
840
    m_root->destroy_deep();
4,197✔
841

1,434✔
842
    auto leaf = std::make_unique<Cluster>(0, m_root->get_alloc(), *this);
4,197✔
843
    leaf->create();
4,197✔
844
    replace_root(std::move(leaf));
4,197✔
845

1,434✔
846
    bump_content_version();
4,197✔
847
    bump_storage_version();
4,197✔
848

1,434✔
849
    m_size = 0;
4,197✔
850
}
4,197✔
851

852
void ClusterTree::enumerate_string_column(ColKey col_key)
853
{
690✔
854
    Allocator& alloc = get_alloc();
690✔
855

345✔
856
    ArrayString keys(alloc);
690✔
857
    ArrayString leaf(alloc);
690✔
858
    keys.create();
690✔
859

345✔
860
    auto collect_strings = [col_key, &leaf, &keys](const Cluster* cluster) {
1,086✔
861
        cluster->init_leaf(col_key, &leaf);
1,086✔
862
        size_t sz = leaf.size();
1,086✔
863
        size_t key_size = keys.size();
1,086✔
864
        for (size_t i = 0; i < sz; i++) {
118,542✔
865
            auto v = leaf.get(i);
117,456✔
866
            size_t pos = keys.lower_bound(v);
117,456✔
867
            if (pos == key_size || keys.get(pos) != v) {
117,456✔
868
                keys.insert(pos, v); // Throws
576✔
869
                key_size++;
576✔
870
            }
576✔
871
        }
117,456✔
872

543✔
873
        return IteratorControl::AdvanceToNext;
1,086✔
874
    };
1,086✔
875

345✔
876
    auto upgrade = [col_key, &keys](Cluster* cluster) {
1,086✔
877
        cluster->upgrade_string_to_enum(col_key, keys);
1,086✔
878
    };
1,086✔
879

345✔
880
    // Populate 'keys' array
345✔
881
    traverse(collect_strings);
690✔
882

345✔
883
    // Store key strings in spec
345✔
884
    size_t spec_ndx = m_owner->colkey2spec_ndx(col_key);
690✔
885
    const_cast<Spec*>(&m_owner->m_spec)->upgrade_string_to_enum(spec_ndx, keys.get_ref());
690✔
886

345✔
887
    // Replace column in all clusters
345✔
888
    update(upgrade);
690✔
889
}
690✔
890

891
void ClusterTree::replace_root(std::unique_ptr<ClusterNode> new_root)
892
{
7,377✔
893
    if (new_root != m_root) {
7,377✔
894
        // Maintain parent.
3,015✔
895
        new_root->set_parent(m_root->get_parent(), m_root->get_ndx_in_parent());
7,377✔
896
        new_root->update_parent(); // Throws
7,377✔
897
        m_root = std::move(new_root);
7,377✔
898
    }
7,377✔
899
}
7,377✔
900

901
bool ClusterTree::init_from_parent()
902
{
6,629,823✔
903
    m_root = get_root_from_parent();
6,629,823✔
904
    if (m_root) {
6,630,210✔
905
        m_size = m_root->get_tree_size();
6,630,210✔
906
        return true;
6,630,210✔
907
    }
6,630,210✔
908
    m_size = 0;
4,294,967,294✔
909
    return false;
4,294,967,294✔
910
}
4,294,967,294✔
911

912
void ClusterTree::update_from_parent() noexcept
913
{
812,160✔
914
    m_root->update_from_parent();
812,160✔
915
    m_size = m_root->get_tree_size();
812,160✔
916
}
812,160✔
917

918
void ClusterTree::insert_fast(ObjKey k, const FieldValues& init_values, ClusterNode::State& state)
919
{
23,161,236✔
920
    ref_type new_sibling_ref = m_root->insert(k, init_values, state);
23,161,236✔
921
    if (REALM_UNLIKELY(new_sibling_ref)) {
23,161,236✔
922
        auto new_root = std::make_unique<ClusterNodeInner>(m_root->get_alloc(), *this);
3,060✔
923
        new_root->create(m_root->get_sub_tree_depth() + 1);
3,060✔
924

1,521✔
925
        new_root->add(m_root->get_ref());                // Throws
3,060✔
926
        new_root->add(new_sibling_ref, state.split_key); // Throws
3,060✔
927
        new_root->update_sub_tree_size();
3,060✔
928

1,521✔
929
        replace_root(std::move(new_root));
3,060✔
930
    }
3,060✔
931
    m_size++;
23,161,236✔
932
}
23,161,236✔
933

934
Obj ClusterTree::insert(ObjKey k, const FieldValues& init_values)
935
{
23,141,181✔
936
    ClusterNode::State state;
23,141,181✔
937

11,490,714✔
938
    insert_fast(k, init_values, state);
23,141,181✔
939
    m_owner->update_indexes(k, init_values);
23,141,181✔
940

11,490,714✔
941
    bump_content_version();
23,141,181✔
942
    bump_storage_version();
23,141,181✔
943

11,490,714✔
944
    // Replicate setting of values
11,490,714✔
945
    if (Replication* repl = m_owner->get_repl()) {
23,141,181✔
946
        auto pk_col = m_owner->get_primary_key_column();
7,843,002✔
947
        for (const auto& v : init_values) {
4,179,891✔
948
            if (v.col_key != pk_col)
520,698✔
949
                repl->set(m_owner, v.col_key, k, v.value, v.is_default ? _impl::instr_SetDefault : _impl::instr_Set);
49,116✔
950
        }
520,698✔
951
    }
7,843,002✔
952

11,490,714✔
953
    return Obj(get_table_ref(), state.mem, k, state.index);
23,141,181✔
954
}
23,141,181✔
955

956
bool ClusterTree::is_valid(ObjKey k) const noexcept
957
{
24,685,503✔
958
    if (m_size == 0)
24,685,503✔
959
        return false;
256,785✔
960

11,697,534✔
961
    ClusterNode::State state;
24,428,718✔
962
    return m_root->try_get(k, state);
24,428,718✔
963
}
24,428,718✔
964

965
ClusterNode::State ClusterTree::try_get(ObjKey k) const noexcept
966
{
19,789,896✔
967
    ClusterNode::State state;
19,789,896✔
968
    if (!(k && m_root->try_get(k, state)))
19,847,379✔
969
        state.index = realm::npos;
60,447✔
970
    return state;
19,789,896✔
971
}
19,789,896✔
972

973
ClusterNode::State ClusterTree::get(size_t ndx, ObjKey& k) const
974
{
8,917,248✔
975
    if (ndx >= m_size) {
8,917,248✔
976
        throw Exception(ErrorCodes::InvalidatedObject, "Object was deleted");
×
977
    }
×
978
    ClusterNode::State state;
8,917,248✔
979
    k = m_root->get(ndx, state);
8,917,248✔
980
    return state;
8,917,248✔
981
}
8,917,248✔
982

983
size_t ClusterTree::get_ndx(ObjKey k) const noexcept
984
{
99,585✔
985
    return m_root->get_ndx(k, 0);
99,585✔
986
}
99,585✔
987

988
void ClusterTree::erase(ObjKey k, CascadeState& state)
989
{
5,086,770✔
990
    if (!k.is_unresolved()) {
5,086,770✔
991
        if (auto table = get_owning_table()) {
5,073,513✔
992
            if (Replication* repl = table->get_repl()) {
5,073,501✔
993
                repl->remove_object(table, k);
4,906,269✔
994
            }
4,906,269✔
995
        }
5,073,501✔
996
    }
5,073,507✔
997
    m_owner->free_local_id_after_hash_collision(k);
5,086,770✔
998
    m_owner->erase_from_search_indexes(k);
5,086,770✔
999

2,543,493✔
1000
    size_t root_size = m_root->erase(k, state);
5,086,770✔
1001

2,543,493✔
1002
    bump_content_version();
5,086,770✔
1003
    bump_storage_version();
5,086,770✔
1004
    m_size--;
5,086,770✔
1005
    while (!m_root->is_leaf() && root_size == 1) {
5,086,890✔
1006
        ClusterNodeInner* node = static_cast<ClusterNodeInner*>(m_root.get());
120✔
1007

60✔
1008
        REALM_ASSERT(node->get_first_key_value() == 0);
120✔
1009
        auto new_root = node->return_and_clear_first_child();
120✔
1010
        node->destroy_deep();
120✔
1011

60✔
1012
        replace_root(std::move(new_root));
120✔
1013
        root_size = m_root->node_size();
120✔
1014
    }
120✔
1015
}
5,086,770✔
1016

1017
bool ClusterTree::get_leaf(ObjKey key, ClusterNode::IteratorState& state) const noexcept
1018
{
2,261,685✔
1019
    state.clear();
2,261,685✔
1020

1,142,739✔
1021
    if (m_root->is_leaf()) {
2,261,685✔
1022
        Cluster* node = static_cast<Cluster*>(m_root.get());
775,860✔
1023
        REALM_ASSERT_DEBUG(node->get_offset() == 0);
775,860✔
1024
        state.m_key_offset = 0;
775,860✔
1025
        state.m_current_leaf.init(node->get_mem());
775,860✔
1026
        state.m_current_leaf.set_offset(state.m_key_offset);
775,860✔
1027
        state.m_current_index = node->lower_bound_key(key);
775,860✔
1028
        return state.m_current_index < state.m_current_leaf.node_size();
775,860✔
1029
    }
775,860✔
1030
    else {
1,485,825✔
1031
        ClusterNodeInner* node = static_cast<ClusterNodeInner*>(m_root.get());
1,485,825✔
1032
        return node->get_leaf(key, state);
1,485,825✔
1033
    }
1,485,825✔
1034
}
2,261,685✔
1035

1036
bool ClusterTree::traverse(TraverseFunction func) const
1037
{
5,737,884✔
1038
    if (m_root->is_leaf()) {
5,737,884✔
1039
        return func(static_cast<Cluster*>(m_root.get())) == IteratorControl::Stop;
4,203,042✔
1040
    }
4,203,042✔
1041
    else {
1,534,842✔
1042
        return static_cast<ClusterNodeInner*>(m_root.get())->traverse(func, 0);
1,534,842✔
1043
    }
1,534,842✔
1044
}
5,737,884✔
1045

1046
void ClusterTree::update(UpdateFunction func)
1047
{
972✔
1048
    if (m_root->is_leaf()) {
972✔
1049
        func(static_cast<Cluster*>(m_root.get()));
936✔
1050
    }
936✔
1051
    else {
36✔
1052
        static_cast<ClusterNodeInner*>(m_root.get())->update(func, 0);
36✔
1053
    }
36✔
1054
}
972✔
1055

1056
void ClusterTree::set_spec(ArrayPayload& arr, ColKey::Idx col_ndx) const
1057
{
5,208,483✔
1058
    // Check for owner. This function may be called in context of DictionaryClusterTree
2,561,703✔
1059
    // in which case m_owner is null (and spec never needed).
2,561,703✔
1060
    if (m_owner) {
5,208,612✔
1061
        auto spec_ndx = m_owner->leaf_ndx2spec_ndx(col_ndx);
5,208,612✔
1062
        arr.set_spec(&m_owner->m_spec, spec_ndx);
5,208,612✔
1063
    }
5,208,612✔
1064
}
5,208,483✔
1065

1066
TableRef ClusterTree::get_table_ref() const
1067
{
170,306,217✔
1068
    REALM_ASSERT(m_owner != nullptr);
170,306,217✔
1069
    // as safe as storing the TableRef locally in the ClusterTree,
86,620,434✔
1070
    // because the cluster tree and the table is basically one object :-O
86,620,434✔
1071
    return m_owner->m_own_ref;
170,306,217✔
1072
}
170,306,217✔
1073

1074
std::unique_ptr<ClusterNode> ClusterTree::get_root_from_parent()
1075
{
6,629,595✔
1076
    return create_root_from_parent(&m_owner->m_top, m_top_position_for_cluster_tree);
6,629,595✔
1077
}
6,629,595✔
1078

1079
void ClusterTree::verify() const
1080
{
3,696,534✔
1081
#ifdef REALM_DEBUG
3,696,534✔
1082
    traverse([](const Cluster* cluster) {
3,778,383✔
1083
        cluster->verify();
3,778,383✔
1084
        return IteratorControl::AdvanceToNext;
3,778,383✔
1085
    });
3,778,383✔
1086
#endif
3,696,534✔
1087
}
3,696,534✔
1088

1089
void ClusterTree::nullify_incoming_links(ObjKey obj_key, CascadeState& state)
1090
{
4,987,119✔
1091
    REALM_ASSERT(state.m_group);
4,987,119✔
1092
    m_root->nullify_incoming_links(obj_key, state);
4,987,119✔
1093
}
4,987,119✔
1094

1095
bool ClusterTree::is_string_enum_type(ColKey::Idx col_ndx) const
1096
{
44,499✔
1097
    size_t spec_ndx = m_owner->leaf_ndx2spec_ndx(col_ndx);
44,499✔
1098
    return m_owner->m_spec.is_string_enum_type(spec_ndx);
44,499✔
1099
}
44,499✔
1100

1101
void ClusterTree::remove_all_links(CascadeState& state)
1102
{
3,045✔
1103
    Allocator& alloc = get_alloc();
3,045✔
1104
    auto origin_table = get_owning_table();
3,045✔
1105
    // This function will add objects that should be deleted to 'state'
858✔
1106
    auto func = [&](const Cluster* cluster) {
3,423✔
1107
        auto remove_link_from_column = [&](ColKey col_key) {
58,194✔
1108
            // Prevent making changes to table that is going to be removed anyway
10,641✔
1109
            // Furthermore it is a prerequisite for using 'traverse' that the tree
10,641✔
1110
            // is not modified
10,641✔
1111
            if (origin_table->links_to_self(col_key)) {
58,194✔
1112
                return IteratorControl::AdvanceToNext;
234✔
1113
            }
234✔
1114
            auto col_type = col_key.get_type();
57,960✔
1115
            if (col_type == col_type_LinkList)
57,960✔
1116
                col_type = col_type_Link;
1,524✔
1117
            if (col_key.is_collection()) {
57,960✔
1118
                ArrayInteger values(alloc);
3,456✔
1119
                cluster->init_leaf(col_key, &values);
3,456✔
1120
                size_t sz = values.size();
3,456✔
1121

1,725✔
1122
                for (size_t i = 0; i < sz; i++) {
16,590✔
1123
                    if (ref_type ref = values.get_as_ref(i)) {
13,134✔
1124
                        ObjKey origin_key = cluster->get_real_key(i);
372✔
1125
                        if (col_key.is_list() || col_key.is_set()) {
372✔
1126
                            if (col_type == col_type_Link) {
300✔
1127
                                BPlusTree<ObjKey> links(alloc);
240✔
1128
                                links.init_from_ref(ref);
240✔
1129
                                if (links.size() > 0) {
240✔
1130
                                    cluster->do_remove_backlinks(origin_key, col_key, links.get_all(), state);
228✔
1131
                                }
228✔
1132
                            }
240✔
1133
                            else if (col_type == col_type_TypedLink) {
60✔
NEW
1134
                                BPlusTree<ObjLink> links(alloc);
×
NEW
1135
                                links.init_from_ref(ref);
×
NEW
1136
                                if (links.size() > 0) {
×
NEW
1137
                                    cluster->do_remove_backlinks(origin_key, col_key, links.get_all(), state);
×
NEW
1138
                                }
×
UNCOV
1139
                            }
×
1140
                            else if (col_type == col_type_Mixed) {
60✔
1141
                                BPlusTree<Mixed> mix_arr(alloc);
60✔
1142
                                mix_arr.init_from_ref(ref);
60✔
1143
                                auto sz = mix_arr.size();
60✔
1144
                                std::vector<ObjLink> links;
60✔
1145
                                for (size_t j = 0; j < sz; j++) {
180✔
1146
                                    auto mix = mix_arr.get(j);
120✔
1147
                                    if (mix.is_type(type_TypedLink)) {
120✔
1148
                                        links.push_back(mix.get<ObjLink>());
120✔
1149
                                    }
120✔
1150
                                }
120✔
1151
                                if (links.size())
60✔
1152
                                    cluster->do_remove_backlinks(origin_key, col_key, links, state);
60✔
1153
                            }
60✔
1154
                        }
300✔
1155
                        else if (col_key.is_dictionary()) {
72✔
1156
                            std::vector<ObjLink> links;
72✔
1157

36✔
1158
                            Array top(alloc);
72✔
1159
                            top.set_parent(&values, i);
72✔
1160
                            top.init_from_parent();
72✔
1161
                            BPlusTree<Mixed> values(alloc);
72✔
1162
                            values.set_parent(&top, 1);
72✔
1163
                            values.init_from_parent();
72✔
1164

36✔
1165
                            // Iterate through values and insert all link values
36✔
1166
                            values.for_all([&](Mixed val) {
108✔
1167
                                if (val.is_type(type_TypedLink)) {
108✔
1168
                                    links.push_back(val.get<ObjLink>());
108✔
1169
                                }
108✔
1170
                            });
108✔
1171

36✔
1172
                            if (links.size() > 0) {
72✔
1173
                                cluster->do_remove_backlinks(origin_key, col_key, links, state);
72✔
1174
                            }
72✔
1175
                        }
72✔
1176
                    }
372✔
1177
                }
13,134✔
1178
            }
3,456✔
1179
            else {
54,504✔
1180
                if (col_type == col_type_Link) {
54,504✔
1181
                    ArrayKey values(alloc);
1,488✔
1182
                    cluster->init_leaf(col_key, &values);
1,488✔
1183
                    size_t sz = values.size();
1,488✔
1184
                    for (size_t i = 0; i < sz; i++) {
4,602✔
1185
                        if (ObjKey key = values.get(i)) {
3,114✔
1186
                            cluster->do_remove_backlinks(cluster->get_real_key(i), col_key, std::vector<ObjKey>{key},
522✔
1187
                                                         state);
522✔
1188
                        }
522✔
1189
                    }
3,114✔
1190
                }
1,488✔
1191
                else if (col_type == col_type_TypedLink) {
53,016✔
UNCOV
1192
                    ArrayTypedLink values(alloc);
×
UNCOV
1193
                    cluster->init_leaf(col_key, &values);
×
UNCOV
1194
                    size_t sz = values.size();
×
UNCOV
1195
                    for (size_t i = 0; i < sz; i++) {
×
UNCOV
1196
                        if (ObjLink link = values.get(i)) {
×
NEW
1197
                            cluster->do_remove_backlinks(cluster->get_real_key(i), col_key,
×
NEW
1198
                                                         std::vector<ObjLink>{link}, state);
×
UNCOV
1199
                        }
×
UNCOV
1200
                    }
×
UNCOV
1201
                }
×
1202
                else if (col_type == col_type_Mixed) {
53,016✔
1203
                    ArrayMixed values(alloc);
72✔
1204
                    cluster->init_leaf(col_key, &values);
72✔
1205
                    size_t sz = values.size();
72✔
1206
                    for (size_t i = 0; i < sz; i++) {
576✔
1207
                        Mixed mix = values.get(i);
504✔
1208
                        if (mix.is_type(type_TypedLink)) {
504✔
1209
                            cluster->do_remove_backlinks(cluster->get_real_key(i), col_key,
12✔
1210
                                                         std::vector<ObjLink>{mix.get<ObjLink>()}, state);
12✔
1211
                        }
12✔
1212
                    }
504✔
1213
                }
72✔
1214
                else if (col_type == col_type_BackLink) {
52,944✔
1215
                    ArrayBacklink values(alloc);
285✔
1216
                    cluster->init_leaf(col_key, &values);
285✔
1217
                    values.set_parent(const_cast<Cluster*>(cluster),
285✔
1218
                                      col_key.get_index().val + Cluster::s_first_col_index);
285✔
1219
                    size_t sz = values.size();
285✔
1220
                    for (size_t i = 0; i < sz; i++) {
2,109✔
1221
                        values.nullify_fwd_links(i, state);
1,824✔
1222
                    }
1,824✔
1223
                }
285✔
1224
            }
54,504✔
1225
            return IteratorControl::AdvanceToNext;
57,960✔
1226
        };
57,960✔
1227
        m_owner->for_each_and_every_column(remove_link_from_column);
3,423✔
1228
        return IteratorControl::AdvanceToNext;
3,423✔
1229
    };
3,423✔
1230

858✔
1231
    // Go through all clusters
858✔
1232
    traverse(func);
3,045✔
1233

858✔
1234
    const_cast<Table*>(get_owning_table())->remove_recursive(state);
3,045✔
1235
}
3,045✔
1236

1237
/**************************  ClusterTree::Iterator  **************************/
1238

1239
ClusterTree::Iterator::Iterator(const ClusterTree& t, size_t ndx)
1240
    : m_tree(t)
1241
    , m_leaf(0, t.get_alloc(), t)
1242
    , m_state(m_leaf)
1243
    , m_instance_version(t.get_instance_version())
1244
    , m_leaf_invalid(false)
1245
    , m_position(ndx)
1246
{
10,093,530✔
1247
    auto sz = t.size();
10,093,530✔
1248
    if (ndx >= sz) {
10,093,530✔
1249
        // end
4,657,290✔
1250
        m_position = sz;
9,448,953✔
1251
        m_leaf_invalid = true;
9,448,953✔
1252
    }
9,448,953✔
1253
    else if (ndx == 0) {
644,577✔
1254
        // begin
314,520✔
1255
        m_key = load_leaf(ObjKey(0));
602,148✔
1256
        m_leaf_start_pos = 0;
602,148✔
1257
    }
602,148✔
1258
    else {
42,429✔
1259
        auto s = const_cast<ClusterTree&>(m_tree).get(ndx, m_key);
42,429✔
1260
        m_state.init(s, m_key);
42,429✔
1261
        m_leaf_start_pos = ndx - m_state.m_current_index;
42,429✔
1262
    }
42,429✔
1263
}
10,093,530✔
1264

1265
ClusterTree::Iterator::Iterator(const Iterator& other)
1266
    : m_tree(other.m_tree)
1267
    , m_leaf(0, m_tree.get_alloc(), m_tree)
1268
    , m_state(m_leaf)
1269
    , m_instance_version(m_tree.get_instance_version())
1270
    , m_key(other.m_key)
1271
    , m_leaf_invalid(other.m_leaf_invalid)
1272
    , m_position(other.m_position)
1273
{
288✔
1274
    m_leaf_start_pos = m_position - m_state.m_current_index;
288✔
1275
}
288✔
1276

1277
size_t ClusterTree::Iterator::get_position()
1278
{
81,417✔
1279
    auto ndx = m_tree.get_ndx(m_key);
81,417✔
1280
    if (ndx == realm::npos) {
81,417✔
1281
        throw StaleAccessor("Stale iterator");
×
1282
    }
×
1283
    return ndx;
81,417✔
1284
}
81,417✔
1285

1286
ObjKey ClusterTree::Iterator::load_leaf(ObjKey key) const
1287
{
2,261,952✔
1288
    m_storage_version = m_tree.get_storage_version(m_instance_version);
2,261,952✔
1289
    // 'key' may or may not exist. If it does not exist, state is updated
1,143,069✔
1290
    // to point to the next object in line.
1,143,069✔
1291
    if (m_tree.get_leaf(key, m_state)) {
2,261,952✔
1292
        m_leaf_start_pos = m_position - m_state.m_current_index;
2,099,919✔
1293
        // Get the actual key value
1,062,453✔
1294
        return m_leaf.get_real_key(m_state.m_current_index);
2,099,919✔
1295
    }
2,099,919✔
1296
    else {
162,033✔
1297
        // end of table
80,616✔
1298
        return null_key;
162,033✔
1299
    }
162,033✔
1300
}
2,261,952✔
1301

1302
void ClusterTree::Iterator::go(size_t abs_pos)
1303
{
43,452✔
1304
    size_t sz = m_tree.size();
43,452✔
1305
    if (abs_pos >= sz) {
43,452✔
1306
        throw OutOfBounds("go() on Iterator", abs_pos, sz);
×
1307
    }
×
1308

21,720✔
1309
    m_position = abs_pos;
43,452✔
1310

21,720✔
1311
    // If the position is within the current leaf then just set the iterator to that position
21,720✔
1312
    if (!m_leaf_invalid && m_storage_version == m_tree.get_storage_version(m_instance_version)) {
43,452✔
1313
        if (abs_pos >= m_leaf_start_pos && abs_pos < (m_leaf_start_pos + m_leaf.node_size())) {
15,126✔
1314
            m_state.m_current_index = abs_pos - m_leaf_start_pos;
4,509✔
1315
            m_key = m_leaf.get_real_key(m_state.m_current_index);
4,509✔
1316
            return;
4,509✔
1317
        }
4,509✔
1318
    }
38,943✔
1319

19,494✔
1320
    // Find cluster holding requested position
19,494✔
1321
    auto s = m_tree.get(abs_pos, m_key);
38,943✔
1322
    m_state.init(s, m_key);
38,943✔
1323
    m_leaf_start_pos = abs_pos - s.index;
38,943✔
1324
    m_leaf_invalid = false;
38,943✔
1325
}
38,943✔
1326

1327
bool ClusterTree::Iterator::update() const
1328
{
19,974,090✔
1329
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
19,981,464✔
1330
        ObjKey k = load_leaf(m_key);
76,917✔
1331
        m_leaf_invalid = !k || (k != m_key);
76,917✔
1332
        if (m_leaf_invalid) {
76,917✔
1333
            throw StaleAccessor("Stale iterator");
30✔
1334
        }
30✔
1335
        return true;
76,887✔
1336
    }
76,887✔
1337

9,597,663✔
1338
    REALM_ASSERT(m_leaf.is_attached());
19,897,173✔
1339
    return false;
19,897,173✔
1340
}
19,897,173✔
1341

1342
ClusterTree::Iterator& ClusterTree::Iterator::operator++()
1343
{
18,671,055✔
1344
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
18,675,216✔
1345
        ObjKey k = load_leaf(m_key);
1,378,185✔
1346
        if (k != m_key) {
1,378,185✔
1347
            // Objects was deleted. k points to the next object
30,447✔
1348
            m_key = k;
60,894✔
1349
            m_leaf_invalid = !m_key;
60,894✔
1350
            return *this;
60,894✔
1351
        }
60,894✔
1352
    }
18,610,161✔
1353

8,961,591✔
1354
    m_state.m_current_index++;
18,610,161✔
1355
    m_position++;
18,610,161✔
1356
    if (m_state.m_current_index == m_leaf.node_size()) {
18,610,161✔
1357
        m_key = load_leaf(ObjKey(m_key.value + 1));
205,056✔
1358
        m_leaf_invalid = !m_key;
205,056✔
1359
    }
205,056✔
1360
    else {
18,405,105✔
1361
        m_key = m_leaf.get_real_key(m_state.m_current_index);
18,405,105✔
1362
    }
18,405,105✔
1363
    return *this;
18,610,161✔
1364
}
18,610,161✔
1365

1366
ClusterTree::Iterator& ClusterTree::Iterator::operator+=(ptrdiff_t adj)
1367
{
2,016✔
1368
    // If you have to jump far away and thus have to load many leaves,
1,008✔
1369
    // this function will be slow
1,008✔
1370
    REALM_ASSERT(adj >= 0);
2,016✔
1371
    if (adj == 0) {
2,016✔
1372
        return *this;
6✔
1373
    }
6✔
1374

1,005✔
1375
    size_t n = size_t(adj);
2,010✔
1376
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
2,010✔
1377
        ObjKey k = load_leaf(m_key);
12✔
1378
        if (k != m_key) {
12✔
1379
            // Objects was deleted. k points to the next object
3✔
1380
            m_key = k;
6✔
1381
            m_position = m_key ? m_tree.get_ndx(m_key) : m_tree.size();
6✔
1382
            n--;
6✔
1383
        }
6✔
1384
    }
12✔
1385
    if (n > 0) {
2,010✔
1386
        m_position += n;
2,010✔
1387
        size_t left_in_leaf = m_leaf.node_size() - m_state.m_current_index;
2,010✔
1388
        if (n < left_in_leaf) {
2,010✔
1389
            m_state.m_current_index += n;
1,986✔
1390
            m_key = m_leaf.get_real_key(m_state.m_current_index);
1,986✔
1391
        }
1,986✔
1392
        else {
24✔
1393
            if (m_position < m_tree.size()) {
24✔
1394
                auto s = const_cast<ClusterTree&>(m_tree).get(m_position, m_key);
18✔
1395
                m_state.init(s, m_key);
18✔
1396
                m_leaf_start_pos = m_position - m_state.m_current_index;
18✔
1397
            }
18✔
1398
            else {
6✔
1399
                m_key = ObjKey();
6✔
1400
                m_position = m_tree.size();
6✔
1401
            }
6✔
1402
        }
24✔
1403
    }
2,010✔
1404
    m_leaf_invalid = !m_key;
2,010✔
1405
    return *this;
2,010✔
1406
}
2,010✔
1407

1408
ClusterTree::Iterator::pointer ClusterTree::Iterator::operator->() const
1409
{
19,910,850✔
1410
    if (update() || m_key != m_obj.get_key()) {
19,910,850✔
1411
        new (&m_obj) Obj(m_tree.get_table_ref(), m_leaf.get_mem(), m_key, m_state.m_current_index);
16,150,830✔
1412
    }
16,150,830✔
1413

9,563,340✔
1414
    return &m_obj;
19,910,850✔
1415
}
19,910,850✔
1416

1417
} // namespace realm
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

© 2025 Coveralls, Inc