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

realm / realm-core / nicola.cabiddu_1042

27 Sep 2023 06:04PM CUT coverage: 91.085% (-1.8%) from 92.915%
nicola.cabiddu_1042

Pull #6766

Evergreen

nicola-cab
Fix logic for dictionaries
Pull Request #6766: Client Reset for collections in mixed / nested collections

97276 of 178892 branches covered (0.0%)

1994 of 2029 new or added lines in 7 files covered. (98.28%)

4556 existing lines in 112 files now uncovered.

237059 of 260260 relevant lines covered (91.09%)

6321099.55 hits per line

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

88.59
/src/realm/sync/changeset_parser.cpp
1

2
#include <realm/sync/changeset_parser.hpp>
3

4
#include <realm/global_key.hpp>
5
#include <realm/mixed.hpp>
6
#include <realm/sync/changeset.hpp>
7
#include <realm/sync/instructions.hpp>
8
#include <realm/sync/noinst/integer_codec.hpp>
9
#include <realm/table.hpp>
10
#include <realm/util/base64.hpp>
11

12
#include <unordered_set>
13

14
using namespace realm;
15
using namespace realm::sync;
16

17
namespace {
18

19
struct State {
20
    util::InputStream& m_input;
21
    InstructionHandler& m_handler;
22

23
    explicit State(util::InputStream& input, InstructionHandler& handler)
24
        : m_input(input)
25
        , m_handler(handler)
26
    {
10,538,448✔
27
    }
10,538,448✔
28

29
    // pointer into transaction log, each instruction is parsed from m_input_begin and onwards.
30
    // Each instruction are assumed to be contiguous in memory.
31
    const char* m_input_begin = nullptr;
32
    // pointer to one past current instruction log chunk. If m_input_begin reaches m_input_end,
33
    // a call to next_input_buffer will move m_input_begin and m_input_end to a new chunk of
34
    // memory. Setting m_input_end to 0 disables this check, and is used if it is already known
35
    // that all of the instructions are in memory.
36
    const char* m_input_end = nullptr;
37

38
    std::string m_buffer;
39
    // Cannot use StringData as key type since m_input_begin may start pointing
40
    // to a new chunk of memory.
41
    std::unordered_set<std::string> m_intern_strings;
42

43

44
    void parse_one(); // Throws
45
    bool has_next() noexcept;
46

47
    // Advance m_input_begin and m_input_end to reflect the next block of
48
    // instructions.
49
    // Returns false if no more input was available
50
    bool next_input_buffer() noexcept;
51

52
    template <class T = int64_t>
53
    T read_int(); // Throws
54

55
    util::Optional<Instruction::Payload::Type> read_optional_payload_type();
56
    Instruction::Payload::Type read_payload_type();
57
    Instruction::AddColumn::CollectionType read_collection_type();
58
    Instruction::Payload read_payload();
59
    Instruction::Payload::Link read_link();
60
    Instruction::PrimaryKey read_object_key();
61
    Instruction::Path read_path();
62
    bool read_char(char& c) noexcept;
63
    void read_bytes(char* data, size_t size); // Throws
64
    bool read_bool();                         // Throws
65
    float read_float();                       // Throws
66
    double read_double();                     // Throws
67
    InternString read_intern_string();        // Throws
68
    GlobalKey read_global_key();              // Throws
69
    Timestamp read_timestamp();               // Throws
70
    ObjectId read_object_id();                // Throws
71
    Decimal128 read_decimal();                // Throws
72
    UUID read_uuid();                         // Throws
73

74
    void read_path_instr(Instruction::PathInstruction& instr);
75

76
    // Reads a string value from the stream. The returned value is only valid
77
    // until the next call to `read_string()` or `read_binary()`.
78
    StringData read_string(); // Throws
79

80
    // Reads a binary blob value from the stream. The returned value is only
81
    // valid until the next call to `read_string()` or `read_binary()`.
82
    BinaryData read_binary(); // Throws
83

84
    BinaryData read_buffer(size_t size);
85

86
    REALM_NORETURN void parser_error(std::string_view complaint); // Throws
87
    REALM_NORETURN void parser_error()
88
    {
×
89
        parser_error("Bad input");
×
90
    } // Throws
×
91
};
92

93
struct UnreachableInstructionHandler : public InstructionHandler {
94
    void set_intern_string(uint32_t, StringBufferRange) override
95
    {
×
96
        REALM_UNREACHABLE();
×
97
    }
×
98

99
    StringBufferRange add_string_range(StringData) override
100
    {
×
101
        REALM_UNREACHABLE();
×
102
    }
×
103

104
    void operator()(const Instruction&) override
105
    {
×
106
        REALM_UNREACHABLE();
×
107
    }
×
108
};
109

110
struct InstructionBuilder : InstructionHandler {
111
    explicit InstructionBuilder(Changeset& log)
112
        : m_log(log)
113
    {
10,538,454✔
114
        log.interned_strings().clear();
10,538,454✔
115
    }
10,538,454✔
116
    Changeset& m_log;
117

118
    void operator()(const Instruction& instr) final
119
    {
10,049,370✔
120
        m_log.push_back(instr);
10,049,370✔
121
    }
10,049,370✔
122

123
    StringBufferRange add_string_range(StringData string) final
124
    {
15,343,198✔
125
        return m_log.append_string(string);
15,343,198✔
126
    }
15,343,198✔
127

128
    void set_intern_string(uint32_t index, StringBufferRange range) final
129
    {
14,229,672✔
130
        InternStrings& strings = m_log.interned_strings();
14,229,672✔
131
        REALM_ASSERT(index == strings.size());
14,229,672✔
132
        strings.push_back(range);
14,229,672✔
133
    }
14,229,672✔
134
};
135

136
Instruction::Payload::Type State::read_payload_type()
137
{
12,895,542✔
138
    using Type = Instruction::Payload::Type;
12,895,542✔
139
    auto type = Instruction::Payload::Type(read_int());
12,895,542✔
140
    // Validate the type.
6,418,128✔
141
    switch (type) {
12,895,542✔
142
        case Type::GlobalKey:
64✔
143
            [[fallthrough]];
64✔
144
        case Type::Erased:
412✔
145
            [[fallthrough]];
412✔
146
        case Type::Set:
440✔
147
            [[fallthrough]];
440✔
148
        case Type::List:
596✔
149
            [[fallthrough]];
596✔
150
        case Type::Dictionary:
688✔
151
            [[fallthrough]];
688✔
152
        case Type::ObjectValue:
5,356✔
153
            [[fallthrough]];
5,356✔
154
        case Type::Null:
31,584✔
155
            [[fallthrough]];
31,584✔
156
        case Type::Int:
9,795,954✔
157
            [[fallthrough]];
9,795,954✔
158
        case Type::Bool:
9,796,498✔
159
            [[fallthrough]];
9,796,498✔
160
        case Type::String:
12,650,746✔
161
            [[fallthrough]];
12,650,746✔
162
        case Type::Binary:
12,672,842✔
163
            [[fallthrough]];
12,672,842✔
164
        case Type::Timestamp:
12,677,822✔
165
            [[fallthrough]];
12,677,822✔
166
        case Type::Float:
12,678,594✔
167
            [[fallthrough]];
12,678,594✔
168
        case Type::Double:
12,680,130✔
169
            [[fallthrough]];
12,680,130✔
170
        case Type::Decimal:
12,680,554✔
171
            [[fallthrough]];
12,680,554✔
172
        case Type::Link:
12,688,554✔
173
            [[fallthrough]];
12,688,554✔
174
        case Type::ObjectId:
12,894,112✔
175
            [[fallthrough]];
12,894,112✔
176
        case Type::UUID:
12,895,288✔
177
            return type;
12,895,288✔
UNCOV
178
    }
×
UNCOV
179
    parser_error("Unsupported data type");
×
UNCOV
180
}
×
181

182
Instruction::AddColumn::CollectionType State::read_collection_type()
183
{
592,938✔
184
    using CollectionType = Instruction::AddColumn::CollectionType;
592,938✔
185
    auto type = Instruction::AddColumn::CollectionType(read_int<uint8_t>());
592,938✔
186
    // Validate the type.
294,414✔
187
    switch (type) {
592,938✔
188
        case CollectionType::Single:
387,816✔
189
            [[fallthrough]];
387,816✔
190
        case CollectionType::List:
592,310✔
191
            [[fallthrough]];
592,310✔
192
        case CollectionType::Dictionary:
592,642✔
193
            [[fallthrough]];
592,642✔
194
        case CollectionType::Set:
592,942✔
195
            return type;
592,942✔
UNCOV
196
    }
×
UNCOV
197
    parser_error("Unsupported collection type");
×
UNCOV
198
}
×
199

200
Instruction::Payload State::read_payload()
201
{
2,988,762✔
202
    using Type = Instruction::Payload::Type;
2,988,762✔
203

1,478,522✔
204
    Instruction::Payload payload;
2,988,762✔
205
    payload.type = read_payload_type();
2,988,762✔
206
    auto& data = payload.data;
2,988,762✔
207
    switch (payload.type) {
2,988,762✔
UNCOV
208
        case Type::GlobalKey: {
✔
UNCOV
209
            parser_error("Unsupported payload data type");
×
210
        }
×
211
        case Type::Int: {
1,831,432✔
212
            data.integer = read_int();
1,831,432✔
213
            return payload;
1,831,432✔
214
        }
×
215
        case Type::Bool: {
328✔
216
            data.boolean = read_bool();
328✔
217
            return payload;
328✔
218
        }
×
219
        case Type::Float: {
512✔
220
            data.fnum = read_float();
512✔
221
            return payload;
512✔
222
        }
×
223
        case Type::Double: {
1,280✔
224
            data.dnum = read_double();
1,280✔
225
            return payload;
1,280✔
UNCOV
226
        }
×
227
        case Type::String: {
1,091,996✔
228
            StringData value = read_string();
1,091,996✔
229
            data.str = m_handler.add_string_range(value);
1,091,996✔
230
            return payload;
1,091,996✔
UNCOV
231
        }
×
232
        case Type::Binary: {
21,830✔
233
            BinaryData value = read_binary();
21,830✔
234
            data.binary = m_handler.add_string_range(StringData{value.data(), value.size()});
21,830✔
235
            return payload;
21,830✔
236
        }
×
237
        case Type::Timestamp: {
4,548✔
238
            data.timestamp = read_timestamp();
4,548✔
239
            return payload;
4,548✔
240
        }
×
241
        case Type::ObjectId: {
1,372✔
242
            data.object_id = read_object_id();
1,372✔
243
            return payload;
1,372✔
244
        }
×
245
        case Type::Decimal: {
276✔
246
            data.decimal = read_decimal();
276✔
247
            return payload;
276✔
248
        }
×
249
        case Type::UUID: {
488✔
250
            data.uuid = read_uuid();
488✔
251
            return payload;
488✔
252
        }
×
253
        case Type::Link: {
2,820✔
254
            data.link = read_link();
2,820✔
255
            return payload;
2,820✔
UNCOV
256
        }
×
257

258
        case Type::Null:
25,744✔
259
            [[fallthrough]];
25,744✔
260
        case Type::Set:
25,772✔
261
            [[fallthrough]];
25,772✔
262
        case Type::List:
25,928✔
263
            [[fallthrough]];
25,928✔
264
        case Type::Dictionary:
26,020✔
265
            [[fallthrough]];
26,020✔
266
        case Type::Erased:
26,368✔
267
            [[fallthrough]];
26,368✔
268
        case Type::ObjectValue:
31,036✔
269
            return payload;
31,036✔
UNCOV
270
    }
×
271

UNCOV
272
    parser_error("Unsupported payload type");
×
UNCOV
273
}
×
274

275
Instruction::PrimaryKey State::read_object_key()
276
{
9,067,702✔
277
    using Type = Instruction::Payload::Type;
9,067,702✔
278
    Type type = read_payload_type();
9,067,702✔
279
    switch (type) {
9,067,702✔
280
        case Type::Null:
100✔
281
            return mpark::monostate{};
100✔
282
        case Type::Int:
7,437,746✔
283
            return read_int();
7,437,746✔
284
        case Type::String:
1,431,366✔
285
            return read_intern_string();
1,431,366✔
286
        case Type::GlobalKey:
40✔
287
            return read_global_key();
40✔
288
        case Type::ObjectId:
198,028✔
289
            return read_object_id();
198,028✔
290
        case Type::UUID:
216✔
291
            return read_uuid();
216✔
UNCOV
292
        default:
✔
UNCOV
293
            break;
×
UNCOV
294
    }
×
UNCOV
295
    parser_error("Unsupported object key type");
×
UNCOV
296
}
×
297

298
Instruction::Payload::Link State::read_link()
299
{
2,820✔
300
    auto target_class = read_intern_string();
2,820✔
301
    auto key = read_object_key();
2,820✔
302
    return Instruction::Payload::Link{target_class, key};
2,820✔
303
}
2,820✔
304

305
Instruction::Path State::read_path()
306
{
3,273,150✔
307
    Instruction::Path path;
3,273,150✔
308
    size_t path_len = read_int<uint32_t>();
3,273,150✔
309

1,612,078✔
310
    // Note: Not reserving `path_len`, because a corrupt changeset could cause std::bad_alloc to be thrown.
1,612,078✔
311
    if (path_len != 0)
3,273,150✔
312
        path.reserve(16);
1,769,380✔
313

1,612,078✔
314
    for (size_t i = 0; i < path_len; ++i) {
5,069,498✔
315
        int64_t element = read_int();
1,796,348✔
316
        if (element >= 0) {
1,796,348✔
317
            // Integer path element
872,240✔
318
            path.push_back(uint32_t(element));
1,771,562✔
319
        }
1,771,562✔
320
        else {
24,786✔
321
            // String path element
12,392✔
322
            path.push_back(read_intern_string());
24,786✔
323
        }
24,786✔
324
    }
1,796,348✔
325

1,612,078✔
326
    return path;
3,273,150✔
327
}
3,273,150✔
328

329
void State::read_path_instr(Instruction::PathInstruction& instr)
330
{
3,272,934✔
331
    instr.table = read_intern_string();
3,272,934✔
332
    instr.object = read_object_key();
3,272,934✔
333
    instr.field = read_intern_string();
3,272,934✔
334
    instr.path = read_path();
3,272,934✔
335
}
3,272,934✔
336

337
void State::parse_one()
338
{
24,277,110✔
339
    uint64_t t = read_int<uint64_t>();
24,277,110✔
340

12,048,272✔
341
    if (t == InstrTypeInternString) {
24,277,110✔
342
        uint32_t index = read_int<uint32_t>();
14,229,034✔
343
        if (index != m_intern_strings.size()) {
14,229,034✔
344
            parser_error(util::format("Unexpected intern index: %1", index));
12✔
345
        }
12✔
346
        StringData str = read_string();
14,229,034✔
347
        if (!m_intern_strings.insert(str).second) {
14,229,034✔
348
            parser_error(util::format("Unexpected intern string: %1", str));
4✔
349
        }
4✔
350
        StringBufferRange range = m_handler.add_string_range(str);
14,229,034✔
351
        m_handler.set_intern_string(index, range);
14,229,034✔
352
        return;
14,229,034✔
353
    }
14,229,034✔
354

5,008,058✔
355
    switch (Instruction::Type(t)) {
10,048,076✔
356
        case Instruction::Type::AddTable: {
249,970✔
357
            Instruction::AddTable instr;
249,970✔
358
            instr.table = read_intern_string();
249,970✔
359
            auto table_type = Table::Type(read_int<uint8_t>());
249,970✔
360
            switch (table_type) {
249,970✔
361
                case Table::Type::TopLevel:
249,224✔
362
                case Table::Type::TopLevelAsymmetric: {
249,290✔
363
                    Instruction::AddTable::TopLevelTable spec;
249,290✔
364
                    spec.pk_field = read_intern_string();
249,290✔
365
                    spec.pk_type = read_payload_type();
249,290✔
366
                    if (!is_valid_key_type(spec.pk_type)) {
249,290✔
UNCOV
367
                        parser_error(util::format("Invalid primary key type in AddTable: %1",
×
UNCOV
368
                                                  static_cast<uint8_t>(spec.pk_type)));
×
UNCOV
369
                    }
×
370
                    spec.pk_nullable = read_bool();
249,290✔
371
                    spec.is_asymmetric = (table_type == Table::Type::TopLevelAsymmetric);
249,290✔
372
                    instr.type = spec;
249,290✔
373
                    break;
249,290✔
374
                }
249,224✔
375
                case Table::Type::Embedded: {
126,398✔
376
                    instr.type = Instruction::AddTable::EmbeddedTable{};
680✔
377
                    break;
680✔
378
                }
249,224✔
379
                default:
126,058✔
UNCOV
380
                    parser_error(util::format("AddTable: unknown table type: %1", table_type));
×
381
            }
249,970✔
382
            m_handler(instr);
249,970✔
383
            return;
249,970✔
384
        }
249,970✔
385
        case Instruction::Type::EraseTable: {
199,428✔
386
            Instruction::EraseTable instr;
140,034✔
387
            instr.table = read_intern_string();
140,034✔
388
            m_handler(instr);
140,034✔
389
            return;
140,034✔
390
        }
249,970✔
391
        case Instruction::Type::CreateObject: {
3,531,240✔
392
            Instruction::CreateObject instr;
3,531,240✔
393
            instr.table = read_intern_string();
3,531,240✔
394
            instr.object = read_object_key();
3,531,240✔
395
            m_handler(instr);
3,531,240✔
396
            return;
3,531,240✔
397
        }
249,970✔
398
        case Instruction::Type::EraseObject: {
2,261,536✔
399
            Instruction::EraseObject instr;
2,261,536✔
400
            instr.table = read_intern_string();
2,261,536✔
401
            instr.object = read_object_key();
2,261,536✔
402
            m_handler(instr);
2,261,536✔
403
            return;
2,261,536✔
404
        }
249,970✔
405
        case Instruction::Type::Update: {
1,722,056✔
406
            Instruction::Update instr;
1,722,056✔
407
            read_path_instr(instr);
1,722,056✔
408
            instr.value = read_payload();
1,722,056✔
409

847,388✔
410
            // If the last path element is a string, we are setting a field. Otherwise, we are setting an array
847,388✔
411
            // element.
847,388✔
412
            if (!instr.is_array_update()) {
1,722,056✔
413
                instr.is_default = read_bool();
1,451,652✔
414
            }
1,451,652✔
415
            else {
270,404✔
416
                instr.prior_size = read_int<uint32_t>();
270,404✔
417
            }
270,404✔
418
            m_handler(instr);
1,722,056✔
419
            return;
1,722,056✔
420
        }
249,970✔
421
        case Instruction::Type::AddInteger: {
156,916✔
422
            Instruction::AddInteger instr;
54,906✔
423
            read_path_instr(instr);
54,906✔
424
            instr.value = read_int();
54,906✔
425
            m_handler(instr);
54,906✔
426
            return;
54,906✔
427
        }
249,970✔
428
        case Instruction::Type::AddColumn: {
592,940✔
429
            Instruction::AddColumn instr;
592,940✔
430
            instr.table = read_intern_string();
592,940✔
431
            instr.field = read_intern_string();
592,940✔
432
            instr.type = read_payload_type();
592,940✔
433
            instr.nullable = read_bool();
592,940✔
434
            instr.collection_type = read_collection_type();
592,940✔
435
            if (instr.type == Instruction::Payload::Type::Link) {
592,940✔
436
                instr.link_target_table = read_intern_string();
5,180✔
437
            }
5,180✔
438
            if (instr.collection_type == Instruction::AddColumn::CollectionType::Dictionary) {
592,940✔
439
                instr.key_type = read_payload_type();
332✔
440
            }
332✔
441
            else {
592,608✔
442
                instr.key_type = Instruction::Payload::Type::Null;
592,608✔
443
            }
592,608✔
444
            m_handler(instr);
592,940✔
445
            return;
592,940✔
446
        }
249,970✔
447
        case Instruction::Type::EraseColumn: {
126,404✔
448
            Instruction::EraseColumn instr;
12✔
449
            instr.table = read_intern_string();
12✔
450
            instr.field = read_intern_string();
12✔
451
            m_handler(instr);
12✔
452
            return;
12✔
453
        }
249,970✔
454
        case Instruction::Type::ArrayInsert: {
1,263,232✔
455
            Instruction::ArrayInsert instr;
1,263,232✔
456
            read_path_instr(instr);
1,263,232✔
457
            if (!instr.path.is_array_index()) {
1,263,232✔
UNCOV
458
                parser_error("ArrayInsert without an index");
×
UNCOV
459
            }
×
460
            instr.value = read_payload();
1,263,232✔
461
            instr.prior_size = read_int<uint32_t>();
1,263,232✔
462
            m_handler(instr);
1,263,232✔
463
            return;
1,263,232✔
464
        }
249,970✔
465
        case Instruction::Type::ArrayMove: {
126,484✔
466
            Instruction::ArrayMove instr;
172✔
467
            read_path_instr(instr);
172✔
468
            if (!instr.path.is_array_index()) {
172✔
UNCOV
469
                parser_error("ArrayMove without an index");
×
UNCOV
470
            }
×
471
            instr.ndx_2 = read_int<uint32_t>();
172✔
472
            instr.prior_size = read_int<uint32_t>();
172✔
473
            m_handler(instr);
172✔
474
            return;
172✔
475
        }
249,970✔
476
        case Instruction::Type::ArrayErase: {
244,292✔
477
            Instruction::ArrayErase instr;
225,398✔
478
            read_path_instr(instr);
225,398✔
479
            if (!instr.path.is_array_index()) {
225,398✔
UNCOV
480
                parser_error("ArrayErase without an index");
×
UNCOV
481
            }
×
482
            instr.prior_size = read_int<uint32_t>();
225,398✔
483
            m_handler(instr);
225,398✔
484
            return;
225,398✔
485
        }
249,970✔
486
        case Instruction::Type::Clear: {
128,922✔
487
            Instruction::Clear instr;
4,122✔
488
            read_path_instr(instr);
4,122✔
489
            uint32_t prior_size = read_int<uint32_t>();
4,122✔
490
            static_cast<void>(prior_size); // Ignored
4,122✔
491
            m_handler(instr);
4,122✔
492
            return;
4,122✔
493
        }
249,970✔
494
        case Instruction::Type::SetInsert: {
127,546✔
495
            Instruction::SetInsert instr;
2,296✔
496
            read_path_instr(instr);
2,296✔
497
            instr.value = read_payload();
2,296✔
498
            m_handler(instr);
2,296✔
499
            return;
2,296✔
500
        }
249,970✔
501
        case Instruction::Type::SetErase: {
126,686✔
502
            Instruction::SetErase instr;
576✔
503
            read_path_instr(instr);
576✔
504
            instr.value = read_payload();
576✔
505
            m_handler(instr);
576✔
506
            return;
576✔
507
        }
8✔
508
    }
8✔
509

4✔
510
    parser_error(util::format("Unknown instruction type: %1", t));
8✔
511
}
8✔
512

513

514
bool State::has_next() noexcept
515
{
34,814,722✔
516
    return m_input_begin != m_input_end || next_input_buffer();
34,814,722✔
517
}
34,814,722✔
518

519
bool State::next_input_buffer() noexcept
520
{
19,519,300✔
521
    auto next = m_input.next_block();
19,519,300✔
522
    m_input_begin = next.begin();
19,519,300✔
523
    m_input_end = next.end();
19,519,300✔
524
    return m_input_begin != m_input_end;
19,519,300✔
525
}
19,519,300✔
526

527
template <class T>
528
T State::read_int()
529
{
101,644,216✔
530
    T value = 0;
101,644,216✔
531
    if (REALM_LIKELY(_impl::decode_int(*this, value)))
101,644,216✔
532
        return value;
101,643,674✔
533
    parser_error("bad changeset - integer decoding failure");
554✔
534
}
554✔
535

536
bool State::read_char(char& c) noexcept
537
{
103,530,556✔
538
    if (m_input_begin == m_input_end && !next_input_buffer())
103,530,556✔
539
        return false;
8✔
540
    c = *m_input_begin++;
103,530,548✔
541
    return true;
103,530,548✔
542
}
103,530,548✔
543

544
void State::read_bytes(char* data, size_t size)
545
{
202,646✔
546
    for (;;) {
204,114✔
547
        const size_t avail = m_input_end - m_input_begin;
204,114✔
548
        if (size <= avail)
204,114✔
549
            break;
202,642✔
550
        std::copy_n(m_input_begin, avail, data);
1,472✔
551
        if (!next_input_buffer())
1,472✔
552
            parser_error("truncated input");
4✔
553
        data += avail;
1,472✔
554
        size -= avail;
1,472✔
555
    }
1,472✔
556
    const char* to = m_input_begin + size;
202,646✔
557
    std::copy_n(m_input_begin, size, data);
202,646✔
558
    m_input_begin = to;
202,646✔
559
}
202,646✔
560

561
bool State::read_bool()
562
{
2,294,016✔
563
    return read_int<uint8_t>(); // Throws
2,294,016✔
564
}
2,294,016✔
565

566
float State::read_float()
567
{
512✔
568
    static_assert(std::numeric_limits<float>::is_iec559 &&
512✔
569
                      sizeof(float) * std::numeric_limits<unsigned char>::digits == 32,
512✔
570
                  "Unsupported 'float' representation");
512✔
571
    float value;
512✔
572
    read_bytes(reinterpret_cast<char*>(&value), sizeof value); // Throws
512✔
573
    return value;
512✔
574
}
512✔
575

576
double State::read_double()
577
{
1,280✔
578
    static_assert(std::numeric_limits<double>::is_iec559 &&
1,280✔
579
                      sizeof(double) * std::numeric_limits<unsigned char>::digits == 64,
1,280✔
580
                  "Unsupported 'double' representation");
1,280✔
581
    double value;
1,280✔
582
    read_bytes(reinterpret_cast<char*>(&value), sizeof value); // Throws
1,280✔
583
    return value;
1,280✔
584
}
1,280✔
585

586
InternString State::read_intern_string()
587
{
15,626,466✔
588
    uint32_t index = read_int<uint32_t>(); // Throws
15,626,466✔
589
    if (index >= m_intern_strings.size())
15,626,466✔
590
        parser_error("Invalid interned string");
8✔
591
    return InternString{index};
15,626,466✔
592
}
15,626,466✔
593

594
GlobalKey State::read_global_key()
595
{
40✔
596
    uint64_t hi = read_int<uint64_t>(); // Throws
40✔
597
    uint64_t lo = read_int<uint64_t>(); // Throws
40✔
598
    return GlobalKey{hi, lo};
40✔
599
}
40✔
600

601
Timestamp State::read_timestamp()
602
{
4,548✔
603
    int64_t seconds = read_int<int64_t>();     // Throws
4,548✔
604
    int64_t nanoseconds = read_int<int64_t>(); // Throws
4,548✔
605
    if (nanoseconds > std::numeric_limits<int32_t>::max())
4,548✔
UNCOV
606
        parser_error("timestamp out of range");
×
607
    return Timestamp{seconds, int32_t(nanoseconds)};
4,548✔
608
}
4,548✔
609

610
ObjectId State::read_object_id()
611
{
199,430✔
612
    // FIXME: This is completely wrong and unsafe.
92,384✔
613
    ObjectId id;
199,430✔
614
    read_bytes(reinterpret_cast<char*>(&id), sizeof(id));
199,430✔
615
    return id;
199,430✔
616
}
199,430✔
617

618
UUID State::read_uuid()
619
{
708✔
620
    UUID::UUIDBytes bytes{};
708✔
621
    read_bytes(reinterpret_cast<char*>(bytes.data()), bytes.size());
708✔
622
    return UUID(bytes);
708✔
623
}
708✔
624

625
Decimal128 State::read_decimal()
626
{
276✔
627
    _impl::Bid128 cx;
276✔
628
    if (!_impl::decode_int(*this, cx))
276✔
UNCOV
629
        parser_error("bad changeset - decimal decoding failure");
×
630

138✔
631
    int exp = read_int<int>();
276✔
632
    bool sign = read_int<int>() != 0;
276✔
633
    Decimal128::Bid128 tmp;
276✔
634
    memcpy(&tmp, &cx, sizeof(Decimal128::Bid128));
276✔
635
    return Decimal128(tmp, exp, sign);
276✔
636
}
276✔
637

638
StringData State::read_string()
639
{
15,320,950✔
640
    uint64_t size = read_int<uint64_t>(); // Throws
15,320,950✔
641

7,595,340✔
642
    if (size > realm::Table::max_string_size)
15,320,950✔
643
        parser_error("string too long"); // Throws
4✔
644
    if (size > std::numeric_limits<size_t>::max())
15,320,950✔
UNCOV
645
        parser_error("invalid length"); // Throws
×
646

7,595,340✔
647
    BinaryData buffer = read_buffer(size_t(size));
15,320,950✔
648
    return StringData{buffer.data(), size_t(size)};
15,320,950✔
649
}
15,320,950✔
650

651
BinaryData State::read_binary()
652
{
21,832✔
653
    uint64_t size = read_int<uint64_t>(); // Throws
21,832✔
654

10,916✔
655
    if (size > std::numeric_limits<size_t>::max())
21,832✔
UNCOV
656
        parser_error("invalid binary length"); // Throws
×
657

10,916✔
658
    return read_buffer(size_t(size));
21,832✔
659
}
21,832✔
660

661
BinaryData State::read_buffer(size_t size)
662
{
15,342,558✔
663
    const size_t avail = m_input_end - m_input_begin;
15,342,558✔
664
    if (avail >= size) {
15,342,558✔
665
        m_input_begin += size;
15,341,998✔
666
        return BinaryData(m_input_begin - size, size);
15,341,998✔
667
    }
15,341,998✔
668

342✔
669
    m_buffer.clear();
560✔
670
    m_buffer.resize(size); // Throws
560✔
671
    read_bytes(m_buffer.data(), size);
560✔
672
    return BinaryData(m_buffer.data(), size);
560✔
673
}
560✔
674

675
void State::parser_error(std::string_view complaints)
676
{
56✔
677
    throw BadChangesetError{std::string(complaints)};
56✔
678
}
56✔
679

680
} // anonymous namespace
681

682
namespace realm::sync {
683

684
void parse_changeset(util::InputStream& input, Changeset& out_log)
685
{
10,538,442✔
686
    InstructionBuilder builder{out_log};
10,538,442✔
687
    State state{input, builder};
10,538,442✔
688

5,248,510✔
689
    while (state.has_next())
34,815,468✔
690
        state.parse_one();
24,277,026✔
691
}
10,538,442✔
692

693
OwnedMixed parse_base64_encoded_primary_key(std::string_view str)
694
{
48✔
695
    auto bin_encoded = util::base64_decode_to_vector(str);
48✔
696
    if (!bin_encoded) {
48✔
697
        throw BadChangesetError("invalid base64 in base64-encoded primary key");
×
698
    }
×
699
    util::SimpleInputStream stream(*bin_encoded);
48✔
700
    UnreachableInstructionHandler fake_encoder;
48✔
701
    State state{stream, fake_encoder};
48✔
702
    using Type = Instruction::Payload::Type;
48✔
703
    Type type = state.read_payload_type();
48✔
704
    switch (type) {
48✔
705
        case Type::Null:
✔
706
            return OwnedMixed{};
×
707
        case Type::Int:
4✔
708
            return OwnedMixed{state.read_int()};
4✔
709
        case Type::String: {
8✔
710
            auto str = state.read_string();
8✔
711
            return OwnedMixed{std::string{str.data(), str.size()}};
8✔
UNCOV
712
        }
×
713
        case Type::GlobalKey:
✔
714
            // GlobalKey's are not actually used as primary keys in sync. We currently have wire protocol support
715
            // for them, but we've never sent them to the sync server.
UNCOV
716
            REALM_UNREACHABLE();
×
717
        case Type::ObjectId:
32✔
718
            return OwnedMixed{state.read_object_id()};
32✔
719
        case Type::UUID:
4✔
720
            return OwnedMixed{state.read_uuid()};
4✔
UNCOV
721
        default:
✔
UNCOV
722
            throw BadChangesetError(util::format("invalid primary key type %1", static_cast<int>(type)));
×
723
    }
48✔
724
}
48✔
725

726
} // namespace realm::sync
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