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

realm / realm-core / james.stone_381

25 Sep 2023 06:35PM UTC coverage: 90.919% (+0.03%) from 90.892%
james.stone_381

Pull #6670

Evergreen

ironage
optimize: only compare strings once
Pull Request #6670: Sorting stage 3

97114 of 177952 branches covered (0.0%)

879 of 887 new or added lines in 12 files covered. (99.1%)

105 existing lines in 17 files now uncovered.

236103 of 259684 relevant lines covered (90.92%)

7062315.99 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,438,019✔
66
        return false;
44,438,019✔
67
    }
44,438,019✔
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
    {
73,324,260✔
79
        return Array::size() - s_first_node_index;
73,324,260✔
80
    }
73,324,260✔
81
    size_t get_tree_size() const override
82
    {
42,119,928✔
83
        return size_t(Array::get(s_sub_tree_size)) >> 1;
42,119,928✔
84
    }
42,119,928✔
85
    void set_tree_size(size_t sub_tree_size)
86
    {
39,350,760✔
87
        Array::set(s_sub_tree_size, sub_tree_size << 1 | 1);
39,350,760✔
88
    }
39,350,760✔
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
    {
311,459,697✔
140
        if (m_keys.is_attached()) {
311,459,697✔
141
            auto upper = m_keys.upper_bound(uint64_t(key.value));
259,628,154✔
142
            // The first entry in m_keys will always be smaller than or equal
128,946,582✔
143
            // to all keys in this subtree. If you get zero returned here, the
128,946,582✔
144
            // key is not in the tree
128,946,582✔
145
            if (upper == 0) {
259,628,154✔
146
                return false;
×
147
            }
×
148
            ret.ndx = upper - 1;
259,628,154✔
149
            ret.offset = m_keys.get(ret.ndx);
259,628,154✔
150
        }
259,628,154✔
151
        else {
51,831,543✔
152
            size_t sz = node_size();
51,831,543✔
153
            REALM_ASSERT_DEBUG(sz > 0);
51,831,543✔
154
            size_t max_ndx = sz - 1;
51,831,543✔
155
            ret.ndx = std::min(size_t(key.value) >> m_shift_factor, max_ndx);
51,831,543✔
156
            ret.offset = ret.ndx << m_shift_factor;
51,831,543✔
157
        }
51,831,543✔
158
        ret.key = ObjKey(key.value - ret.offset);
311,459,697✔
159
        ref_type child_ref = _get_child_ref(ret.ndx);
311,459,697✔
160
        char* child_header = m_alloc.translate(child_ref);
311,459,697✔
161
        ret.mem = MemRef(child_header, child_ref, m_alloc);
311,459,697✔
162
        return true;
311,459,697✔
163
    }
311,459,697✔
164

165
    ref_type _get_child_ref(size_t ndx) const noexcept
166
    {
369,066,660✔
167
        return Array::get_as_ref(ndx + s_first_node_index);
369,066,660✔
168
    }
369,066,660✔
169
    void _insert_child_ref(size_t ndx, ref_type ref)
170
    {
85,224✔
171
        Array::insert(ndx + s_first_node_index, from_ref(ref));
85,224✔
172
    }
85,224✔
173
    void _erase_child_ref(size_t ndx)
174
    {
20,202✔
175
        Array::erase(ndx + s_first_node_index);
20,202✔
176
    }
20,202✔
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
{
119,197,449✔
197
}
119,197,449✔
198

199
ClusterNodeInner::~ClusterNodeInner() {}
119,218,005✔
200

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

1,614✔
205
    Array::set(s_key_ref_index, 0);
3,237✔
206

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

213
void ClusterNodeInner::init(MemRef mem)
214
{
117,271,119✔
215
    Array::init_from_mem(mem);
117,271,119✔
216
    m_keys.set_parent(this, s_key_ref_index);
117,271,119✔
217
    ref_type ref = Array::get_as_ref(s_key_ref_index);
117,271,119✔
218
    if (ref) {
117,271,119✔
219
        m_keys.init_from_ref(ref);
103,447,890✔
220
    }
103,447,890✔
221
    else {
13,823,229✔
222
        m_keys.detach();
13,823,229✔
223
    }
13,823,229✔
224
    m_sub_tree_depth = int(Array::get(s_sub_tree_depth_index)) >> 1;
117,271,119✔
225
    m_shift_factor = m_sub_tree_depth * node_shift_factor;
117,271,119✔
226
}
117,271,119✔
227

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

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

248
template <class T, class F>
249
T ClusterNodeInner::recurse(ChildInfo& child_info, F func)
250
{
310,982,805✔
251
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_info.mem.get_addr());
310,982,805✔
252
    if (child_is_leaf) {
310,982,805!
253
        Cluster leaf(child_info.offset + m_offset, m_alloc, m_tree_top);
199,905,351✔
254
        leaf.set_parent(this, child_info.ndx + s_first_node_index);
199,905,351✔
255
        leaf.init(child_info.mem);
199,905,351✔
256
        return func(&leaf, child_info);
199,905,351✔
257
    }
199,905,351✔
258
    else {
111,077,454✔
259
        ClusterNodeInner node(m_alloc, m_tree_top);
111,077,454✔
260
        node.set_parent(this, child_info.ndx + s_first_node_index);
111,077,454✔
261
        node.init(child_info.mem);
111,077,454✔
262
        node.set_offset(child_info.offset + m_offset);
111,077,454✔
263
        return func(&node, child_info);
111,077,454✔
264
    }
111,077,454✔
265
}
310,982,805✔
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
{
485,946✔
276
    ChildInfo child_info;
485,946✔
277
    if (!find_child(key, child_info)) {
485,946✔
278
        throw KeyNotFound("Child not found in update_ref_in_parent");
×
279
    }
×
280
    if (this->m_sub_tree_depth == 1) {
485,946✔
281
        set(child_info.ndx + s_first_node_index, ref);
269,316✔
282
    }
269,316✔
283
    else {
216,630✔
284
        ClusterNodeInner node(m_alloc, m_tree_top);
216,630✔
285
        node.set_parent(this, child_info.ndx + s_first_node_index);
216,630✔
286
        node.init(child_info.mem);
216,630✔
287
        node.set_offset(child_info.offset + m_offset);
216,630✔
288
        node.update_ref_in_parent(child_info.key, ref);
216,630✔
289
    }
216,630✔
290
}
485,946✔
291

292
ref_type ClusterNodeInner::insert(ObjKey key, const FieldValues& init_values, ClusterNode::State& state)
293
{
31,335,597✔
294
    return recurse<ref_type>(key, [this, &state, &init_values](ClusterNode* node, ChildInfo& child_info) {
31,295,343✔
295
        ref_type new_sibling_ref = node->insert(child_info.key, init_values, state);
31,233,417✔
296

15,572,214✔
297
        set_tree_size(get_tree_size() + 1);
31,233,417✔
298

15,572,214✔
299
        if (!new_sibling_ref) {
31,233,417✔
300
            return ref_type(0);
31,200,558✔
301
        }
31,200,558✔
302

31,725✔
303
        size_t new_ref_ndx = child_info.ndx + 1;
32,859✔
304

31,725✔
305
        int64_t split_key_value = state.split_key + child_info.offset;
32,859✔
306
        uint64_t sz = node_size();
32,859✔
307
        if (sz < cluster_node_size) {
85,224✔
308
            if (m_keys.is_attached()) {
85,224✔
309
                m_keys.insert(new_ref_ndx, split_key_value);
81,435✔
310
            }
81,435✔
311
            else {
3,789✔
312
                if (uint64_t(split_key_value) != sz << m_shift_factor) {
3,789✔
313
                    ensure_general_form();
291✔
314
                    m_keys.insert(new_ref_ndx, split_key_value);
291✔
315
                }
291✔
316
            }
3,789✔
317
            _insert_child_ref(new_ref_ndx, new_sibling_ref);
85,224✔
318
            return ref_type(0);
85,224✔
319
        }
85,224✔
320

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

2,147,483,647✔
335
        // Some objects has been moved out of this tree - find out how many
2,147,483,647✔
336
        size_t child_sub_tree_size = child.update_sub_tree_size();
4,294,967,294✔
337
        set_tree_size(get_tree_size() - child_sub_tree_size);
4,294,967,294✔
338

2,147,483,647✔
339
        return child.get_ref();
4,294,967,294✔
340
    });
4,294,967,294✔
341
}
31,335,597✔
342

343
bool ClusterNodeInner::try_get(ObjKey key, ClusterNode::State& state) const noexcept
344
{
264,431,694✔
345
    ChildInfo child_info;
264,431,694✔
346
    if (!find_child(key, child_info)) {
264,431,694✔
347
        return false;
×
348
    }
×
349
    return const_cast<ClusterNodeInner*>(this)->recurse<bool>(child_info,
264,431,694✔
350
                                                              [&state](const ClusterNode* node, ChildInfo& info) {
263,695,419✔
351
                                                                  return node->try_get(info.key, state);
262,827,783✔
352
                                                              });
262,827,783✔
353
}
264,431,694✔
354

355
ObjKey ClusterNodeInner::get(size_t ndx, ClusterNode::State& state) const
356
{
5,227,785✔
357
    size_t sz = node_size();
5,227,785✔
358
    size_t child_ndx = 0;
5,227,785✔
359
    while (child_ndx < sz) {
53,191,404✔
360
        int64_t key_offset = m_keys.is_attached() ? m_keys.get(child_ndx) : (child_ndx << m_shift_factor);
52,996,950✔
361

26,509,197✔
362
        ref_type child_ref = _get_child_ref(child_ndx);
53,191,404✔
363
        char* child_header = m_alloc.translate(child_ref);
53,191,404✔
364
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
53,191,404✔
365
        size_t sub_tree_size;
53,191,404✔
366
        if (child_is_leaf) {
53,191,404✔
367
            sub_tree_size = Cluster::node_size_from_header(m_alloc, child_header);
50,731,248✔
368
            if (ndx < sub_tree_size) {
50,731,248✔
369
                Cluster leaf(key_offset + m_offset, m_alloc, m_tree_top);
2,641,593✔
370
                leaf.init(MemRef(child_header, child_ref, m_alloc));
2,641,593✔
371
                REALM_ASSERT(sub_tree_size == leaf.get_tree_size());
2,641,593✔
372
                return leaf.get(ndx, state);
2,641,593✔
373
            }
2,641,593✔
374
        }
2,460,156✔
375
        else {
2,460,156✔
376
            ClusterNodeInner node(m_alloc, m_tree_top);
2,460,156✔
377
            node.init(MemRef(child_header, child_ref, m_alloc));
2,460,156✔
378
            node.set_offset(key_offset + m_offset);
2,460,156✔
379
            sub_tree_size = node.get_tree_size();
2,460,156✔
380
            if (ndx < sub_tree_size) {
2,588,973✔
381
                return node.get(ndx, state);
2,588,973✔
382
            }
2,588,973✔
383
        }
47,960,838✔
384
        child_ndx++;
47,960,838✔
385
        ndx -= sub_tree_size;
47,960,838✔
386
    }
47,960,838✔
387
    return {};
4,294,967,294✔
388
}
5,227,785✔
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✔
UNCOV
411
    else {
×
UNCOV
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
        }
×
UNCOV
416
        ClusterNodeInner node(m_alloc, m_tree_top);
×
UNCOV
417
        node.init(child_info.mem);
×
UNCOV
418
        node.set_offset(child_info.offset + m_offset);
×
UNCOV
419
        return node.get_ndx(child_info.key, ndx);
×
UNCOV
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,170,575✔
455
    return recurse<size_t>(key, [this, &state](ClusterNode* erase_node, ChildInfo& child_info) {
8,170,521✔
456
        size_t erase_node_size = erase_node->erase(child_info.key, state);
8,170,437✔
457
        bool is_leaf = erase_node->is_leaf();
8,170,437✔
458
        set_tree_size(get_tree_size() - 1);
8,170,437✔
459

4,084,764✔
460
        if (erase_node_size == 0) {
8,170,437✔
461
            erase_node->destroy_deep();
13,344✔
462

6,672✔
463
            ensure_general_form();
13,344✔
464
            _erase_child_ref(child_info.ndx);
13,344✔
465
            m_keys.erase(child_info.ndx);
13,344✔
466
            if (child_info.ndx == 0 && m_keys.size() > 0) {
13,344✔
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,344✔
474
        else if (erase_node_size < cluster_node_size / 2 && child_info.ndx < (node_size() - 1)) {
8,157,093✔
475
            // Candidate for merge. First calculate if the combined size of current and
1,622,439✔
476
            // next sibling is small enough.
1,622,439✔
477
            size_t sibling_ndx = child_info.ndx + 1;
3,255,843✔
478
            Cluster l2(child_info.offset, m_alloc, m_tree_top);
3,255,843✔
479
            ClusterNodeInner n2(m_alloc, m_tree_top);
3,255,843✔
480
            ClusterNode* sibling_node = is_leaf ? static_cast<ClusterNode*>(&l2) : static_cast<ClusterNode*>(&n2);
2,660,496✔
481
            sibling_node->set_parent(this, sibling_ndx + s_first_node_index);
3,255,843✔
482
            sibling_node->init_from_parent();
3,255,843✔
483

1,622,439✔
484
            size_t combined_size = sibling_node->node_size() + erase_node_size;
3,255,843✔
485

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

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

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

3,456✔
503
                ensure_general_form();
6,858✔
504
                _erase_child_ref(sibling_ndx);
6,858✔
505
                m_keys.erase(sibling_ndx);
6,858✔
506
            }
6,858✔
507
        }
3,255,843✔
508

4,084,764✔
509
        return node_size();
8,170,437✔
510
    });
8,170,437✔
511
}
8,170,575✔
512

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

520
void ClusterNodeInner::ensure_general_form()
521
{
23,340✔
522
    if (!m_keys.is_attached()) {
23,340✔
523
        size_t current_size = node_size();
3,072✔
524
        m_keys.create(current_size, (current_size - 1) << m_shift_factor);
3,072✔
525
        m_keys.update_parent();
3,072✔
526
        for (size_t i = 0; i < current_size; i++) {
9,402✔
527
            m_keys.set(i, i << m_shift_factor);
6,330✔
528
        }
6,330✔
529
    }
3,072✔
530
}
23,340✔
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,267✔
570
    if (m_keys.is_attached()) {
6,267✔
571
        m_keys.add(key_value);
36✔
572
    }
36✔
573
    else {
6,231✔
574
        if (uint64_t(key_value) != (uint64_t(node_size()) << m_shift_factor)) {
6,231✔
575
            ensure_general_form();
2,679✔
576
            m_keys.add(key_value);
2,679✔
577
        }
2,679✔
578
    }
6,231✔
579
    Array::add(from_ref(ref));
6,267✔
580
}
6,267✔
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,690,460✔
586
    size_t child_ndx;
2,690,460✔
587
    if (m_keys.is_attached()) {
2,690,460✔
588
        child_ndx = m_keys.upper_bound(uint64_t(key.value));
2,653,035✔
589
        if (child_ndx > 0)
2,653,035✔
590
            child_ndx--;
2,653,479✔
591
    }
2,653,035✔
592
    else {
37,425✔
593
        REALM_ASSERT_DEBUG(node_size() > 0);
37,425✔
594
        size_t max_ndx = node_size() - 1;
37,425✔
595
        if (key.value < 0)
37,425✔
596
            child_ndx = 0;
×
597
        else
37,425✔
598
            child_ndx = std::min(size_t(key.value) >> m_shift_factor, max_ndx);
37,425✔
599
    }
37,425✔
600

1,345,437✔
601
    size_t sz = node_size();
2,690,460✔
602
    while (child_ndx < sz) {
2,739,558✔
603
        uint64_t key_offset = m_keys.is_attached() ? m_keys.get(child_ndx) : (child_ndx << m_shift_factor);
2,713,311✔
604
        ObjKey new_key(key_offset < uint64_t(key.value) ? key.value - key_offset : 0);
2,695,425✔
605
        state.m_key_offset += key_offset;
2,731,992✔
606

1,366,305✔
607
        ref_type child_ref = _get_child_ref(child_ndx);
2,731,992✔
608
        char* child_header = m_alloc.translate(child_ref);
2,731,992✔
609
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
2,731,992✔
610
        if (child_is_leaf) {
2,731,992✔
611
            state.m_current_leaf.init(MemRef(child_header, child_ref, m_alloc));
1,527,639✔
612
            state.m_current_leaf.set_offset(state.m_key_offset);
1,527,639✔
613
            state.m_current_index = state.m_current_leaf.lower_bound_key(new_key);
1,527,639✔
614
            if (state.m_current_index < state.m_current_leaf.node_size())
1,527,639✔
615
                return true;
1,478,202✔
616
        }
1,204,353✔
617
        else {
1,204,353✔
618
            ClusterNodeInner node(m_alloc, m_tree_top);
1,204,353✔
619
            node.init(MemRef(child_header, child_ref, m_alloc));
1,204,353✔
620
            if (node.get_leaf(new_key, state))
1,204,353✔
621
                return true;
1,204,692✔
622
        }
49,098✔
623
        state.m_key_offset -= key_offset;
49,098✔
624
        child_ndx++;
49,098✔
625
    }
49,098✔
626
    return false;
1,349,055✔
627
}
2,690,460✔
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
{
87✔
653
    auto new_cluster_node_inner = static_cast<ClusterNodeInner*>(new_node);
87✔
654
    for (size_t i = ndx; i < node_size(); i++) {
8,004✔
655
        new_cluster_node_inner->Array::add(_get_child_ref(i));
7,917✔
656
    }
7,917✔
657
    for (size_t i = ndx; i < m_keys.size(); i++) {
8,004✔
658
        new_cluster_node_inner->m_keys.add(m_keys.get(i) - key_adj);
7,917✔
659
    }
7,917✔
660
    truncate(ndx + s_first_node_index);
87✔
661
    if (m_keys.is_attached()) {
87✔
662
        m_keys.truncate(ndx);
87✔
663
    }
87✔
664
}
87✔
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,635✔
671
    for (unsigned i = 0; i < sz; i++) {
21,921✔
672
        ref_type ref = _get_child_ref(i);
18,633✔
673
        char* header = m_alloc.translate(ref);
18,633✔
674
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
18,633✔
675
        if (child_is_leaf) {
18,633✔
676
            sub_tree_size += Cluster::node_size_from_header(m_alloc, header);
18,525✔
677
        }
18,525✔
678
        else {
108✔
679
            sub_tree_size += size_t(Array::get(header, s_sub_tree_size)) >> 1;
108✔
680
        }
108✔
681
    }
18,633✔
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,565,607✔
688
    auto sz = node_size();
1,565,607✔
689

740,847✔
690
    for (unsigned i = 0; i < sz; i++) {
7,395,216✔
691
        ref_type ref = _get_child_ref(i);
5,858,736✔
692
        char* header = m_alloc.translate(ref);
5,858,736✔
693
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
5,858,736✔
694
        MemRef mem(header, ref, m_alloc);
5,858,736✔
695
        int64_t offs = (m_keys.is_attached() ? m_keys.get(i) : i << m_shift_factor) + key_offset;
2,150,745,562✔
696
        if (child_is_leaf) {
5,858,736✔
697
            Cluster leaf(offs, m_alloc, m_tree_top);
5,704,008✔
698
            leaf.init(mem);
5,704,008✔
699
            if (func(&leaf) == IteratorControl::Stop) {
5,704,008✔
700
                return true;
29,127✔
701
            }
29,127✔
702
        }
154,728✔
703
        else {
154,728✔
704
            ClusterNodeInner node(m_alloc, m_tree_top);
154,728✔
705
            node.init(mem);
154,728✔
706
            if (node.traverse(func, offs)) {
154,728✔
707
                return true;
×
708
            }
×
709
        }
154,728✔
710
    }
5,858,736✔
711
    return false;
1,551,045✔
712
}
1,565,607✔
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
{
563,448✔
765
}
563,448✔
766

767
ClusterTree::~ClusterTree() {}
537,609✔
768

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

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

3,487,548✔
779
    bool can_reuse_root_accessor = m_root && m_root->is_leaf() == is_leaf;
6,330,408✔
780
    if (can_reuse_root_accessor) {
6,330,408✔
781
        m_root->init(mem);
5,724,879✔
782
        return std::move(m_root); // Same root will be reinstalled.
5,724,879✔
783
    }
5,724,879✔
784

314,733✔
785
    // Not reusing root note, allocating a new one.
314,733✔
786
    std::unique_ptr<ClusterNode> new_root;
605,529✔
787
    if (is_leaf) {
605,529✔
788
        new_root = std::make_unique<Cluster>(0, m_alloc, *this);
586,152✔
789
    }
586,152✔
790
    else {
19,377✔
791
        new_root = std::make_unique<ClusterNodeInner>(m_alloc, *this);
19,377✔
792
    }
19,377✔
793
    new_root->init(mem);
605,529✔
794
    new_root->set_parent(parent, ndx_in_parent);
605,529✔
795

314,733✔
796
    return new_root;
605,529✔
797
}
605,529✔
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
{
3,522✔
821
    m_owner->clear_indexes();
3,522✔
822

1,110✔
823
    if (state.m_group) {
3,522✔
824
        remove_all_links(state); // This will also delete objects loosing their last strong link
2,376✔
825
    }
2,376✔
826

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

1,110✔
840
    m_root->destroy_deep();
3,522✔
841

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

1,110✔
846
    bump_content_version();
3,522✔
847
    bump_storage_version();
3,522✔
848

1,110✔
849
    m_size = 0;
3,522✔
850
}
3,522✔
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
{
6,672✔
893
    if (new_root != m_root) {
6,672✔
894
        // Maintain parent.
2,685✔
895
        new_root->set_parent(m_root->get_parent(), m_root->get_ndx_in_parent());
6,672✔
896
        new_root->update_parent(); // Throws
6,672✔
897
        m_root = std::move(new_root);
6,672✔
898
    }
6,672✔
899
}
6,672✔
900

901
bool ClusterTree::init_from_parent()
902
{
6,328,218✔
903
    m_root = get_root_from_parent();
6,328,218✔
904
    if (m_root) {
6,332,703✔
905
        m_size = m_root->get_tree_size();
6,332,703✔
906
        return true;
6,332,703✔
907
    }
6,332,703✔
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
{
806,292✔
914
    m_root->update_from_parent();
806,292✔
915
    m_size = m_root->get_tree_size();
806,292✔
916
}
806,292✔
917

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

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

1,515✔
929
        replace_root(std::move(new_root));
3,030✔
930
    }
3,030✔
931
    m_size++;
23,066,280✔
932
}
23,066,280✔
933

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

11,470,875✔
938
    insert_fast(k, init_values, state);
23,055,000✔
939
    m_owner->update_indexes(k, init_values);
23,055,000✔
940

11,470,875✔
941
    bump_content_version();
23,055,000✔
942
    bump_storage_version();
23,055,000✔
943

11,470,875✔
944
    // Replicate setting of values
11,470,875✔
945
    if (Replication* repl = m_owner->get_repl()) {
23,055,000✔
946
        auto pk_col = m_owner->get_primary_key_column();
7,828,572✔
947
        for (const auto& v : init_values) {
4,172,727✔
948
            if (v.col_key != pk_col)
518,625✔
949
                repl->set(m_owner, v.col_key, k, v.value, v.is_default ? _impl::instr_SetDefault : _impl::instr_Set);
49,116✔
950
        }
518,625✔
951
    }
7,828,572✔
952

11,470,875✔
953
    return Obj(get_table_ref(), state.mem, k, state.index);
23,055,000✔
954
}
23,055,000✔
955

956
bool ClusterTree::is_valid(ObjKey k) const noexcept
957
{
24,593,232✔
958
    if (m_size == 0)
24,593,232✔
959
        return false;
258,036✔
960

11,664,780✔
961
    ClusterNode::State state;
24,335,196✔
962
    return m_root->try_get(k, state);
24,335,196✔
963
}
24,335,196✔
964

965
ClusterNode::State ClusterTree::try_get(ObjKey k) const noexcept
966
{
19,423,983✔
967
    ClusterNode::State state;
19,423,983✔
968
    if (!(k && m_root->try_get(k, state)))
19,473,999✔
969
        state.index = realm::npos;
59,172✔
970
    return state;
19,423,983✔
971
}
19,423,983✔
972

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

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

988
void ClusterTree::erase(ObjKey k, CascadeState& state)
989
{
5,087,139✔
990
    if (!k.is_unresolved()) {
5,087,139✔
991
        if (auto table = get_owning_table()) {
5,074,083✔
992
            if (Replication* repl = table->get_repl()) {
5,074,080✔
993
                repl->remove_object(table, k);
4,906,911✔
994
            }
4,906,911✔
995
        }
5,074,080✔
996
    }
5,074,077✔
997
    m_owner->free_local_id_after_hash_collision(k);
5,087,139✔
998
    m_owner->erase_from_search_indexes(k);
5,087,139✔
999

2,543,676✔
1000
    size_t root_size = m_root->erase(k, state);
5,087,139✔
1001

2,543,676✔
1002
    bump_content_version();
5,087,139✔
1003
    bump_storage_version();
5,087,139✔
1004
    m_size--;
5,087,139✔
1005
    while (!m_root->is_leaf() && root_size == 1) {
5,087,259✔
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,087,139✔
1016

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

1,106,583✔
1021
    if (m_root->is_leaf()) {
2,192,901✔
1022
        Cluster* node = static_cast<Cluster*>(m_root.get());
706,956✔
1023
        REALM_ASSERT_DEBUG(node->get_offset() == 0);
706,956✔
1024
        state.m_key_offset = 0;
706,956✔
1025
        state.m_current_leaf.init(node->get_mem());
706,956✔
1026
        state.m_current_leaf.set_offset(state.m_key_offset);
706,956✔
1027
        state.m_current_index = node->lower_bound_key(key);
706,956✔
1028
        return state.m_current_index < state.m_current_leaf.node_size();
706,956✔
1029
    }
706,956✔
1030
    else {
1,485,945✔
1031
        ClusterNodeInner* node = static_cast<ClusterNodeInner*>(m_root.get());
1,485,945✔
1032
        return node->get_leaf(key, state);
1,485,945✔
1033
    }
1,485,945✔
1034
}
2,192,901✔
1035

1036
bool ClusterTree::traverse(TraverseFunction func) const
1037
{
5,769,753✔
1038
    if (m_root->is_leaf()) {
5,769,753✔
1039
        return func(static_cast<Cluster*>(m_root.get())) == IteratorControl::Stop;
4,227,612✔
1040
    }
4,227,612✔
1041
    else {
1,542,141✔
1042
        return static_cast<ClusterNodeInner*>(m_root.get())->traverse(func, 0);
1,542,141✔
1043
    }
1,542,141✔
1044
}
5,769,753✔
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,214,750✔
1058
    // Check for owner. This function may be called in context of DictionaryClusterTree
2,584,125✔
1059
    // in which case m_owner is null (and spec never needed).
2,584,125✔
1060
    if (m_owner) {
5,215,002✔
1061
        auto spec_ndx = m_owner->leaf_ndx2spec_ndx(col_ndx);
5,214,963✔
1062
        arr.set_spec(&m_owner->m_spec, spec_ndx);
5,214,963✔
1063
    }
5,214,963✔
1064
}
5,214,750✔
1065

1066
TableRef ClusterTree::get_table_ref() const
1067
{
196,866,909✔
1068
    REALM_ASSERT(m_owner != nullptr);
196,866,909✔
1069
    // as safe as storing the TableRef locally in the ClusterTree,
98,151,483✔
1070
    // because the cluster tree and the table is basically one object :-O
98,151,483✔
1071
    return m_owner->m_own_ref;
196,866,909✔
1072
}
196,866,909✔
1073

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

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

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

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

1101
void ClusterTree::remove_all_links(CascadeState& state)
1102
{
2,376✔
1103
    Allocator& alloc = get_alloc();
2,376✔
1104
    auto origin_table = get_owning_table();
2,376✔
1105
    // This function will add objects that should be deleted to 'state'
537✔
1106
    auto func = [&](const Cluster* cluster) {
2,754✔
1107
        auto remove_link_from_column = [&](ColKey col_key) {
52,932✔
1108
            // Prevent making changes to table that is going to be removed anyway
8,013✔
1109
            // Furthermore it is a prerequisite for using 'traverse' that the tree
8,013✔
1110
            // is not modified
8,013✔
1111
            if (origin_table->links_to_self(col_key)) {
52,932✔
1112
                return IteratorControl::AdvanceToNext;
258✔
1113
            }
258✔
1114
            auto col_type = col_key.get_type();
52,674✔
1115
            if (col_type == col_type_LinkList)
52,674✔
1116
                col_type = col_type_Link;
213✔
1117
            if (col_key.is_collection()) {
52,674✔
1118
                ArrayInteger values(alloc);
837✔
1119
                cluster->init_leaf(col_key, &values);
837✔
1120
                size_t sz = values.size();
837✔
1121

417✔
1122
                for (size_t i = 0; i < sz; i++) {
9,015✔
1123
                    if (ref_type ref = values.get_as_ref(i)) {
8,178✔
1124
                        ObjKey origin_key = cluster->get_real_key(i);
348✔
1125
                        if (col_key.is_list() || col_key.is_set()) {
348✔
1126
                            if (col_type == col_type_Link) {
288✔
1127
                                BPlusTree<ObjKey> links(alloc);
228✔
1128
                                links.init_from_ref(ref);
228✔
1129
                                if (links.size() > 0) {
228✔
1130
                                    cluster->do_remove_backlinks(origin_key, col_key, links.get_all(), state);
216✔
1131
                                }
216✔
1132
                            }
228✔
1133
                            else if (col_type == col_type_TypedLink) {
60✔
1134
                                BPlusTree<ObjLink> links(alloc);
×
1135
                                links.init_from_ref(ref);
×
1136
                                if (links.size() > 0) {
×
1137
                                    cluster->do_remove_backlinks(origin_key, col_key, links.get_all(), state);
×
1138
                                }
×
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
                        }
288✔
1155
                        else if (col_key.is_dictionary()) {
60✔
1156
                            std::vector<ObjLink> links;
60✔
1157

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

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

30✔
1172
                            if (links.size() > 0) {
60✔
1173
                                cluster->do_remove_backlinks(origin_key, col_key, links, state);
60✔
1174
                            }
60✔
1175
                        }
60✔
1176
                    }
348✔
1177
                }
8,178✔
1178
            }
837✔
1179
            else {
51,837✔
1180
                if (col_type == col_type_Link) {
51,837✔
1181
                    ArrayKey values(alloc);
174✔
1182
                    cluster->init_leaf(col_key, &values);
174✔
1183
                    size_t sz = values.size();
174✔
1184
                    for (size_t i = 0; i < sz; i++) {
774✔
1185
                        if (ObjKey key = values.get(i)) {
600✔
1186
                            cluster->do_remove_backlinks(cluster->get_real_key(i), col_key, std::vector<ObjKey>{key},
498✔
1187
                                                         state);
498✔
1188
                        }
498✔
1189
                    }
600✔
1190
                }
174✔
1191
                else if (col_type == col_type_TypedLink) {
51,663✔
1192
                    ArrayTypedLink values(alloc);
×
1193
                    cluster->init_leaf(col_key, &values);
×
1194
                    size_t sz = values.size();
×
1195
                    for (size_t i = 0; i < sz; i++) {
×
1196
                        if (ObjLink link = values.get(i)) {
×
1197
                            cluster->do_remove_backlinks(cluster->get_real_key(i), col_key,
×
1198
                                                         std::vector<ObjLink>{link}, state);
×
1199
                        }
×
1200
                    }
×
1201
                }
×
1202
                else if (col_type == col_type_Mixed) {
51,663✔
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) {
51,591✔
1215
                    ArrayBacklink values(alloc);
279✔
1216
                    cluster->init_leaf(col_key, &values);
279✔
1217
                    values.set_parent(const_cast<Cluster*>(cluster),
279✔
1218
                                      col_key.get_index().val + Cluster::s_first_col_index);
279✔
1219
                    size_t sz = values.size();
279✔
1220
                    for (size_t i = 0; i < sz; i++) {
2,625✔
1221
                        values.nullify_fwd_links(i, state);
2,346✔
1222
                    }
2,346✔
1223
                }
279✔
1224
            }
51,837✔
1225
            return IteratorControl::AdvanceToNext;
52,674✔
1226
        };
52,674✔
1227
        m_owner->for_each_and_every_column(remove_link_from_column);
2,754✔
1228
        return IteratorControl::AdvanceToNext;
2,754✔
1229
    };
2,754✔
1230

537✔
1231
    // Go through all clusters
537✔
1232
    traverse(func);
2,376✔
1233

537✔
1234
    const_cast<Table*>(get_owning_table())->remove_recursive(state);
2,376✔
1235
}
2,376✔
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
{
9,812,028✔
1247
    auto sz = t.size();
9,812,028✔
1248
    if (ndx >= sz) {
9,812,028✔
1249
        // end
4,691,289✔
1250
        m_position = sz;
9,234,996✔
1251
        m_leaf_invalid = true;
9,234,996✔
1252
    }
9,234,996✔
1253
    else if (ndx == 0) {
577,032✔
1254
        // begin
275,769✔
1255
        m_key = load_leaf(ObjKey(0));
533,325✔
1256
        m_leaf_start_pos = 0;
533,325✔
1257
    }
533,325✔
1258
    else {
43,707✔
1259
        auto s = const_cast<ClusterTree&>(m_tree).get(ndx, m_key);
43,707✔
1260
        m_state.init(s, m_key);
43,707✔
1261
        m_leaf_start_pos = ndx - m_state.m_current_index;
43,707✔
1262
    }
43,707✔
1263
}
9,812,028✔
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
{
82,308✔
1279
    auto ndx = m_tree.get_ndx(m_key);
82,308✔
1280
    if (ndx == realm::npos) {
82,308✔
1281
        throw StaleAccessor("Stale iterator");
×
1282
    }
×
1283
    return ndx;
82,308✔
1284
}
82,308✔
1285

1286
ObjKey ClusterTree::Iterator::load_leaf(ObjKey key) const
1287
{
2,192,826✔
1288
    m_storage_version = m_tree.get_storage_version(m_instance_version);
2,192,826✔
1289
    // 'key' may or may not exist. If it does not exist, state is updated
1,106,514✔
1290
    // to point to the next object in line.
1,106,514✔
1291
    if (m_tree.get_leaf(key, m_state)) {
2,192,826✔
1292
        m_leaf_start_pos = m_position - m_state.m_current_index;
2,030,970✔
1293
        // Get the actual key value
1,024,575✔
1294
        return m_leaf.get_real_key(m_state.m_current_index);
2,030,970✔
1295
    }
2,030,970✔
1296
    else {
161,856✔
1297
        // end of table
81,939✔
1298
        return null_key;
161,856✔
1299
    }
161,856✔
1300
}
2,192,826✔
1301

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

21,738✔
1309
    m_position = abs_pos;
43,455✔
1310

21,738✔
1311
    // If the position is within the current leaf then just set the iterator to that position
21,738✔
1312
    if (!m_leaf_invalid && m_storage_version == m_tree.get_storage_version(m_instance_version)) {
43,455✔
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,485✔
1315
            m_key = m_leaf.get_real_key(m_state.m_current_index);
4,485✔
1316
            return;
4,485✔
1317
        }
4,485✔
1318
    }
38,970✔
1319

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

1327
bool ClusterTree::Iterator::update() const
1328
{
19,382,703✔
1329
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
19,384,833✔
1330
        ObjKey k = load_leaf(m_key);
77,406✔
1331
        m_leaf_invalid = !k || (k != m_key);
77,406✔
1332
        if (m_leaf_invalid) {
77,406✔
1333
            throw StaleAccessor("Stale iterator");
30✔
1334
        }
30✔
1335
        return true;
77,376✔
1336
    }
77,376✔
1337

9,773,664✔
1338
    REALM_ASSERT(m_leaf.is_attached());
19,305,297✔
1339
    return false;
19,305,297✔
1340
}
19,305,297✔
1341

1342
ClusterTree::Iterator& ClusterTree::Iterator::operator++()
1343
{
18,115,101✔
1344
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
18,160,761✔
1345
        ObjKey k = load_leaf(m_key);
1,378,524✔
1346
        if (k != m_key) {
1,378,524✔
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,054,207✔
1353

9,069,114✔
1354
    m_state.m_current_index++;
18,054,207✔
1355
    m_position++;
18,054,207✔
1356
    if (m_state.m_current_index == m_leaf.node_size()) {
18,054,207✔
1357
        m_key = load_leaf(ObjKey(m_key.value + 1));
203,643✔
1358
        m_leaf_invalid = !m_key;
203,643✔
1359
    }
203,643✔
1360
    else {
17,850,564✔
1361
        m_key = m_leaf.get_real_key(m_state.m_current_index);
17,850,564✔
1362
    }
17,850,564✔
1363
    return *this;
18,054,207✔
1364
}
18,054,207✔
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,384,911✔
1410
    if (update() || m_key != m_obj.get_key()) {
19,384,911✔
1411
        new (&m_obj) Obj(m_tree.get_table_ref(), m_leaf.get_mem(), m_key, m_state.m_current_index);
15,954,915✔
1412
    }
15,954,915✔
1413

9,815,322✔
1414
    return &m_obj;
19,384,911✔
1415
}
19,384,911✔
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