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

realm / realm-core / thomas.goyne_232

13 Mar 2024 01:00AM UTC coverage: 91.787% (+0.9%) from 90.924%
thomas.goyne_232

Pull #7402

Evergreen

tgoyne
Add more UpdateIfNeeded tests
Pull Request #7402: Make Obj trivial and add a separate ObjCollectionParent type

94460 of 174600 branches covered (54.1%)

496 of 559 new or added lines in 21 files covered. (88.73%)

848 existing lines in 34 files now uncovered.

242761 of 264484 relevant lines covered (91.79%)

6342666.36 hits per line

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

85.13
/src/realm/list.hpp
1
/*************************************************************************
2
 *
3
 * Copyright 2016 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
#ifndef REALM_LIST_HPP
20
#define REALM_LIST_HPP
21

22
#include <realm/collection.hpp>
23

24
#include <realm/obj.hpp>
25
#include <realm/column_mixed.hpp>
26
#include <realm/obj_list.hpp>
27
#include <realm/array_basic.hpp>
28
#include <realm/array_integer.hpp>
29
#include <realm/array_key.hpp>
30
#include <realm/array_bool.hpp>
31
#include <realm/array_string.hpp>
32
#include <realm/array_binary.hpp>
33
#include <realm/array_timestamp.hpp>
34
#include <realm/array_ref.hpp>
35
#include <realm/array_fixed_bytes.hpp>
36
#include <realm/array_decimal128.hpp>
37
#include <realm/array_typed_link.hpp>
38
#include <realm/replication.hpp>
39

40
namespace realm {
41

42
class TableView;
43
class SortDescriptor;
44
class Group;
45
template <class>
46
class Lst;
47

48
template <class T>
49
using LstIterator = CollectionIterator<Lst<T>>;
50

51
/*
52
 * This class defines a virtual interface to a writable list
53
 */
54
class LstBase : public CollectionBase {
55
public:
56
    using CollectionBase::CollectionBase;
57

58
    virtual ~LstBase() {}
2,634,801✔
59
    virtual LstBasePtr clone() const = 0;
60
    virtual DataType get_data_type() const noexcept = 0;
61
    virtual void set_null(size_t ndx) = 0;
62
    virtual void set_any(size_t ndx, Mixed val) = 0;
63
    virtual void insert_null(size_t ndx) = 0;
64
    virtual void insert_any(size_t ndx, Mixed val) = 0;
65
    virtual void resize(size_t new_size) = 0;
66
    virtual void remove(size_t from, size_t to) = 0;
67
    virtual void move(size_t from, size_t to) = 0;
68
    virtual void swap(size_t ndx1, size_t ndx2) = 0;
69

70
protected:
71
    static constexpr CollectionType s_collection_type = CollectionType::List;
72
    void swap_repl(Replication* repl, size_t ndx1, size_t ndx2) const;
73
};
74

75
template <class T>
76
class Lst final : public CollectionBaseImpl<LstBase> {
77
public:
78
    using Base = CollectionBaseImpl<LstBase>;
79
    using iterator = LstIterator<T>;
80
    using value_type = T;
81

82
    Lst() = default;
156✔
83
    Lst(const Obj& owner, ColKey col_key)
84
        : Lst<T>(col_key)
85
    {
1,673,316✔
86
        this->set_owner(owner, col_key);
1,673,316✔
87
    }
1,673,316✔
88
    Lst(ColKey col_key)
89
        : Base(col_key)
90
    {
2,050,833✔
91
        if (!(col_key.is_list() || col_key.get_type() == col_type_Mixed)) {
2,050,833!
92
            throw InvalidArgument(ErrorCodes::TypeMismatch, "Property not a list");
×
93
        }
×
94

1,024,803✔
95
        check_column_type<T>(m_col_key);
2,050,833✔
96
    }
2,050,833✔
97
    Lst(DummyParent& parent, CollectionParent::Index index)
98
        : Base(parent, index)
99
    {
×
100
    }
×
101
    Lst(const Lst& other)
102
        : Base(other)
103
    {
2,313✔
104
    }
2,313✔
105
    Lst(Lst&&) noexcept;
106
    Lst& operator=(const Lst& other);
107
    Lst& operator=(Lst&& other) noexcept;
108

109
    iterator begin() const noexcept
110
    {
24,264✔
111
        return iterator{this, 0};
24,264✔
112
    }
24,264✔
113

114
    iterator end() const noexcept
115
    {
24,126✔
116
        return iterator{this, size()};
24,126✔
117
    }
24,126✔
118

119
    T get(size_t ndx) const;
120
    size_t find_first(const T& value) const;
121
    T set(size_t ndx, T value);
122
    void insert(size_t ndx, T value);
123
    T remove(size_t ndx);
124

125
    // Overriding members of CollectionBase:
126
    size_t size() const final;
127
    void clear() final;
128
    Mixed get_any(size_t ndx) const final;
129
    bool is_null(size_t ndx) const final;
130
    CollectionBasePtr clone_collection() const final;
131
    util::Optional<Mixed> min(size_t* return_ndx = nullptr) const final;
132
    util::Optional<Mixed> max(size_t* return_ndx = nullptr) const final;
133
    util::Optional<Mixed> sum(size_t* return_cnt = nullptr) const final;
134
    util::Optional<Mixed> avg(size_t* return_cnt = nullptr) const final;
135
    void sort(std::vector<size_t>& indices, bool ascending = true) const final;
136
    void distinct(std::vector<size_t>& indices, util::Optional<bool> sort_order = util::none) const final;
137

138
    // Overriding members of LstBase:
139
    LstBasePtr clone() const final;
140
    void set_null(size_t ndx) final;
141
    void set_any(size_t ndx, Mixed val) final;
142
    DataType get_data_type() const noexcept final
143
    {
240,441✔
144
        return ColumnTypeTraits<T>::id;
240,441✔
145
    }
240,441✔
146
    void insert_null(size_t ndx) final;
147
    void insert_any(size_t ndx, Mixed val) final;
148
    size_t find_any(Mixed val) const final;
149
    void resize(size_t new_size) final;
150
    void remove(size_t from, size_t to) final;
151
    void move(size_t from, size_t to) final;
152
    void swap(size_t ndx1, size_t ndx2) final;
153

154
    // Lst<T> interface:
155
    T remove(const iterator& it);
156

157
    void add(T value)
158
    {
1,438,179✔
159
        insert(size(), std::move(value));
1,438,179✔
160
    }
1,438,179✔
161

162
    T operator[](size_t ndx) const
163
    {
8,196✔
164
        return this->get(ndx);
8,196✔
165
    }
8,196✔
166

167
    template <typename Func>
168
    void find_all(value_type value, Func&& func) const
169
    {
12✔
170
        if (update()) {
12!
171
            if constexpr (std::is_same_v<T, Mixed>) {
8✔
172
                if (value.is_null()) {
8✔
173
                    // if value is null then we find also all the unresolved links with a O(n lg n) scan
4✔
174
                    find_all_mixed_unresolved_links(std::forward<Func>(func));
8✔
175
                }
8✔
176
            }
8✔
177
            m_tree->find_all(value, std::forward<Func>(func));
8✔
178
        }
8✔
179
    }
12✔
180

181
    inline const BPlusTree<T>& get_tree() const
182
    {
2,310✔
183
        return *m_tree;
2,310✔
184
    }
2,310✔
185

186
    UpdateStatus update_if_needed() const final
187
    {
32,911,707✔
188
        auto status = Base::get_update_status();
32,911,707✔
189
        switch (status) {
32,911,707✔
190
            case UpdateStatus::Detached: {
522✔
191
                m_tree.reset();
522✔
192
                return UpdateStatus::Detached;
522✔
193
            }
×
194
            case UpdateStatus::NoChange:
31,132,047✔
195
                if (m_tree && m_tree->is_attached()) {
31,132,428✔
196
                    return UpdateStatus::NoChange;
30,539,877✔
197
                }
30,539,877✔
198
                // The tree has not been initialized yet for this accessor, so
294,237✔
199
                // perform lazy initialization by treating it as an update.
294,237✔
200
                [[fallthrough]];
592,170✔
201
            case UpdateStatus::Updated: {
2,366,031✔
202
                return init_from_parent(false);
2,366,031✔
UNCOV
203
            }
×
204
        }
×
205
        REALM_UNREACHABLE();
206
    }
×
207

208
    void ensure_created()
209
    {
8,094,771✔
210
        if (Base::should_update() || !(m_tree && m_tree->is_attached())) {
8,094,903✔
211
            // When allow_create is true, init_from_parent will always succeed
163,332✔
212
            // In case of errors, an exception is thrown.
163,332✔
213
            constexpr bool allow_create = true;
326,967✔
214
            init_from_parent(allow_create); // Throws
326,967✔
215
        }
326,967✔
216
    }
8,094,771✔
217

218
    /// Update the accessor and return true if it is attached after the update.
219
    inline bool update() const
220
    {
19,685,481✔
221
        return update_if_needed() != UpdateStatus::Detached;
19,685,481✔
222
    }
19,685,481✔
223

224
    size_t translate_index(size_t ndx) const noexcept override
225
    {
1,436,658✔
226
        if constexpr (std::is_same_v<T, ObjKey>) {
1,436,658✔
227
            return _impl::virtual2real(m_tree.get(), ndx);
1,291,152✔
228
        }
1,291,152✔
229
        else {
1,291,152✔
230
            return ndx;
1,291,152✔
231
        }
1,291,152✔
232
    }
1,436,658✔
233

234
protected:
235
    // Friend because it needs access to `m_tree` in the implementation of
236
    // `ObjCollectionBase::get_mutable_tree()`.
237
    friend class LnkLst;
238

239
    // `do_` methods here perform the action after preconditions have been
240
    // checked (bounds check, writability, etc.).
241
    void do_set(size_t ndx, T value);
242
    void do_insert(size_t ndx, T value);
243
    void do_remove(size_t ndx);
244
    void do_clear();
245

246
    // BPlusTree must be wrapped in an `std::unique_ptr` because it is not
247
    // default-constructible, due to its `Allocator&` member.
248
    mutable std::unique_ptr<BPlusTree<T>> m_tree;
249

250
    using Base::bump_content_version;
251
    using Base::get_alloc;
252
    using Base::m_col_key;
253
    using Base::m_nullable;
254

255
    UpdateStatus init_from_parent(bool allow_create) const
256
    {
2,691,729✔
257
        if (!m_tree) {
2,691,729✔
258
            m_tree.reset(new BPlusTree<T>(get_alloc()));
1,724,205✔
259
            const ArrayParent* parent = this;
1,724,205✔
260
            m_tree->set_parent(const_cast<ArrayParent*>(parent), 0);
1,724,205✔
261
        }
1,724,205✔
262
        Base::update_content_version();
2,691,729✔
263
        return do_init_from_parent(m_tree.get(), 0, allow_create);
2,691,729✔
264
    }
2,691,729✔
265

266
    template <class Func>
267
    void find_all_mixed_unresolved_links(Func&& func) const
268
    {
269
        for (size_t i = 0; i < m_tree->size(); ++i) {
270
            auto mixed = m_tree->get(i);
271
            if (mixed.is_unresolved_link()) {
272
                func(i);
273
            }
274
        }
275
    }
276

277
private:
278
    T do_get(size_t ndx, const char* msg) const;
279
};
280

281
template <>
282
class Lst<Mixed> final : public CollectionBaseImpl<LstBase>, public CollectionParent {
283
public:
284
    using Base = CollectionBaseImpl<LstBase>;
285
    using iterator = LstIterator<Mixed>;
286
    using value_type = Mixed;
287

288
    Lst() = default;
×
289
    Lst(const Obj& owner, ColKey col_key)
290
        : Lst(col_key)
291
    {
14,901✔
292
        this->set_owner(owner, col_key);
14,901✔
293
    }
14,901✔
294
    Lst(ColKey col_key, size_t level = 1)
295
        : Base(col_key)
296
        , CollectionParent(level)
297
    {
23,667✔
298
        check_column_type<Mixed>(m_col_key);
23,667✔
299
    }
23,667✔
300
    Lst(CollectionParent& parent, Index index)
301
        : Base(parent, index)
302
    {
1,272✔
303
    }
1,272✔
304
    Lst(const Lst& other)
305
        : Base(other)
306
        , CollectionParent(other.get_level())
307
    {
3,342✔
308
    }
3,342✔
309
    Lst(Lst&&) noexcept;
310
    Lst& operator=(const Lst& other);
311
    Lst& operator=(Lst&& other) noexcept;
312

313
    iterator begin() const noexcept
314
    {
300✔
315
        return iterator{this, 0};
300✔
316
    }
300✔
317

318
    iterator end() const noexcept
319
    {
300✔
320
        return iterator{this, size()};
300✔
321
    }
300✔
322

323
    Mixed get(size_t ndx) const
324
    {
37,674✔
325
        return do_get(ndx, "get()");
37,674✔
326
    }
37,674✔
327
    size_t find_first(const Mixed& value) const;
328
    Mixed set(size_t ndx, Mixed value);
329

330
    void insert(size_t ndx, Mixed value);
331
    Mixed remove(size_t ndx);
332

333
    void insert_collection(const PathElement&, CollectionType dict_or_list) override;
334
    void set_collection(const PathElement& path_element, CollectionType dict_or_list) override;
335
    DictionaryPtr get_dictionary(const PathElement& path_elem) const override;
336
    ListMixedPtr get_list(const PathElement& path_elem) const override;
337

338
    int64_t get_key(size_t ndx)
339
    {
628✔
340
        return m_tree->get_key(ndx);
628✔
341
    }
628✔
342

343
    // Overriding members of CollectionBase:
344
    size_t size() const final
345
    {
103,467✔
346
        return update() ? m_tree->size() : 0;
99,972✔
347
    }
103,467✔
348
    void clear() final;
349
    Mixed get_any(size_t ndx) const final
350
    {
22,704✔
351
        return get(ndx);
22,704✔
352
    }
22,704✔
353
    bool is_null(size_t ndx) const final
354
    {
42✔
355
        return get(ndx).is_null();
42✔
356
    }
42✔
357
    CollectionBasePtr clone_collection() const final
358
    {
×
359
        return std::make_unique<Lst<Mixed>>(*this);
×
360
    }
×
361
    util::Optional<Mixed> min(size_t* return_ndx = nullptr) const final;
362
    util::Optional<Mixed> max(size_t* return_ndx = nullptr) const final;
363
    util::Optional<Mixed> sum(size_t* return_cnt = nullptr) const final;
364
    util::Optional<Mixed> avg(size_t* return_cnt = nullptr) const final;
365
    void sort(std::vector<size_t>& indices, bool ascending = true) const final;
366
    void distinct(std::vector<size_t>& indices, util::Optional<bool> sort_order = util::none) const final;
367

368
    // Overriding members of LstBase:
369
    LstBasePtr clone() const final
370
    {
×
371
        return std::make_unique<Lst<Mixed>>(*this);
×
372
    }
×
373
    void set_null(size_t ndx) final
374
    {
×
375
        set(ndx, Mixed());
×
376
    }
×
377
    void set_any(size_t ndx, Mixed val) final
378
    {
696✔
379
        set(ndx, val);
696✔
380
    }
696✔
381
    DataType get_data_type() const noexcept final
382
    {
2,142✔
383
        return type_Mixed;
2,142✔
384
    }
2,142✔
385
    void insert_null(size_t ndx) final
386
    {
24✔
387
        insert(ndx, Mixed());
24✔
388
    }
24✔
389
    void insert_any(size_t ndx, Mixed val) final
390
    {
1,284✔
391
        insert(ndx, val);
1,284✔
392
    }
1,284✔
393
    size_t find_any(Mixed val) const final
394
    {
396✔
395
        return find_first(val);
396✔
396
    }
396✔
397
    void resize(size_t new_size) final;
398
    void remove(size_t from, size_t to) final;
399
    void move(size_t from, size_t to) final;
400
    void swap(size_t ndx1, size_t ndx2) final;
401

402
    // Lst<T> interface:
403
    Mixed remove(const iterator& it);
404

405
    void add(Mixed value)
406
    {
13,296✔
407
        insert(size(), std::move(value));
13,296✔
408
    }
13,296✔
409

410
    template <typename T>
411
    void add_json(const T&);
412

413
    Mixed operator[](size_t ndx) const
414
    {
×
415
        return this->get(ndx);
×
416
    }
×
417

418
    template <typename Func>
419
    void find_all(value_type value, Func&& func) const
420
    {
2✔
421
        if (update()) {
2✔
422
            if (value.is_null()) {
2✔
423
                // if value is null then we find also all the unresolved links with a O(n lg n) scan
1✔
424
                find_all_mixed_unresolved_links(std::forward<Func>(func));
2✔
425
            }
2✔
426
            m_tree->find_all(value, std::forward<Func>(func));
2✔
427
        }
2✔
428
    }
2✔
429

430
    inline const BPlusTree<Mixed>& get_tree() const
431
    {
×
432
        return *m_tree;
×
433
    }
×
434

435
    UpdateStatus update_if_needed() const final;
436

437
    void ensure_created()
438
    {
18,954✔
439
        if (Base::should_update() || !(m_tree && m_tree->is_attached())) {
18,954✔
440
            // When allow_create is true, init_from_parent will always succeed
3,261✔
441
            // In case of errors, an exception is thrown.
3,261✔
442
            constexpr bool allow_create = true;
6,522✔
443
            init_from_parent(allow_create); // Throws
6,522✔
444
        }
6,522✔
445
    }
18,954✔
446

447
    /// Update the accessor and return true if it is attached after the update.
448
    inline bool update() const
449
    {
120,681✔
450
        return update_if_needed() != UpdateStatus::Detached;
120,681✔
451
    }
120,681✔
452

453
    // Overriding members in CollectionParent
454
    FullPath get_path() const override
455
    {
12✔
456
        return Base::get_path();
12✔
457
    }
12✔
458

459
    Path get_short_path() const override
460
    {
10,632✔
461
        update();
10,632✔
462
        return Base::get_short_path();
10,632✔
463
    }
10,632✔
464

465
    StablePath get_stable_path() const override
466
    {
34,620✔
467
        return Base::get_stable_path();
34,620✔
468
    }
34,620✔
469

470
    ColKey get_col_key() const noexcept override
471
    {
22,560✔
472
        return Base::get_col_key();
22,560✔
473
    }
22,560✔
474

475
    void add_index(Path& path, const Index& ndx) const final;
476
    size_t find_index(const Index& ndx) const final;
477

478
    bool nullify(ObjLink);
479
    bool replace_link(ObjLink old_link, ObjLink replace_link);
480
    bool remove_backlinks(CascadeState& state) const;
481
    TableRef get_table() const noexcept override
482
    {
10,782✔
483
        return get_obj().get_table();
10,782✔
484
    }
10,782✔
485
    const Obj& get_object() const noexcept override
486
    {
4,824✔
487
        return get_obj();
4,824✔
488
    }
4,824✔
489
    uint32_t parent_version() const noexcept override
490
    {
16,614✔
491
        return m_parent_version;
16,614✔
492
    }
16,614✔
493
    ref_type get_collection_ref(Index, CollectionType) const override;
494
    bool check_collection_ref(Index, CollectionType) const noexcept override;
495
    void set_collection_ref(Index, ref_type ref, CollectionType) override;
496

497
    void to_json(std::ostream&, JSONOutputMode, util::FunctionRef<void(const Mixed&)>) const override;
498

499
private:
500
    // `do_` methods here perform the action after preconditions have been
501
    // checked (bounds check, writability, etc.).
502
    void do_set(size_t ndx, Mixed value);
503
    void do_insert(size_t ndx, Mixed value);
504
    void do_remove(size_t ndx);
505

506
    // BPlusTree must be wrapped in an `std::unique_ptr` because it is not
507
    // default-constructible, due to its `Allocator&` member.
508
    mutable std::unique_ptr<BPlusTreeMixed> m_tree;
509

510
    using Base::bump_content_version;
511
    using Base::get_alloc;
512
    using Base::m_col_key;
513
    using Base::m_nullable;
514

515
    UpdateStatus init_from_parent(bool allow_create) const;
516

517
    template <class Func>
518
    void find_all_mixed_unresolved_links(Func&& func) const
519
    {
2✔
520
        for (size_t i = 0; i < m_tree->size(); ++i) {
8✔
521
            auto mixed = m_tree->get(i);
6✔
522
            if (mixed.is_unresolved_link()) {
6✔
523
                func(i);
2✔
524
            }
2✔
525
        }
6✔
526
    }
2✔
527

528
    static Mixed unresolved_to_null(Mixed value) noexcept
529
    {
74,280✔
530
        return value.is_unresolved_link() ? Mixed{} : value;
74,076✔
531
    }
74,280✔
532
    Mixed do_get(size_t ndx, const char* msg) const
533
    {
40,704✔
534
        const auto current_size = size();
40,704✔
535
        CollectionBase::validate_index(msg, ndx, current_size);
40,704✔
536

20,352✔
537
        return unresolved_to_null(m_tree->get(ndx));
40,704✔
538
    }
40,704✔
539
    bool clear_backlink(size_t ndx, CascadeState& state) const;
540
};
541

542
// Specialization of Lst<StringData>:
543
template <>
544
void Lst<StringData>::do_insert(size_t, StringData);
545
template <>
546
void Lst<StringData>::do_set(size_t, StringData);
547
template <>
548
void Lst<StringData>::do_remove(size_t);
549
template <>
550
void Lst<StringData>::do_clear();
551
// Specialization of Lst<ObjKey>:
552
template <>
553
void Lst<ObjKey>::do_set(size_t, ObjKey);
554
template <>
555
void Lst<ObjKey>::do_insert(size_t, ObjKey);
556
template <>
557
void Lst<ObjKey>::do_remove(size_t);
558
template <>
559
void Lst<ObjKey>::do_clear();
560

561
extern template class Lst<ObjKey>;
562

563
// Specialization of Lst<ObjLink>:
564
template <>
565
void Lst<ObjLink>::do_set(size_t, ObjLink);
566
template <>
567
void Lst<ObjLink>::do_insert(size_t, ObjLink);
568
template <>
569
void Lst<ObjLink>::do_remove(size_t);
570
extern template class Lst<ObjLink>;
571

572
// Extern template declarations for lists of primitives:
573
extern template class Lst<int64_t>;
574
extern template class Lst<bool>;
575
extern template class Lst<StringData>;
576
extern template class Lst<BinaryData>;
577
extern template class Lst<Timestamp>;
578
extern template class Lst<float>;
579
extern template class Lst<double>;
580
extern template class Lst<Decimal128>;
581
extern template class Lst<ObjectId>;
582
extern template class Lst<UUID>;
583
extern template class Lst<util::Optional<int64_t>>;
584
extern template class Lst<util::Optional<bool>>;
585
extern template class Lst<util::Optional<float>>;
586
extern template class Lst<util::Optional<double>>;
587
extern template class Lst<util::Optional<ObjectId>>;
588
extern template class Lst<util::Optional<UUID>>;
589

590
class LnkLst final : public ObjCollectionBase<LstBase> {
591
public:
592
    using Base = ObjCollectionBase<LstBase>;
593
    using value_type = ObjKey;
594
    using iterator = CollectionIterator<LnkLst>;
595

596
    LnkLst() = default;
72✔
597

598
    LnkLst(const Obj& owner, ColKey col_key)
599
        : m_list(owner, col_key)
600
    {
521,370✔
601
    }
521,370✔
602
    LnkLst(ColKey col_key)
603
        : m_list(col_key)
604
    {
31,686✔
605
    }
31,686✔
606

607
    LnkLst(const LnkLst& other) = default;
2,181✔
608
    LnkLst(LnkLst&& other) = default;
609
    LnkLst& operator=(const LnkLst& other) = default;
8✔
610
    LnkLst& operator=(LnkLst&& other) = default;
52✔
611
    bool operator==(const LnkLst& other) const;
612
    bool operator!=(const LnkLst& other) const;
613

614
    Obj operator[](size_t ndx) const
615
    {
18✔
616
        return get_object(ndx);
18✔
617
    }
18✔
618

619
    ObjKey get(size_t ndx) const;
620
    size_t find_first(const ObjKey&) const;
621
    void insert(size_t ndx, ObjKey value);
622
    ObjKey set(size_t ndx, ObjKey value);
623
    ObjKey remove(size_t ndx);
624

625
    void add(ObjKey value)
626
    {
6,374,751✔
627
        // FIXME: Should this add to the end of the unresolved list?
3,187,497✔
628
        insert(size(), value);
6,374,751✔
629
    }
6,374,751✔
630
    void add(const Obj& obj)
631
    {
10✔
632
        if (get_target_table() != obj.get_table()) {
10✔
633
            throw InvalidArgument("LnkLst::add: Wrong object type");
×
634
        }
×
635
        add(obj.get_key());
10✔
636
    }
10✔
637

638
    // Overriding members of CollectionBase:
639
    using CollectionBase::get_owner_key;
640
    size_t size() const final;
641
    bool is_null(size_t ndx) const final;
642
    Mixed get_any(size_t ndx) const final;
643
    void clear() final;
644
    util::Optional<Mixed> min(size_t* return_ndx = nullptr) const final;
645
    util::Optional<Mixed> max(size_t* return_ndx = nullptr) const final;
646
    util::Optional<Mixed> sum(size_t* return_cnt = nullptr) const final;
647
    util::Optional<Mixed> avg(size_t* return_cnt = nullptr) const final;
648
    CollectionBasePtr clone_collection() const final;
649
    void sort(std::vector<size_t>& indices, bool ascending = true) const final;
650
    void distinct(std::vector<size_t>& indices, util::Optional<bool> sort_order = util::none) const final;
651
    const Obj& get_obj() const noexcept final;
652
    bool is_attached() const noexcept final
653
    {
10,632✔
654
        return m_list.is_attached();
10,632✔
655
    }
10,632✔
656
    bool has_changed() const noexcept final;
657
    ColKey get_col_key() const noexcept final;
658
    CollectionType get_collection_type() const noexcept override
659
    {
24✔
660
        return CollectionType::List;
24✔
661
    }
24✔
662

663
    FullPath get_path() const noexcept final
664
    {
×
665
        return m_list.get_path();
×
666
    }
×
667

668
    Path get_short_path() const noexcept final
669
    {
72✔
670
        return m_list.get_short_path();
72✔
671
    }
72✔
672

673
    StablePath get_stable_path() const noexcept final
674
    {
1,986✔
675
        return m_list.get_stable_path();
1,986✔
676
    }
1,986✔
677

678
    // Overriding members of LstBase:
679
    LstBasePtr clone() const override
680
    {
6✔
681
        return clone_linklist();
6✔
682
    }
6✔
683
    // Overriding members of ObjList:
684
    LinkCollectionPtr clone_obj_list() const override
685
    {
1,527✔
686
        return clone_linklist();
1,527✔
687
    }
1,527✔
688
    void set_null(size_t ndx) final;
689
    void set_any(size_t ndx, Mixed val) final;
690
    DataType get_data_type() const noexcept final
691
    {
2,490✔
692
        return type_Link;
2,490✔
693
    }
2,490✔
694
    void insert_null(size_t ndx) final;
695
    void insert_any(size_t ndx, Mixed val) final;
696
    size_t find_any(Mixed value) const final;
697
    void resize(size_t new_size) final;
698
    void remove(size_t from, size_t to) final;
699
    void move(size_t from, size_t to) final;
700
    void swap(size_t ndx1, size_t ndx2) final;
701

702
    // Overriding members of ObjList:
703
    Obj get_object(size_t ndx) const final
704
    {
245,961✔
705
        ObjKey key = this->get(ndx);
245,961✔
706
        return get_target_table()->get_object(key);
245,961✔
707
    }
245,961✔
708
    ObjKey get_key(size_t ndx) const final
709
    {
10,140✔
710
        return get(ndx);
10,140✔
711
    }
10,140✔
712

713
    // LnkLst interface:
714

715
    std::unique_ptr<LnkLst> clone_linklist() const
716
    {
2,151✔
717
        // FIXME: The copy constructor requires this.
1,071✔
718
        update_if_needed();
2,151✔
719
        return std::make_unique<LnkLst>(*this);
2,151✔
720
    }
2,151✔
721

722
    template <class Func>
723
    void find_all(ObjKey value, Func&& func) const
724
    {
14✔
725
        if (value.is_unresolved())
14✔
726
            return;
2✔
727

6✔
728
        m_list.find_all(value, [&](size_t ndx) {
12✔
729
            func(real2virtual(ndx));
12✔
730
        });
12✔
731
    }
12✔
732

733
    // Create a new object in insert a link to it
734
    Obj create_and_insert_linked_object(size_t ndx);
735

736
    // Create a new object and link it. If an embedded object
737
    // is already set, it will be removed. TBD: If a non-embedded
738
    // object is already set, we throw LogicError (to prevent
739
    // dangling objects, since they do not delete automatically
740
    // if they are not embedded...)
741
    Obj create_and_set_linked_object(size_t ndx);
742

743
    // to be implemented:
744
    Obj clear_linked_object(size_t ndx);
745

746
    TableView get_sorted_view(SortDescriptor order) const;
747
    TableView get_sorted_view(ColKey column_key, bool ascending = true) const;
748
    void remove_target_row(size_t link_ndx);
749
    void remove_all_target_rows();
750

751
    iterator begin() const noexcept
752
    {
×
753
        return iterator{this, 0};
×
754
    }
×
755
    iterator end() const noexcept
756
    {
×
757
        return iterator{this, size()};
×
758
    }
×
759

760
    const BPlusTree<ObjKey>& get_tree() const
761
    {
×
762
        return m_list.get_tree();
×
763
    }
×
764

765
    void set_owner(const Obj& obj, ColKey ck) override
766
    {
31,686✔
767
        m_list.set_owner(obj, ck);
31,686✔
768
    }
31,686✔
769

770
    void set_owner(std::shared_ptr<CollectionParent> parent, CollectionParent::Index index) override
771
    {
×
772
        m_list.set_owner(std::move(parent), index);
×
773
    }
×
774

775
    void replace_link(ObjKey old_link, ObjKey new_link);
776
    void to_json(std::ostream&, JSONOutputMode, util::FunctionRef<void(const Mixed&)>) const override;
777

778
private:
779
    friend class TableView;
780
    friend class Query;
781

782
    Lst<ObjKey> m_list;
783

784
    // Overriding members of ObjCollectionBase:
785

786
    UpdateStatus do_update_if_needed() const final
787
    {
13,212,228✔
788
        return m_list.update_if_needed();
13,212,228✔
789
    }
13,212,228✔
790

791
    BPlusTree<ObjKey>* get_mutable_tree() const final
792
    {
6,478,239✔
793
        return m_list.m_tree.get();
6,478,239✔
794
    }
6,478,239✔
795
};
796

797

798
// Implementation:
799

800
inline void LstBase::swap_repl(Replication* repl, size_t ndx1, size_t ndx2) const
801
{
258✔
802
    if (ndx2 < ndx1)
258✔
803
        std::swap(ndx1, ndx2);
36✔
804
    repl->list_move(*this, ndx2, ndx1);
258✔
805
    if (ndx1 + 1 != ndx2)
258✔
806
        repl->list_move(*this, ndx1 + 1, ndx2);
204✔
807
}
258✔
808

809
template <class T>
810
inline Lst<T>::Lst(Lst&& other) noexcept
811
    : Base(static_cast<Base&&>(other))
812
    , m_tree(std::exchange(other.m_tree, nullptr))
813
{
×
814
    if (m_tree) {
×
815
        m_tree->set_parent(this, 0);
×
816
    }
×
817
}
×
818

819
template <class T>
820
Lst<T>& Lst<T>::operator=(const Lst& other)
821
{
144✔
822
    Base::operator=(static_cast<const Base&>(other));
144✔
823

72✔
824
    if (this != &other) {
144✔
825
        // Just reset the pointer and rely on init_from_parent() being called
72✔
826
        // when the accessor is actually used.
72✔
827
        m_tree.reset();
144✔
828
        Base::reset_content_version();
144✔
829
    }
144✔
830

72✔
831
    return *this;
144✔
832
}
144✔
833

834
template <class T>
835
inline Lst<T>& Lst<T>::operator=(Lst&& other) noexcept
836
{
126✔
837
    Base::operator=(static_cast<Base&&>(other));
126✔
838

63✔
839
    if (this != &other) {
126!
840
        m_tree = std::exchange(other.m_tree, nullptr);
126✔
841
        if (m_tree) {
126!
842
            m_tree->set_parent(this, 0);
×
843
        }
×
844
    }
126✔
845

63✔
846
    return *this;
126✔
847
}
126✔
848

849
template <class T>
850
inline T Lst<T>::remove(const iterator& it)
851
{
×
852
    return remove(it.index());
×
853
}
×
854

855
template <class T>
856
inline size_t Lst<T>::size() const
857
{
18,734,235✔
858
    return update() ? m_tree->size() : 0;
18,364,974✔
859
}
18,734,235✔
860

861
template <class T>
862
inline bool Lst<T>::is_null(size_t ndx) const
863
{
438✔
864
    return m_nullable && value_is_null(get(ndx));
438!
865
}
438✔
866

867
template <class T>
868
inline Mixed Lst<T>::get_any(size_t ndx) const
869
{
74,361✔
870
    return get(ndx);
74,361✔
871
}
74,361✔
872

873
template <class T>
874
inline void Lst<T>::do_set(size_t ndx, T value)
875
{
15,486✔
876
    m_tree->set(ndx, value);
15,486✔
877
}
15,486✔
878

879
template <class T>
880
inline void Lst<T>::do_insert(size_t ndx, T value)
881
{
994,410✔
882
    m_tree->insert(ndx, value);
994,410✔
883
}
994,410✔
884

885
template <class T>
886
inline void Lst<T>::do_remove(size_t ndx)
887
{
188,265✔
888
    m_tree->erase(ndx);
188,265✔
889
}
188,265✔
890

891
template <class T>
892
inline void Lst<T>::do_clear()
893
{
1,662✔
894
    m_tree->clear();
1,662✔
895
}
1,662✔
896

897
template <typename U>
898
inline Lst<U> Obj::get_list(ColKey col_key) const
899
{
1,004,885✔
900
    return Lst<U>(*this, col_key);
1,004,885✔
901
}
1,004,885✔
902

903
template <typename U>
904
inline LstPtr<U> Obj::get_list_ptr(ColKey col_key) const
905
{
2,383✔
906
    return std::make_unique<Lst<U>>(*this, col_key);
2,383✔
907
}
2,383✔
908

909
inline LnkLst Obj::get_linklist(ColKey col_key) const
910
{
214,095✔
911
    return LnkLst(*this, col_key);
214,095✔
912
}
214,095✔
913

914
inline LnkLstPtr Obj::get_linklist_ptr(ColKey col_key) const
915
{
307,278✔
916
    return std::make_unique<LnkLst>(*this, col_key);
307,278✔
917
}
307,278✔
918

919
inline LnkLst Obj::get_linklist(StringData col_name) const
920
{
17,673✔
921
    return get_linklist(get_column_key(col_name));
17,673✔
922
}
17,673✔
923

924
template <class T>
925
void Lst<T>::clear()
926
{
12,279✔
927
    if (size() > 0) {
12,279✔
928
        if (Replication* repl = Base::get_replication()) {
4,980✔
929
            repl->list_clear(*this);
4,764✔
930
        }
4,764✔
931
        do_clear();
4,980✔
932
        bump_content_version();
4,980✔
933
    }
4,980✔
934
}
12,279✔
935

936
template <class T>
937
inline CollectionBasePtr Lst<T>::clone_collection() const
938
{
×
939
    return std::make_unique<Lst<T>>(*this);
×
940
}
×
941

942
template <class T>
943
inline T Lst<T>::get(size_t ndx) const
944
{
1,483,326✔
945
    return do_get(ndx, "get()");
1,483,326✔
946
}
1,483,326✔
947

948
template <class T>
949
inline T Lst<T>::do_get(size_t ndx, const char* msg) const
950
{
1,709,790✔
951
    const auto current_size = size();
1,709,790✔
952
    CollectionBase::validate_index(msg, ndx, current_size);
1,709,790✔
953

856,305✔
954
    return m_tree->get(ndx);
1,709,790✔
955
}
1,709,790✔
956

957
template <class T>
958
inline size_t Lst<T>::find_first(const T& value) const
959
{
90,738✔
960
    if (!update())
90,738✔
961
        return not_found;
×
962

45,333✔
963
    return m_tree->find_first(value);
90,738✔
964
}
90,738✔
965

966
template <class T>
967
inline util::Optional<Mixed> Lst<T>::min(size_t* return_ndx) const
968
{
426,930✔
969
    if (update()) {
426,930✔
970
        return MinHelper<T>::eval(*m_tree, return_ndx);
426,924✔
971
    }
426,924✔
972
    return MinHelper<T>::not_found(return_ndx);
6✔
973
}
6✔
974

975
template <class T>
976
inline util::Optional<Mixed> Lst<T>::max(size_t* return_ndx) const
977
{
426,924✔
978
    if (update()) {
426,924✔
979
        return MaxHelper<T>::eval(*m_tree, return_ndx);
426,918✔
980
    }
426,918✔
981
    return MaxHelper<T>::not_found(return_ndx);
6✔
982
}
6✔
983

984
template <class T>
985
inline util::Optional<Mixed> Lst<T>::sum(size_t* return_cnt) const
986
{
516✔
987
    if (update()) {
516✔
988
        return SumHelper<T>::eval(*m_tree, return_cnt);
510✔
989
    }
510✔
990
    return SumHelper<T>::not_found(return_cnt);
6✔
991
}
6✔
992

993
template <class T>
994
inline util::Optional<Mixed> Lst<T>::avg(size_t* return_cnt) const
995
{
516✔
996
    if (update()) {
516✔
997
        return AverageHelper<T>::eval(*m_tree, return_cnt);
510✔
998
    }
510✔
999
    return AverageHelper<T>::not_found(return_cnt);
6✔
1000
}
6✔
1001

1002
template <class T>
1003
inline LstBasePtr Lst<T>::clone() const
1004
{
12✔
1005
    return std::make_unique<Lst<T>>(*this);
12✔
1006
}
12✔
1007

1008
template <class T>
1009
inline void Lst<T>::set_null(size_t ndx)
1010
{
84✔
1011
    set(ndx, BPlusTree<T>::default_value(m_nullable));
84✔
1012
}
84✔
1013

1014
template <class T>
1015
void Lst<T>::set_any(size_t ndx, Mixed val)
1016
{
2,844✔
1017
    if (val.is_null()) {
2,844✔
1018
        set_null(ndx);
60✔
1019
    }
60✔
1020
    else {
2,784✔
1021
        set(ndx, val.get<typename util::RemoveOptional<T>::type>());
2,784✔
1022
    }
2,784✔
1023
}
2,844✔
1024

1025
template <class T>
1026
inline void Lst<T>::insert_null(size_t ndx)
1027
{
636✔
1028
    insert(ndx, BPlusTree<T>::default_value(m_nullable));
636✔
1029
}
636✔
1030

1031
template <class T>
1032
inline void Lst<T>::insert_any(size_t ndx, Mixed val)
1033
{
242,298✔
1034
    if (val.is_null()) {
242,298✔
1035
        insert_null(ndx);
156✔
1036
    }
156✔
1037
    else {
242,142✔
1038
        insert(ndx, val.get<typename util::RemoveOptional<T>::type>());
242,142✔
1039
    }
242,142✔
1040
}
242,298✔
1041

1042
template <class T>
1043
size_t Lst<T>::find_any(Mixed val) const
1044
{
1,704✔
1045
    if (val.is_null()) {
1,704✔
1046
        return find_first(BPlusTree<T>::default_value(m_nullable));
198✔
1047
    }
198✔
1048
    else if (val.get_type() == ColumnTypeTraits<T>::id) {
1,506✔
1049
        return find_first(val.get<typename util::RemoveOptional<T>::type>());
1,446✔
1050
    }
1,446✔
1051
    return realm::not_found;
60✔
1052
}
60✔
1053

1054
template <class T>
1055
void Lst<T>::resize(size_t new_size)
1056
{
1,182✔
1057
    size_t current_size = size();
1,182✔
1058
    if (new_size != current_size) {
1,182✔
1059
        while (new_size > current_size) {
1,530✔
1060
            insert_null(current_size++);
348✔
1061
        }
348✔
1062
        remove(new_size, current_size);
1,182✔
1063
        Base::bump_both_versions();
1,182✔
1064
    }
1,182✔
1065
}
1,182✔
1066

1067
template <class T>
1068
inline void Lst<T>::remove(size_t from, size_t to)
1069
{
3,789✔
1070
    while (from < to) {
9,510✔
1071
        remove(--to);
5,721✔
1072
    }
5,721✔
1073
}
3,789✔
1074

1075
template <class T>
1076
void Lst<T>::move(size_t from, size_t to)
1077
{
1,104✔
1078
    auto sz = size();
1,104✔
1079
    CollectionBase::validate_index("move()", from, sz);
1,104✔
1080
    CollectionBase::validate_index("move()", to, sz);
1,104✔
1081

552✔
1082
    if (from != to) {
1,104✔
1083
        if (Replication* repl = Base::get_replication()) {
1,092✔
1084
            repl->list_move(*this, from, to);
1,068✔
1085
        }
1,068✔
1086
        if (to > from) {
1,092✔
1087
            to++;
558✔
1088
        }
558✔
1089
        else {
534✔
1090
            from++;
534✔
1091
        }
534✔
1092
        // We use swap here as it handles the special case for StringData where
546✔
1093
        // 'to' and 'from' points into the same array. In this case you cannot
546✔
1094
        // set an entry with the result of a get from another entry in the same
546✔
1095
        // leaf.
546✔
1096
        m_tree->insert(to, BPlusTree<T>::default_value(m_nullable));
1,092✔
1097
        m_tree->swap(from, to);
1,092✔
1098
        m_tree->erase(from);
1,092✔
1099

546✔
1100
        bump_content_version();
1,092✔
1101
    }
1,092✔
1102
}
1,104✔
1103

1104
template <class T>
1105
void Lst<T>::swap(size_t ndx1, size_t ndx2)
1106
{
408✔
1107
    auto sz = size();
408✔
1108
    CollectionBase::validate_index("swap()", ndx1, sz);
408✔
1109
    CollectionBase::validate_index("swap()", ndx2, sz);
408✔
1110

204✔
1111
    if (ndx1 != ndx2) {
408✔
1112
        if (Replication* repl = Base::get_replication()) {
390✔
1113
            LstBase::swap_repl(repl, ndx1, ndx2);
246✔
1114
        }
246✔
1115
        m_tree->swap(ndx1, ndx2);
390✔
1116
        bump_content_version();
390✔
1117
    }
390✔
1118
}
408✔
1119

1120
template <class T>
1121
T Lst<T>::set(size_t ndx, T value)
1122
{
21,825✔
1123
    if (value_is_null(value) && !m_nullable)
21,825!
1124
        throw InvalidArgument(ErrorCodes::PropertyNotNullable,
12✔
1125
                              util::format("List: %1", CollectionBase::get_property_name()));
12✔
1126

10,890✔
1127
    // get will check for ndx out of bounds
10,890✔
1128
    T old = do_get(ndx, "set()");
21,813✔
1129
    if (Replication* repl = Base::get_replication()) {
21,813✔
1130
        repl->list_set(*this, ndx, value);
20,751✔
1131
    }
20,751✔
1132
    if (old != value) {
21,813✔
1133
        do_set(ndx, value);
18,135✔
1134
        bump_content_version();
18,135✔
1135
    }
18,135✔
1136
    return old;
21,813✔
1137
}
21,813✔
1138

1139
template <class T>
1140
void Lst<T>::insert(size_t ndx, T value)
1141
{
8,095,125✔
1142
    if (value_is_null(value) && !m_nullable)
8,095,125!
1143
        throw InvalidArgument(ErrorCodes::PropertyNotNullable,
12✔
1144
                              util::format("List: %1", CollectionBase::get_property_name()));
12✔
1145

4,047,765✔
1146
    auto sz = size();
8,095,113✔
1147
    CollectionBase::validate_index("insert()", ndx, sz + 1);
8,095,113✔
1148
    ensure_created();
8,095,113✔
1149
    if (Replication* repl = Base::get_replication()) {
8,095,113✔
1150
        repl->list_insert(*this, ndx, value, sz);
1,188,105✔
1151
    }
1,188,105✔
1152
    do_insert(ndx, value);
8,095,113✔
1153
    bump_content_version();
8,095,113✔
1154
}
8,095,113✔
1155

1156
template <class T>
1157
T Lst<T>::remove(size_t ndx)
1158
{
204,657✔
1159
    // get will check for ndx out of bounds
102,699✔
1160
    T old = do_get(ndx, "remove()");
204,657✔
1161
    if (Replication* repl = Base::get_replication()) {
204,657✔
1162
        repl->list_erase(*this, ndx);
197,532✔
1163
    }
197,532✔
1164

102,699✔
1165
    do_remove(ndx);
204,657✔
1166
    bump_content_version();
204,657✔
1167
    return old;
204,657✔
1168
}
204,657✔
1169

1170
inline bool LnkLst::operator==(const LnkLst& other) const
1171
{
×
1172
    return m_list == other.m_list;
×
1173
}
×
1174

1175
inline bool LnkLst::operator!=(const LnkLst& other) const
1176
{
×
1177
    return m_list != other.m_list;
×
1178
}
×
1179

1180
inline size_t LnkLst::size() const
1181
{
6,791,829✔
1182
    update_if_needed();
6,791,829✔
1183
    return m_list.size() - num_unresolved();
6,791,829✔
1184
}
6,791,829✔
1185

1186
inline bool LnkLst::is_null(size_t ndx) const
1187
{
×
1188
    update_if_needed();
×
1189
    return m_list.is_null(virtual2real(ndx));
×
1190
}
×
1191

1192
inline Mixed LnkLst::get_any(size_t ndx) const
1193
{
11,577✔
1194
    update_if_needed();
11,577✔
1195
    auto obj_key = m_list.get(virtual2real(ndx));
11,577✔
1196
    return ObjLink{get_target_table()->get_key(), obj_key};
11,577✔
1197
}
11,577✔
1198

1199
inline void LnkLst::clear()
1200
{
8,877✔
1201
    m_list.clear();
8,877✔
1202
    clear_unresolved();
8,877✔
1203
}
8,877✔
1204

1205
inline util::Optional<Mixed> LnkLst::min(size_t* return_ndx) const
1206
{
×
1207
    static_cast<void>(return_ndx);
×
1208
    REALM_TERMINATE("Not implemented yet");
1209
}
×
1210

1211
inline util::Optional<Mixed> LnkLst::max(size_t* return_ndx) const
1212
{
×
1213
    static_cast<void>(return_ndx);
×
1214
    REALM_TERMINATE("Not implemented yet");
1215
}
×
1216

1217
inline util::Optional<Mixed> LnkLst::sum(size_t* return_cnt) const
1218
{
×
1219
    static_cast<void>(return_cnt);
×
1220
    REALM_TERMINATE("Not implemented yet");
1221
}
×
1222

1223
inline util::Optional<Mixed> LnkLst::avg(size_t* return_cnt) const
1224
{
×
1225
    static_cast<void>(return_cnt);
×
1226
    REALM_TERMINATE("Not implemented yet");
1227
}
×
1228

1229
inline CollectionBasePtr LnkLst::clone_collection() const
1230
{
528✔
1231
    return clone_linklist();
528✔
1232
}
528✔
1233

1234
inline void LnkLst::sort(std::vector<size_t>& indices, bool ascending) const
1235
{
×
1236
    static_cast<void>(indices);
×
1237
    static_cast<void>(ascending);
×
1238
    REALM_TERMINATE("Not implemented yet");
1239
}
×
1240

1241
inline void LnkLst::distinct(std::vector<size_t>& indices, util::Optional<bool> sort_order) const
1242
{
×
1243
    static_cast<void>(indices);
×
1244
    static_cast<void>(sort_order);
×
1245
    REALM_TERMINATE("Not implemented yet");
1246
}
×
1247

1248
inline const Obj& LnkLst::get_obj() const noexcept
1249
{
6,735,681✔
1250
    return m_list.get_obj();
6,735,681✔
1251
}
6,735,681✔
1252

1253
inline bool LnkLst::has_changed() const noexcept
1254
{
×
1255
    return m_list.has_changed();
×
1256
}
×
1257

1258
inline ColKey LnkLst::get_col_key() const noexcept
1259
{
6,685,317✔
1260
    return m_list.get_col_key();
6,685,317✔
1261
}
6,685,317✔
1262

1263
inline void LnkLst::set_null(size_t ndx)
1264
{
×
1265
    update_if_needed();
×
1266
    m_list.set_null(virtual2real(ndx));
×
1267
}
×
1268

1269
inline void LnkLst::set_any(size_t ndx, Mixed val)
1270
{
258✔
1271
    update_if_needed();
258✔
1272
    m_list.set_any(virtual2real(ndx), val);
258✔
1273
}
258✔
1274

1275
inline void LnkLst::insert_null(size_t ndx)
1276
{
×
1277
    update_if_needed();
×
1278
    m_list.insert_null(virtual2real(ndx));
×
1279
}
×
1280

1281
inline void LnkLst::insert_any(size_t ndx, Mixed val)
1282
{
486✔
1283
    update_if_needed();
486✔
1284
    m_list.insert_any(virtual2real(ndx), val);
486✔
1285
}
486✔
1286

1287
inline size_t LnkLst::find_any(Mixed value) const
1288
{
126✔
1289
    if (value.is_null()) {
126✔
1290
        return find_first(ObjKey());
×
1291
    }
×
1292
    if (value.get_type() == type_Link) {
126✔
1293
        return find_first(value.get<ObjKey>());
78✔
1294
    }
78✔
1295
    else if (value.get_type() == type_TypedLink) {
48✔
1296
        auto link = value.get_link();
48✔
1297
        if (link.get_table_key() == get_target_table()->get_key()) {
48✔
1298
            return find_first(link.get_obj_key());
48✔
1299
        }
48✔
1300
    }
×
1301
    return realm::not_found;
×
1302
}
×
1303

1304
inline void LnkLst::resize(size_t new_size)
1305
{
×
1306
    update_if_needed();
×
1307
    m_list.resize(new_size + num_unresolved());
×
1308
}
×
1309

1310
inline void LnkLst::remove(size_t from, size_t to)
1311
{
594✔
1312
    update_if_needed();
594✔
1313
    m_list.remove(virtual2real(from), virtual2real(to));
594✔
1314
    update_unresolved(UpdateStatus::Updated);
594✔
1315
}
594✔
1316

1317
inline void LnkLst::move(size_t from, size_t to)
1318
{
552✔
1319
    update_if_needed();
552✔
1320
    m_list.move(virtual2real(from), virtual2real(to));
552✔
1321
}
552✔
1322

1323
inline void LnkLst::swap(size_t ndx1, size_t ndx2)
1324
{
102✔
1325
    update_if_needed();
102✔
1326
    m_list.swap(virtual2real(ndx1), virtual2real(ndx2));
102✔
1327
}
102✔
1328

1329
inline ObjKey LnkLst::get(size_t ndx) const
1330
{
258,099✔
1331
    const auto current_size = size();
258,099✔
1332
    CollectionBase::validate_index("get()", ndx, current_size);
258,099✔
1333
    return m_list.m_tree->get(virtual2real(ndx));
258,099✔
1334
}
258,099✔
1335

1336
inline size_t LnkLst::find_first(const ObjKey& key) const
1337
{
1,056✔
1338
    if (key.is_unresolved())
1,056✔
1339
        return not_found;
6✔
1340

525✔
1341
    size_t found = not_found;
1,050✔
1342
    if (update_if_needed() != UpdateStatus::Detached) {
1,050✔
1343
        found = m_list.m_tree->find_first(key);
1,038✔
1344
    }
1,038✔
1345

525✔
1346
    return (found != not_found) ? real2virtual(found) : not_found;
999✔
1347
}
1,050✔
1348

1349
inline void LnkLst::insert(size_t ndx, ObjKey value)
1350
{
6,375,054✔
1351
    REALM_ASSERT(!value.is_unresolved());
6,375,054✔
1352
    if (get_target_table()->is_embedded() && value != ObjKey())
6,375,054✔
1353
        throw IllegalOperation(
12✔
1354
            util::format("Cannot insert an already managed object into list of embedded objects '%1.%2'",
12✔
1355
                         get_table()->get_class_name(), CollectionBase::get_property_name()));
12✔
1356

3,187,725✔
1357
    update_if_needed();
6,375,042✔
1358
    m_list.insert(virtual2real(ndx), value);
6,375,042✔
1359
    update_unresolved(UpdateStatus::Updated);
6,375,042✔
1360
}
6,375,042✔
1361

1362
inline ObjKey LnkLst::set(size_t ndx, ObjKey value)
1363
{
456✔
1364
    REALM_ASSERT(!value.is_unresolved());
456✔
1365
    if (get_target_table()->is_embedded() && value != ObjKey())
456✔
1366
        throw IllegalOperation(
6✔
1367
            util::format("Cannot insert an already managed object into list of embedded objects '%1.%2'",
6✔
1368
                         get_table()->get_class_name(), CollectionBase::get_property_name()));
6✔
1369

225✔
1370
    update_if_needed();
450✔
1371
    ObjKey old = m_list.set(virtual2real(ndx), value);
450✔
1372
    REALM_ASSERT(!old.is_unresolved());
450✔
1373
    return old;
450✔
1374
}
450✔
1375

1376
inline ObjKey LnkLst::remove(size_t ndx)
1377
{
1,290✔
1378
    update_if_needed();
1,290✔
1379
    ObjKey old = m_list.remove(virtual2real(ndx));
1,290✔
1380
    REALM_ASSERT(!old.is_unresolved());
1,290✔
1381
    update_unresolved(UpdateStatus::Updated);
1,290✔
1382
    return old;
1,290✔
1383
}
1,290✔
1384

1385
} // namespace realm
1386

1387
#endif /* REALM_LIST_HPP */
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