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

realm / realm-core / jorgen.edelbo_333

01 Jul 2024 07:21AM UTC coverage: 90.865% (-0.08%) from 90.948%
jorgen.edelbo_333

Pull #7826

Evergreen

jedelbo
Merge tag 'v14.10.2' into next-major
Pull Request #7826: Merge Next major

102912 of 181138 branches covered (56.81%)

3131 of 3738 new or added lines in 54 files covered. (83.76%)

80 existing lines in 14 files now uncovered.

217498 of 239364 relevant lines covered (90.86%)

6655796.15 hits per line

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

86.76
/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
    {
47,662,821✔
66
        return false;
47,662,821✔
67
    }
47,662,821✔
68

69
    int get_sub_tree_depth() const override
70
    {
66✔
71
        return m_sub_tree_depth;
66✔
72
    }
66✔
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
    {
80,563,536✔
79
        return Array::size() - s_first_node_index;
80,563,536✔
80
    }
80,563,536✔
81
    size_t get_tree_size() const override
82
    {
44,399,187✔
83
        return size_t(Array::get(s_sub_tree_size)) >> 1;
44,399,187✔
84
    }
44,399,187✔
85
    void set_tree_size(size_t sub_tree_size)
86
    {
41,620,671✔
87
        Array::set(s_sub_tree_size, sub_tree_size << 1 | 1);
41,620,671✔
88
    }
41,620,671✔
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
    virtual ref_type typed_write(ref_type ref, _impl::ArrayWriterBase& out) const override
124
    {
104,286✔
125
        REALM_ASSERT_DEBUG(ref == get_mem().get_ref());
104,286✔
126
        if (out.only_modified && m_alloc.is_read_only(ref)) {
104,286✔
NEW
127
            return ref;
×
NEW
128
        }
×
129
        REALM_ASSERT_DEBUG(get_is_inner_bptree_node_from_header(get_header()));
104,286✔
130
        REALM_ASSERT_DEBUG(!get_context_flag_from_header(get_header()));
104,286✔
131
        REALM_ASSERT_DEBUG(has_refs());
104,286✔
132
        TempArray written_node(size(), type_InnerBptreeNode);
104,286✔
133
        for (unsigned j = 0; j < size(); ++j) {
2,640,165✔
134
            RefOrTagged rot = get_as_ref_or_tagged(j);
2,535,879✔
135
            if (rot.is_ref() && rot.get_as_ref()) {
2,535,879✔
136
                if (out.only_modified && m_alloc.is_read_only(rot.get_as_ref())) {
2,303,901✔
137
                    written_node.set(j, rot);
1,924,113✔
138
                    continue;
1,924,113✔
139
                }
1,924,113✔
140
                if (j == 0) {
379,788✔
141
                    // keys (ArrayUnsigned, me thinks)
142
                    Array array_unsigned(m_alloc);
1,026✔
143
                    array_unsigned.init_from_ref(rot.get_as_ref());
1,026✔
144
                    written_node.set_as_ref(j, array_unsigned.write(out, false, out.only_modified, false));
1,026✔
145
                }
1,026✔
146
                else {
378,762✔
147
                    auto header = m_alloc.translate(rot.get_as_ref());
378,762✔
148
                    MemRef m(header, rot.get_as_ref(), m_alloc);
378,762✔
149
                    if (get_is_inner_bptree_node_from_header(header)) {
378,762✔
150
                        ClusterNodeInner inner_node(m_alloc, m_tree_top);
7,467✔
151
                        inner_node.init(m);
7,467✔
152
                        written_node.set_as_ref(j, inner_node.typed_write(rot.get_as_ref(), out));
7,467✔
153
                    }
7,467✔
154
                    else {
371,295✔
155
                        Cluster cluster(j, m_alloc, m_tree_top);
371,295✔
156
                        cluster.init(m);
371,295✔
157
                        written_node.set_as_ref(j, cluster.typed_write(rot.get_as_ref(), out));
371,295✔
158
                    }
371,295✔
159
                }
378,762✔
160
            }
379,788✔
161
            else { // not a ref, just copy value over
231,978✔
162
                written_node.set(j, rot);
231,978✔
163
            }
231,978✔
164
        }
2,535,879✔
165
        return written_node.write(out);
104,286✔
166
    }
104,286✔
167

168
    virtual void typed_print(std::string prefix) const override
NEW
169
    {
×
NEW
170
        REALM_ASSERT(get_is_inner_bptree_node_from_header(get_header()));
×
NEW
171
        REALM_ASSERT(has_refs());
×
NEW
172
        std::cout << "ClusterNodeInner " << header_to_string(get_header()) << std::endl;
×
NEW
173
        for (unsigned j = 0; j < size(); ++j) {
×
NEW
174
            RefOrTagged rot = get_as_ref_or_tagged(j);
×
NEW
175
            auto pref = prefix + "  " + std::to_string(j) + ":\t";
×
NEW
176
            if (rot.is_ref() && rot.get_as_ref()) {
×
NEW
177
                if (j == 0) {
×
NEW
178
                    std::cout << pref << "Keys as ArrayUnsigned as ";
×
NEW
179
                    Array a(m_alloc);
×
NEW
180
                    a.init_from_ref(rot.get_as_ref());
×
NEW
181
                    a.typed_print(pref);
×
NEW
182
                }
×
NEW
183
                else {
×
NEW
184
                    auto header = m_alloc.translate(rot.get_as_ref());
×
NEW
185
                    MemRef m(header, rot.get_as_ref(), m_alloc);
×
NEW
186
                    if (get_is_inner_bptree_node_from_header(header)) {
×
NEW
187
                        ClusterNodeInner a(m_alloc, m_tree_top);
×
NEW
188
                        a.init(m);
×
NEW
189
                        std::cout << pref;
×
NEW
190
                        a.typed_print(pref);
×
NEW
191
                    }
×
NEW
192
                    else {
×
NEW
193
                        Cluster a(j, m_alloc, m_tree_top);
×
NEW
194
                        a.init(m);
×
NEW
195
                        std::cout << pref;
×
NEW
196
                        a.typed_print(pref);
×
NEW
197
                    }
×
NEW
198
                }
×
NEW
199
            }
×
200
            // just ignore entries, which are not refs.
NEW
201
        }
×
NEW
202
        Array::typed_print(prefix);
×
NEW
203
    }
×
204

205
private:
206
    static constexpr size_t s_key_ref_index = 0;
207
    static constexpr size_t s_sub_tree_depth_index = 1;
208
    static constexpr size_t s_sub_tree_size = 2;
209
    static constexpr size_t s_first_node_index = 3;
210

211
    int m_sub_tree_depth = 0;
212
    int m_shift_factor = 0;
213

214
    struct ChildInfo {
215
        size_t ndx;
216
        uint64_t offset;
217
        ObjKey key;
218
        MemRef mem;
219
    };
220
    bool find_child(ObjKey key, ChildInfo& ret) const noexcept
221
    {
389,211,933✔
222
        if (m_keys.is_attached()) {
389,211,933✔
223
            auto upper = m_keys.upper_bound(uint64_t(key.value));
336,078,537✔
224
            // The first entry in m_keys will always be smaller than or equal
225
            // to all keys in this subtree. If you get zero returned here, the
226
            // key is not in the tree
227
            if (upper == 0) {
336,078,537✔
228
                return false;
×
229
            }
×
230
            ret.ndx = upper - 1;
336,078,537✔
231
            ret.offset = m_keys.get(ret.ndx);
336,078,537✔
232
        }
336,078,537✔
233
        else {
53,133,396✔
234
            size_t sz = node_size();
53,133,396✔
235
            REALM_ASSERT_DEBUG(sz > 0);
53,133,396✔
236
            size_t max_ndx = sz - 1;
53,133,396✔
237
            ret.ndx = std::min(size_t(key.value) >> m_shift_factor, max_ndx);
53,133,396✔
238
            ret.offset = ret.ndx << m_shift_factor;
53,133,396✔
239
        }
53,133,396✔
240
        ret.key = ObjKey(key.value - ret.offset);
389,211,933✔
241
        ref_type child_ref = _get_child_ref(ret.ndx);
389,211,933✔
242
        char* child_header = m_alloc.translate(child_ref);
389,211,933✔
243
        ret.mem = MemRef(child_header, child_ref, m_alloc);
389,211,933✔
244
        return true;
389,211,933✔
245
    }
389,211,933✔
246

247
    ref_type _get_child_ref(size_t ndx) const noexcept
248
    {
465,848,562✔
249
        return Array::get_as_ref(ndx + s_first_node_index);
465,848,562✔
250
    }
465,848,562✔
251
    void _insert_child_ref(size_t ndx, ref_type ref)
252
    {
92,202✔
253
        Array::insert(ndx + s_first_node_index, from_ref(ref));
92,202✔
254
    }
92,202✔
255
    void _erase_child_ref(size_t ndx)
256
    {
20,172✔
257
        Array::erase(ndx + s_first_node_index);
20,172✔
258
    }
20,172✔
259
    void move(size_t ndx, ClusterNode* new_node, int64_t key_adj) override;
260

261
    template <class T, class F>
262
    T recurse(ObjKey key, F func);
263

264
    template <class T, class F>
265
    T recurse(ChildInfo& child_info, F func);
266
    // Adjust key offset values by adding offset
267
    void adjust_keys(int64_t offset);
268
    // Make sure the first key offset value is 0
269
    // This is done by adjusting the child node by current first offset
270
    // and setting it to 0 thereafter
271
    void adjust_keys_first_child(int64_t adj);
272
};
273

274
/***************************** ClusterNodeInner ******************************/
275

276
ClusterNodeInner::ClusterNodeInner(Allocator& allocator, const ClusterTree& tree_top)
277
    : ClusterNode(0, allocator, tree_top)
48,737,514✔
278
{
97,570,893✔
279
}
97,570,893✔
280

281
ClusterNodeInner::~ClusterNodeInner() {}
97,562,841✔
282

283
void ClusterNodeInner::create(int sub_tree_depth)
284
{
3,390✔
285
    Array::create(Array::type_InnerBptreeNode, false, s_first_node_index);
3,390✔
286

287
    Array::set(s_key_ref_index, 0);
3,390✔
288

289
    Array::set(s_sub_tree_depth_index, RefOrTagged::make_tagged(sub_tree_depth));
3,390✔
290
    Array::set(s_sub_tree_size, 1); // sub_tree_size = 0 (as tagged value)
3,390✔
291
    m_sub_tree_depth = sub_tree_depth;
3,390✔
292
    m_shift_factor = m_sub_tree_depth * node_shift_factor;
3,390✔
293
}
3,390✔
294

295
void ClusterNodeInner::init(MemRef mem)
296
{
95,552,913✔
297
    Array::init_from_mem(mem);
95,552,913✔
298
    m_keys.set_parent(this, s_key_ref_index);
95,552,913✔
299
    ref_type ref = Array::get_as_ref(s_key_ref_index);
95,552,913✔
300
    if (ref) {
95,552,913✔
301
        m_keys.init_from_ref(ref);
81,482,538✔
302
    }
81,482,538✔
303
    else {
14,070,375✔
304
        m_keys.detach();
14,070,375✔
305
    }
14,070,375✔
306
    m_sub_tree_depth = int(Array::get(s_sub_tree_depth_index)) >> 1;
95,552,913✔
307
    m_shift_factor = m_sub_tree_depth * node_shift_factor;
95,552,913✔
308
}
95,552,913✔
309

310
void ClusterNodeInner::update_from_parent() noexcept
311
{
86,232✔
312
    Array::update_from_parent();
86,232✔
313
    ref_type ref = Array::get_as_ref(s_key_ref_index);
86,232✔
314
    if (ref) {
86,232✔
315
        m_keys.update_from_parent();
73,812✔
316
    }
73,812✔
317
    m_sub_tree_depth = int(Array::get(s_sub_tree_depth_index)) >> 1;
86,232✔
318
}
86,232✔
319

320
template <class T, class F>
321
T ClusterNodeInner::recurse(ObjKey key, F func)
322
{
49,867,416✔
323
    ChildInfo child_info;
49,867,416✔
324
    if (!find_child(key, child_info)) {
49,867,416✔
325
        throw KeyNotFound("Child not found in recurse");
×
326
    }
×
327
    return recurse<T>(child_info, func);
49,867,416✔
328
}
49,867,416✔
329

330
template <class T, class F>
331
T ClusterNodeInner::recurse(ChildInfo& child_info, F func)
332
{
387,064,629✔
333
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_info.mem.get_addr());
387,064,629✔
334
    if (child_is_leaf) {
387,064,629✔
335
        Cluster leaf(child_info.offset + m_offset, m_alloc, m_tree_top);
297,990,816✔
336
        leaf.set_parent(this, child_info.ndx + s_first_node_index);
297,990,816✔
337
        leaf.init(child_info.mem);
297,990,816✔
338
        return func(&leaf, child_info);
297,990,816✔
339
    }
297,990,816✔
340
    else {
89,073,813✔
341
        ClusterNodeInner node(m_alloc, m_tree_top);
89,073,813✔
342
        node.set_parent(this, child_info.ndx + s_first_node_index);
89,073,813✔
343
        node.init(child_info.mem);
89,073,813✔
344
        node.set_offset(child_info.offset + m_offset);
89,073,813✔
345
        return func(&node, child_info);
89,073,813✔
346
    }
89,073,813✔
347
}
387,064,629✔
348

349
MemRef ClusterNodeInner::ensure_writeable(ObjKey key)
350
{
×
351
    return recurse<MemRef>(key, [](ClusterNode* node, ChildInfo& child_info) {
×
352
        return node->ensure_writeable(child_info.key);
×
353
    });
×
354
}
×
355

356
void ClusterNodeInner::update_ref_in_parent(ObjKey key, ref_type ref)
357
{
486,969✔
358
    ChildInfo child_info;
486,969✔
359
    if (!find_child(key, child_info)) {
486,969✔
360
        throw KeyNotFound("Child not found in update_ref_in_parent");
×
361
    }
×
362
    if (this->m_sub_tree_depth == 1) {
486,969✔
363
        set(child_info.ndx + s_first_node_index, ref);
269,976✔
364
    }
269,976✔
365
    else {
216,993✔
366
        ClusterNodeInner node(m_alloc, m_tree_top);
216,993✔
367
        node.set_parent(this, child_info.ndx + s_first_node_index);
216,993✔
368
        node.init(child_info.mem);
216,993✔
369
        node.set_offset(child_info.offset + m_offset);
216,993✔
370
        node.update_ref_in_parent(child_info.key, ref);
216,993✔
371
    }
216,993✔
372
}
486,969✔
373

374
ref_type ClusterNodeInner::insert(ObjKey key, const FieldValues& init_values, ClusterNode::State& state)
375
{
33,574,401✔
376
    return recurse<ref_type>(key, [this, &state, &init_values](ClusterNode* node, ChildInfo& child_info) {
33,574,401✔
377
        ref_type new_sibling_ref = node->insert(child_info.key, init_values, state);
33,484,812✔
378

379
        set_tree_size(get_tree_size() + 1);
33,484,812✔
380

381
        if (!new_sibling_ref) {
33,484,812✔
382
            return ref_type(0);
33,398,685✔
383
        }
33,398,685✔
384

385
        size_t new_ref_ndx = child_info.ndx + 1;
86,127✔
386

387
        int64_t split_key_value = state.split_key + child_info.offset;
86,127✔
388
        uint64_t sz = node_size();
86,127✔
389
        if (sz < cluster_node_size) {
97,398✔
390
            if (m_keys.is_attached()) {
92,205✔
391
                m_keys.insert(new_ref_ndx, split_key_value);
83,538✔
392
            }
83,538✔
393
            else {
8,667✔
394
                if (uint64_t(split_key_value) != sz << m_shift_factor) {
8,667✔
395
                    ensure_general_form();
288✔
396
                    m_keys.insert(new_ref_ndx, split_key_value);
288✔
397
                }
288✔
398
            }
8,667✔
399
            _insert_child_ref(new_ref_ndx, new_sibling_ref);
92,205✔
400
            return ref_type(0);
92,205✔
401
        }
92,205✔
402

403
        ClusterNodeInner child(m_alloc, m_tree_top);
2,147,488,840✔
404
        child.create(m_sub_tree_depth);
2,147,488,840✔
405
        if (new_ref_ndx == sz) {
2,147,488,840✔
406
            child.add(new_sibling_ref);
180✔
407
            state.split_key = split_key_value;
180✔
408
        }
180✔
409
        else {
2,147,488,750✔
410
            int64_t first_key_value = m_keys.get(new_ref_ndx);
2,147,488,750✔
411
            child.ensure_general_form();
2,147,488,750✔
412
            move(new_ref_ndx, &child, first_key_value);
2,147,488,750✔
413
            add(new_sibling_ref, split_key_value); // Throws
2,147,488,750✔
414
            state.split_key = first_key_value;
2,147,488,750✔
415
        }
2,147,488,750✔
416

417
        // Some objects has been moved out of this tree - find out how many
418
        size_t child_sub_tree_size = child.update_sub_tree_size();
2,147,488,840✔
419
        set_tree_size(get_tree_size() - child_sub_tree_size);
2,147,488,840✔
420

421
        return child.get_ref();
2,147,488,840✔
422
    });
86,127✔
423
}
33,574,401✔
424

425
bool ClusterNodeInner::try_get(ObjKey key, ClusterNode::State& state) const noexcept
426
{
339,299,703✔
427
    ChildInfo child_info;
339,299,703✔
428
    if (!find_child(key, child_info)) {
339,299,703✔
429
        return false;
×
430
    }
×
431
    return const_cast<ClusterNodeInner*>(this)->recurse<bool>(child_info,
339,299,703✔
432
                                                              [&state](const ClusterNode* node, ChildInfo& info) {
339,299,703✔
433
                                                                  return node->try_get(info.key, state);
336,166,629✔
434
                                                              });
336,166,629✔
435
}
339,299,703✔
436

437
ObjKey ClusterNodeInner::get(size_t ndx, ClusterNode::State& state) const
438
{
11,780,175✔
439
    size_t sz = node_size();
11,780,175✔
440
    size_t child_ndx = 0;
11,780,175✔
441
    while (child_ndx < sz) {
72,580,728✔
442
        int64_t key_offset = m_keys.is_attached() ? m_keys.get(child_ndx) : (child_ndx << m_shift_factor);
72,561,366✔
443

444
        ref_type child_ref = _get_child_ref(child_ndx);
72,561,366✔
445
        char* child_header = m_alloc.translate(child_ref);
72,561,366✔
446
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
72,561,366✔
447
        size_t sub_tree_size;
72,561,366✔
448
        if (child_is_leaf) {
72,561,366✔
449
            sub_tree_size = Cluster::node_size_from_header(m_alloc, child_header);
69,782,193✔
450
            if (ndx < sub_tree_size) {
69,782,193✔
451
                Cluster leaf(key_offset + m_offset, m_alloc, m_tree_top);
9,171,075✔
452
                leaf.init(MemRef(child_header, child_ref, m_alloc));
9,171,075✔
453
                REALM_ASSERT(sub_tree_size == leaf.get_tree_size());
9,171,075✔
454
                return leaf.get(ndx, state);
9,171,075✔
455
            }
9,171,075✔
456
        }
69,782,193✔
457
        else {
2,779,173✔
458
            ClusterNodeInner node(m_alloc, m_tree_top);
2,779,173✔
459
            node.init(MemRef(child_header, child_ref, m_alloc));
2,779,173✔
460
            node.set_offset(key_offset + m_offset);
2,779,173✔
461
            sub_tree_size = node.get_tree_size();
2,779,173✔
462
            if (ndx < sub_tree_size) {
2,785,056✔
463
                return node.get(ndx, state);
2,590,608✔
464
            }
2,590,608✔
465
        }
2,779,173✔
466
        child_ndx++;
60,799,683✔
467
        ndx -= sub_tree_size;
60,799,683✔
468
    }
60,799,683✔
469
    return {};
2,147,503,009✔
470
}
11,780,175✔
471

472
size_t ClusterNodeInner::get_ndx(ObjKey key, size_t ndx) const noexcept
473
{
24,723✔
474
    ChildInfo child_info;
24,723✔
475
    if (!find_child(key, child_info)) {
24,723✔
476
        return realm::npos;
×
477
    }
×
478

479
    // First figure out how many objects there are in nodes before actual one
480
    // then descent in tree
481

482
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_info.mem.get_addr());
24,723✔
483
    if (child_is_leaf) {
24,723✔
484
        for (unsigned i = 0; i < child_info.ndx; i++) {
51,075✔
485
            ref_type ref = _get_child_ref(i);
26,352✔
486
            char* header = m_alloc.translate(ref);
26,352✔
487
            ndx += Cluster::node_size_from_header(m_alloc, header);
26,352✔
488
        }
26,352✔
489
        Cluster leaf(child_info.offset + m_offset, m_alloc, m_tree_top);
24,723✔
490
        leaf.init(child_info.mem);
24,723✔
491
        return leaf.get_ndx(child_info.key, ndx);
24,723✔
492
    }
24,723✔
493
    else {
×
494
        for (unsigned i = 0; i < child_info.ndx; i++) {
×
495
            char* header = m_alloc.translate(_get_child_ref(i));
×
496
            ndx += size_t(Array::get(header, s_sub_tree_size)) >> 1;
×
497
        }
×
498
        ClusterNodeInner node(m_alloc, m_tree_top);
×
499
        node.init(child_info.mem);
×
500
        node.set_offset(child_info.offset + m_offset);
×
501
        return node.get_ndx(child_info.key, ndx);
×
502
    }
×
503
}
24,723✔
504

505
void ClusterNodeInner::adjust_keys(int64_t adj)
506
{
30✔
507
    ensure_general_form();
30✔
508
    REALM_ASSERT(m_keys.get(0) == 0);
30✔
509
    m_keys.adjust(0, m_keys.size(), adj);
30✔
510

511
    // Now the first key offset value is 'adj' - it must be 0
512
    adjust_keys_first_child(adj);
30✔
513
}
30✔
514

515
void ClusterNodeInner::adjust_keys_first_child(int64_t adj)
516
{
12,060✔
517
    ref_type child_ref = _get_child_ref(0);
12,060✔
518
    char* child_header = m_alloc.translate(child_ref);
12,060✔
519
    auto mem = MemRef(child_header, child_ref, m_alloc);
12,060✔
520
    if (Array::get_is_inner_bptree_node_from_header(child_header)) {
12,060✔
521
        ClusterNodeInner node(m_alloc, m_tree_top);
30✔
522
        node.set_parent(this, s_first_node_index);
30✔
523
        node.init(mem);
30✔
524
        node.adjust_keys(adj);
30✔
525
    }
30✔
526
    else {
12,030✔
527
        Cluster node(0, m_alloc, m_tree_top);
12,030✔
528
        node.set_parent(this, s_first_node_index);
12,030✔
529
        node.init(mem);
12,030✔
530
        node.adjust_keys(adj);
12,030✔
531
    }
12,030✔
532
    m_keys.set(0, 0);
12,060✔
533
}
12,060✔
534

535
size_t ClusterNodeInner::erase(ObjKey key, CascadeState& state)
536
{
8,171,835✔
537
    return recurse<size_t>(key, [this, &state](ClusterNode* erase_node, ChildInfo& child_info) {
8,171,835✔
538
        size_t erase_node_size = erase_node->erase(child_info.key, state);
8,171,820✔
539
        bool is_leaf = erase_node->is_leaf();
8,171,820✔
540
        set_tree_size(get_tree_size() - 1);
8,171,820✔
541

542
        if (erase_node_size == 0) {
8,171,820✔
543
            erase_node->destroy_deep();
13,344✔
544

545
            ensure_general_form();
13,344✔
546
            _erase_child_ref(child_info.ndx);
13,344✔
547
            m_keys.erase(child_info.ndx);
13,344✔
548
            if (child_info.ndx == 0 && m_keys.size() > 0) {
13,344✔
549
                auto first_offset = m_keys.get(0);
12,030✔
550
                // Adjust all key values in new first node
551
                // We have to make sure that the first key offset value
552
                // in all inner nodes is 0
553
                adjust_keys_first_child(first_offset);
12,030✔
554
            }
12,030✔
555
        }
13,344✔
556
        else if (erase_node_size < cluster_node_size / 2 && child_info.ndx < (node_size() - 1)) {
8,158,476✔
557
            // Candidate for merge. First calculate if the combined size of current and
558
            // next sibling is small enough.
559
            size_t sibling_ndx = child_info.ndx + 1;
3,252,732✔
560
            Cluster l2(child_info.offset, m_alloc, m_tree_top);
3,252,732✔
561
            ClusterNodeInner n2(m_alloc, m_tree_top);
3,252,732✔
562
            ClusterNode* sibling_node = is_leaf ? static_cast<ClusterNode*>(&l2) : static_cast<ClusterNode*>(&n2);
3,252,732✔
563
            sibling_node->set_parent(this, sibling_ndx + s_first_node_index);
3,252,732✔
564
            sibling_node->init_from_parent();
3,252,732✔
565

566
            size_t combined_size = sibling_node->node_size() + erase_node_size;
3,252,732✔
567

568
            if (combined_size < cluster_node_size * 3 / 4) {
3,252,732✔
569
                // Calculate value that must be subtracted from the moved keys
570
                // (will be negative as the sibling has bigger keys)
571
                int64_t key_adj = m_keys.is_attached() ? (m_keys.get(child_info.ndx) - m_keys.get(sibling_ndx))
6,828✔
572
                                                       : 0 - (1 << m_shift_factor);
6,828✔
573
                // And then move all elements into current node
574
                sibling_node->ensure_general_form();
6,828✔
575
                erase_node->ensure_general_form();
6,828✔
576
                sibling_node->move(0, erase_node, key_adj);
6,828✔
577

578
                if (!erase_node->is_leaf()) {
6,828✔
579
                    static_cast<ClusterNodeInner*>(erase_node)->update_sub_tree_size();
45✔
580
                }
45✔
581

582
                // Destroy sibling
583
                sibling_node->destroy_deep();
6,828✔
584

585
                ensure_general_form();
6,828✔
586
                _erase_child_ref(sibling_ndx);
6,828✔
587
                m_keys.erase(sibling_ndx);
6,828✔
588
            }
6,828✔
589
        }
3,252,732✔
590

591
        return node_size();
8,171,820✔
592
    });
8,171,820✔
593
}
8,171,835✔
594

595
void ClusterNodeInner::nullify_incoming_links(ObjKey key, CascadeState& state)
596
{
8,101,377✔
597
    recurse<void>(key, [&state](ClusterNode* node, ChildInfo& child_info) {
8,101,377✔
598
        node->nullify_incoming_links(child_info.key, state);
8,101,356✔
599
    });
8,101,356✔
600
}
8,101,377✔
601

602
void ClusterNodeInner::ensure_general_form()
603
{
23,409✔
604
    if (!m_keys.is_attached()) {
23,409✔
605
        size_t current_size = node_size();
3,183✔
606
        m_keys.create(current_size, (current_size - 1) << m_shift_factor);
3,183✔
607
        m_keys.update_parent();
3,183✔
608
        for (size_t i = 0; i < current_size; i++) {
9,624✔
609
            m_keys.set(i, i << m_shift_factor);
6,441✔
610
        }
6,441✔
611
    }
3,183✔
612
}
23,409✔
613

614
void ClusterNodeInner::insert_column(ColKey col)
615
{
162✔
616
    size_t sz = node_size();
162✔
617
    for (size_t i = 0; i < sz; i++) {
24,252✔
618
        std::shared_ptr<ClusterNode> node = m_tree_top.get_node(this, i + s_first_node_index);
24,090✔
619
        node->insert_column(col);
24,090✔
620
    }
24,090✔
621
}
162✔
622

623
void ClusterNodeInner::remove_column(ColKey col)
624
{
6✔
625
    size_t sz = node_size();
6✔
626
    for (size_t i = 0; i < sz; i++) {
18✔
627
        std::shared_ptr<ClusterNode> node = m_tree_top.get_node(this, i + s_first_node_index);
12✔
628
        node->remove_column(col);
12✔
629
    }
12✔
630
}
6✔
631

632
size_t ClusterNodeInner::nb_columns() const
633
{
×
634
    ref_type ref = _get_child_ref(0);
×
635
    char* header = m_alloc.translate(ref);
×
636
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
×
637
    MemRef mem(header, ref, m_alloc);
×
638
    if (child_is_leaf) {
×
639
        Cluster leaf(0, m_alloc, m_tree_top);
×
640
        leaf.init(mem);
×
641
        return leaf.nb_columns();
×
642
    }
×
643
    else {
×
644
        ClusterNodeInner node(m_alloc, m_tree_top);
×
645
        node.init(mem);
×
646
        return node.nb_columns();
×
647
    }
×
648
}
×
649

650
void ClusterNodeInner::add(ref_type ref, int64_t key_value)
651
{
6,567✔
652
    if (m_keys.is_attached()) {
6,567✔
653
        m_keys.add(key_value);
33✔
654
    }
33✔
655
    else {
6,534✔
656
        if (uint64_t(key_value) != (uint64_t(node_size()) << m_shift_factor)) {
6,534✔
657
            ensure_general_form();
2,796✔
658
            m_keys.add(key_value);
2,796✔
659
        }
2,796✔
660
    }
6,534✔
661
    Array::add(from_ref(ref));
6,567✔
662
}
6,567✔
663

664
// Find leaf that contains the object identified by key. If this does not exist return the
665
// leaf that contains the next object
666
bool ClusterNodeInner::get_leaf(ObjKey key, ClusterNode::IteratorState& state) const noexcept
667
{
2,701,590✔
668
    size_t child_ndx;
2,701,590✔
669
    if (m_keys.is_attached()) {
2,701,590✔
670
        child_ndx = m_keys.upper_bound(uint64_t(key.value));
2,664,345✔
671
        if (child_ndx > 0)
2,664,345✔
672
            child_ndx--;
2,664,447✔
673
    }
2,664,345✔
674
    else {
37,245✔
675
        REALM_ASSERT_DEBUG(node_size() > 0);
37,245✔
676
        size_t max_ndx = node_size() - 1;
37,245✔
677
        if (key.value < 0)
37,245✔
678
            child_ndx = 0;
×
679
        else
37,245✔
680
            child_ndx = std::min(size_t(key.value) >> m_shift_factor, max_ndx);
37,245✔
681
    }
37,245✔
682

683
    size_t sz = node_size();
2,701,590✔
684
    while (child_ndx < sz) {
2,761,665✔
685
        uint64_t key_offset = m_keys.is_attached() ? m_keys.get(child_ndx) : (child_ndx << m_shift_factor);
2,753,853✔
686
        ObjKey new_key(key_offset < uint64_t(key.value) ? key.value - key_offset : 0);
2,753,853✔
687
        state.m_key_offset += key_offset;
2,753,853✔
688

689
        ref_type child_ref = _get_child_ref(child_ndx);
2,753,853✔
690
        char* child_header = m_alloc.translate(child_ref);
2,753,853✔
691
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
2,753,853✔
692
        if (child_is_leaf) {
2,753,853✔
693
            state.m_current_leaf.init(MemRef(child_header, child_ref, m_alloc));
1,549,083✔
694
            state.m_current_leaf.set_offset(state.m_key_offset);
1,549,083✔
695
            state.m_current_index = state.m_current_leaf.lower_bound_key(new_key);
1,549,083✔
696
            if (state.m_current_index < state.m_current_leaf.node_size())
1,549,083✔
697
                return true;
1,489,095✔
698
        }
1,549,083✔
699
        else {
1,204,770✔
700
            ClusterNodeInner node(m_alloc, m_tree_top);
1,204,770✔
701
            node.init(MemRef(child_header, child_ref, m_alloc));
1,204,770✔
702
            if (node.get_leaf(new_key, state))
1,204,770✔
703
                return true;
1,204,683✔
704
        }
1,204,770✔
705
        state.m_key_offset -= key_offset;
60,075✔
706
        child_ndx++;
60,075✔
707
    }
60,075✔
708
    return false;
7,812✔
709
}
2,701,590✔
710

711
// LCOV_EXCL_START
712
void ClusterNodeInner::dump_objects(int64_t key_offset, std::string lead) const
713
{
×
714
    std::cout << lead << "node" << std::endl;
×
715
    if (!m_keys.is_attached()) {
×
716
        std::cout << lead << "compact form" << std::endl;
×
717
    }
×
718
    size_t sz = node_size();
×
719
    for (unsigned i = 0; i < sz; i++) {
×
720
        int64_t key_value;
×
721
        if (m_keys.is_attached()) {
×
722
            key_value = m_keys.get(i) + key_offset;
×
723
        }
×
724
        else {
×
725
            key_value = (i << m_shift_factor) + key_offset;
×
726
        }
×
727
        std::cout << lead << std::hex << "split: " << key_value << std::dec << std::endl;
×
728
        m_tree_top.get_node(const_cast<ClusterNodeInner*>(this), i + s_first_node_index)
×
729
            ->dump_objects(key_value, lead + "   ");
×
730
    }
×
731
}
×
732
// LCOV_EXCL_STOP
733
void ClusterNodeInner::move(size_t ndx, ClusterNode* new_node, int64_t key_adj)
734
{
78✔
735
    auto new_cluster_node_inner = static_cast<ClusterNodeInner*>(new_node);
78✔
736
    for (size_t i = ndx; i < node_size(); i++) {
6,918✔
737
        new_cluster_node_inner->Array::add(_get_child_ref(i));
6,840✔
738
    }
6,840✔
739
    for (size_t i = ndx; i < m_keys.size(); i++) {
6,918✔
740
        new_cluster_node_inner->m_keys.add(m_keys.get(i) - key_adj);
6,840✔
741
    }
6,840✔
742
    truncate(ndx + s_first_node_index);
78✔
743
    if (m_keys.is_attached()) {
78✔
744
        m_keys.truncate(ndx);
78✔
745
    }
78✔
746
}
78✔
747

748
size_t ClusterNodeInner::update_sub_tree_size()
749
{
3,435✔
750
    size_t sub_tree_size = 0;
3,435✔
751
    auto sz = node_size();
3,435✔
752

753
    for (unsigned i = 0; i < sz; i++) {
20,616✔
754
        ref_type ref = _get_child_ref(i);
17,181✔
755
        char* header = m_alloc.translate(ref);
17,181✔
756
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
17,181✔
757
        if (child_is_leaf) {
17,181✔
758
            sub_tree_size += Cluster::node_size_from_header(m_alloc, header);
17,049✔
759
        }
17,049✔
760
        else {
132✔
761
            sub_tree_size += size_t(Array::get(header, s_sub_tree_size)) >> 1;
132✔
762
        }
132✔
763
    }
17,181✔
764
    set_tree_size(sub_tree_size);
3,435✔
765
    return sub_tree_size;
3,435✔
766
}
3,435✔
767

768
bool ClusterNodeInner::traverse(ClusterTree::TraverseFunction func, int64_t key_offset) const
769
{
1,688,040✔
770
    auto sz = node_size();
1,688,040✔
771

772
    for (unsigned i = 0; i < sz; i++) {
8,576,355✔
773
        ref_type ref = _get_child_ref(i);
6,917,442✔
774
        char* header = m_alloc.translate(ref);
6,917,442✔
775
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
6,917,442✔
776
        MemRef mem(header, ref, m_alloc);
6,917,442✔
777
        int64_t offs = (m_keys.is_attached() ? m_keys.get(i) : i << m_shift_factor) + key_offset;
6,917,442✔
778
        if (child_is_leaf) {
6,917,442✔
779
            Cluster leaf(offs, m_alloc, m_tree_top);
5,931,687✔
780
            leaf.init(mem);
5,931,687✔
781
            if (func(&leaf) == IteratorControl::Stop) {
5,931,687✔
782
                return true;
29,127✔
783
            }
29,127✔
784
        }
5,931,687✔
785
        else {
985,755✔
786
            ClusterNodeInner node(m_alloc, m_tree_top);
985,755✔
787
            node.init(mem);
985,755✔
788
            if (node.traverse(func, offs)) {
985,755✔
789
                return true;
×
790
            }
×
791
        }
985,755✔
792
    }
6,917,442✔
793
    return false;
1,658,913✔
794
}
1,688,040✔
795

796
void ClusterNodeInner::update(ClusterTree::UpdateFunction func, int64_t key_offset)
797
{
36✔
798
    auto sz = node_size();
36✔
799

800
    for (unsigned i = 0; i < sz; i++) {
468✔
801
        ref_type ref = _get_child_ref(i);
432✔
802
        char* header = m_alloc.translate(ref);
432✔
803
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
432✔
804
        MemRef mem(header, ref, m_alloc);
432✔
805
        int64_t offs = (m_keys.is_attached() ? m_keys.get(i) : i << m_shift_factor) + key_offset;
432✔
806
        if (child_is_leaf) {
432✔
807
            Cluster leaf(offs, m_alloc, m_tree_top);
432✔
808
            leaf.init(mem);
432✔
809
            leaf.set_parent(this, i + s_first_node_index);
432✔
810
            func(&leaf);
432✔
811
        }
432✔
812
        else {
×
813
            ClusterNodeInner node(m_alloc, m_tree_top);
×
814
            node.init(mem);
×
815
            node.set_parent(this, i + s_first_node_index);
×
816
            node.update(func, offs);
×
817
        }
×
818
    }
432✔
819
}
36✔
820

821
int64_t ClusterNodeInner::get_last_key_value() const
822
{
×
823
    auto last_ndx = node_size() - 1;
×
824

825
    ref_type ref = _get_child_ref(last_ndx);
×
826
    char* header = m_alloc.translate(ref);
×
827
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
×
828
    MemRef mem(header, ref, m_alloc);
×
829
    int64_t offset = m_keys.is_attached() ? m_keys.get(last_ndx) : last_ndx << m_shift_factor;
×
830
    if (child_is_leaf) {
×
831
        Cluster leaf(offset, m_alloc, m_tree_top);
×
832
        leaf.init(mem);
×
833
        return offset + leaf.get_last_key_value();
×
834
    }
×
835
    else {
×
836
        ClusterNodeInner node(m_alloc, m_tree_top);
×
837
        node.init(mem);
×
838
        return offset + node.get_last_key_value();
×
839
    }
×
840
}
×
841

842
ClusterTree::ClusterTree(Table* owner, Allocator& alloc, size_t top_position_for_cluster_tree)
843
    : m_alloc(alloc)
32,022✔
844
    , m_owner(owner)
32,022✔
845
    , m_top_position_for_cluster_tree(top_position_for_cluster_tree)
32,022✔
846
{
62,925✔
847
}
62,925✔
848

849
ClusterTree::~ClusterTree() {}
40,833✔
850

851
std::unique_ptr<ClusterNode> ClusterTree::create_root_from_parent(ArrayParent* parent, size_t ndx_in_parent)
852
{
2,655,984✔
853
    ref_type ref = parent->get_child_ref(ndx_in_parent);
2,655,984✔
854
    if (!ref)
2,655,984✔
855
        return nullptr;
×
856

857
    MemRef mem{m_alloc.translate(ref), ref, m_alloc};
2,655,984✔
858
    const char* header = mem.get_addr();
2,655,984✔
859
    bool is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
2,655,984✔
860

861
    bool can_reuse_root_accessor = m_root && m_root->is_leaf() == is_leaf;
2,655,984✔
862
    if (can_reuse_root_accessor) {
2,655,984✔
863
        m_root->init(mem);
2,515,506✔
864
        return std::move(m_root); // Same root will be reinstalled.
2,515,506✔
865
    }
2,515,506✔
866

867
    // Not reusing root note, allocating a new one.
868
    std::unique_ptr<ClusterNode> new_root;
140,478✔
869
    if (is_leaf) {
140,478✔
870
        new_root = std::make_unique<Cluster>(0, m_alloc, *this);
98,193✔
871
    }
98,193✔
872
    else {
42,285✔
873
        new_root = std::make_unique<ClusterNodeInner>(m_alloc, *this);
42,285✔
874
    }
42,285✔
875
    new_root->init(mem);
140,478✔
876
    new_root->set_parent(parent, ndx_in_parent);
140,478✔
877

878
    return new_root;
140,478✔
879
}
2,655,984✔
880

881
std::unique_ptr<ClusterNode> ClusterTree::get_node(ArrayParent* parent, size_t ndx_in_parent) const
882
{
24,222✔
883
    ref_type ref = parent->get_child_ref(ndx_in_parent);
24,222✔
884

885
    std::unique_ptr<ClusterNode> node;
24,222✔
886

887
    char* child_header = static_cast<char*>(m_alloc.translate(ref));
24,222✔
888
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
24,222✔
889
    if (child_is_leaf) {
24,222✔
890
        node = std::make_unique<Cluster>(0, m_alloc, *this);
24,108✔
891
    }
24,108✔
892
    else {
114✔
893
        node = std::make_unique<ClusterNodeInner>(m_alloc, *this);
114✔
894
    }
114✔
895
    node->init(MemRef(child_header, ref, m_alloc));
24,222✔
896
    node->set_parent(parent, ndx_in_parent);
24,222✔
897

898
    return node;
24,222✔
899
}
24,222✔
900

901
void ClusterTree::clear(CascadeState& state)
902
{
6,012✔
903
    m_owner->clear_indexes();
6,012✔
904

905
    if (state.m_group) {
6,012✔
906
        remove_all_links(state); // This will also delete objects loosing their last strong link
4,860✔
907
    }
4,860✔
908

909
    // We no longer have "clear table" instruction, so we have to report the removal of each
910
    // object individually
911
    if (Replication* repl = m_owner->get_repl()) {
6,012✔
912
        // Go through all clusters
913
        traverse([repl, this](const Cluster* cluster) {
3,705✔
914
            auto sz = cluster->node_size();
3,705✔
915
            for (size_t i = 0; i < sz; i++) {
104,889✔
916
                repl->remove_object(m_owner, cluster->get_real_key(i));
101,184✔
917
            }
101,184✔
918
            return IteratorControl::AdvanceToNext;
3,705✔
919
        });
3,705✔
920
    }
3,351✔
921

922
    m_root->destroy_deep();
6,012✔
923

924
    auto leaf = std::make_unique<Cluster>(0, m_root->get_alloc(), *this);
6,012✔
925
    leaf->create();
6,012✔
926
    replace_root(std::move(leaf));
6,012✔
927

928
    bump_content_version();
6,012✔
929
    bump_storage_version();
6,012✔
930

931
    m_size = 0;
6,012✔
932
}
6,012✔
933

934
void ClusterTree::enumerate_string_column(ColKey col_key)
935
{
690✔
936
    Allocator& alloc = get_alloc();
690✔
937

938
    ArrayString keys(alloc);
690✔
939
    ArrayString leaf(alloc);
690✔
940
    keys.create();
690✔
941

942
    auto collect_strings = [col_key, &leaf, &keys](const Cluster* cluster) {
1,086✔
943
        cluster->init_leaf(col_key, &leaf);
1,086✔
944
        size_t sz = leaf.size();
1,086✔
945
        size_t key_size = keys.size();
1,086✔
946
        for (size_t i = 0; i < sz; i++) {
118,542✔
947
            auto v = leaf.get(i);
117,456✔
948
            size_t pos = keys.lower_bound(v);
117,456✔
949
            if (pos == key_size || keys.get(pos) != v) {
117,456✔
950
                keys.insert(pos, v); // Throws
576✔
951
                key_size++;
576✔
952
            }
576✔
953
        }
117,456✔
954

955
        return IteratorControl::AdvanceToNext;
1,086✔
956
    };
1,086✔
957

958
    auto upgrade = [col_key, &keys](Cluster* cluster) {
1,086✔
959
        cluster->upgrade_string_to_enum(col_key, keys);
1,086✔
960
    };
1,086✔
961

962
    // Populate 'keys' array
963
    traverse(collect_strings);
690✔
964

965
    // Store key strings in spec
966
    size_t spec_ndx = m_owner->colkey2spec_ndx(col_key);
690✔
967
    const_cast<Spec*>(&m_owner->m_spec)->upgrade_string_to_enum(spec_ndx, keys.get_ref());
690✔
968

969
    // Replace column in all clusters
970
    update(upgrade);
690✔
971
}
690✔
972

973
void ClusterTree::replace_root(std::unique_ptr<ClusterNode> new_root)
974
{
9,309✔
975
    if (new_root != m_root) {
9,309✔
976
        // Maintain parent.
977
        new_root->set_parent(m_root->get_parent(), m_root->get_ndx_in_parent());
9,309✔
978
        new_root->update_parent(); // Throws
9,309✔
979
        m_root = std::move(new_root);
9,309✔
980
    }
9,309✔
981
}
9,309✔
982

983
bool ClusterTree::init_from_parent()
984
{
2,652,567✔
985
    m_root = get_root_from_parent();
2,652,567✔
986
    if (m_root) {
2,657,469✔
987
        m_size = m_root->get_tree_size();
2,657,469✔
988
        return true;
2,657,469✔
989
    }
2,657,469✔
990
    m_size = 0;
4,294,967,294✔
991
    return false;
4,294,967,294✔
992
}
2,652,567✔
993

994
void ClusterTree::update_from_parent() noexcept
995
{
1,091,064✔
996
    m_root->update_from_parent();
1,091,064✔
997
    m_size = m_root->get_tree_size();
1,091,064✔
998
}
1,091,064✔
999

1000
void ClusterTree::insert_fast(ObjKey k, const FieldValues& init_values, ClusterNode::State& state)
1001
{
24,818,619✔
1002
    ref_type new_sibling_ref = m_root->insert(k, init_values, state);
24,818,619✔
1003
    if (REALM_UNLIKELY(new_sibling_ref)) {
24,818,619✔
1004
        auto new_root = std::make_unique<ClusterNodeInner>(m_root->get_alloc(), *this);
3,177✔
1005
        new_root->create(m_root->get_sub_tree_depth() + 1);
3,177✔
1006

1007
        new_root->add(m_root->get_ref());                // Throws
3,177✔
1008
        new_root->add(new_sibling_ref, state.split_key); // Throws
3,177✔
1009
        new_root->update_sub_tree_size();
3,177✔
1010

1011
        replace_root(std::move(new_root));
3,177✔
1012
    }
3,177✔
1013
    m_size++;
24,818,619✔
1014
}
24,818,619✔
1015

1016
Obj ClusterTree::insert(ObjKey k, const FieldValues& init_values)
1017
{
24,799,719✔
1018
    ClusterNode::State state;
24,799,719✔
1019

1020
    insert_fast(k, init_values, state);
24,799,719✔
1021
    m_owner->update_indexes(k, init_values);
24,799,719✔
1022

1023
    bump_content_version();
24,799,719✔
1024
    bump_storage_version();
24,799,719✔
1025

1026
    // Replicate setting of values
1027
    if (Replication* repl = m_owner->get_repl()) {
24,799,719✔
1028
        auto pk_col = m_owner->get_primary_key_column();
8,885,202✔
1029
        for (const auto& v : init_values) {
8,885,202✔
1030
            if (v.col_key != pk_col)
384,804✔
1031
                repl->set(m_owner, v.col_key, k, v.value, v.is_default ? _impl::instr_SetDefault : _impl::instr_Set);
54,012✔
1032
        }
384,804✔
1033
    }
8,885,202✔
1034

1035
    return Obj(get_table_ref(), state.mem, k, state.index);
24,799,719✔
1036
}
24,799,719✔
1037

1038
bool ClusterTree::is_valid(ObjKey k) const noexcept
1039
{
84,914,064✔
1040
    if (m_size == 0)
84,914,064✔
1041
        return false;
216,081✔
1042

1043
    ClusterNode::State state;
84,697,983✔
1044
    return m_root->try_get(k, state);
84,697,983✔
1045
}
84,914,064✔
1046

1047
ClusterNode::State ClusterTree::try_get(ObjKey k) const noexcept
1048
{
27,716,367✔
1049
    ClusterNode::State state;
27,716,367✔
1050
    if (!(k && m_root->try_get(k, state)))
27,725,310✔
1051
        state.index = realm::npos;
19,863✔
1052
    return state;
27,716,367✔
1053
}
27,716,367✔
1054

1055
ClusterNode::State ClusterTree::get(size_t ndx, ObjKey& k) const
1056
{
11,391,888✔
1057
    if (ndx >= m_size) {
11,391,888✔
1058
        throw Exception(ErrorCodes::InvalidatedObject, "Object was deleted");
×
1059
    }
×
1060
    ClusterNode::State state;
11,391,888✔
1061
    k = m_root->get(ndx, state);
11,391,888✔
1062
    return state;
11,391,888✔
1063
}
11,391,888✔
1064

1065
size_t ClusterTree::get_ndx(ObjKey k) const noexcept
1066
{
36,837✔
1067
    return m_root->get_ndx(k, 0);
36,837✔
1068
}
36,837✔
1069

1070
void ClusterTree::erase(ObjKey k, CascadeState& state)
1071
{
5,000,967✔
1072
    if (!k.is_unresolved()) {
5,000,967✔
1073
        if (auto table = get_owning_table()) {
4,989,900✔
1074
            if (Replication* repl = table->get_repl()) {
4,989,900✔
1075
                repl->remove_object(table, k);
4,822,725✔
1076
            }
4,822,725✔
1077
        }
4,989,900✔
1078
    }
4,989,900✔
1079
    m_owner->free_local_id_after_hash_collision(k);
5,000,967✔
1080
    m_owner->erase_from_search_indexes(k);
5,000,967✔
1081

1082
    size_t root_size = m_root->erase(k, state);
5,000,967✔
1083

1084
    bump_content_version();
5,000,967✔
1085
    bump_storage_version();
5,000,967✔
1086
    m_size--;
5,000,967✔
1087
    while (!m_root->is_leaf() && root_size == 1) {
5,001,087✔
1088
        ClusterNodeInner* node = static_cast<ClusterNodeInner*>(m_root.get());
120✔
1089

1090
        REALM_ASSERT(node->get_first_key_value() == 0);
120✔
1091
        auto new_root = node->return_and_clear_first_child();
120✔
1092
        node->destroy_deep();
120✔
1093

1094
        replace_root(std::move(new_root));
120✔
1095
        root_size = m_root->node_size();
120✔
1096
    }
120✔
1097
}
5,000,967✔
1098

1099
bool ClusterTree::get_leaf(ObjKey key, ClusterNode::IteratorState& state) const noexcept
1100
{
2,139,132✔
1101
    state.clear();
2,139,132✔
1102

1103
    if (m_root->is_leaf()) {
2,139,132✔
1104
        Cluster* node = static_cast<Cluster*>(m_root.get());
643,716✔
1105
        REALM_ASSERT_DEBUG(node->get_offset() == 0);
643,716✔
1106
        state.m_key_offset = 0;
643,716✔
1107
        state.m_current_leaf.init(node->get_mem());
643,716✔
1108
        state.m_current_leaf.set_offset(state.m_key_offset);
643,716✔
1109
        state.m_current_index = node->lower_bound_key(key);
643,716✔
1110
        return state.m_current_index < state.m_current_leaf.node_size();
643,716✔
1111
    }
643,716✔
1112
    else {
1,495,416✔
1113
        ClusterNodeInner* node = static_cast<ClusterNodeInner*>(m_root.get());
1,495,416✔
1114
        return node->get_leaf(key, state);
1,495,416✔
1115
    }
1,495,416✔
1116
}
2,139,132✔
1117

1118
bool ClusterTree::traverse(TraverseFunction func) const
1119
{
2,356,107✔
1120
    if (m_root->is_leaf()) {
2,356,107✔
1121
        return func(static_cast<Cluster*>(m_root.get())) == IteratorControl::Stop;
727,476✔
1122
    }
727,476✔
1123
    else {
1,628,631✔
1124
        return static_cast<ClusterNodeInner*>(m_root.get())->traverse(func, 0);
1,628,631✔
1125
    }
1,628,631✔
1126
}
2,356,107✔
1127

1128
void ClusterTree::update(UpdateFunction func)
1129
{
972✔
1130
    if (m_root->is_leaf()) {
972✔
1131
        func(static_cast<Cluster*>(m_root.get()));
936✔
1132
    }
936✔
1133
    else {
36✔
1134
        static_cast<ClusterNodeInner*>(m_root.get())->update(func, 0);
36✔
1135
    }
36✔
1136
}
972✔
1137

1138
void ClusterTree::set_spec(ArrayPayload& arr, ColKey::Idx col_ndx) const
1139
{
3,019,302✔
1140
    // Check for owner. This function may be called in context of DictionaryClusterTree
1141
    // in which case m_owner is null (and spec never needed).
1142
    if (m_owner) {
3,019,302✔
1143
        auto spec_ndx = m_owner->leaf_ndx2spec_ndx(col_ndx);
3,019,296✔
1144
        arr.set_spec(&m_owner->m_spec, spec_ndx);
3,019,296✔
1145
    }
3,019,296✔
1146
}
3,019,302✔
1147

1148
TableRef ClusterTree::get_table_ref() const
1149
{
242,622,066✔
1150
    REALM_ASSERT(m_owner != nullptr);
242,622,066✔
1151
    // as safe as storing the TableRef locally in the ClusterTree,
1152
    // because the cluster tree and the table is basically one object :-O
1153
    return m_owner->m_own_ref;
242,622,066✔
1154
}
242,622,066✔
1155

1156
std::unique_ptr<ClusterNode> ClusterTree::get_root_from_parent()
1157
{
2,653,623✔
1158
    return create_root_from_parent(&m_owner->m_top, m_top_position_for_cluster_tree);
2,653,623✔
1159
}
2,653,623✔
1160

1161
void ClusterTree::verify() const
1162
{
275,937✔
1163
#ifdef REALM_DEBUG
275,937✔
1164
    traverse([](const Cluster* cluster) {
395,988✔
1165
        cluster->verify();
395,988✔
1166
        return IteratorControl::AdvanceToNext;
395,988✔
1167
    });
395,988✔
1168
#endif
275,937✔
1169
}
275,937✔
1170

1171
void ClusterTree::nullify_incoming_links(ObjKey obj_key, CascadeState& state)
1172
{
4,902,564✔
1173
    REALM_ASSERT(state.m_group);
4,902,564✔
1174
    m_root->nullify_incoming_links(obj_key, state);
4,902,564✔
1175
}
4,902,564✔
1176

1177
bool ClusterTree::is_string_enum_type(ColKey::Idx col_ndx) const
1178
{
46,761✔
1179
    size_t spec_ndx = m_owner->leaf_ndx2spec_ndx(col_ndx);
46,761✔
1180
    return m_owner->m_spec.is_string_enum_type(spec_ndx);
46,761✔
1181
}
46,761✔
1182

1183
void ClusterTree::remove_all_links(CascadeState& state)
1184
{
4,860✔
1185
    Allocator& alloc = get_alloc();
4,860✔
1186
    auto origin_table = get_owning_table();
4,860✔
1187
    // This function will add objects that should be deleted to 'state'
1188
    auto func = [&](const Cluster* cluster) {
5,238✔
1189
        auto remove_link_from_column = [&](ColKey col_key) {
67,953✔
1190
            // Prevent making changes to table that is going to be removed anyway
1191
            // Furthermore it is a prerequisite for using 'traverse' that the tree
1192
            // is not modified
1193
            if (origin_table->links_to_self(col_key)) {
67,953✔
1194
                return IteratorControl::AdvanceToNext;
228✔
1195
            }
228✔
1196
            auto col_type = col_key.get_type();
67,725✔
1197
            if (col_key.is_collection()) {
67,725✔
1198
                ArrayInteger values(alloc);
5,094✔
1199
                cluster->init_leaf(col_key, &values);
5,094✔
1200
                size_t sz = values.size();
5,094✔
1201

1202
                for (size_t i = 0; i < sz; i++) {
18,252✔
1203
                    if (ref_type ref = values.get_as_ref(i)) {
13,158✔
1204
                        ObjKey origin_key = cluster->get_real_key(i);
372✔
1205
                        if (col_key.is_list() || col_key.is_set()) {
372✔
1206
                            if (col_type == col_type_Link) {
300✔
1207
                                BPlusTree<ObjKey> links(alloc);
240✔
1208
                                links.init_from_ref(ref);
240✔
1209
                                if (links.size() > 0) {
240✔
1210
                                    cluster->do_remove_backlinks(origin_key, col_key, links.get_all(), state);
228✔
1211
                                }
228✔
1212
                            }
240✔
1213
                            else if (col_type == col_type_TypedLink) {
60✔
1214
                                BPlusTree<ObjLink> links(alloc);
×
1215
                                links.init_from_ref(ref);
×
1216
                                if (links.size() > 0) {
×
1217
                                    cluster->do_remove_backlinks(origin_key, col_key, links.get_all(), state);
×
1218
                                }
×
1219
                            }
×
1220
                            else if (col_type == col_type_Mixed) {
60✔
1221
                                BPlusTree<Mixed> mix_arr(alloc);
60✔
1222
                                mix_arr.init_from_ref(ref);
60✔
1223
                                auto sz = mix_arr.size();
60✔
1224
                                std::vector<ObjLink> links;
60✔
1225
                                for (size_t j = 0; j < sz; j++) {
180✔
1226
                                    auto mix = mix_arr.get(j);
120✔
1227
                                    if (mix.is_type(type_TypedLink)) {
120✔
1228
                                        links.push_back(mix.get<ObjLink>());
120✔
1229
                                    }
120✔
1230
                                }
120✔
1231
                                if (links.size())
60✔
1232
                                    cluster->do_remove_backlinks(origin_key, col_key, links, state);
60✔
1233
                            }
60✔
1234
                        }
300✔
1235
                        else if (col_key.is_dictionary()) {
72✔
1236
                            std::vector<ObjLink> links;
72✔
1237

1238
                            Array top(alloc);
72✔
1239
                            top.set_parent(&values, i);
72✔
1240
                            top.init_from_parent();
72✔
1241
                            BPlusTree<Mixed> values(alloc);
72✔
1242
                            values.set_parent(&top, 1);
72✔
1243
                            values.init_from_parent();
72✔
1244

1245
                            // Iterate through values and insert all link values
1246
                            values.for_all([&](Mixed val) {
108✔
1247
                                if (val.is_type(type_TypedLink)) {
108✔
1248
                                    links.push_back(val.get<ObjLink>());
108✔
1249
                                }
108✔
1250
                            });
108✔
1251

1252
                            if (links.size() > 0) {
72✔
1253
                                cluster->do_remove_backlinks(origin_key, col_key, links, state);
72✔
1254
                            }
72✔
1255
                        }
72✔
1256
                    }
372✔
1257
                }
13,158✔
1258
            }
5,094✔
1259
            else {
62,631✔
1260
                if (col_type == col_type_Link) {
62,631✔
1261
                    ArrayKey values(alloc);
1,488✔
1262
                    cluster->init_leaf(col_key, &values);
1,488✔
1263
                    size_t sz = values.size();
1,488✔
1264
                    for (size_t i = 0; i < sz; i++) {
4,602✔
1265
                        if (ObjKey key = values.get(i)) {
3,114✔
1266
                            cluster->do_remove_backlinks(cluster->get_real_key(i), col_key, std::vector<ObjKey>{key},
522✔
1267
                                                         state);
522✔
1268
                        }
522✔
1269
                    }
3,114✔
1270
                }
1,488✔
1271
                else if (col_type == col_type_TypedLink) {
61,143✔
1272
                    ArrayTypedLink values(alloc);
×
1273
                    cluster->init_leaf(col_key, &values);
×
1274
                    size_t sz = values.size();
×
1275
                    for (size_t i = 0; i < sz; i++) {
×
1276
                        if (ObjLink link = values.get(i)) {
×
1277
                            cluster->do_remove_backlinks(cluster->get_real_key(i), col_key,
×
1278
                                                         std::vector<ObjLink>{link}, state);
×
1279
                        }
×
1280
                    }
×
1281
                }
×
1282
                else if (col_type == col_type_Mixed) {
61,143✔
1283
                    ArrayMixed values(alloc);
108✔
1284
                    cluster->init_leaf(col_key, &values);
108✔
1285
                    size_t sz = values.size();
108✔
1286
                    for (size_t i = 0; i < sz; i++) {
648✔
1287
                        Mixed mix = values.get(i);
540✔
1288
                        if (mix.is_type(type_TypedLink)) {
540✔
1289
                            cluster->do_remove_backlinks(cluster->get_real_key(i), col_key,
12✔
1290
                                                         std::vector<ObjLink>{mix.get<ObjLink>()}, state);
12✔
1291
                        }
12✔
1292
                    }
540✔
1293
                }
108✔
1294
                else if (col_type == col_type_BackLink) {
61,035✔
1295
                    ArrayBacklink values(alloc);
276✔
1296
                    cluster->init_leaf(col_key, &values);
276✔
1297
                    values.set_parent(const_cast<Cluster*>(cluster),
276✔
1298
                                      col_key.get_index().val + Cluster::s_first_col_index);
276✔
1299
                    size_t sz = values.size();
276✔
1300
                    for (size_t i = 0; i < sz; i++) {
2,100✔
1301
                        values.nullify_fwd_links(i, state);
1,824✔
1302
                    }
1,824✔
1303
                }
276✔
1304
            }
62,631✔
1305
            return IteratorControl::AdvanceToNext;
67,725✔
1306
        };
67,953✔
1307
        m_owner->for_each_and_every_column(remove_link_from_column);
5,238✔
1308
        return IteratorControl::AdvanceToNext;
5,238✔
1309
    };
5,238✔
1310

1311
    // Go through all clusters
1312
    traverse(func);
4,860✔
1313

1314
    const_cast<Table*>(get_owning_table())->remove_recursive(state);
4,860✔
1315
}
4,860✔
1316

1317
/**************************  ClusterTree::Iterator  **************************/
1318

1319
ClusterTree::Iterator::Iterator(const ClusterTree& t, size_t ndx)
1320
    : m_tree(t)
4,907,061✔
1321
    , m_leaf(0, t.get_alloc(), t)
4,907,061✔
1322
    , m_state(m_leaf)
4,907,061✔
1323
    , m_instance_version(t.get_instance_version())
4,907,061✔
1324
    , m_leaf_invalid(false)
4,907,061✔
1325
    , m_position(ndx)
4,907,061✔
1326
{
10,118,547✔
1327
    auto sz = t.size();
10,118,547✔
1328
    if (ndx >= sz) {
10,118,547✔
1329
        // end
1330
        m_position = sz;
9,607,323✔
1331
        m_leaf_invalid = true;
9,607,323✔
1332
    }
9,607,323✔
1333
    else if (ndx == 0) {
511,224✔
1334
        // begin
1335
        m_key = load_leaf(ObjKey(0));
497,358✔
1336
        m_leaf_start_pos = 0;
497,358✔
1337
    }
497,358✔
1338
    else {
13,866✔
1339
        auto s = const_cast<ClusterTree&>(m_tree).get(ndx, m_key);
13,866✔
1340
        m_state.init(s, m_key);
13,866✔
1341
        m_leaf_start_pos = ndx - m_state.m_current_index;
13,866✔
1342
    }
13,866✔
1343
}
10,118,547✔
1344

1345
ClusterTree::Iterator::Iterator(const Iterator& other)
1346
    : m_tree(other.m_tree)
336✔
1347
    , m_leaf(0, m_tree.get_alloc(), m_tree)
336✔
1348
    , m_state(m_leaf)
336✔
1349
    , m_instance_version(m_tree.get_instance_version())
336✔
1350
    , m_key(other.m_key)
336✔
1351
    , m_leaf_invalid(other.m_leaf_invalid)
336✔
1352
    , m_position(other.m_position)
336✔
1353
{
429✔
1354
    m_leaf_start_pos = m_position - m_state.m_current_index;
429✔
1355
}
429✔
1356

1357
size_t ClusterTree::Iterator::get_position()
1358
{
18,669✔
1359
    auto ndx = m_tree.get_ndx(m_key);
18,669✔
1360
    if (ndx == realm::npos) {
18,669✔
1361
        throw StaleAccessor("Stale iterator");
×
1362
    }
×
1363
    return ndx;
18,669✔
1364
}
18,669✔
1365

1366
ObjKey ClusterTree::Iterator::load_leaf(ObjKey key) const
1367
{
2,140,485✔
1368
    m_storage_version = m_tree.get_storage_version(m_instance_version);
2,140,485✔
1369
    // 'key' may or may not exist. If it does not exist, state is updated
1370
    // to point to the next object in line.
1371
    if (m_tree.get_leaf(key, m_state)) {
2,140,485✔
1372
        m_leaf_start_pos = m_position - m_state.m_current_index;
1,979,568✔
1373
        // Get the actual key value
1374
        return m_leaf.get_real_key(m_state.m_current_index);
1,979,568✔
1375
    }
1,979,568✔
1376
    else {
160,917✔
1377
        // end of table
1378
        return null_key;
160,917✔
1379
    }
160,917✔
1380
}
2,140,485✔
1381

1382
void ClusterTree::Iterator::go(size_t abs_pos)
1383
{
43,431✔
1384
    size_t sz = m_tree.size();
43,431✔
1385
    if (abs_pos >= sz) {
43,431✔
1386
        throw OutOfBounds("go() on Iterator", abs_pos, sz);
×
1387
    }
×
1388

1389
    m_position = abs_pos;
43,431✔
1390

1391
    // If the position is within the current leaf then just set the iterator to that position
1392
    if (!m_leaf_invalid && m_storage_version == m_tree.get_storage_version(m_instance_version)) {
43,431✔
1393
        if (abs_pos >= m_leaf_start_pos && abs_pos < (m_leaf_start_pos + m_leaf.node_size())) {
15,114✔
1394
            m_state.m_current_index = abs_pos - m_leaf_start_pos;
4,326✔
1395
            m_key = m_leaf.get_real_key(m_state.m_current_index);
4,326✔
1396
            return;
4,326✔
1397
        }
4,326✔
1398
    }
15,114✔
1399

1400
    // Find cluster holding requested position
1401
    auto s = m_tree.get(abs_pos, m_key);
39,105✔
1402
    m_state.init(s, m_key);
39,105✔
1403
    m_leaf_start_pos = abs_pos - s.index;
39,105✔
1404
    m_leaf_invalid = false;
39,105✔
1405
}
39,105✔
1406

1407
bool ClusterTree::Iterator::update() const
1408
{
22,725,315✔
1409
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
22,725,315✔
1410
        ObjKey k = load_leaf(m_key);
46,764✔
1411
        m_leaf_invalid = !k || (k != m_key);
46,764✔
1412
        if (m_leaf_invalid) {
46,764✔
1413
            throw StaleAccessor("Stale iterator");
30✔
1414
        }
30✔
1415
        return true;
46,734✔
1416
    }
46,764✔
1417

1418
    REALM_ASSERT(m_leaf.is_attached());
22,678,551✔
1419
    return false;
22,678,551✔
1420
}
22,725,315✔
1421

1422
ClusterTree::Iterator& ClusterTree::Iterator::operator++()
1423
{
21,338,505✔
1424
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
21,412,482✔
1425
        ObjKey k = load_leaf(m_key);
1,383,366✔
1426
        if (k != m_key) {
1,383,366✔
1427
            // Objects was deleted. k points to the next object
1428
            m_key = k;
60,939✔
1429
            m_leaf_invalid = !m_key;
60,939✔
1430
            return *this;
60,939✔
1431
        }
60,939✔
1432
    }
1,383,366✔
1433

1434
    m_state.m_current_index++;
21,277,566✔
1435
    m_position++;
21,277,566✔
1436
    if (m_state.m_current_index == m_leaf.node_size()) {
21,277,566✔
1437
        m_key = load_leaf(ObjKey(m_key.value + 1));
213,189✔
1438
        m_leaf_invalid = !m_key;
213,189✔
1439
    }
213,189✔
1440
    else {
21,064,377✔
1441
        m_key = m_leaf.get_real_key(m_state.m_current_index);
21,064,377✔
1442
    }
21,064,377✔
1443
    return *this;
21,277,566✔
1444
}
21,338,505✔
1445

1446
ClusterTree::Iterator& ClusterTree::Iterator::operator+=(ptrdiff_t adj)
1447
{
2,016✔
1448
    // If you have to jump far away and thus have to load many leaves,
1449
    // this function will be slow
1450
    REALM_ASSERT(adj >= 0);
2,016✔
1451
    if (adj == 0) {
2,016✔
1452
        return *this;
6✔
1453
    }
6✔
1454

1455
    size_t n = size_t(adj);
2,010✔
1456
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
2,010✔
1457
        ObjKey k = load_leaf(m_key);
12✔
1458
        if (k != m_key) {
12✔
1459
            // Objects was deleted. k points to the next object
1460
            m_key = k;
6✔
1461
            m_position = m_key ? m_tree.get_ndx(m_key) : m_tree.size();
6✔
1462
            n--;
6✔
1463
        }
6✔
1464
    }
12✔
1465
    if (n > 0) {
2,010✔
1466
        m_position += n;
2,010✔
1467
        size_t left_in_leaf = m_leaf.node_size() - m_state.m_current_index;
2,010✔
1468
        if (n < left_in_leaf) {
2,010✔
1469
            m_state.m_current_index += n;
1,986✔
1470
            m_key = m_leaf.get_real_key(m_state.m_current_index);
1,986✔
1471
        }
1,986✔
1472
        else {
24✔
1473
            if (m_position < m_tree.size()) {
24✔
1474
                auto s = const_cast<ClusterTree&>(m_tree).get(m_position, m_key);
18✔
1475
                m_state.init(s, m_key);
18✔
1476
                m_leaf_start_pos = m_position - m_state.m_current_index;
18✔
1477
            }
18✔
1478
            else {
6✔
1479
                m_key = ObjKey();
6✔
1480
                m_position = m_tree.size();
6✔
1481
            }
6✔
1482
        }
24✔
1483
    }
2,010✔
1484
    m_leaf_invalid = !m_key;
2,010✔
1485
    return *this;
2,010✔
1486
}
2,016✔
1487

1488
ClusterTree::Iterator::pointer ClusterTree::Iterator::operator->() const
1489
{
22,736,898✔
1490
    if (update() || m_key != m_obj.get_key()) {
22,736,898✔
1491
        new (&m_obj) Obj(m_tree.get_table_ref(), m_leaf.get_mem(), m_key, m_state.m_current_index);
18,834,654✔
1492
    }
18,834,654✔
1493

1494
    return &m_obj;
22,736,898✔
1495
}
22,736,898✔
1496

1497
} // 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

© 2026 Coveralls, Inc