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

realm / realm-core / github_pull_request_281750

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

Pull #6073

Evergreen

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

95488 of 175952 branches covered (0.0%)

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

622 existing lines in 51 files now uncovered.

233503 of 257934 relevant lines covered (90.53%)

6533720.56 hits per line

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

62.56
/src/realm/dictionary.hpp
1
/*************************************************************************
2
 *
3
 * Copyright 2019 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_DICTIONARY_HPP
20
#define REALM_DICTIONARY_HPP
21

22
#include <realm/collection.hpp>
23
#include <realm/obj.hpp>
24
#include <realm/mixed.hpp>
25
#include <realm/column_mixed.hpp>
26

27
namespace realm {
28

29
class DictionaryBase : public CollectionBase {
30
public:
31
    using CollectionBase::CollectionBase;
32

33
protected:
34
    static constexpr CollectionType s_collection_type = CollectionType::Dictionary;
35
};
36

37
class Dictionary final : public CollectionBaseImpl<DictionaryBase>, public CollectionParent {
38
public:
39
    using Base = CollectionBaseImpl<DictionaryBase>;
40
    class Iterator;
41

42
    Dictionary()
43
        : CollectionParent(0)
44
    {
100✔
45
    }
100✔
46
    ~Dictionary();
47

48
    Dictionary(const Obj& obj, ColKey col_key)
49
        : Dictionary(col_key, 1)
50
    {
85,653✔
51
        this->set_owner(obj, col_key);
85,653✔
52
    }
85,653✔
53
    Dictionary(CollectionParent& parent, Index index)
54
        : Base(parent, index)
55
    {
54✔
56
    }
54✔
57
    Dictionary(ColKey col_key, size_t level = 1);
58
    Dictionary(const Dictionary& other)
59
        : Base(static_cast<const Base&>(other))
60
        , CollectionParent(other.get_level())
61
        , m_key_type(other.m_key_type)
62
    {
5,634✔
63
        *this = other;
5,634✔
64
    }
5,634✔
65
    Dictionary& operator=(const Dictionary& other);
66

67
    DataType get_key_data_type() const;
68
    DataType get_value_data_type() const;
69

70
    std::pair<Mixed, Mixed> get_pair(size_t ndx) const;
71
    Mixed get_key(size_t ndx) const;
72
    PathElement get_path_element(size_t ndx) const override
73
    {
36✔
74
        return {get_key(ndx).get_string()};
36✔
75
    }
36✔
76

77
    // Overriding members of CollectionBase:
78
    CollectionBasePtr clone_collection() const final;
79
    size_t size() const final;
80
    bool is_null(size_t ndx) const final;
81
    Mixed get_any(size_t ndx) const final;
82
    size_t find_any(Mixed value) const final;
83
    size_t find_any_key(Mixed value) const noexcept;
84

85
    util::Optional<Mixed> min(size_t* return_ndx = nullptr) const final;
86
    util::Optional<Mixed> max(size_t* return_ndx = nullptr) const final;
87
    util::Optional<Mixed> sum(size_t* return_cnt = nullptr) const final;
88
    util::Optional<Mixed> avg(size_t* return_cnt = nullptr) const final;
89

90
    void sort(std::vector<size_t>& indices, bool ascending = true) const final;
91
    void distinct(std::vector<size_t>& indices, util::Optional<bool> sort_order = util::none) const final;
92
    void sort_keys(std::vector<size_t>& indices, bool ascending = true) const;
93
    void distinct_keys(std::vector<size_t>& indices, util::Optional<bool> sort_order = util::none) const;
94

95
    void set_owner(const Obj& obj, ColKey ck) override
96
    {
152,388✔
97
        Base::set_owner(obj, ck);
152,388✔
98
        get_key_type();
152,388✔
99
    }
152,388✔
100
    void set_owner(std::shared_ptr<CollectionParent> parent, CollectionParent::Index index) override
101
    {
342✔
102
        Base::set_owner(std::move(parent), index);
342✔
103
        get_key_type();
342✔
104
    }
342✔
105

106
    // first points to inserted/updated element.
107
    // second is true if the element was inserted
108
    std::pair<Iterator, bool> insert(Mixed key, Mixed value);
109
    std::pair<Iterator, bool> insert(Mixed key, const Obj& obj);
110

111
    template <typename T>
112
    void insert_json(const std::string&, const T&);
113

114
    Obj create_and_insert_linked_object(Mixed key);
115

116
    void insert_collection(const PathElement&, CollectionType dict_or_list) override;
117
    DictionaryPtr get_dictionary(const PathElement& path_elem) const override;
118
    SetMixedPtr get_set(const PathElement&) const override;
119
    ListMixedPtr get_list(const PathElement& path_elem) const override;
120

121
    // throws std::out_of_range if key is not found
122
    Mixed get(Mixed key) const;
123
    // Noexcept version
124
    util::Optional<Mixed> try_get(Mixed key) const;
125
    // adds entry if key is not found
126
    const Mixed operator[](Mixed key);
127

128
    Obj get_object(StringData key);
129

130
    bool contains(Mixed key) const noexcept;
131
    Iterator find(Mixed key) const noexcept;
132

133
    void erase(Mixed key);
134
    Iterator erase(Iterator it);
135
    bool try_erase(Mixed key);
136

137
    void nullify(size_t);
138
    bool nullify(ObjLink target_link);
139
    bool replace_link(ObjLink old_link, ObjLink replace_link);
140
    bool remove_backlinks(CascadeState& state) const;
141
    size_t find_first(Mixed value) const;
142

143
    void clear() final;
144

145
    template <class T>
146
    void for_all_values(T&& f)
147
    {
168✔
148
        if (update()) {
168✔
149
            BPlusTree<Mixed> values(get_alloc());
24✔
150
            values.init_from_ref(m_dictionary_top->get_as_ref(1));
24✔
151
            auto func = [&f](BPlusTreeNode* node, size_t) {
24✔
152
                auto leaf = static_cast<BPlusTree<Mixed>::LeafNode*>(node);
24✔
153
                size_t sz = leaf->size();
24✔
154
                for (size_t i = 0; i < sz; i++) {
78✔
155
                    f(leaf->get(i));
54✔
156
                }
54✔
157
                return IteratorControl::AdvanceToNext;
24✔
158
            };
24✔
159

12✔
160
            values.traverse(func);
24✔
161
        }
24✔
162
    }
168✔
163

164
    template <class T, class Func>
165
    void for_all_keys(Func&& f)
166
    {
60✔
167
        if (update()) {
60✔
168
            BPlusTree<T> keys(get_alloc());
60✔
169
            keys.init_from_ref(m_dictionary_top->get_as_ref(0));
60✔
170
            auto func = [&f](BPlusTreeNode* node, size_t) {
60✔
171
                auto leaf = static_cast<typename BPlusTree<T>::LeafNode*>(node);
60✔
172
                size_t sz = leaf->size();
60✔
173
                for (size_t i = 0; i < sz; i++) {
210✔
174
                    f(leaf->get(i));
150✔
175
                }
150✔
176
                return IteratorControl::AdvanceToNext;
60✔
177
            };
60✔
178

30✔
179
            keys.traverse(func);
60✔
180
        }
60✔
181
    }
60✔
182

183

184
    Iterator begin() const;
185
    Iterator end() const;
186

187
    void migrate();
188

189
    // Overriding members in CollectionParent
190
    FullPath get_path() const override
191
    {
37,080✔
192
        return Base::get_path();
37,080✔
193
    }
37,080✔
194

195
    Path get_short_path() const override
196
    {
51,066✔
197
        return Base::get_short_path();
51,066✔
198
    }
51,066✔
199

200
    StablePath get_stable_path() const override
201
    {
177,372✔
202
        return Base::get_stable_path();
177,372✔
203
    }
177,372✔
204

205
    void add_index(Path& path, const Index& ndx) const final;
206
    size_t find_index(const Index&) const final;
207

208
    TableRef get_table() const noexcept override
209
    {
169,197✔
210
        return get_obj().get_table();
169,197✔
211
    }
169,197✔
212
    UpdateStatus update_if_needed_with_status() const override;
213
    bool update_if_needed() const override;
214
    const Obj& get_object() const noexcept override
215
    {
528✔
216
        return get_obj();
528✔
217
    }
528✔
218
    ref_type get_collection_ref(Index, CollectionType) const override;
219
    bool check_collection_ref(Index, CollectionType) const noexcept override;
220
    void set_collection_ref(Index, ref_type ref, CollectionType) override;
221
    StableIndex build_index(Mixed key) const;
222

223
    void to_json(std::ostream&, size_t, JSONOutputMode, util::FunctionRef<void(const Mixed&)>) const override;
224

225
private:
226
    template <typename T, typename Op>
227
    friend class CollectionColumnAggregate;
228
    friend class DictionaryLinkValues;
229
    friend class Cluster;
230

231
    mutable std::unique_ptr<Array> m_dictionary_top;
232
    mutable std::unique_ptr<BPlusTreeBase> m_keys;
233
    mutable std::unique_ptr<BPlusTreeMixed> m_values;
234
    DataType m_key_type = type_String;
235

236
    Dictionary(Allocator& alloc, ColKey col_key, ref_type ref);
237

238
    bool init_from_parent(bool allow_create) const;
239
    Mixed do_get(size_t ndx) const;
240
    void do_erase(size_t ndx, Mixed key);
241
    Mixed do_get_key(size_t ndx) const;
242
    size_t do_find_key(Mixed key) const noexcept;
243
    std::pair<size_t, Mixed> find_impl(Mixed key) const noexcept;
244
    std::pair<Mixed, Mixed> do_get_pair(size_t ndx) const;
245
    bool clear_backlink(size_t ndx, CascadeState& state) const;
246
    void align_indices(std::vector<size_t>& indices) const;
247
    void swap_content(Array& fields1, Array& fields2, size_t index1, size_t index2);
248

249
    util::Optional<Mixed> do_min(size_t* return_ndx = nullptr) const;
250
    util::Optional<Mixed> do_max(size_t* return_ndx = nullptr) const;
251
    util::Optional<Mixed> do_sum(size_t* return_cnt = nullptr) const;
252
    util::Optional<Mixed> do_avg(size_t* return_cnt = nullptr) const;
253

254
    Mixed find_value(Mixed) const noexcept;
255

256
    template <typename AggregateType>
257
    void do_accumulate(size_t* return_ndx, AggregateType& agg) const;
258

259
    void ensure_created();
260
    inline bool update() const
261
    {
1,314,144✔
262
        return update_if_needed_with_status() != UpdateStatus::Detached;
1,314,144✔
263
    }
1,314,144✔
264
    void verify() const;
265
    void get_key_type();
266
};
267

268
class Dictionary::Iterator {
269
public:
270
    using iterator_category = std::random_access_iterator_tag;
271
    using value_type = std::pair<Mixed, Mixed>;
272
    using difference_type = ptrdiff_t;
273
    using pointer = const value_type*;
274
    using reference = const value_type&;
275

276
    pointer operator->() const
277
    {
60,708✔
278
        m_val = m_list->get_pair(m_ndx);
60,708✔
279
        return &m_val;
60,708✔
280
    }
60,708✔
281

282
    reference operator*() const
283
    {
60,708✔
284
        return *operator->();
60,708✔
285
    }
60,708✔
286

287
    Iterator& operator++() noexcept
288
    {
25,710✔
289
        ++m_ndx;
25,710✔
290
        return *this;
25,710✔
291
    }
25,710✔
292

293
    Iterator operator++(int) noexcept
294
    {
×
295
        auto tmp = *this;
×
296
        operator++();
×
297
        return tmp;
×
298
    }
×
299

300
    Iterator& operator--() noexcept
301
    {
×
302
        --m_ndx;
×
303
        return *this;
×
304
    }
×
305

306
    Iterator operator--(int) noexcept
307
    {
×
308
        auto tmp = *this;
×
309
        operator--();
×
310
        return tmp;
×
311
    }
×
312

313
    Iterator& operator+=(ptrdiff_t n) noexcept
314
    {
×
315
        m_ndx += n;
×
316
        return *this;
×
317
    }
×
318

319
    Iterator& operator-=(ptrdiff_t n) noexcept
320
    {
×
321
        m_ndx -= n;
×
322
        return *this;
×
323
    }
×
324

325
    friend ptrdiff_t operator-(const Iterator& lhs, const Iterator& rhs) noexcept
326
    {
472✔
327
        return ptrdiff_t(lhs.m_ndx) - ptrdiff_t(rhs.m_ndx);
472✔
328
    }
472✔
329

330
    friend Iterator operator+(Iterator lhs, ptrdiff_t rhs) noexcept
331
    {
1,086✔
332
        lhs.m_ndx += rhs;
1,086✔
333
        return lhs;
1,086✔
334
    }
1,086✔
335

336
    friend Iterator operator+(ptrdiff_t lhs, Iterator rhs) noexcept
337
    {
×
338
        return rhs + lhs;
×
339
    }
×
340

341
    bool operator!=(const Iterator& rhs) const noexcept
342
    {
57,036✔
343
        REALM_ASSERT_DEBUG(m_list == rhs.m_list);
57,036✔
344
        return m_ndx != rhs.m_ndx;
57,036✔
345
    }
57,036✔
346

347
    bool operator==(const Iterator& rhs) const noexcept
348
    {
20,292✔
349
        REALM_ASSERT_DEBUG(m_list == rhs.m_list);
20,292✔
350
        return m_ndx == rhs.m_ndx;
20,292✔
351
    }
20,292✔
352

353
    size_t index() const noexcept
354
    {
1,200✔
355
        return m_ndx;
1,200✔
356
    }
1,200✔
357

358
private:
359
    Iterator(const Dictionary* l, size_t ndx) noexcept
360
        : m_list(l)
361
        , m_ndx(ndx)
362
    {
225,516✔
363
    }
225,516✔
364

365
    friend class Dictionary;
366
    mutable value_type m_val;
367
    const Dictionary* m_list;
368
    size_t m_ndx = size_t(-1);
369
};
370

371
// An interface used when the value type of the dictionary consists of
372
// links to a single table. Implementation of the ObjList interface on
373
// top of a Dictionary of objects. This is the dictionary equivilent of
374
// LnkLst and LnkSet.
375
class DictionaryLinkValues final : public ObjCollectionBase<CollectionBase> {
376
public:
377
    DictionaryLinkValues() = default;
378
    DictionaryLinkValues(const Obj& obj, ColKey col_key);
379
    DictionaryLinkValues(const Dictionary& source);
380

381
    // Overrides of ObjList:
382
    ObjKey get_key(size_t ndx) const final;
383
    Obj get_object(size_t row_ndx) const final;
384

385
    // Overrides of CollectionBase, these simply forward to the underlying dictionary.
386
    size_t size() const final
387
    {
1,044✔
388
        return m_source.size();
1,044✔
389
    }
1,044✔
390
    bool is_null(size_t ndx) const final
391
    {
×
392
        return m_source.is_null(ndx);
×
393
    }
×
394
    Mixed get_any(size_t ndx) const final
395
    {
×
396
        return m_source.get_any(ndx);
×
397
    }
×
398
    void clear() final
399
    {
×
400
        m_source.clear();
×
401
    }
×
402
    util::Optional<Mixed> min(size_t* return_ndx = nullptr) const final
403
    {
×
404
        return m_source.min(return_ndx);
×
405
    }
×
406
    util::Optional<Mixed> max(size_t* return_ndx = nullptr) const final
407
    {
×
408
        return m_source.max(return_ndx);
×
409
    }
×
410
    util::Optional<Mixed> sum(size_t* return_cnt = nullptr) const final
411
    {
×
412
        return m_source.sum(return_cnt);
×
413
    }
×
414
    util::Optional<Mixed> avg(size_t* return_cnt = nullptr) const final
415
    {
×
416
        return m_source.avg(return_cnt);
×
417
    }
×
418
    CollectionBasePtr clone_collection() const final
419
    {
×
420
        return std::make_unique<DictionaryLinkValues>(m_source);
×
421
    }
×
422
    LinkCollectionPtr clone_obj_list() const final
423
    {
1,548✔
424
        return std::make_unique<DictionaryLinkValues>(m_source);
1,548✔
425
    }
1,548✔
426
    void sort(std::vector<size_t>& indices, bool ascending = true) const final
427
    {
×
428
        m_source.sort(indices, ascending);
×
429
    }
×
430
    void distinct(std::vector<size_t>& indices, util::Optional<bool> sort_order = util::none) const final
431
    {
×
432
        m_source.distinct(indices, sort_order);
×
433
    }
×
434
    size_t find_any(Mixed value) const final
435
    {
×
436
        return m_source.find_any(value);
×
437
    }
×
438
    const Obj& get_obj() const noexcept final
439
    {
3,978✔
440
        return m_source.get_obj();
3,978✔
441
    }
3,978✔
442
    ColKey get_col_key() const noexcept final
443
    {
1,524✔
444
        return m_source.get_col_key();
1,524✔
445
    }
1,524✔
446
    bool has_changed() const noexcept final
447
    {
×
448
        return m_source.has_changed();
×
449
    }
×
450
    CollectionType get_collection_type() const noexcept override
NEW
451
    {
×
NEW
452
        return CollectionType::List;
×
NEW
453
    }
×
454

455
    // Overrides of ObjCollectionBase:
456
    UpdateStatus do_update_if_needed() const final
457
    {
36✔
458
        return m_source.update_if_needed_with_status();
36✔
459
    }
36✔
460
    BPlusTree<ObjKey>* get_mutable_tree() const final
461
    {
24✔
462
        // We are faking being an ObjList because the underlying storage is not
12✔
463
        // actually a BPlusTree<ObjKey> for dictionaries it is all mixed values.
12✔
464
        // But this is ok, because we don't need to deal with unresolved link
12✔
465
        // maintenance because they are not hidden from view in dictionaries in
12✔
466
        // the same way as for LnkSet and LnkLst. This means that the functions
12✔
467
        // that call get_mutable_tree do not need to do anything for dictionaries.
12✔
468
        return nullptr;
24✔
469
    }
24✔
470

471
    void set_owner(const Obj& obj, ColKey ck) override
NEW
472
    {
×
NEW
473
        m_source.set_owner(obj, ck);
×
NEW
474
    }
×
475

476
    void set_owner(std::shared_ptr<CollectionParent> parent, CollectionParent::Index index) override
NEW
477
    {
×
NEW
478
        m_source.set_owner(std::move(parent), index);
×
NEW
479
    }
×
480

481
    FullPath get_path() const noexcept final
NEW
482
    {
×
NEW
483
        return m_source.get_path();
×
NEW
484
    }
×
485

486
    Path get_short_path() const noexcept final
NEW
487
    {
×
NEW
488
        return m_source.get_short_path();
×
NEW
489
    }
×
490

491
    StablePath get_stable_path() const noexcept final
NEW
492
    {
×
NEW
493
        return m_source.get_stable_path();
×
NEW
494
    }
×
495

496
private:
497
    Dictionary m_source;
498
};
499

500

501
inline std::pair<Dictionary::Iterator, bool> Dictionary::insert(Mixed key, const Obj& obj)
502
{
2,430✔
503
    return insert(key, Mixed(obj.get_link()));
2,430✔
504
}
2,430✔
505

506
inline CollectionBasePtr Dictionary::clone_collection() const
507
{
24✔
508
    return std::make_unique<Dictionary>(m_obj_mem, this->get_col_key());
24✔
509
}
24✔
510

511

512
} // namespace realm
513

514
#endif /* SRC_REALM_DICTIONARY_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

© 2026 Coveralls, Inc