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

realm / realm-core / jorgen.edelbo_322

20 Jun 2024 09:43AM UTC coverage: 84.879% (-6.1%) from 90.966%
jorgen.edelbo_322

Pull #7826

Evergreen

jedelbo
remove set_direct methods from integer compressors
Pull Request #7826: Merge Next major

66056 of 81292 branches covered (81.26%)

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

566 existing lines in 29 files now uncovered.

84791 of 99896 relevant lines covered (84.88%)

15351931.14 hits per line

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

91.07
/src/realm/node.hpp
1
/*************************************************************************
2
 *
3
 * Copyright 2018 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_NODE_HPP
20
#define REALM_NODE_HPP
21

22
#include <realm/node_header.hpp>
23
#include <realm/alloc.hpp>
24

25
#include <iostream>
26

27
namespace realm {
28

29
class Mixed;
30

31
/// Special index value. It has various meanings depending on
32
/// context. It is returned by some search functions to indicate 'not
33
/// found'. It is similar in function to std::string::npos.
34
const size_t npos = size_t(-1);
35

36
/// Alias for realm::npos.
37
const size_t not_found = npos;
38

39
/// All accessor classes that logically contains other objects must inherit
40
/// this class.
41
///
42
/// A database node accessor contains information about the parent of the
43
/// referenced node. This 'reverse' reference is not explicitly present in the
44
/// underlying node hierarchy, but it is needed when modifying an array. A
45
/// modification may lead to relocation of the underlying array node, and the
46
/// parent must be updated accordingly. Since this applies recursivly all the
47
/// way to the root node, it is essential that the entire chain of parent
48
/// accessors is constructed and propperly maintained when a particular array is
49
/// modified.
50
class ArrayParent {
51
public:
52
    virtual ~ArrayParent() noexcept {}
817,914,099✔
53

54
    virtual ref_type get_child_ref(size_t child_ndx) const noexcept = 0;
55
    virtual void update_child_ref(size_t child_ndx, ref_type new_ref) = 0;
56
};
57

58
/// Provides access to individual array nodes of the database.
59
///
60
/// This class serves purely as an accessor, it assumes no ownership of the
61
/// referenced memory.
62
///
63
/// An node accessor can be in one of two states: attached or unattached. It is
64
/// in the attached state if, and only if is_attached() returns true. Most
65
/// non-static member functions of this class have undefined behaviour if the
66
/// accessor is in the unattached state. The exceptions are: is_attached(),
67
/// detach(), create(), init_from_ref(), init_from_mem(), init_from_parent(),
68
/// has_parent(), get_parent(), set_parent(), get_ndx_in_parent(),
69
/// set_ndx_in_parent(), adjust_ndx_in_parent(), and get_ref_from_parent().
70
///
71
/// An node accessor contains information about the parent of the referenced
72
/// node. This 'reverse' reference is not explicitly present in the
73
/// underlying node hierarchy, but it is needed when modifying a node. A
74
/// modification may lead to relocation of the underlying node, and the
75
/// parent must be updated accordingly. Since this applies recursively all the
76
/// way to the root node, it is essential that the entire chain of parent
77
/// accessors is constructed and properly maintained when a particular node is
78
/// modified.
79
///
80
/// The parent reference (`pointer to parent`, `index in parent`) is updated
81
/// independently from the state of attachment to an underlying node. In
82
/// particular, the parent reference remains valid and is unaffected by changes
83
/// in attachment. These two aspects of the state of the accessor is updated
84
/// independently, and it is entirely the responsibility of the caller to update
85
/// them such that they are consistent with the underlying node hierarchy before
86
/// calling any method that modifies the underlying node.
87
///
88
/// FIXME: This class currently has fragments of ownership, in particular the
89
/// constructors that allocate underlying memory. On the other hand, the
90
/// destructor never frees the memory. This is a problematic situation, because
91
/// it so easily becomes an obscure source of leaks. There are three options for
92
/// a fix of which the third is most attractive but hardest to implement: (1)
93
/// Remove all traces of ownership semantics, that is, remove the constructors
94
/// that allocate memory, but keep the trivial copy constructor. For this to
95
/// work, it is important that the constness of the accessor has nothing to do
96
/// with the constness of the underlying memory, otherwise constness can be
97
/// violated simply by copying the accessor. (2) Disallov copying but associate
98
/// the constness of the accessor with the constness of the underlying
99
/// memory. (3) Provide full ownership semantics like is done for Table
100
/// accessors, and provide a proper copy constructor that really produces a copy
101
/// of the node. For this to work, the class should assume ownership if, and
102
/// only if there is no parent. A copy produced by a copy constructor will not
103
/// have a parent. Even if the original was part of a database, the copy will be
104
/// free-standing, that is, not be part of any database. For intra, or inter
105
/// database copying, one would have to also specify the target allocator.
106
class Node : public NodeHeader {
107
public:
108
    // FIXME: Should not be public
109
    char* m_data = nullptr; // Points to first byte after header
110

111
    /*********************** Constructor / destructor ************************/
112

113
    // The object will not be fully initialized when using this constructor
114
    explicit Node(Allocator& allocator) noexcept
115
        : m_alloc(allocator)
642,010,023✔
116
    {
1,293,286,017✔
117
    }
1,293,286,017✔
118

119
    virtual ~Node() = default;
1,241,089,461✔
120

121
    /**************************** Initializers *******************************/
122

123
    /// Same as init_from_ref(ref_type) but avoid the mapping of 'ref' to memory
124
    /// pointer.
125
    char* init_from_mem(MemRef mem) noexcept
126
    {
982,040,466✔
127
        char* header = mem.get_addr();
982,040,466✔
128
        REALM_ASSERT_DEBUG(!wtype_is_extended(header));
982,040,466✔
129
        m_ref = mem.get_ref();
982,040,466✔
130
        m_data = get_data_from_header(header);
982,040,466✔
131
        m_size = get_size_from_header(header);
982,040,466✔
132
        return header;
982,040,466✔
133
    }
982,040,466✔
134

135
    /************************** access functions *****************************/
136

137
    bool is_attached() const noexcept
138
    {
4,294,967,294✔
139
        return m_data != nullptr;
4,294,967,294✔
140
    }
4,294,967,294✔
141

142
    inline bool is_read_only() const noexcept
143
    {
1,232,849,823✔
144
        REALM_ASSERT_DEBUG(is_attached());
1,232,849,823✔
145
        return m_alloc.is_read_only(m_ref);
1,232,849,823✔
146
    }
1,232,849,823✔
147

148
    size_t size() const noexcept
149
    {
777,436,194✔
150
        REALM_ASSERT_DEBUG(is_attached());
777,436,194✔
151
        return m_size;
777,436,194✔
152
    }
777,436,194✔
153

154
    bool is_empty() const noexcept
155
    {
4✔
156
        return size() == 0;
4✔
157
    }
4✔
158

159
    ref_type get_ref() const noexcept
160
    {
62,556,747✔
161
        return m_ref;
62,556,747✔
162
    }
62,556,747✔
163
    MemRef get_mem() const noexcept
164
    {
528,546✔
165
        return MemRef(get_header_from_data(m_data), m_ref, m_alloc);
528,546✔
166
    }
528,546✔
167
    Allocator& get_alloc() const noexcept
168
    {
40,566,972✔
169
        return m_alloc;
40,566,972✔
170
    }
40,566,972✔
171
    /// Get the address of the header of this array.
172
    char* get_header() const noexcept
173
    {
2,574,170,592✔
174
        return get_header_from_data(m_data);
2,574,170,592✔
175
    }
2,574,170,592✔
176

177
    bool has_parent() const noexcept
178
    {
×
179
        return m_parent != nullptr;
×
180
    }
×
181
    ArrayParent* get_parent() const noexcept
182
    {
112,262,322✔
183
        return m_parent;
112,262,322✔
184
    }
112,262,322✔
185
    size_t get_ndx_in_parent() const noexcept
186
    {
35,220,255✔
187
        return m_ndx_in_parent;
35,220,255✔
188
    }
35,220,255✔
189
    bool has_missing_parent_update() const noexcept
190
    {
39,538,926✔
191
        return m_missing_parent_update;
39,538,926✔
192
    }
39,538,926✔
193

194
    /// Get the ref of this array as known to the parent. The caller must ensure
195
    /// that the parent information ('pointer to parent' and 'index in parent')
196
    /// is correct before calling this function.
197
    ref_type get_ref_from_parent() const noexcept
198
    {
137,201,430✔
199
        REALM_ASSERT_DEBUG(m_parent);
137,201,430✔
200
        ref_type ref = m_parent->get_child_ref(m_ndx_in_parent);
137,201,430✔
201
        return ref;
137,201,430✔
202
    }
137,201,430✔
203

204
    /***************************** modifiers *********************************/
205

206
    /// Detach from the underlying array node. This method has no effect if the
207
    /// accessor is currently unattached (idempotency).
208
    void detach() noexcept
209
    {
307,938,606✔
210
        m_data = nullptr;
307,938,606✔
211
    }
307,938,606✔
212

213
    /// Destroy only the array that this accessor is attached to, not the
214
    /// children of that array. See non-static destroy_deep() for an
215
    /// alternative. If this accessor is already in the detached state, this
216
    /// function has no effect (idempotency).
217
    void destroy() noexcept;
218

219
    /// Shorthand for `destroy(MemRef(ref, alloc), alloc)`.
220
    static void destroy(ref_type ref, Allocator& alloc) noexcept
221
    {
111✔
222
        destroy(MemRef(ref, alloc), alloc);
111✔
223
    }
111✔
224

225
    /// Destroy only the specified array node, not its children. See also
226
    /// destroy_deep(MemRef, Allocator&).
227
    static void destroy(MemRef mem, Allocator& alloc) noexcept
228
    {
111✔
229
        alloc.free_(mem);
111✔
230
    }
111✔
231

232
    /// Setting a new parent affects ownership of the attached array node, if
233
    /// any. If a non-null parent is specified, and there was no parent
234
    /// originally, then the caller passes ownership to the parent, and vice
235
    /// versa. This assumes, of course, that the change in parentship reflects a
236
    /// corresponding change in the list of children in the affected parents.
237
    void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept
238
    {
1,177,606,170✔
239
        m_parent = parent;
1,177,606,170✔
240
        m_ndx_in_parent = ndx_in_parent;
1,177,606,170✔
241
    }
1,177,606,170✔
242

243
    void set_ndx_in_parent(size_t ndx) noexcept
244
    {
×
245
        m_ndx_in_parent = ndx;
×
246
    }
×
247

248
    void clear_missing_parent_update()
249
    {
100,818✔
250
        m_missing_parent_update = false;
100,818✔
251
    }
100,818✔
252

253
    /// Update the parents reference to this child. This requires, of course,
254
    /// that the parent information stored in this child is up to date. If the
255
    /// parent pointer is set to null, this function has no effect.
256
    void update_parent()
257
    {
20,189,829✔
258
        if (m_parent) {
20,189,829✔
259
            m_parent->update_child_ref(m_ndx_in_parent, m_ref);
16,266,801✔
260
        }
16,266,801✔
261
        else {
3,923,028✔
262
            m_missing_parent_update = true;
3,923,028✔
263
        }
3,923,028✔
264
    }
20,189,829✔
265

266
    void typed_print(int) const
NEW
267
    {
×
NEW
268
        std::cout << "Generic Node ERROR\n";
×
NEW
269
    }
×
270

271
protected:
272
    /// The total size in bytes (including the header) of a new empty
273
    /// array. Must be a multiple of 8 (i.e., 64-bit aligned).
274
    static const size_t initial_capacity = 128;
275

276
    size_t m_ref;
277
    Allocator& m_alloc;
278
    size_t m_size = 0; // Number of elements currently stored.
279

280
#if REALM_ENABLE_MEMDEBUG
281
    // If m_no_relocation is false, then copy_on_write() will always relocate this array, regardless if it's
282
    // required or not. If it's true, then it will never relocate, which is currently only expeted inside
283
    // GroupWriter::write_group() due to a unique chicken/egg problem (see description there).
284
    bool m_no_relocation = false;
285
#endif
286

287
    void alloc(size_t init_size, size_t new_width);
288
    void copy_on_write()
289
    {
28,542,291✔
290
#if REALM_ENABLE_MEMDEBUG
291
        // We want to relocate this array regardless if there is a need or not, in order to catch use-after-free bugs.
292
        // Only exception is inside GroupWriter::write_group() (see explanation at the definition of the
293
        // m_no_relocation
294
        // member)
295
        if (!m_no_relocation) {
296
#else
297
        if (is_read_only()) {
28,542,291✔
298
#endif
8,023,125✔
299
            do_copy_on_write();
8,023,125✔
300
        }
8,023,125✔
301
    }
28,542,291✔
302
    void copy_on_write(size_t min_size)
303
    {
221,505✔
304
#if REALM_ENABLE_MEMDEBUG
305
        // We want to relocate this array regardless if there is a need or not, in order to catch use-after-free bugs.
306
        // Only exception is inside GroupWriter::write_group() (see explanation at the definition of the
307
        // m_no_relocation
308
        // member)
309
        if (!m_no_relocation) {
310
#else
311
        if (is_read_only()) {
221,508✔
312
#endif
221,508✔
313
            do_copy_on_write(min_size);
221,508✔
314
        }
221,508✔
315
    }
221,505✔
316
    void ensure_size(size_t min_size)
317
    {
100,818✔
318
        char* header = get_header_from_data(m_data);
100,818✔
319
        size_t orig_capacity_bytes = get_capacity_from_header(header);
100,818✔
320
        if (orig_capacity_bytes < min_size) {
100,818✔
321
            do_copy_on_write(min_size);
762✔
322
        }
762✔
323
    }
100,818✔
324

325
    static MemRef create_node(size_t size, Allocator& alloc, bool context_flag = false, Type type = type_Normal,
326
                              WidthType width_type = wtype_Ignore, uint8_t width = 1);
327

328
    void set_header_size(size_t value) noexcept
329
    {
17,320,578✔
330
        set_size_in_header(value, get_header());
17,320,578✔
331
    }
17,320,578✔
332

333
    // Includes array header. Not necessarily 8-byte aligned.
334
    virtual size_t calc_byte_len(size_t num_items, size_t width) const;
335
    virtual size_t calc_item_count(size_t bytes, size_t width) const noexcept;
336

337
private:
338
    friend class NodeTree;
339
    ArrayParent* m_parent = nullptr;
340
    size_t m_ndx_in_parent = 0; // Ignored if m_parent is null.
341
    bool m_missing_parent_update = false;
342

343
    void do_copy_on_write(size_t minimum_size = 0);
344
};
345

346
class Spec;
347
class Mixed;
348

349
namespace _impl {
350
class ArrayWriterBase;
351
}
352

353
/// Base class for all nodes holding user data
354
class ArrayPayload {
355
public:
356
    virtual ~ArrayPayload();
357
    virtual void init_from_ref(ref_type) noexcept = 0;
358
    virtual void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept = 0;
359
    virtual Mixed get_any(size_t ndx) const = 0;
360
    virtual bool need_spec() const
361
    {
8,018,157✔
362
        return false;
8,018,157✔
363
    }
8,018,157✔
364
    virtual void set_spec(Spec*, size_t) const {}
×
365
    static ref_type typed_write(ref_type ref, _impl::ArrayWriterBase& out, Allocator& alloc);
366
};
367

368
} // namespace realm
369

370
#endif /* REALM_NODE_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