• 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

63.35
/src/realm/node_header.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_HEADER_HPP
20
#define REALM_NODE_HEADER_HPP
21

22
#include <realm/util/assert.hpp>
23
#include <realm/utilities.hpp>
24

25
namespace {
26
// helper converting a number of bits into bytes and aligning to 8 byte boundary
27
static inline size_t align_bits_to8(size_t n)
28
{
15,950,900✔
29
    n = (n + 7) >> 3;
15,950,900✔
30
    return (n + 7) & ~size_t(7);
15,950,900✔
31
}
15,950,900✔
32
} // namespace
33

34
namespace realm {
35

36
// The header holds metadata for all allocations. It is 8 bytes.
37
// A field in byte 5 indicates the type of the allocation.
38
//
39
// Up to and including Core v 13, this field would always hold values 0,1 or 2.
40
// when stored in the file. This value now indicates that the chunk of memory
41
// must be interpreted according to the methods in NodeHeader.
42
//
43
const size_t max_array_size = 0x00ffffffL;            // Maximum number of elements in an array
44
const size_t max_array_payload_aligned = 0x07ffffc0L; // Maximum number of bytes that the payload of an array can be
45
// Even though the encoding supports arrays with size up to max_array_payload_aligned,
46
// the maximum allocation size is smaller as it must fit within a memory section
47
// (a contiguous virtual address range). This limitation is enforced in SlabAlloc::do_alloc().
48

49

50
class NodeHeader {
51

52
public:
53
    enum Type {
54
        type_Normal,
55

56
        /// This array is the main array of an innner node of a B+-tree as used
57
        /// in table columns.
58
        type_InnerBptreeNode,
59

60
        /// This array may contain refs to subarrays. An element whose least
61
        /// significant bit is zero, is a ref pointing to a subarray. An element
62
        /// whose least significant bit is one, is just a value. It is the
63
        /// responsibility of the application to ensure that non-ref values have
64
        /// their least significant bit set. This will generally be done by
65
        /// shifting the desired vlue to the left by one bit position, and then
66
        /// setting the vacated bit to one.
67
        type_HasRefs
68
    };
69

70
    enum WidthType {
71
        // The first 3 encodings where the only one used as far as Core v13.
72
        wtype_Bits = 0,     // width indicates how many bits every element occupies
73
        wtype_Multiply = 1, // width indicates how many bytes every element occupies
74
        wtype_Ignore = 2,   // each element is 1 byte
75
        wtype_Extend = 3    // the layouts are described in byte 4 of the header.
76
    };
77
    // Accessing flags.
78
    enum class Flags { // bit positions in flags "byte", used for masking
79
        Context = 1,
80
        HasRefs = 2,
81
        InnerBPTree = 4,
82
        // additional flags can be supported by new layouts, but old layout is full
83
    };
84
    // Possible header encodings (and corresponding memory layouts):
85
    enum class Encoding {
86
        WTypBits = 0, // Corresponds to wtype_Bits
87
        WTypMult = 1, // Corresponds to wtype_Multiply
88
        WTypIgn = 2,  // Corresponds to wtype_Ignore
89
        Packed = 3,   // wtype is wtype_Extend
90
        Flex = 4      // wtype is wtype_Extend
91
    };
92
    // * Packed: tightly packed array (any element size <= 64)
93
    // * WTypBits: less tightly packed. Correspond to wtype_Bits
94
    // * WTypMult: less tightly packed. Correspond to wtype_Multiply
95
    // * WTypIgn: single byte elements. Correspond to wtype_Ignore
96
    // encodings with more flexibility but lower number of elements:
97
    // * Flex: Pair of arrays (2 element sizes, 2 element count)
98
    //
99
    // Encodings:     bytes:
100
    // name:       |  b0   |  b1   |  b2   |  b3   | b4:0-2 | b4:3-4 | b4:5-7 |  b5   |  b6   |  b7  |
101
    // oldies      |  cap/chksum           |  'A'  | width  | wtype  | flags  |          size        |
102
    // Packed      |  cap/chksum   | -     | width | flags2 | wtype  | flags  | enc   |     size     |
103
    // Flex        |  cap/chksum   |  w_A + size_A | flags2 | wtype  | flags  | enc   | w_B + size_B |
104
    //
105
    // legend: cap = capacity, chksum = checksum, flags = 3 flag bits, flags2 = 3 additional flag bits
106
    //         size = number of elements, w_A = bits per A element, w_B = bits per B element
107
    //         size_A = number of A elements, size_B = number of B elements,
108
    //         enc = the encoding for the array, corresponding to different memory layouts
109
    //         For Flex: w + size is 6 bits for element width, 10 bits for number of elements
110
    //
111

112
    static const int header_size = 8; // Number of bytes used by header
113

114
    // The encryption layer relies on headers always fitting within a single page.
115
    static_assert(header_size == 8, "Header must always fit in entirely on a page");
116

117
    static char* get_data_from_header(char* header) noexcept
118
    {
1,497,807,672✔
119
        return header + header_size;
1,497,807,672✔
120
    }
1,497,807,672✔
121

122
    static char* get_header_from_data(char* data) noexcept
123
    {
4,284,066,955✔
124
        return data - header_size;
4,284,066,955✔
125
    }
4,284,066,955✔
126

127
    static const char* get_data_from_header(const char* header) noexcept
128
    {
560,177,688✔
129
        return get_data_from_header(const_cast<char*>(header));
560,177,688✔
130
    }
560,177,688✔
131

132
    // Helpers for NodeHeader::Type
133
    // handles all header formats
134
    static inline bool get_is_inner_bptree_node_from_header(const char* header) noexcept
135
    {
1,326,807,855✔
136
        typedef unsigned char uchar;
1,326,807,855✔
137
        const uchar* h = reinterpret_cast<const uchar*>(header);
1,326,807,855✔
138
        return (int(h[4]) & 0x80) != 0;
1,326,807,855✔
139
    }
1,326,807,855✔
140

141
    static inline bool get_hasrefs_from_header(const char* header) noexcept
142
    {
1,036,465,464✔
143
        typedef unsigned char uchar;
1,036,465,464✔
144
        const uchar* h = reinterpret_cast<const uchar*>(header);
1,036,465,464✔
145
        return (int(h[4]) & 0x40) != 0;
1,036,465,464✔
146
    }
1,036,465,464✔
147

148
    static Type get_type_from_header(const char* header) noexcept
NEW
149
    {
×
NEW
150
        if (get_is_inner_bptree_node_from_header(header))
×
NEW
151
            return type_InnerBptreeNode;
×
NEW
152
        if (get_hasrefs_from_header(header))
×
NEW
153
            return type_HasRefs;
×
NEW
154
        return type_Normal;
×
NEW
155
    }
×
156

157
    static inline bool get_context_flag_from_header(const char* header) noexcept
158
    {
896,325,006✔
159
        typedef unsigned char uchar;
896,325,006✔
160
        const uchar* h = reinterpret_cast<const uchar*>(header);
896,325,006✔
161
        return (int(h[4]) & 0x20) != 0;
896,325,006✔
162
    }
896,325,006✔
163
    static inline void set_is_inner_bptree_node_in_header(bool value, char* header) noexcept
164
    {
4,779✔
165
        typedef unsigned char uchar;
4,779✔
166
        uchar* h = reinterpret_cast<uchar*>(header);
4,779✔
167
        h[4] = uchar((int(h[4]) & ~0x80) | int(value) << 7);
4,779✔
168
    }
4,779✔
169

170
    static inline void set_hasrefs_in_header(bool value, char* header) noexcept
171
    {
4,779✔
172
        typedef unsigned char uchar;
4,779✔
173
        uchar* h = reinterpret_cast<uchar*>(header);
4,779✔
174
        h[4] = uchar((int(h[4]) & ~0x40) | int(value) << 6);
4,779✔
175
    }
4,779✔
176

177
    static inline void set_context_flag_in_header(bool value, char* header) noexcept
178
    {
714,555✔
179
        typedef unsigned char uchar;
714,555✔
180
        uchar* h = reinterpret_cast<uchar*>(header);
714,555✔
181
        h[4] = uchar((int(h[4]) & ~0x20) | int(value) << 5);
714,555✔
182
    }
714,555✔
183

184
    // Helpers for NodeHeader::WidthType:
185
    // handles all header formats
186
    static inline WidthType get_wtype_from_header(const char* header) noexcept
187
    {
4,294,967,294✔
188
        typedef unsigned char uchar;
4,294,967,294✔
189
        const uchar* h = reinterpret_cast<const uchar*>(header);
4,294,967,294✔
190
        int h4 = h[4];
4,294,967,294✔
191
        return WidthType((h4 & 0x18) >> 3);
4,294,967,294✔
192
    }
4,294,967,294✔
193

194
    static inline bool wtype_is_extended(const char* header) noexcept
195
    {
4,294,967,294✔
196
        return get_wtype_from_header(header) == wtype_Extend;
4,294,967,294✔
197
    }
4,294,967,294✔
198

199
    static inline void set_wtype_in_header(WidthType value, char* header) noexcept
200
    {
10,807✔
201
        typedef unsigned char uchar;
10,807✔
202
        uchar* h = reinterpret_cast<uchar*>(header);
10,807✔
203
        auto h4 = h[4];
10,807✔
204
        h4 = (h4 & ~0x18) | int(value) << 3;
10,807✔
205
        h[4] = h4;
10,807✔
206
    }
10,807✔
207

208
    static uint8_t unsigned_to_num_bits(uint64_t value)
209
    {
1,646,337✔
210
        if constexpr (sizeof(size_t) == sizeof(uint64_t))
800,331✔
211
            return 1 + log2(static_cast<size_t>(value));
1,646,337✔
NEW
212
        uint32_t high = value >> 32;
×
213
        if (high)
846,006✔
NEW
214
            return 33 + log2(high);
×
215
        uint32_t low = value & 0xFFFFFFFFUL;
846,006✔
216
        if (low)
846,006✔
NEW
217
            return 1 + log2(low);
×
218
        return 0;
846,006✔
219
    }
846,006✔
220

221
    static inline uint8_t signed_to_num_bits(int64_t value)
222
    {
1,097,550✔
223
        if (value >= 0)
1,097,550✔
224
            return 1 + unsigned_to_num_bits(value);
1,057,464✔
225
        else
40,086✔
226
            return 1 + unsigned_to_num_bits(~value); // <-- is this correct????
40,086✔
227
    }
1,097,550✔
228

229

230
    // Helper functions for old layouts only:
231
    // Handling width and sizes:
232
    static inline uint_least8_t get_width_from_header(const char* header) noexcept;
233

234
    static inline size_t get_size_from_header(const char* header) noexcept;
235

236
    static inline void set_width_in_header(size_t value, char* header) noexcept
237
    {
40,703,112✔
238
        REALM_ASSERT_DEBUG(!wtype_is_extended(header));
40,703,112✔
239
        // Pack width in 3 bits (log2)
240
        int w = 0;
40,703,112✔
241
        while (value) {
145,232,802✔
242
            ++w;
104,529,690✔
243
            value >>= 1;
104,529,690✔
244
        }
104,529,690✔
245
        REALM_ASSERT_3(w, <, 8);
40,703,112✔
246

247
        typedef unsigned char uchar;
40,703,112✔
248
        uchar* h = reinterpret_cast<uchar*>(header);
40,703,112✔
249
        h[4] = uchar((int(h[4]) & ~0x7) | w);
40,703,112✔
250
    }
40,703,112✔
251

252
    static inline void set_size_in_header(size_t value, char* header) noexcept
253
    {
970,017,990✔
254
        REALM_ASSERT_DEBUG(!wtype_is_extended(header));
970,017,990✔
255
        REALM_ASSERT_3(value, <=, max_array_size);
970,017,990✔
256
        typedef unsigned char uchar;
970,017,990✔
257
        uchar* h = reinterpret_cast<uchar*>(header);
970,017,990✔
258
        h[5] = uchar((value >> 16) & 0x000000FF);
970,017,990✔
259
        h[6] = uchar((value >> 8) & 0x000000FF);
970,017,990✔
260
        h[7] = uchar(value & 0x000000FF);
970,017,990✔
261
    }
970,017,990✔
262

263

264
    // Note: The wtype must have been set prior to calling this function
265
    static size_t get_capacity_from_header(const char* header) noexcept
266
    {
965,134,134✔
267
        if (!wtype_is_extended(header)) {
966,794,082✔
268
            typedef unsigned char uchar;
965,507,574✔
269
            const uchar* h = reinterpret_cast<const uchar*>(header);
965,507,574✔
270
            return (size_t(h[0]) << 19) + (size_t(h[1]) << 11) + (h[2] << 3);
965,507,574✔
271
        }
965,507,574✔
272
        else {
2,148,770,155✔
273
            return reinterpret_cast<const uint16_t*>(header)[0] << 3;
2,148,770,155✔
274
        }
2,148,770,155✔
275
    }
965,134,134✔
276

277
    // Note: There is a (no longer a correct) copy of this function is test_alloc.cpp
278
    // Note 2: The wtype must have been set prior to calling this function
279
    static void set_capacity_in_header(size_t value, char* header) noexcept
280
    {
35,706,897✔
281
        if (!wtype_is_extended(header)) {
35,706,897✔
282
            REALM_ASSERT_3(value, <=, (0xffffff << 3));
35,585,031✔
283
            typedef unsigned char uchar;
35,585,031✔
284
            uchar* h = reinterpret_cast<uchar*>(header);
35,585,031✔
285
            h[0] = uchar((value >> 19) & 0x000000FF);
35,585,031✔
286
            h[1] = uchar((value >> 11) & 0x000000FF);
35,585,031✔
287
            h[2] = uchar(value >> 3 & 0x000000FF);
35,585,031✔
288
        }
35,585,031✔
289
        else {
121,866✔
290
            REALM_ASSERT_DEBUG(value < (65536 << 3));
121,866✔
291
            REALM_ASSERT_DEBUG((value & 0x7) == 0);
121,866✔
292
            (reinterpret_cast<uint16_t*>(header))[0] = static_cast<uint16_t>(value >> 3);
121,866✔
293
        }
121,866✔
294
    }
35,706,897✔
295
    static size_t get_byte_size_from_header(const char* header) noexcept;
296

297
    // ^ First 3 must overlap numerically with corresponding wtype_X enum.
298
    static Encoding get_encoding(const char* header)
299
    {
3,829,048,533✔
300
        auto wtype = get_wtype_from_header(header);
3,829,048,533✔
301
        if (wtype == wtype_Extend) {
3,829,048,533✔
302
            const auto h = reinterpret_cast<const uint8_t*>(header);
249,297,378✔
303
            int encoding = h[5] + 3;
249,297,378✔
304
            REALM_ASSERT_DEBUG_EX(encoding >= int(Encoding::Packed) && encoding <= int(Encoding::Flex), encoding);
249,297,378✔
305
            return static_cast<Encoding>(encoding);
249,297,378✔
306
        }
249,297,378✔
307
        return Encoding(int(wtype));
3,579,751,155✔
308
    }
3,829,048,533✔
309
    static void set_encoding(char* header, Encoding enc)
NEW
310
    {
×
NEW
311
        if (enc < Encoding::Packed) {
×
NEW
312
            set_wtype_in_header(static_cast<WidthType>(enc), header);
×
NEW
313
        }
×
NEW
314
        else {
×
NEW
315
            set_wtype_in_header(wtype_Extend, header);
×
NEW
316
            auto h = reinterpret_cast<uint8_t*>(header);
×
NEW
317
            h[5] = static_cast<uint8_t>(enc) - 3;
×
NEW
318
        }
×
NEW
319
    }
×
320
    static std::string enc_to_string(Encoding enc)
NEW
321
    {
×
NEW
322
        switch (enc) {
×
NEW
323
            case Encoding::WTypMult:
×
NEW
324
                return "Mult";
×
NEW
325
            case Encoding::WTypIgn:
×
NEW
326
                return "Ign";
×
NEW
327
            case Encoding::WTypBits:
×
NEW
328
                return "Bits";
×
NEW
329
            case Encoding::Packed:
×
NEW
330
                return "Pack";
×
NEW
331
            case Encoding::Flex:
×
NEW
332
                return "Flex";
×
NEW
333
            default:
×
NEW
334
                return "Err";
×
NEW
335
        }
×
NEW
336
    }
×
337
    static std::string header_to_string(const char* header)
NEW
338
    {
×
NEW
339
        std::string retval = "{" + enc_to_string(get_encoding(header)) + "}";
×
NEW
340
        return retval;
×
UNCOV
341
    }
×
342

343
private:
344
    friend class Node;
345
    friend class IntegerCompressor;
346
    // Setting element size for encodings with a single element size:
347
    static void inline set_element_size(char* header, uint8_t bits_per_element, Encoding);
348
    // Getting element size for encodings with a single element size:
349
    static inline uint8_t get_element_size(const char* header, Encoding);
350
    // Used only by flex at this stage.
351
    // Setting element sizes for encodings with two element sizes (called A and B)
352
    static inline void set_elementA_size(char* header, uint8_t bits_per_element);
353
    static inline void set_elementB_size(char* header, uint8_t bits_per_element);
354
    // Getting element sizes for encodings with two element sizes (called A and B)
355
    static inline uint8_t get_elementA_size(const char* header);
356
    static inline uint8_t get_elementB_size(const char* header);
357
    // Setting num of elements for encodings with two element sizes (called A and B)
358
    static inline void set_arrayA_num_elements(char* header, size_t num_elements);
359
    static inline void set_arrayB_num_elements(char* header, size_t num_elements);
360
    // Getting number of elements for encodings with two element sizes (called A and B)
361
    static inline size_t get_arrayA_num_elements(const char* header);
362
    static inline size_t get_arrayB_num_elements(const char* header);
363
    // Getting the number of elements in the array(s). All encodings except Flex have one number of elements.
364
    static inline size_t get_num_elements(const char* header, Encoding);
365
    // Setting the number of elements in the array(s). All encodings except Flex have one number of elements.
366
    static inline void set_num_elements(char* header, size_t num_elements, Encoding);
367

368
    static inline size_t calc_size(size_t num_elements);
369
    static inline size_t calc_size(size_t num_elements, uint8_t element_size, Encoding);
370
    static inline size_t calc_size(size_t arrayA_num_elements, size_t arrayB_num_elements, uint8_t elementA_size,
371
                                   uint8_t elementB_size);
372

373
    static size_t calc_byte_size(WidthType wtype, size_t size, uint_least8_t width) noexcept
374
    {
1,190,960,052✔
375
        // the width need to be adjusted to nearest power of two:
376
        if (width > 8) {
1,190,960,052✔
377
            if (width > 32)
763,905,249✔
378
                width = 64;
42,414,000✔
379
            else if (width > 16)
721,491,249✔
380
                width = 32;
571,993,215✔
381
            else
149,498,034✔
382
                width = 16;
149,498,034✔
383
        }
763,905,249✔
384
        else { // width <= 8
427,054,803✔
385
            if (width > 4)
427,054,803✔
386
                width = 8;
57,227,025✔
387
            else if (width > 2)
369,827,778✔
388
                width = 4;
11,897,847✔
389
            // else width is already a power of 2
390
        }
427,054,803✔
391
        size_t num_bytes = 0;
1,190,960,052✔
392
        switch (wtype) {
1,190,960,052✔
393
            case wtype_Bits: {
890,017,515✔
394
                // Current assumption is that size is at most 2^24 and that width is at most 64.
395
                // In that case the following will never overflow. (Assuming that size_t is at least 32 bits)
396
                REALM_ASSERT_3(size, <, 0x1000000);
890,017,515✔
397
                size_t num_bits = size * width;
890,017,515✔
398
                num_bytes = (num_bits + 7) >> 3;
890,017,515✔
399
                break;
890,017,515✔
400
            }
×
401
            case wtype_Multiply: {
29,435,973✔
402
                num_bytes = size * width;
29,435,973✔
403
                break;
29,435,973✔
404
            }
×
405
            case wtype_Ignore:
328,287,873✔
406
                num_bytes = size;
328,287,873✔
407
                break;
328,287,873✔
NEW
408
            default: {
✔
NEW
409
                REALM_ASSERT(false);
×
NEW
410
                break;
×
NEW
411
            }
×
412
        }
1,190,960,052✔
413
        num_bytes += header_size;
1,199,875,344✔
414
        // Ensure 8-byte alignment
415
        num_bytes = (num_bytes + 7) & ~size_t(7);
1,199,875,344✔
416
        return num_bytes;
1,199,875,344✔
417
    }
1,190,960,052✔
418

419
    static inline void set_flags(char* header, uint8_t flags)
NEW
420
    {
×
NEW
421
        REALM_ASSERT_DEBUG(flags <= 7);
×
NEW
422
        auto h = reinterpret_cast<uint8_t*>(header);
×
NEW
423
        h[4] = (h[4] & 0b00011111) | flags << 5;
×
NEW
424
    }
×
425
    static inline uint8_t get_flags(char* header)
426
    {
207,756✔
427
        auto h = reinterpret_cast<uint8_t*>(header);
207,756✔
428
        return h[4] >> 5;
207,756✔
429
    }
207,756✔
430

431
    static inline void set_flags2(char* header, uint8_t flags)
NEW
432
    {
×
NEW
433
        REALM_ASSERT_DEBUG(flags <= 7);
×
NEW
434
        auto h = reinterpret_cast<uint8_t*>(header);
×
NEW
435
        h[4] = (h[4] & 0b11111000) | flags;
×
NEW
436
    }
×
437
    static inline uint8_t get_flags2(char* header)
NEW
438
    {
×
NEW
439
        auto h = reinterpret_cast<uint8_t*>(header);
×
NEW
440
        return h[4] & 0b0111;
×
UNCOV
441
    }
×
442
};
443

444
inline void NodeHeader::set_element_size(char* header, uint8_t bits_per_element, Encoding encoding)
NEW
445
{
×
NEW
446
    switch (encoding) {
×
NEW
447
        case NodeHeader::Encoding::Packed: {
×
NEW
448
            REALM_ASSERT_DEBUG(get_encoding(header) == Encoding::Packed);
×
NEW
449
            REALM_ASSERT_DEBUG(bits_per_element <= 64);
×
NEW
450
            (reinterpret_cast<uint8_t*>(header)[3] = static_cast<uint8_t>(bits_per_element));
×
NEW
451
        } break;
×
NEW
452
        case NodeHeader::Encoding::WTypBits: {
×
NEW
453
            REALM_ASSERT_DEBUG(bits_per_element <= 64);
×
NEW
454
            // TODO: Only powers of two allowed
×
NEW
455
            // TODO: Optimize
×
NEW
456
            NodeHeader::set_wtype_in_header(wtype_Bits, reinterpret_cast<char*>(header));
×
NEW
457
            NodeHeader::set_width_in_header(bits_per_element, reinterpret_cast<char*>(header));
×
NEW
458
        } break;
×
NEW
459
        case NodeHeader::Encoding::WTypMult: {
×
NEW
460
            REALM_ASSERT_DEBUG(bits_per_element <= 64);
×
NEW
461
            REALM_ASSERT_DEBUG((bits_per_element & 0x7) == 0);
×
NEW
462
            // TODO: Only powers of two allowed
×
NEW
463
            // TODO: Optimize
×
NEW
464
            NodeHeader::set_wtype_in_header(wtype_Multiply, reinterpret_cast<char*>(header));
×
NEW
465
            NodeHeader::set_width_in_header(bits_per_element >> 3, reinterpret_cast<char*>(header));
×
NEW
466
        } break;
×
NEW
467
        default:
×
468
            REALM_UNREACHABLE();
NEW
469
    }
×
NEW
470
}
×
471

472
inline uint8_t NodeHeader::get_element_size(const char* header, Encoding encoding)
473
{
85,885,290✔
474
    switch (encoding) {
85,885,290✔
475
        case NodeHeader::Encoding::Packed: {
88,856,319✔
476
            REALM_ASSERT_DEBUG(get_encoding(header) == Encoding::Packed);
88,856,319✔
477
            const auto bits_per_element = (reinterpret_cast<const uint8_t*>(header))[3];
88,856,319✔
478
            REALM_ASSERT_DEBUG(bits_per_element <= 64);
88,856,319✔
479
            return bits_per_element;
88,856,319✔
NEW
480
        } break;
×
NEW
481
        case NodeHeader::Encoding::WTypBits: {
✔
NEW
482
            REALM_ASSERT_DEBUG(get_wtype_from_header(header) == wtype_Bits);
×
NEW
483
            const auto bits_per_element = NodeHeader::get_width_from_header(reinterpret_cast<const char*>(header));
×
NEW
484
            REALM_ASSERT_DEBUG(bits_per_element <= 64);
×
NEW
485
            return bits_per_element;
×
NEW
486
        } break;
×
NEW
487
        case NodeHeader::Encoding::WTypMult: {
✔
NEW
488
            REALM_ASSERT_DEBUG(get_wtype_from_header(header) == wtype_Multiply);
×
NEW
489
            const auto bits_per_element = NodeHeader::get_width_from_header(reinterpret_cast<const char*>(header))
×
NEW
490
                                          << 3;
×
NEW
491
            REALM_ASSERT_DEBUG(bits_per_element <= 64);
×
NEW
492
            return bits_per_element;
×
NEW
493
        } break;
×
NEW
494
        default:
✔
495
            REALM_UNREACHABLE();
496
    }
85,885,290✔
497
}
85,885,290✔
498

499
inline void NodeHeader::set_elementA_size(char* header, uint8_t bits_per_element)
NEW
500
{
×
NEW
501
    // we're a bit low on bits for the Flex encoding, so we need to squeeze stuff
×
NEW
502
    REALM_ASSERT_DEBUG(get_encoding(header) == Encoding::Flex);
×
NEW
503
    REALM_ASSERT_DEBUG(bits_per_element <= 64);
×
NEW
504
    REALM_ASSERT_DEBUG(bits_per_element > 0);
×
NEW
505
    uint16_t word = (reinterpret_cast<uint16_t*>(header))[1];
×
NEW
506
    word &= ~(0b111111 << 10);
×
NEW
507
    //  we only have 6 bits, so store values in range 1-64 as 0-63
×
NEW
508
    word |= (bits_per_element - 1) << 10;
×
NEW
509
    (reinterpret_cast<uint16_t*>(header))[1] = word;
×
NEW
510
}
×
511

512
inline void NodeHeader::set_elementB_size(char* header, uint8_t bits_per_element)
NEW
513
{
×
NEW
514
    // we're a bit low on bits for the Flex encoding, so we need to squeeze stuff
×
NEW
515
    REALM_ASSERT_DEBUG(get_encoding(header) == Encoding::Flex);
×
NEW
516
    REALM_ASSERT_DEBUG(bits_per_element <= 64);
×
NEW
517
    REALM_ASSERT_DEBUG(bits_per_element > 0);
×
NEW
518
    uint16_t word = (reinterpret_cast<uint16_t*>(header))[3];
×
NEW
519
    word &= ~(0b111111 << 10);
×
NEW
520
    //  we only have 6 bits, so store values in range 1-64 as 0-63
×
NEW
521
    word |= (bits_per_element - 1) << 10;
×
NEW
522
    (reinterpret_cast<uint16_t*>(header))[3] = word;
×
NEW
523
}
×
524

525
inline uint8_t NodeHeader::get_elementA_size(const char* header)
526
{
272,163✔
527
    const auto encoding = get_encoding(header);
272,163✔
528
    REALM_ASSERT_DEBUG(encoding == Encoding::Flex);
272,163✔
529
    uint16_t word = (reinterpret_cast<const uint16_t*>(header))[1];
272,163✔
530
    auto bits_per_element = (word >> 10) & 0b111111;
272,163✔
531
    //  we only have 6 bits, so store values in range 1-64 as 0-63
532
    // this means that Flex cannot support element sizes of 0
533
    bits_per_element++;
272,163✔
534
    REALM_ASSERT_DEBUG(bits_per_element <= 64);
272,163✔
535
    REALM_ASSERT_DEBUG(bits_per_element > 0);
272,163✔
536
    return bits_per_element;
272,163✔
537
}
272,163✔
538

539
inline uint8_t NodeHeader::get_elementB_size(const char* header)
540
{
272,166✔
541
    REALM_ASSERT_DEBUG(get_encoding(header) == Encoding::Flex);
272,166✔
542
    uint16_t word = (reinterpret_cast<const uint16_t*>(header))[3];
272,166✔
543
    auto bits_per_element = (word >> 10) & 0b111111;
272,166✔
544
    // same as above
545
    bits_per_element++;
272,166✔
546
    REALM_ASSERT_DEBUG(bits_per_element <= 64);
272,166✔
547
    REALM_ASSERT_DEBUG(bits_per_element > 0);
272,166✔
548
    return bits_per_element;
272,166✔
549
}
272,166✔
550

551
inline size_t NodeHeader::get_num_elements(const char* header, Encoding encoding)
552
{
2,971,081,257✔
553
    switch (encoding) {
2,971,081,257✔
554
        case NodeHeader::Encoding::Packed:
85,674,834✔
555
            REALM_ASSERT_DEBUG(get_encoding(header) == Encoding::Packed);
85,674,834✔
556
            return (reinterpret_cast<const uint16_t*>(header))[3];
85,674,834✔
NEW
557
            break;
×
558
        case NodeHeader::Encoding::WTypBits:
2,544,365,364✔
559
        case NodeHeader::Encoding::WTypMult:
2,662,082,304✔
560
        case NodeHeader::Encoding::WTypIgn: {
3,034,952,004✔
561
            REALM_ASSERT_DEBUG(get_wtype_from_header(header) != wtype_Extend);
3,034,952,004✔
562
            typedef unsigned char uchar;
3,034,952,004✔
563
            const uchar* h = reinterpret_cast<const uchar*>(header);
3,034,952,004✔
564
            return (size_t(h[5]) << 16) + (size_t(h[6]) << 8) + h[7];
3,034,952,004✔
NEW
565
            break;
×
566
        }
2,662,082,304✔
567
        case NodeHeader::Encoding::Flex:
174,306✔
568
            return get_arrayB_num_elements(header);
174,306✔
NEW
569
            break;
×
NEW
570
        default:
✔
NEW
571
            printf("Encoding %d\n", int(encoding));
×
572
            REALM_UNREACHABLE();
573
    }
2,971,081,257✔
574
}
2,971,081,257✔
575

576
inline void NodeHeader::set_num_elements(char* header, size_t num_elements, Encoding encoding)
NEW
577
{
×
NEW
578
    switch (encoding) {
×
NEW
579
        case NodeHeader::Encoding::Packed: {
×
NEW
580
            REALM_ASSERT_DEBUG(get_encoding(header) == Encoding::Packed);
×
NEW
581
            REALM_ASSERT_DEBUG(num_elements < 0x10000);
×
NEW
582
            (reinterpret_cast<uint16_t*>(header))[3] = static_cast<uint16_t>(num_elements);
×
NEW
583
        } break;
×
NEW
584
        case NodeHeader::Encoding::WTypBits: {
×
NEW
585
            REALM_ASSERT_DEBUG(get_encoding(header) == Encoding::WTypBits);
×
NEW
586
            NodeHeader::set_wtype_in_header(wtype_Bits, header);
×
NEW
587
            NodeHeader::set_size_in_header(num_elements, header);
×
NEW
588
        } break;
×
NEW
589
        case NodeHeader::Encoding::WTypMult: {
×
NEW
590
            REALM_ASSERT_DEBUG(get_encoding(header) == Encoding::WTypMult);
×
NEW
591
            NodeHeader::set_wtype_in_header(wtype_Multiply, header);
×
NEW
592
            NodeHeader::set_size_in_header(num_elements, header);
×
NEW
593
        } break;
×
NEW
594
        case NodeHeader::Encoding::WTypIgn: {
×
NEW
595
            REALM_ASSERT_DEBUG(get_encoding(header) == Encoding::WTypIgn);
×
NEW
596
            NodeHeader::set_wtype_in_header(wtype_Ignore, header);
×
NEW
597
            NodeHeader::set_size_in_header(num_elements, header);
×
NEW
598
        } break;
×
NEW
599
        default:
×
600
            REALM_UNREACHABLE();
NEW
601
    }
×
NEW
602
}
×
603

604
inline void NodeHeader::set_arrayA_num_elements(char* header, size_t num_elements)
NEW
605
{
×
NEW
606
    REALM_ASSERT_DEBUG(get_encoding(header) == Encoding::Flex);
×
NEW
607
    REALM_ASSERT_DEBUG(num_elements < 0b10000000000); // 10 bits
×
NEW
608
    uint16_t word = (reinterpret_cast<uint16_t*>(header))[1];
×
NEW
609
    word &= ~(0b1111111111 << 10);
×
NEW
610
    word |= num_elements << 10;
×
NEW
611
    (reinterpret_cast<uint16_t*>(header))[1] = word;
×
NEW
612
}
×
613

614
inline void NodeHeader::set_arrayB_num_elements(char* header, size_t num_elements)
NEW
615
{
×
NEW
616
    REALM_ASSERT_DEBUG(get_encoding(header) == Encoding::Flex);
×
NEW
617
    REALM_ASSERT_DEBUG(num_elements < 0b10000000000); // 10 bits
×
NEW
618
    uint16_t word = (reinterpret_cast<uint16_t*>(header))[3];
×
NEW
619
    word &= ~(0b1111111111 << 10);
×
NEW
620
    word |= num_elements << 10;
×
NEW
621
    (reinterpret_cast<uint16_t*>(header))[3] = word;
×
NEW
622
}
×
623

624
inline size_t NodeHeader::get_arrayA_num_elements(const char* header)
625
{
272,163✔
626
    const auto encoding = get_encoding(header);
272,163✔
627
    REALM_ASSERT_DEBUG(encoding == Encoding::Flex);
272,163✔
628
    const uint16_t word = (reinterpret_cast<const uint16_t*>(header))[1];
272,163✔
629
    const auto num_elements = word & 0b1111111111;
272,163✔
630
    return num_elements;
272,163✔
631
}
272,163✔
632

633
inline size_t NodeHeader::get_arrayB_num_elements(const char* header)
634
{
446,445✔
635
    REALM_ASSERT_DEBUG(get_encoding(header) == Encoding::Flex);
446,445✔
636
    const uint16_t word = (reinterpret_cast<const uint16_t*>(header))[3];
446,445✔
637
    const auto num_elements = word & 0b1111111111;
446,445✔
638
    return num_elements;
446,445✔
639
}
446,445✔
640

641
inline size_t NodeHeader::calc_size(size_t num_elements)
NEW
642
{
×
NEW
643
    return calc_byte_size(wtype_Ignore, num_elements, 0);
×
NEW
644
}
×
645

646
inline size_t NodeHeader::calc_size(size_t num_elements, uint8_t element_size, Encoding encoding)
647
{
647,877✔
648
    using Encoding = NodeHeader::Encoding;
647,877✔
649
    switch (encoding) {
647,877✔
650
        case Encoding::Packed:
548,760✔
651
            return NodeHeader::header_size + align_bits_to8(num_elements * element_size);
548,760✔
652
        case Encoding::WTypBits:
99,120✔
653
            return calc_byte_size(wtype_Bits, num_elements, static_cast<uint_least8_t>(element_size));
99,120✔
NEW
654
        case Encoding::WTypMult:
✔
NEW
655
            return calc_byte_size(wtype_Multiply, num_elements, static_cast<uint_least8_t>(element_size));
×
NEW
656
        case Encoding::WTypIgn:
✔
NEW
657
            return calc_byte_size(wtype_Ignore, num_elements, 0);
×
NEW
658
        default:
✔
659
            REALM_UNREACHABLE();
660
    }
647,877✔
661
}
647,877✔
662

663
inline size_t NodeHeader::calc_size(size_t arrayA_num_elements, size_t arrayB_num_elements, uint8_t elementA_size,
664
                                    uint8_t elementB_size)
665
{
548,763✔
666
    return NodeHeader::header_size +
548,763✔
667
           align_bits_to8(arrayA_num_elements * elementA_size + arrayB_num_elements * elementB_size);
548,763✔
668
}
548,763✔
669

670
size_t inline NodeHeader::get_byte_size_from_header(const char* header) noexcept
671
{
1,244,119,686✔
672
    const auto h = header;
1,244,119,686✔
673

674
    const auto encoding = get_encoding(h);
1,244,119,686✔
675
    const auto size = get_num_elements(h, encoding);
1,244,119,686✔
676
    switch (encoding) {
1,244,119,686✔
677
        case Encoding::WTypBits:
869,188,644✔
678
        case Encoding::WTypIgn:
1,191,250,614✔
679
        case Encoding::WTypMult: {
1,219,738,752✔
680
            const auto width = get_width_from_header(header);
1,219,738,752✔
681
            return calc_byte_size(WidthType(int(encoding)), size, static_cast<uint_least8_t>(width));
1,219,738,752✔
682
        }
1,191,250,614✔
683
        case Encoding::Packed:
44,383,917✔
684
            return NodeHeader::header_size + align_bits_to8(size * get_element_size(h, encoding));
44,383,917✔
685
        case Encoding::Flex:
174,297✔
686
            return NodeHeader::header_size + align_bits_to8(get_arrayA_num_elements(h) * get_elementA_size(h) +
174,297✔
687
                                                            get_arrayB_num_elements(h) * get_elementB_size(h));
174,297✔
NEW
688
        default:
✔
689
            REALM_UNREACHABLE();
690
    }
1,244,119,686✔
691
}
1,244,119,686✔
692

693

694
uint_least8_t inline NodeHeader::get_width_from_header(const char* header) noexcept
695
{
4,294,967,294✔
696
    REALM_ASSERT_DEBUG(!wtype_is_extended(header));
4,294,967,294✔
697
    typedef unsigned char uchar;
4,294,967,294✔
698
    const uchar* h = reinterpret_cast<const uchar*>(header);
4,294,967,294✔
699
    return uint_least8_t((1 << (int(h[4]) & 0x07)) >> 1);
4,294,967,294✔
700
}
4,294,967,294✔
701

702
// A little helper:
703
size_t inline NodeHeader::get_size_from_header(const char* header) noexcept
704
{
1,931,539,125✔
705
    return get_num_elements(header, get_encoding(header));
1,931,539,125✔
706
}
1,931,539,125✔
707

708
} // namespace realm
709

710

711
namespace {
712

713
static inline void init_header(char* header, realm::NodeHeader::Encoding enc, uint8_t flags, uint8_t bits_pr_elem,
714
                               size_t num_elems)
715
{
16,255,735✔
716
    using Encoding = realm::NodeHeader::Encoding;
16,255,735✔
717
    std::fill(header, header + realm::NodeHeader::header_size, 0);
16,255,735✔
718
    const auto hb = reinterpret_cast<uint8_t*>(header);
16,255,735✔
719
    REALM_ASSERT_DEBUG(enc <= Encoding::Packed);
16,255,735!
720
    if (enc < Encoding::Packed) {
16,255,735!
721
        // old layout
722
        uint8_t wtype = static_cast<uint8_t>(enc);
16,182,550✔
723
        hb[4] = (flags << 5) | (wtype << 3);
16,182,550✔
724
        if (enc == Encoding::WTypBits)
16,182,550!
725
            realm::NodeHeader::set_width_in_header(bits_pr_elem, reinterpret_cast<char*>(header));
12,905,847✔
726
        else
3,276,703✔
727
            realm::NodeHeader::set_width_in_header(bits_pr_elem >> 3, reinterpret_cast<char*>(header));
3,276,703✔
728
        realm::NodeHeader::set_size_in_header(num_elems, reinterpret_cast<char*>(header));
16,182,550✔
729
    }
16,182,550✔
730
    else if (enc == Encoding::Packed) {
78,906!
731
        hb[2] = 0;
78,906✔
732
        hb[3] = static_cast<uint8_t>(bits_pr_elem);
78,906✔
733
        hb[4] = (flags << 5) | (realm::NodeHeader::wtype_Extend << 3);
78,906✔
734
        hb[5] = static_cast<uint8_t>(enc) - realm::NodeHeader::wtype_Extend;
78,906✔
735
        const auto hw = reinterpret_cast<uint16_t*>(header);
78,906✔
736
        hw[3] = static_cast<uint16_t>(num_elems);
78,906✔
737
    }
78,906✔
738
}
16,255,735✔
739

740
// init the header for flex array. Passing A bit width and size (values) and B bit width and size (indices)
741
static inline void init_header(char* header, realm::NodeHeader::Encoding enc, uint8_t flags, uint8_t bits_pr_elemA,
742
                               uint8_t bits_pr_elemB, size_t num_elemsA, size_t num_elemsB)
743
{
29,732✔
744
    std::fill(header, header + realm::NodeHeader::header_size, 0);
29,732✔
745
    const auto hb = reinterpret_cast<uint8_t*>(header);
29,732✔
746
    REALM_ASSERT_DEBUG(enc == realm::NodeHeader::Encoding::Flex);
29,732✔
747
    REALM_ASSERT_DEBUG(flags < 8);
29,732✔
748
    hb[4] = (flags << 5) | (realm::NodeHeader::wtype_Extend << 3);
29,732✔
749
    hb[5] =
29,732✔
750
        static_cast<int>(realm::NodeHeader::Encoding::Flex) - static_cast<int>(realm::NodeHeader::Encoding::Packed);
29,732✔
751
    const auto hw = reinterpret_cast<uint16_t*>(header);
29,732✔
752
    REALM_ASSERT_DEBUG(bits_pr_elemA > 0);
29,732✔
753
    REALM_ASSERT_DEBUG(bits_pr_elemB > 0);
29,732✔
754
    REALM_ASSERT_DEBUG(bits_pr_elemA <= 64);
29,732✔
755
    REALM_ASSERT_DEBUG(bits_pr_elemB <= 64);
29,732✔
756
    REALM_ASSERT_DEBUG(num_elemsA < 1024);
29,732✔
757
    REALM_ASSERT_DEBUG(num_elemsB < 1024);
29,732✔
758
    hw[1] = static_cast<uint16_t>(((bits_pr_elemA - 1) << 10) | num_elemsA);
29,732✔
759
    hw[3] = static_cast<uint16_t>(((bits_pr_elemB - 1) << 10) | num_elemsB);
29,732✔
760
    REALM_ASSERT_DEBUG(realm::NodeHeader::get_encoding(header) == realm::NodeHeader::Encoding::Flex);
29,732✔
761
}
29,732✔
762
} // namespace
763

764

765
#endif /* REALM_NODE_HEADER_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