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

realm / realm-core / nicola.cabiddu_1040

26 Sep 2023 05:08PM CUT coverage: 91.056% (-1.9%) from 92.915%
nicola.cabiddu_1040

Pull #6766

Evergreen

nicola-cab
several fixes and final client reset algo for collection in mixed
Pull Request #6766: Client Reset for collections in mixed / nested collections

97128 of 178458 branches covered (0.0%)

1524 of 1603 new or added lines in 5 files covered. (95.07%)

4511 existing lines in 109 files now uncovered.

236619 of 259862 relevant lines covered (91.06%)

7169640.31 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,599,782✔
27
    }
10,599,782✔
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,599,806✔
114
        log.interned_strings().clear();
10,599,806✔
115
    }
10,599,806✔
116
    Changeset& m_log;
117

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

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

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

136
Instruction::Payload::Type State::read_payload_type()
137
{
12,867,874✔
138
    using Type = Instruction::Payload::Type;
12,867,874✔
139
    auto type = Instruction::Payload::Type(read_int());
12,867,874✔
140
    // Validate the type.
6,496,866✔
141
    switch (type) {
12,867,874✔
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:
592✔
149
            [[fallthrough]];
592✔
150
        case Type::Dictionary:
676✔
151
            [[fallthrough]];
676✔
152
        case Type::ObjectValue:
5,344✔
153
            [[fallthrough]];
5,344✔
154
        case Type::Null:
39,530✔
155
            [[fallthrough]];
39,530✔
156
        case Type::Int:
9,693,020✔
157
            [[fallthrough]];
9,693,020✔
158
        case Type::Bool:
9,693,564✔
159
            [[fallthrough]];
9,693,564✔
160
        case Type::String:
12,622,590✔
161
            [[fallthrough]];
12,622,590✔
162
        case Type::Binary:
12,644,678✔
163
            [[fallthrough]];
12,644,678✔
164
        case Type::Timestamp:
12,649,658✔
165
            [[fallthrough]];
12,649,658✔
166
        case Type::Float:
12,650,430✔
167
            [[fallthrough]];
12,650,430✔
168
        case Type::Double:
12,651,966✔
169
            [[fallthrough]];
12,651,966✔
170
        case Type::Decimal:
12,652,390✔
171
            [[fallthrough]];
12,652,390✔
172
        case Type::Link:
12,660,394✔
173
            [[fallthrough]];
12,660,394✔
174
        case Type::ObjectId:
12,865,890✔
175
            [[fallthrough]];
12,865,890✔
176
        case Type::UUID:
12,867,066✔
177
            return type;
12,867,066✔
UNCOV
178
    }
×
UNCOV
179
    parser_error("Unsupported data type");
×
UNCOV
180
}
×
181

182
Instruction::AddColumn::CollectionType State::read_collection_type()
183
{
605,956✔
184
    using CollectionType = Instruction::AddColumn::CollectionType;
605,956✔
185
    auto type = Instruction::AddColumn::CollectionType(read_int<uint8_t>());
605,956✔
186
    // Validate the type.
303,376✔
187
    switch (type) {
605,956✔
188
        case CollectionType::Single:
398,102✔
189
            [[fallthrough]];
398,102✔
190
        case CollectionType::List:
605,332✔
191
            [[fallthrough]];
605,332✔
192
        case CollectionType::Dictionary:
605,664✔
193
            [[fallthrough]];
605,664✔
194
        case CollectionType::Set:
605,964✔
195
            return type;
605,964✔
UNCOV
196
    }
×
UNCOV
197
    parser_error("Unsupported collection type");
×
UNCOV
198
}
×
199

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

1,505,036✔
204
    Instruction::Payload payload;
2,929,322✔
205
    payload.type = read_payload_type();
2,929,322✔
206
    auto& data = payload.data;
2,929,322✔
207
    switch (payload.type) {
2,929,322✔
UNCOV
208
        case Type::GlobalKey: {
✔
UNCOV
209
            parser_error("Unsupported payload data type");
×
210
        }
×
211
        case Type::Int: {
1,727,144✔
212
            data.integer = read_int();
1,727,144✔
213
            return payload;
1,727,144✔
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,128,942✔
228
            StringData value = read_string();
1,128,942✔
229
            data.str = m_handler.add_string_range(value);
1,128,942✔
230
            return payload;
1,128,942✔
UNCOV
231
        }
×
232
        case Type::Binary: {
21,824✔
233
            BinaryData value = read_binary();
21,824✔
234
            data.binary = m_handler.add_string_range(StringData{value.data(), value.size()});
21,824✔
235
            return payload;
21,824✔
236
        }
×
237
        case Type::Timestamp: {
4,546✔
238
            data.timestamp = read_timestamp();
4,546✔
239
            return payload;
4,546✔
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:
33,702✔
259
            [[fallthrough]];
33,702✔
260
        case Type::Set:
33,730✔
261
            [[fallthrough]];
33,730✔
262
        case Type::List:
33,882✔
263
            [[fallthrough]];
33,882✔
264
        case Type::Dictionary:
33,966✔
265
            [[fallthrough]];
33,966✔
266
        case Type::Erased:
34,314✔
267
            [[fallthrough]];
34,314✔
268
        case Type::ObjectValue:
38,982✔
269
            return payload;
38,982✔
UNCOV
270
    }
×
271

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

275
Instruction::PrimaryKey State::read_object_key()
276
{
9,063,078✔
277
    using Type = Instruction::Payload::Type;
9,063,078✔
278
    Type type = read_payload_type();
9,063,078✔
279
    switch (type) {
9,063,078✔
280
        case Type::Null:
100✔
281
            return mpark::monostate{};
100✔
282
        case Type::Int:
7,407,264✔
283
            return read_int();
7,407,264✔
284
        case Type::String:
1,457,146✔
285
            return read_intern_string();
1,457,146✔
286
        case Type::GlobalKey:
40✔
287
            return read_global_key();
40✔
288
        case Type::ObjectId:
197,964✔
289
            return read_object_id();
197,964✔
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,201,686✔
307
    Instruction::Path path;
3,201,686✔
308
    size_t path_len = read_int<uint32_t>();
3,201,686✔
309

1,646,280✔
310
    // Note: Not reserving `path_len`, because a corrupt changeset could cause std::bad_alloc to be thrown.
1,646,280✔
311
    if (path_len != 0)
3,201,686✔
312
        path.reserve(16);
1,630,404✔
313

1,646,280✔
314
    for (size_t i = 0; i < path_len; ++i) {
4,859,046✔
315
        int64_t element = read_int();
1,657,360✔
316
        if (element >= 0) {
1,657,360✔
317
            // Integer path element
867,832✔
318
            path.push_back(uint32_t(element));
1,632,608✔
319
        }
1,632,608✔
320
        else {
24,752✔
321
            // String path element
12,376✔
322
            path.push_back(read_intern_string());
24,752✔
323
        }
24,752✔
324
    }
1,657,360✔
325

1,646,280✔
326
    return path;
3,201,686✔
327
}
3,201,686✔
328

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

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

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

5,064,368✔
355
    switch (Instruction::Type(t)) {
10,082,784✔
356
        case Instruction::Type::AddTable: {
274,430✔
357
            Instruction::AddTable instr;
274,430✔
358
            instr.table = read_intern_string();
274,430✔
359
            auto table_type = Table::Type(read_int<uint8_t>());
274,430✔
360
            switch (table_type) {
274,430✔
361
                case Table::Type::TopLevel:
273,680✔
362
                case Table::Type::TopLevelAsymmetric: {
273,746✔
363
                    Instruction::AddTable::TopLevelTable spec;
273,746✔
364
                    spec.pk_field = read_intern_string();
273,746✔
365
                    spec.pk_type = read_payload_type();
273,746✔
366
                    if (!is_valid_key_type(spec.pk_type)) {
273,746✔
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();
273,746✔
371
                    spec.is_asymmetric = (table_type == Table::Type::TopLevelAsymmetric);
273,746✔
372
                    instr.type = spec;
273,746✔
373
                    break;
273,746✔
374
                }
273,680✔
375
                case Table::Type::Embedded: {
133,130✔
376
                    instr.type = Instruction::AddTable::EmbeddedTable{};
684✔
377
                    break;
684✔
378
                }
273,680✔
379
                default:
132,790✔
UNCOV
380
                    parser_error(util::format("AddTable: unknown table type: %1", table_type));
×
381
            }
274,430✔
382
            m_handler(instr);
274,430✔
383
            return;
274,430✔
384
        }
274,430✔
385
        case Instruction::Type::EraseTable: {
204,964✔
386
            Instruction::EraseTable instr;
142,284✔
387
            instr.table = read_intern_string();
142,284✔
388
            m_handler(instr);
142,284✔
389
            return;
142,284✔
390
        }
274,430✔
391
        case Instruction::Type::CreateObject: {
3,563,708✔
392
            Instruction::CreateObject instr;
3,563,708✔
393
            instr.table = read_intern_string();
3,563,708✔
394
            instr.object = read_object_key();
3,563,708✔
395
            m_handler(instr);
3,563,708✔
396
            return;
3,563,708✔
397
        }
274,430✔
398
        case Instruction::Type::EraseObject: {
2,296,064✔
399
            Instruction::EraseObject instr;
2,296,064✔
400
            instr.table = read_intern_string();
2,296,064✔
401
            instr.object = read_object_key();
2,296,064✔
402
            m_handler(instr);
2,296,064✔
403
            return;
2,296,064✔
404
        }
274,430✔
405
        case Instruction::Type::Update: {
1,732,776✔
406
            Instruction::Update instr;
1,732,776✔
407
            read_path_instr(instr);
1,732,776✔
408
            instr.value = read_payload();
1,732,776✔
409

875,746✔
410
            // If the last path element is a string, we are setting a field. Otherwise, we are setting an array
875,746✔
411
            // element.
875,746✔
412
            if (!instr.is_array_update()) {
1,732,776✔
413
                instr.is_default = read_bool();
1,513,800✔
414
            }
1,513,800✔
415
            else {
218,976✔
416
                instr.prior_size = read_int<uint32_t>();
218,976✔
417
            }
218,976✔
418
            m_handler(instr);
1,732,776✔
419
            return;
1,732,776✔
420
        }
274,430✔
421
        case Instruction::Type::AddInteger: {
164,396✔
422
            Instruction::AddInteger instr;
60,954✔
423
            read_path_instr(instr);
60,954✔
424
            instr.value = read_int();
60,954✔
425
            m_handler(instr);
60,954✔
426
            return;
60,954✔
427
        }
274,430✔
428
        case Instruction::Type::AddColumn: {
605,960✔
429
            Instruction::AddColumn instr;
605,960✔
430
            instr.table = read_intern_string();
605,960✔
431
            instr.field = read_intern_string();
605,960✔
432
            instr.type = read_payload_type();
605,960✔
433
            instr.nullable = read_bool();
605,960✔
434
            instr.collection_type = read_collection_type();
605,960✔
435
            if (instr.type == Instruction::Payload::Type::Link) {
605,960✔
436
                instr.link_target_table = read_intern_string();
5,184✔
437
            }
5,184✔
438
            if (instr.collection_type == Instruction::AddColumn::CollectionType::Dictionary) {
605,960✔
439
                instr.key_type = read_payload_type();
332✔
440
            }
332✔
441
            else {
605,628✔
442
                instr.key_type = Instruction::Payload::Type::Null;
605,628✔
443
            }
605,628✔
444
            m_handler(instr);
605,960✔
445
            return;
605,960✔
446
        }
274,430✔
447
        case Instruction::Type::EraseColumn: {
133,140✔
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
        }
274,430✔
454
        case Instruction::Type::ArrayInsert: {
1,193,084✔
455
            Instruction::ArrayInsert instr;
1,193,084✔
456
            read_path_instr(instr);
1,193,084✔
457
            if (!instr.path.is_array_index()) {
1,193,084✔
UNCOV
458
                parser_error("ArrayInsert without an index");
×
UNCOV
459
            }
×
460
            instr.value = read_payload();
1,193,084✔
461
            instr.prior_size = read_int<uint32_t>();
1,193,084✔
462
            m_handler(instr);
1,193,084✔
463
            return;
1,193,084✔
464
        }
274,430✔
465
        case Instruction::Type::ArrayMove: {
133,220✔
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
        }
274,430✔
476
        case Instruction::Type::ArrayErase: {
231,492✔
477
            Instruction::ArrayErase instr;
208,268✔
478
            read_path_instr(instr);
208,268✔
479
            if (!instr.path.is_array_index()) {
208,268✔
UNCOV
480
                parser_error("ArrayErase without an index");
×
UNCOV
481
            }
×
482
            instr.prior_size = read_int<uint32_t>();
208,268✔
483
            m_handler(instr);
208,268✔
484
            return;
208,268✔
485
        }
274,430✔
486
        case Instruction::Type::Clear: {
134,818✔
487
            Instruction::Clear instr;
2,646✔
488
            read_path_instr(instr);
2,646✔
489
            uint32_t prior_size = read_int<uint32_t>();
2,646✔
490
            static_cast<void>(prior_size); // Ignored
2,646✔
491
            m_handler(instr);
2,646✔
492
            return;
2,646✔
493
        }
274,430✔
494
        case Instruction::Type::SetInsert: {
134,282✔
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
        }
274,430✔
501
        case Instruction::Type::SetErase: {
133,422✔
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,952,942✔
516
    return m_input_begin != m_input_end || next_input_buffer();
34,952,942✔
517
}
34,952,942✔
518

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

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

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

544
void State::read_bytes(char* data, size_t size)
545
{
202,582✔
546
    for (;;) {
204,050✔
547
        const size_t avail = m_input_end - m_input_begin;
204,050✔
548
        if (size <= avail)
204,050✔
549
            break;
202,578✔
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,582✔
557
    std::copy_n(m_input_begin, size, data);
202,582✔
558
    m_input_begin = to;
202,582✔
559
}
202,582✔
560

561
bool State::read_bool()
562
{
2,393,730✔
563
    return read_int<uint8_t>(); // Throws
2,393,730✔
564
}
2,393,730✔
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,653,450✔
588
    uint32_t index = read_int<uint32_t>(); // Throws
15,653,450✔
589
    if (index >= m_intern_strings.size())
15,653,450✔
590
        parser_error("Invalid interned string");
8✔
591
    return InternString{index};
15,653,450✔
592
}
15,653,450✔
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,546✔
603
    int64_t seconds = read_int<int64_t>();     // Throws
4,546✔
604
    int64_t nanoseconds = read_int<int64_t>(); // Throws
4,546✔
605
    if (nanoseconds > std::numeric_limits<int32_t>::max())
4,546✔
UNCOV
606
        parser_error("timestamp out of range");
×
607
    return Timestamp{seconds, int32_t(nanoseconds)};
4,546✔
608
}
4,546✔
609

610
ObjectId State::read_object_id()
611
{
199,368✔
612
    // FIXME: This is completely wrong and unsafe.
92,370✔
613
    ObjectId id;
199,368✔
614
    read_bytes(reinterpret_cast<char*>(&id), sizeof(id));
199,368✔
615
    return id;
199,368✔
616
}
199,368✔
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,400,498✔
640
    uint64_t size = read_int<uint64_t>(); // Throws
15,400,498✔
641

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

7,786,752✔
647
    BinaryData buffer = read_buffer(size_t(size));
15,400,498✔
648
    return StringData{buffer.data(), size_t(size)};
15,400,498✔
649
}
15,400,498✔
650

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

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

10,914✔
658
    return read_buffer(size_t(size));
21,824✔
659
}
21,824✔
660

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

326✔
669
    m_buffer.clear();
546✔
670
    m_buffer.resize(size); // Throws
546✔
671
    read_bytes(m_buffer.data(), size);
546✔
672
    return BinaryData(m_buffer.data(), size);
546✔
673
}
546✔
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,599,794✔
686
    InstructionBuilder builder{out_log};
10,599,794✔
687
    State state{input, builder};
10,599,794✔
688

5,318,856✔
689
    while (state.has_next())
34,954,178✔
690
        state.parse_one();
24,354,384✔
691
}
10,599,794✔
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