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

realm / realm-core / jorgen.edelbo_337

03 Jul 2024 01:04PM UTC coverage: 90.864% (-0.1%) from 90.984%
jorgen.edelbo_337

Pull #7826

Evergreen

nicola-cab
Merge branch 'master' of github.com:realm/realm-core into next-major
Pull Request #7826: Merge Next major

102968 of 181176 branches covered (56.83%)

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

106 existing lines in 23 files now uncovered.

217725 of 239616 relevant lines covered (90.86%)

6844960.2 hits per line

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

86.83
/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(RowKey k) override;
62
    void update_ref_in_parent(RowKey, ref_type) override;
63

64
    bool is_leaf() const override
65
    {
48,703,275✔
66
        return false;
48,703,275✔
67
    }
48,703,275✔
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
    {
84,668,946✔
79
        return Array::size() - s_first_node_index;
84,668,946✔
80
    }
84,668,946✔
81
    size_t get_tree_size() const override
82
    {
45,015,405✔
83
        return size_t(Array::get(s_sub_tree_size)) >> 1;
45,015,405✔
84
    }
45,015,405✔
85
    void set_tree_size(size_t sub_tree_size)
86
    {
42,244,563✔
87
        Array::set(s_sub_tree_size, sub_tree_size << 1 | 1);
42,244,563✔
88
    }
42,244,563✔
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(RowKey k, const FieldValues& init_values, State& state) override;
98
    bool try_get(RowKey k, State& state) const noexcept override;
99
    ObjKey get(size_t ndx, State& state) const override;
100
    size_t get_ndx(RowKey key, size_t ndx) const noexcept override;
101
    size_t erase(RowKey k, CascadeState& state) override;
102
    void nullify_incoming_links(RowKey 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
    {
240✔
108
        REALM_ASSERT(node_size() == 1);
240✔
109
        auto new_root = m_tree_top.get_node(this, s_first_node_index);
240✔
110
        Array::set(s_first_node_index, 0); // The node is no longer belonging to this
240✔
111
        return new_root;
240✔
112
    }
240✔
113

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

119
    bool get_leaf(RowKey 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,607✔
125
        REALM_ASSERT_DEBUG(ref == get_mem().get_ref());
104,607✔
126
        if (out.only_modified && m_alloc.is_read_only(ref)) {
104,607✔
NEW
127
            return ref;
×
NEW
128
        }
×
129
        REALM_ASSERT_DEBUG(get_is_inner_bptree_node_from_header(get_header()));
104,607✔
130
        REALM_ASSERT_DEBUG(!get_context_flag_from_header(get_header()));
104,607✔
131
        REALM_ASSERT_DEBUG(has_refs());
104,607✔
132
        TempArray written_node(size(), type_InnerBptreeNode);
104,607✔
133
        for (unsigned j = 0; j < size(); ++j) {
2,641,812✔
134
            RefOrTagged rot = get_as_ref_or_tagged(j);
2,537,205✔
135
            if (rot.is_ref() && rot.get_as_ref()) {
2,537,205✔
136
                if (out.only_modified && m_alloc.is_read_only(rot.get_as_ref())) {
2,304,549✔
137
                    written_node.set(j, rot);
1,924,821✔
138
                    continue;
1,924,821✔
139
                }
1,924,821✔
140
                if (j == 0) {
379,728✔
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,702✔
147
                    auto header = m_alloc.translate(rot.get_as_ref());
378,702✔
148
                    MemRef m(header, rot.get_as_ref(), m_alloc);
378,702✔
149
                    if (get_is_inner_bptree_node_from_header(header)) {
378,702✔
150
                        ClusterNodeInner inner_node(m_alloc, m_tree_top);
7,461✔
151
                        inner_node.init(m);
7,461✔
152
                        written_node.set_as_ref(j, inner_node.typed_write(rot.get_as_ref(), out));
7,461✔
153
                    }
7,461✔
154
                    else {
371,241✔
155
                        Cluster cluster(j, m_alloc, m_tree_top);
371,241✔
156
                        cluster.init(m);
371,241✔
157
                        written_node.set_as_ref(j, cluster.typed_write(rot.get_as_ref(), out));
371,241✔
158
                    }
371,241✔
159
                }
378,702✔
160
            }
379,728✔
161
            else { // not a ref, just copy value over
232,656✔
162
                written_node.set(j, rot);
232,656✔
163
            }
232,656✔
164
        }
2,537,205✔
165
        return written_node.write(out);
104,607✔
166
    }
104,607✔
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
        RowKey key;
218
        MemRef mem;
219
    };
220
    bool find_child(RowKey key, ChildInfo& ret) const noexcept
221
    {
393,072,360✔
222
        if (m_keys.is_attached()) {
393,072,360✔
223
            auto upper = m_keys.upper_bound(key.value);
337,179,894✔
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) {
337,179,894✔
228
                return false;
×
229
            }
×
230
            ret.ndx = upper - 1;
337,179,894✔
231
            ret.offset = m_keys.get(ret.ndx);
337,179,894✔
232
        }
337,179,894✔
233
        else {
55,892,466✔
234
            size_t sz = node_size();
55,892,466✔
235
            REALM_ASSERT_DEBUG(sz > 0);
55,892,466✔
236
            size_t max_ndx = sz - 1;
55,892,466✔
237
            ret.ndx = std::min(size_t(key.value >> m_shift_factor), max_ndx);
55,892,466✔
238
            ret.offset = ret.ndx << m_shift_factor;
55,892,466✔
239
        }
55,892,466✔
240
        ret.key = RowKey(key.value - ret.offset);
393,072,360✔
241
        ref_type child_ref = _get_child_ref(ret.ndx);
393,072,360✔
242
        char* child_header = m_alloc.translate(child_ref);
393,072,360✔
243
        ret.mem = MemRef(child_header, child_ref, m_alloc);
393,072,360✔
244
        return true;
393,072,360✔
245
    }
393,072,360✔
246

247
    ref_type _get_child_ref(size_t ndx) const noexcept
248
    {
472,592,334✔
249
        return Array::get_as_ref(ndx + s_first_node_index);
472,592,334✔
250
    }
472,592,334✔
251
    void _insert_child_ref(size_t ndx, ref_type ref)
252
    {
93,279✔
253
        Array::insert(ndx + s_first_node_index, from_ref(ref));
93,279✔
254
    }
93,279✔
255
    void _erase_child_ref(size_t ndx)
256
    {
21,108✔
257
        Array::erase(ndx + s_first_node_index);
21,108✔
258
    }
21,108✔
259
    void move(size_t ndx, ClusterNode* new_node, int64_t key_adj) override;
260

261
    template <class T, class F>
262
    T recurse(RowKey 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,901,884✔
278
{
97,765,581✔
279
}
97,765,581✔
280

281
ClusterNodeInner::~ClusterNodeInner() {}
97,771,173✔
282

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

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

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

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

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

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

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

349
MemRef ClusterNodeInner::ensure_writeable(RowKey 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(RowKey key, ref_type ref)
357
{
486,720✔
358
    ChildInfo child_info;
486,720✔
359
    if (!find_child(key, child_info)) {
486,720✔
360
        throw KeyNotFound("Child not found in update_ref_in_parent");
×
361
    }
×
362
    if (this->m_sub_tree_depth == 1) {
486,720✔
363
        set(child_info.ndx + s_first_node_index, ref);
269,910✔
364
    }
269,910✔
365
    else {
216,810✔
366
        ClusterNodeInner node(m_alloc, m_tree_top);
216,810✔
367
        node.set_parent(this, child_info.ndx + s_first_node_index);
216,810✔
368
        node.init(child_info.mem);
216,810✔
369
        node.set_offset(child_info.offset + m_offset);
216,810✔
370
        node.update_ref_in_parent(child_info.key, ref);
216,810✔
371
    }
216,810✔
372
}
486,720✔
373

374
ref_type ClusterNodeInner::insert(RowKey row_key, const FieldValues& init_values, ClusterNode::State& state)
375
{
33,913,794✔
376
    return recurse<ref_type>(row_key, [this, &state, &init_values](ClusterNode* node, ChildInfo& child_info) {
33,913,794✔
377
        ref_type new_sibling_ref = node->insert(child_info.key, init_values, state);
33,831,921✔
378

379
        set_tree_size(get_tree_size() + 1);
33,831,921✔
380

381
        if (!new_sibling_ref) {
33,831,921✔
382
            return ref_type(0);
33,737,559✔
383
        }
33,737,559✔
384

385
        size_t new_ref_ndx = child_info.ndx + 1;
94,362✔
386

387
        int64_t split_key_value = state.split_key + child_info.offset;
94,362✔
388
        uint64_t sz = node_size();
94,362✔
389
        if (sz < cluster_node_size) {
97,773✔
390
            if (m_keys.is_attached()) {
93,279✔
391
                m_keys.insert(new_ref_ndx, split_key_value);
83,196✔
392
            }
83,196✔
393
            else {
10,083✔
394
                if (uint64_t(split_key_value) != sz << m_shift_factor) {
10,083✔
395
                    ensure_general_form();
288✔
396
                    m_keys.insert(new_ref_ndx, split_key_value);
288✔
397
                }
288✔
398
            }
10,083✔
399
            _insert_child_ref(new_ref_ndx, new_sibling_ref);
93,279✔
400
            return ref_type(0);
93,279✔
401
        }
93,279✔
402

403
        ClusterNodeInner child(m_alloc, m_tree_top);
2,147,488,141✔
404
        child.create(m_sub_tree_depth);
2,147,488,141✔
405
        if (new_ref_ndx == sz) {
2,147,488,141✔
406
            child.add(new_sibling_ref);
180✔
407
            state.split_key = split_key_value;
180✔
408
        }
180✔
409
        else {
2,147,488,051✔
410
            int64_t first_key_value = m_keys.get(new_ref_ndx);
2,147,488,051✔
411
            child.ensure_general_form();
2,147,488,051✔
412
            move(new_ref_ndx, &child, first_key_value);
2,147,488,051✔
413
            add(new_sibling_ref, split_key_value); // Throws
2,147,488,051✔
414
            state.split_key = first_key_value;
2,147,488,051✔
415
        }
2,147,488,051✔
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,141✔
419
        set_tree_size(get_tree_size() - child_sub_tree_size);
2,147,488,141✔
420

421
        return child.get_ref();
2,147,488,141✔
422
    });
94,362✔
423
}
33,913,794✔
424

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

437
ObjKey ClusterNodeInner::get(size_t ndx, ClusterNode::State& state) const
438
{
12,007,185✔
439
    size_t sz = node_size();
12,007,185✔
440
    size_t child_ndx = 0;
12,007,185✔
441
    while (child_ndx < sz) {
74,821,020✔
442
        int64_t key_offset = m_keys.is_attached() ? m_keys.get(child_ndx) : (child_ndx << m_shift_factor);
74,803,827✔
443

444
        ref_type child_ref = _get_child_ref(child_ndx);
74,803,827✔
445
        char* child_header = m_alloc.translate(child_ref);
74,803,827✔
446
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
74,803,827✔
447
        size_t sub_tree_size;
74,803,827✔
448
        if (child_is_leaf) {
74,803,827✔
449
            sub_tree_size = Cluster::node_size_from_header(m_alloc, child_header);
71,924,856✔
450
            if (ndx < sub_tree_size) {
71,924,856✔
451
                Cluster leaf(key_offset + m_offset, m_alloc, m_tree_top);
9,398,082✔
452
                leaf.init(MemRef(child_header, child_ref, m_alloc));
9,398,082✔
453
                REALM_ASSERT(sub_tree_size == leaf.get_tree_size());
9,398,082✔
454
                return leaf.get(ndx, state);
9,398,082✔
455
            }
9,398,082✔
456
        }
71,924,856✔
457
        else {
2,878,971✔
458
            ClusterNodeInner node(m_alloc, m_tree_top);
2,878,971✔
459
            node.init(MemRef(child_header, child_ref, m_alloc));
2,878,971✔
460
            node.set_offset(key_offset + m_offset);
2,878,971✔
461
            sub_tree_size = node.get_tree_size();
2,878,971✔
462
            if (ndx < sub_tree_size) {
2,878,971✔
463
                return node.get(ndx, state);
2,593,770✔
464
            }
2,593,770✔
465
        }
2,878,971✔
466
        child_ndx++;
62,811,975✔
467
        ndx -= sub_tree_size;
62,811,975✔
468
    }
62,811,975✔
469
    return {};
2,147,500,840✔
470
}
12,007,185✔
471

472
size_t ClusterNodeInner::get_ndx(RowKey key, size_t ndx) const noexcept
473
{
24,726✔
474
    ChildInfo child_info;
24,726✔
475
    if (!find_child(key, child_info)) {
24,726✔
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,726✔
483
    if (child_is_leaf) {
24,726✔
484
        for (unsigned i = 0; i < child_info.ndx; i++) {
51,078✔
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,726✔
490
        leaf.init(child_info.mem);
24,726✔
491
        return leaf.get_ndx(child_info.key, ndx);
24,726✔
492
    }
24,726✔
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,726✔
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(RowKey key, CascadeState& state)
536
{
8,435,976✔
537
    return recurse<size_t>(key, [this, &state](ClusterNode* erase_node, ChildInfo& child_info) {
8,435,976✔
538
        size_t erase_node_size = erase_node->erase(child_info.key, state);
8,435,670✔
539
        bool is_leaf = erase_node->is_leaf();
8,435,670✔
540
        set_tree_size(get_tree_size() - 1);
8,435,670✔
541

542
        if (erase_node_size == 0) {
8,435,670✔
543
            erase_node->destroy_deep();
13,347✔
544

545
            ensure_general_form();
13,347✔
546
            _erase_child_ref(child_info.ndx);
13,347✔
547
            m_keys.erase(child_info.ndx);
13,347✔
548
            if (child_info.ndx == 0 && m_keys.size() > 0) {
13,347✔
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,347✔
556
        else if (erase_node_size < cluster_node_size / 2 && child_info.ndx < (node_size() - 1)) {
8,422,323✔
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,295,491✔
560
            Cluster l2(child_info.offset, m_alloc, m_tree_top);
3,295,491✔
561
            ClusterNodeInner n2(m_alloc, m_tree_top);
3,295,491✔
562
            ClusterNode* sibling_node = is_leaf ? static_cast<ClusterNode*>(&l2) : static_cast<ClusterNode*>(&n2);
3,295,491✔
563
            sibling_node->set_parent(this, sibling_ndx + s_first_node_index);
3,295,491✔
564
            sibling_node->init_from_parent();
3,295,491✔
565

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

568
            if (combined_size < cluster_node_size * 3 / 4) {
3,295,491✔
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))
7,761✔
572
                                                       : 0 - (1 << m_shift_factor);
7,761✔
573
                // And then move all elements into current node
574
                sibling_node->ensure_general_form();
7,761✔
575
                erase_node->ensure_general_form();
7,761✔
576
                sibling_node->move(0, erase_node, key_adj);
7,761✔
577

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

582
                // Destroy sibling
583
                sibling_node->destroy_deep();
7,761✔
584

585
                ensure_general_form();
7,761✔
586
                _erase_child_ref(sibling_ndx);
7,761✔
587
                m_keys.erase(sibling_ndx);
7,761✔
588
            }
7,761✔
589
        }
3,295,491✔
590

591
        return node_size();
8,435,670✔
592
    });
8,435,670✔
593
}
8,435,976✔
594

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

602
void ClusterNodeInner::ensure_general_form()
603
{
24,342✔
604
    if (!m_keys.is_attached()) {
24,342✔
605
        size_t current_size = node_size();
3,300✔
606
        m_keys.create(current_size, (current_size - 1) << m_shift_factor);
3,300✔
607
        m_keys.update_parent();
3,300✔
608
        for (size_t i = 0; i < current_size; i++) {
10,890✔
609
            m_keys.set(i, i << m_shift_factor);
7,590✔
610
        }
7,590✔
611
    }
3,300✔
612
}
24,342✔
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
{
12✔
625
    size_t sz = node_size();
12✔
626
    for (size_t i = 0; i < sz; i++) {
36✔
627
        std::shared_ptr<ClusterNode> node = m_tree_top.get_node(this, i + s_first_node_index);
24✔
628
        node->remove_column(col);
24✔
629
    }
24✔
630
}
12✔
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,873✔
652
    if (m_keys.is_attached()) {
6,873✔
653
        m_keys.add(key_value);
33✔
654
    }
33✔
655
    else {
6,840✔
656
        if (uint64_t(key_value) != (uint64_t(node_size()) << m_shift_factor)) {
6,840✔
657
            ensure_general_form();
2,793✔
658
            m_keys.add(key_value);
2,793✔
659
        }
2,793✔
660
    }
6,840✔
661
    Array::add(from_ref(ref));
6,873✔
662
}
6,873✔
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(RowKey key, ClusterNode::IteratorState& state) const noexcept
667
{
2,700,897✔
668
    size_t child_ndx;
2,700,897✔
669
    if (m_keys.is_attached()) {
2,700,897✔
670
        child_ndx = m_keys.upper_bound(key.value);
2,663,658✔
671
        if (child_ndx > 0)
2,663,658✔
672
            child_ndx--;
2,663,769✔
673
    }
2,663,658✔
674
    else {
37,239✔
675
        REALM_ASSERT_DEBUG(node_size() > 0);
37,239✔
676
        size_t max_ndx = node_size() - 1;
37,239✔
677
        child_ndx = std::min(size_t(key.value >> m_shift_factor), max_ndx);
37,239✔
678
    }
37,239✔
679

680
    size_t sz = node_size();
2,700,897✔
681
    while (child_ndx < sz) {
2,760,363✔
682
        uint64_t key_offset = m_keys.is_attached() ? m_keys.get(child_ndx) : (child_ndx << m_shift_factor);
2,752,527✔
683
        RowKey new_key(key_offset < key.value ? key.value - key_offset : 0);
2,752,527✔
684
        state.m_key_offset += key_offset;
2,752,527✔
685

686
        ref_type child_ref = _get_child_ref(child_ndx);
2,752,527✔
687
        char* child_header = m_alloc.translate(child_ref);
2,752,527✔
688
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
2,752,527✔
689
        if (child_is_leaf) {
2,752,527✔
690
            state.m_current_leaf.init(MemRef(child_header, child_ref, m_alloc));
1,547,724✔
691
            state.m_current_leaf.set_offset(state.m_key_offset);
1,547,724✔
692
            state.m_current_index = state.m_current_leaf.lower_bound_key(new_key);
1,547,724✔
693
            if (state.m_current_index < state.m_current_leaf.node_size())
1,547,724✔
694
                return true;
1,488,369✔
695
        }
1,547,724✔
696
        else {
1,204,803✔
697
            ClusterNodeInner node(m_alloc, m_tree_top);
1,204,803✔
698
            node.init(MemRef(child_header, child_ref, m_alloc));
1,204,803✔
699
            if (node.get_leaf(new_key, state))
1,204,803✔
700
                return true;
1,204,692✔
701
        }
1,204,803✔
702
        state.m_key_offset -= key_offset;
59,466✔
703
        child_ndx++;
59,466✔
704
    }
59,466✔
705
    return false;
7,836✔
706
}
2,700,897✔
707

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

745
size_t ClusterNodeInner::update_sub_tree_size()
746
{
3,588✔
747
    size_t sub_tree_size = 0;
3,588✔
748
    auto sz = node_size();
3,588✔
749

750
    for (unsigned i = 0; i < sz; i++) {
22,335✔
751
        ref_type ref = _get_child_ref(i);
18,747✔
752
        char* header = m_alloc.translate(ref);
18,747✔
753
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
18,747✔
754
        if (child_is_leaf) {
18,747✔
755
            sub_tree_size += Cluster::node_size_from_header(m_alloc, header);
18,615✔
756
        }
18,615✔
757
        else {
132✔
758
            sub_tree_size += size_t(Array::get(header, s_sub_tree_size)) >> 1;
132✔
759
        }
132✔
760
    }
18,747✔
761
    set_tree_size(sub_tree_size);
3,588✔
762
    return sub_tree_size;
3,588✔
763
}
3,588✔
764

765
bool ClusterNodeInner::traverse(ClusterTree::TraverseFunction func, int64_t key_offset) const
766
{
1,693,386✔
767
    auto sz = node_size();
1,693,386✔
768

769
    for (unsigned i = 0; i < sz; i++) {
8,679,795✔
770
        ref_type ref = _get_child_ref(i);
7,015,542✔
771
        char* header = m_alloc.translate(ref);
7,015,542✔
772
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
7,015,542✔
773
        MemRef mem(header, ref, m_alloc);
7,015,542✔
774
        int64_t offs = (m_keys.is_attached() ? m_keys.get(i) : i << m_shift_factor) + key_offset;
7,015,542✔
775
        if (child_is_leaf) {
7,015,542✔
776
            Cluster leaf(offs, m_alloc, m_tree_top);
6,312,633✔
777
            leaf.init(mem);
6,312,633✔
778
            if (func(&leaf) == IteratorControl::Stop) {
6,312,633✔
779
                return true;
29,133✔
780
            }
29,133✔
781
        }
6,312,633✔
782
        else {
702,909✔
783
            ClusterNodeInner node(m_alloc, m_tree_top);
702,909✔
784
            node.init(mem);
702,909✔
785
            if (node.traverse(func, offs)) {
702,909✔
786
                return true;
×
787
            }
×
788
        }
702,909✔
789
    }
7,015,542✔
790
    return false;
1,664,253✔
791
}
1,693,386✔
792

793
void ClusterNodeInner::update(ClusterTree::UpdateFunction func, int64_t key_offset)
794
{
36✔
795
    auto sz = node_size();
36✔
796

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

818
int64_t ClusterNodeInner::get_last_key_value() const
819
{
×
820
    auto last_ndx = node_size() - 1;
×
821

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

839
ClusterTree::ClusterTree(Table* owner, Allocator& alloc, size_t top_position_for_cluster_tree)
840
    : m_alloc(alloc)
29,466✔
841
    , m_owner(owner)
29,466✔
842
    , m_top_position_for_cluster_tree(top_position_for_cluster_tree)
29,466✔
843
{
63,519✔
844
}
63,519✔
845

846
ClusterTree::~ClusterTree() {}
33,333✔
847

848
std::unique_ptr<ClusterNode> ClusterTree::create_root_from_parent(ArrayParent* parent, size_t ndx_in_parent)
849
{
2,421,186✔
850
    ref_type ref = parent->get_child_ref(ndx_in_parent);
2,421,186✔
851
    if (!ref)
2,421,186✔
852
        return nullptr;
×
853

854
    MemRef mem{m_alloc.translate(ref), ref, m_alloc};
2,421,186✔
855
    const char* header = mem.get_addr();
2,421,186✔
856
    bool is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
2,421,186✔
857

858
    bool can_reuse_root_accessor = m_root && m_root->is_leaf() == is_leaf;
2,421,186✔
859
    if (can_reuse_root_accessor) {
2,421,186✔
860
        m_root->init(mem);
2,274,687✔
861
        return std::move(m_root); // Same root will be reinstalled.
2,274,687✔
862
    }
2,274,687✔
863

864
    // Not reusing root note, allocating a new one.
865
    std::unique_ptr<ClusterNode> new_root;
146,499✔
866
    if (is_leaf) {
146,499✔
867
        new_root = std::make_unique<Cluster>(0, m_alloc, *this);
103,572✔
868
    }
103,572✔
869
    else {
42,927✔
870
        new_root = std::make_unique<ClusterNodeInner>(m_alloc, *this);
42,927✔
871
    }
42,927✔
872
    new_root->init(mem);
146,499✔
873
    new_root->set_parent(parent, ndx_in_parent);
146,499✔
874

875
    return new_root;
146,499✔
876
}
2,421,186✔
877

878
std::unique_ptr<ClusterNode> ClusterTree::get_node(ArrayParent* parent, size_t ndx_in_parent) const
879
{
24,354✔
880
    ref_type ref = parent->get_child_ref(ndx_in_parent);
24,354✔
881

882
    std::unique_ptr<ClusterNode> node;
24,354✔
883

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

895
    return node;
24,354✔
896
}
24,354✔
897

898
void ClusterTree::clear(CascadeState& state)
899
{
6,009✔
900
    m_owner->clear_indexes();
6,009✔
901

902
    if (state.m_group) {
6,009✔
903
        remove_all_links(state); // This will also delete objects loosing their last strong link
4,857✔
904
    }
4,857✔
905

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

919
    m_root->destroy_deep();
6,009✔
920

921
    auto leaf = std::make_unique<Cluster>(0, m_root->get_alloc(), *this);
6,009✔
922
    leaf->create();
6,009✔
923
    replace_root(std::move(leaf));
6,009✔
924

925
    bump_content_version();
6,009✔
926
    bump_storage_version();
6,009✔
927

928
    m_size = 0;
6,009✔
929
}
6,009✔
930

931
void ClusterTree::enumerate_string_column(ColKey col_key)
932
{
693✔
933
    Allocator& alloc = get_alloc();
693✔
934

935
    ArrayString keys(alloc);
693✔
936
    ArrayString leaf(alloc);
693✔
937
    keys.create();
693✔
938

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

952
        return IteratorControl::AdvanceToNext;
1,089✔
953
    };
1,089✔
954

955
    auto upgrade = [col_key, &keys](Cluster* cluster) {
1,089✔
956
        cluster->upgrade_string_to_enum(col_key, keys);
1,089✔
957
    };
1,089✔
958

959
    // Populate 'keys' array
960
    traverse(collect_strings);
693✔
961

962
    // Store key strings in spec
963
    size_t spec_ndx = m_owner->colkey2spec_ndx(col_key);
693✔
964
    const_cast<Spec*>(&m_owner->m_spec)->upgrade_string_to_enum(spec_ndx, keys.get_ref());
693✔
965

966
    // Replace column in all clusters
967
    update(upgrade);
693✔
968
}
693✔
969

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

980
bool ClusterTree::init_from_parent()
981
{
2,418,594✔
982
    m_root = get_root_from_parent();
2,418,594✔
983
    if (m_root) {
2,423,217✔
984
        m_size = m_root->get_tree_size();
2,423,208✔
985
        return true;
2,423,208✔
986
    }
2,423,208✔
987
    m_size = 0;
2,147,483,656✔
988
    return false;
2,147,483,656✔
989
}
2,418,594✔
990

991
void ClusterTree::update_from_parent() noexcept
992
{
1,088,658✔
993
    m_root->update_from_parent();
1,088,658✔
994
    m_size = m_root->get_tree_size();
1,088,658✔
995
}
1,088,658✔
996

997
void ClusterTree::insert_fast(ObjKey k, const FieldValues& init_values, ClusterNode::State& state)
998
{
25,172,991✔
999
    ref_type new_sibling_ref = m_root->insert(ClusterNode::RowKey(k), init_values, state);
25,172,991✔
1000
    if (REALM_UNLIKELY(new_sibling_ref)) {
25,172,991✔
1001
        auto new_root = std::make_unique<ClusterNodeInner>(m_root->get_alloc(), *this);
3,330✔
1002
        new_root->create(m_root->get_sub_tree_depth() + 1);
3,330✔
1003

1004
        new_root->add(m_root->get_ref());                // Throws
3,330✔
1005
        new_root->add(new_sibling_ref, state.split_key); // Throws
3,330✔
1006
        new_root->update_sub_tree_size();
3,330✔
1007

1008
        replace_root(std::move(new_root));
3,330✔
1009
    }
3,330✔
1010
    m_size++;
25,172,991✔
1011
}
25,172,991✔
1012

1013
Obj ClusterTree::insert(ObjKey k, const FieldValues& init_values)
1014
{
25,155,048✔
1015
    ClusterNode::State state;
25,155,048✔
1016

1017
    insert_fast(k, init_values, state);
25,155,048✔
1018
    m_owner->update_indexes(k, init_values);
25,155,048✔
1019

1020
    bump_content_version();
25,155,048✔
1021
    bump_storage_version();
25,155,048✔
1022

1023
    // Replicate setting of values
1024
    if (Replication* repl = m_owner->get_repl()) {
25,155,048✔
1025
        auto pk_col = m_owner->get_primary_key_column();
8,885,679✔
1026
        for (const auto& v : init_values) {
8,885,679✔
1027
            if (v.col_key != pk_col)
384,642✔
1028
                repl->set(m_owner, v.col_key, k, v.value, v.is_default ? _impl::instr_SetDefault : _impl::instr_Set);
54,012✔
1029
        }
384,642✔
1030
    }
8,885,679✔
1031

1032
    return Obj(get_table_ref(), state.mem, k, state.index);
25,155,048✔
1033
}
25,155,048✔
1034

1035
bool ClusterTree::is_valid(ObjKey k) const noexcept
1036
{
85,923,066✔
1037
    if (m_size == 0)
85,923,066✔
1038
        return false;
216,948✔
1039

1040
    ClusterNode::State state;
85,706,118✔
1041
    return m_root->try_get(ClusterNode::RowKey(k), state);
85,706,118✔
1042
}
85,923,066✔
1043

1044
ClusterNode::State ClusterTree::try_get(ObjKey k) const noexcept
1045
{
27,551,814✔
1046
    ClusterNode::State state;
27,551,814✔
1047
    if (!(k && m_root->try_get(ClusterNode::RowKey(k), state)))
27,607,911✔
1048
        state.index = realm::npos;
19,236✔
1049
    return state;
27,551,814✔
1050
}
27,551,814✔
1051

1052
ClusterNode::State ClusterTree::get(size_t ndx, ObjKey& k) const
1053
{
12,263,712✔
1054
    if (ndx >= m_size) {
12,263,712✔
1055
        throw Exception(ErrorCodes::InvalidatedObject, "Object was deleted");
×
1056
    }
×
1057
    ClusterNode::State state;
12,263,712✔
1058
    k = m_root->get(ndx, state);
12,263,712✔
1059
    return state;
12,263,712✔
1060
}
12,263,712✔
1061

1062
size_t ClusterTree::get_ndx(ObjKey k) const noexcept
1063
{
36,909✔
1064
    return m_root->get_ndx(ClusterNode::RowKey(k), 0);
36,909✔
1065
}
36,909✔
1066

1067
void ClusterTree::erase(ObjKey k, CascadeState& state)
1068
{
5,282,535✔
1069
    if (!k.is_unresolved()) {
5,282,535✔
1070
        if (auto table = get_owning_table()) {
5,271,528✔
1071
            if (Replication* repl = table->get_repl()) {
5,271,528✔
1072
                repl->remove_object(table, k);
4,822,275✔
1073
            }
4,822,275✔
1074
        }
5,271,528✔
1075
    }
5,271,525✔
1076
    m_owner->free_local_id_after_hash_collision(k);
5,282,535✔
1077
    m_owner->erase_from_search_indexes(k);
5,282,535✔
1078

1079
    size_t root_size = m_root->erase(ClusterNode::RowKey(k), state);
5,282,535✔
1080

1081
    bump_content_version();
5,282,535✔
1082
    bump_storage_version();
5,282,535✔
1083
    m_size--;
5,282,535✔
1084
    while (!m_root->is_leaf() && root_size == 1) {
5,282,775✔
1085
        ClusterNodeInner* node = static_cast<ClusterNodeInner*>(m_root.get());
240✔
1086

1087
        REALM_ASSERT(node->get_first_key_value() == 0);
240✔
1088
        auto new_root = node->return_and_clear_first_child();
240✔
1089
        node->destroy_deep();
240✔
1090

1091
        replace_root(std::move(new_root));
240✔
1092
        root_size = m_root->node_size();
240✔
1093
    }
240✔
1094
}
5,282,535✔
1095

1096
bool ClusterTree::get_leaf(ObjKey key, ClusterNode::IteratorState& state) const noexcept
1097
{
2,122,455✔
1098
    state.clear();
2,122,455✔
1099
    ClusterNode::RowKey row_key(key);
2,122,455✔
1100
    if (m_root->is_leaf()) {
2,122,455✔
1101
        Cluster* node = static_cast<Cluster*>(m_root.get());
626,847✔
1102
        REALM_ASSERT_DEBUG(node->get_offset() == 0);
626,847✔
1103
        state.m_key_offset = 0;
626,847✔
1104
        state.m_current_leaf.init(node->get_mem());
626,847✔
1105
        state.m_current_leaf.set_offset(state.m_key_offset);
626,847✔
1106
        state.m_current_index = node->lower_bound_key(row_key);
626,847✔
1107
        return state.m_current_index < state.m_current_leaf.node_size();
626,847✔
1108
    }
626,847✔
1109
    else {
1,495,608✔
1110
        ClusterNodeInner* node = static_cast<ClusterNodeInner*>(m_root.get());
1,495,608✔
1111
        return node->get_leaf(row_key, state);
1,495,608✔
1112
    }
1,495,608✔
1113
}
2,122,455✔
1114

1115
bool ClusterTree::traverse(TraverseFunction func) const
1116
{
2,316,630✔
1117
    if (m_root->is_leaf()) {
2,316,630✔
1118
        return func(static_cast<Cluster*>(m_root.get())) == IteratorControl::Stop;
709,413✔
1119
    }
709,413✔
1120
    else {
1,607,217✔
1121
        return static_cast<ClusterNodeInner*>(m_root.get())->traverse(func, 0);
1,607,217✔
1122
    }
1,607,217✔
1123
}
2,316,630✔
1124

1125
void ClusterTree::update(UpdateFunction func)
1126
{
975✔
1127
    if (m_root->is_leaf()) {
975✔
1128
        func(static_cast<Cluster*>(m_root.get()));
939✔
1129
    }
939✔
1130
    else {
36✔
1131
        static_cast<ClusterNodeInner*>(m_root.get())->update(func, 0);
36✔
1132
    }
36✔
1133
}
975✔
1134

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

1145
TableRef ClusterTree::get_table_ref() const
1146
{
244,313,091✔
1147
    REALM_ASSERT(m_owner != nullptr);
244,313,091✔
1148
    // as safe as storing the TableRef locally in the ClusterTree,
1149
    // because the cluster tree and the table is basically one object :-O
1150
    return m_owner->m_own_ref;
244,313,091✔
1151
}
244,313,091✔
1152

1153
std::unique_ptr<ClusterNode> ClusterTree::get_root_from_parent()
1154
{
2,419,089✔
1155
    return create_root_from_parent(&m_owner->m_top, m_top_position_for_cluster_tree);
2,419,089✔
1156
}
2,419,089✔
1157

1158
void ClusterTree::verify() const
1159
{
263,355✔
1160
#ifdef REALM_DEBUG
263,355✔
1161
    traverse([](const Cluster* cluster) {
383,400✔
1162
        cluster->verify();
383,400✔
1163
        return IteratorControl::AdvanceToNext;
383,400✔
1164
    });
383,400✔
1165
#endif
263,355✔
1166
}
263,355✔
1167

1168
void ClusterTree::nullify_incoming_links(ObjKey obj_key, CascadeState& state)
1169
{
5,184,132✔
1170
    REALM_ASSERT(state.m_group);
5,184,132✔
1171
    m_root->nullify_incoming_links(ClusterNode::RowKey(obj_key), state);
5,184,132✔
1172
}
5,184,132✔
1173

1174
bool ClusterTree::is_string_enum_type(ColKey::Idx col_ndx) const
1175
{
49,353✔
1176
    size_t spec_ndx = m_owner->leaf_ndx2spec_ndx(col_ndx);
49,353✔
1177
    return m_owner->m_spec.is_string_enum_type(spec_ndx);
49,353✔
1178
}
49,353✔
1179

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

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

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

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

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

1308
    // Go through all clusters
1309
    traverse(func);
4,857✔
1310

1311
    const_cast<Table*>(get_owning_table())->remove_recursive(state);
4,857✔
1312
}
4,857✔
1313

1314
/**************************  ClusterTree::Iterator  **************************/
1315

1316
ClusterTree::Iterator::Iterator(const ClusterTree& t, size_t ndx)
1317
    : m_tree(t)
4,882,743✔
1318
    , m_leaf(0, t.get_alloc(), t)
4,882,743✔
1319
    , m_state(m_leaf)
4,882,743✔
1320
    , m_instance_version(t.get_instance_version())
4,882,743✔
1321
    , m_leaf_invalid(false)
4,882,743✔
1322
    , m_position(ndx)
4,882,743✔
1323
{
10,146,486✔
1324
    auto sz = t.size();
10,146,486✔
1325
    if (ndx >= sz) {
10,146,486✔
1326
        // end
1327
        m_position = sz;
9,649,560✔
1328
        m_leaf_invalid = true;
9,649,560✔
1329
    }
9,649,560✔
1330
    else if (ndx == 0) {
496,926✔
1331
        // begin
1332
        m_key = load_leaf(ObjKey(0));
480,969✔
1333
        m_leaf_start_pos = 0;
480,969✔
1334
    }
480,969✔
1335
    else {
15,957✔
1336
        auto s = const_cast<ClusterTree&>(m_tree).get(ndx, m_key);
15,957✔
1337
        m_state.init(s, m_key);
15,957✔
1338
        m_leaf_start_pos = ndx - m_state.m_current_index;
15,957✔
1339
    }
15,957✔
1340
}
10,146,486✔
1341

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

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

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

1379
void ClusterTree::Iterator::go(size_t abs_pos)
1380
{
43,467✔
1381
    size_t sz = m_tree.size();
43,467✔
1382
    if (abs_pos >= sz) {
43,467✔
1383
        throw OutOfBounds("go() on Iterator", abs_pos, sz);
×
1384
    }
×
1385

1386
    m_position = abs_pos;
43,467✔
1387

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

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

1404
bool ClusterTree::Iterator::update() const
1405
{
22,658,772✔
1406
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
22,661,562✔
1407
        ObjKey k = load_leaf(m_key);
46,599✔
1408
        m_leaf_invalid = !k || (k != m_key);
46,599✔
1409
        if (m_leaf_invalid) {
46,599✔
1410
            throw StaleAccessor("Stale iterator");
30✔
1411
        }
30✔
1412
        return true;
46,569✔
1413
    }
46,599✔
1414

1415
    REALM_ASSERT(m_leaf.is_attached());
22,612,173✔
1416
    return false;
22,612,173✔
1417
}
22,658,772✔
1418

1419
ClusterTree::Iterator& ClusterTree::Iterator::operator++()
1420
{
21,244,167✔
1421
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
21,246,171✔
1422
        ObjKey k = load_leaf(m_key);
1,383,312✔
1423
        if (k != m_key) {
1,383,312✔
1424
            // Objects was deleted. k points to the next object
1425
            m_key = k;
60,894✔
1426
            m_leaf_invalid = !m_key;
60,894✔
1427
            return *this;
60,894✔
1428
        }
60,894✔
1429
    }
1,383,312✔
1430

1431
    m_state.m_current_index++;
21,183,273✔
1432
    m_position++;
21,183,273✔
1433
    if (m_state.m_current_index == m_leaf.node_size()) {
21,183,273✔
1434
        m_key = load_leaf(ObjKey(m_key.value + 1));
212,184✔
1435
        m_leaf_invalid = !m_key;
212,184✔
1436
    }
212,184✔
1437
    else {
20,971,089✔
1438
        m_key = m_leaf.get_real_key(m_state.m_current_index);
20,971,089✔
1439
    }
20,971,089✔
1440
    return *this;
21,183,273✔
1441
}
21,244,167✔
1442

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

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

1485
ClusterTree::Iterator::pointer ClusterTree::Iterator::operator->() const
1486
{
22,666,449✔
1487
    if (update() || m_key != m_obj.get_key()) {
22,666,449✔
1488
        new (&m_obj) Obj(m_tree.get_table_ref(), m_leaf.get_mem(), m_key, m_state.m_current_index);
18,803,583✔
1489
    }
18,803,583✔
1490

1491
    return &m_obj;
22,666,449✔
1492
}
22,666,449✔
1493

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