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

realm / realm-core / github_pull_request_281922

31 Oct 2023 09:13AM UTC coverage: 90.445% (-0.08%) from 90.528%
github_pull_request_281922

Pull #7039

Evergreen

jedelbo
Merge branch 'next-major' into je/global-key
Pull Request #7039: Remove ability to synchronize objects without primary key

95324 of 175822 branches covered (0.0%)

101 of 105 new or added lines in 13 files covered. (96.19%)

238 existing lines in 19 files now uncovered.

232657 of 257235 relevant lines covered (90.45%)

6351359.67 hits per line

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

90.77
/src/realm/sync/changeset_encoder.cpp
1
#include <realm/sync/noinst/integer_codec.hpp>
2
#include <realm/sync/changeset_encoder.hpp>
3

4
using namespace realm;
5
using namespace realm::sync;
6

7
void ChangesetEncoder::operator()(const Instruction::AddTable& instr)
8
{
52,736✔
9
    auto spec = mpark::get_if<Instruction::AddTable::TopLevelTable>(&instr.type);
52,736✔
10
    const bool is_embedded = (spec == nullptr);
52,736✔
11
    Table::Type table_type;
52,736✔
12
    if (!is_embedded) {
52,736✔
13
        if (spec->is_asymmetric) {
50,864✔
14
            table_type = Table::Type::TopLevelAsymmetric;
112✔
15
        }
112✔
16
        else {
50,752✔
17
            table_type = Table::Type::TopLevel;
50,752✔
18
        }
50,752✔
19
    }
50,864✔
20
    else {
1,872✔
21
        table_type = Table::Type::Embedded;
1,872✔
22
    }
1,872✔
23
    auto table_type_int = static_cast<uint8_t>(table_type);
52,736✔
24
    append(Instruction::Type::AddTable, instr.table, table_type_int);
52,736✔
25
    if (!is_embedded) {
52,736✔
26
        append_value(spec->pk_field);
50,864✔
27
        append_value(spec->pk_type);
50,864✔
28
        append_value(spec->pk_nullable);
50,864✔
29
    }
50,864✔
30
}
52,736✔
31

32
void ChangesetEncoder::operator()(const Instruction::EraseTable& instr)
33
{
5,000✔
34
    append(Instruction::Type::EraseTable, instr.table);
5,000✔
35
}
5,000✔
36

37
void ChangesetEncoder::operator()(const Instruction::CreateObject& instr)
38
{
244,486✔
39
    append(Instruction::Type::CreateObject, instr.table, instr.object);
244,486✔
40
}
244,486✔
41

42
void ChangesetEncoder::operator()(const Instruction::EraseObject& instr)
43
{
77,482✔
44
    append(Instruction::Type::EraseObject, instr.table, instr.object);
77,482✔
45
}
77,482✔
46

47
void ChangesetEncoder::operator()(const Instruction::Update& instr)
48
{
838,838✔
49
    if (instr.is_array_update()) {
838,838✔
50
        append_path_instr(Instruction::Type::Update, instr, instr.value, instr.prior_size);
16,014✔
51
    }
16,014✔
52
    else {
822,824✔
53
        append_path_instr(Instruction::Type::Update, instr, instr.value, instr.is_default);
822,824✔
54
    }
822,824✔
55
}
838,838✔
56

57
// Appends sequence [value-type, dumb-value]
58
void ChangesetEncoder::append_value(const Instruction::Payload& payload)
59
{
1,071,114✔
60
    using Type = Instruction::Payload::Type;
1,071,114✔
61

521,914✔
62
    append_value(payload.type);
1,071,114✔
63
    const auto& data = payload.data;
1,071,114✔
64

521,914✔
65
    switch (payload.type) {
1,071,114✔
66
        case Type::GlobalKey: {
✔
67
            return append_value(data.key);
×
68
        }
×
69
        case Type::Int: {
746,012✔
70
            return append_value(data.integer);
746,012✔
71
        }
×
72
        case Type::Bool: {
2,150✔
73
            return append_value(data.boolean);
2,150✔
74
        }
×
75
        case Type::String: {
233,860✔
76
            return append_string(data.str);
233,860✔
77
        }
×
78
        case Type::Binary: {
19,584✔
79
            return append_string(data.binary);
19,584✔
80
        }
×
81
        case Type::Timestamp: {
12,750✔
82
            return append_value(data.timestamp);
12,750✔
83
        }
×
84
        case Type::Float: {
2,372✔
85
            return append_value(data.fnum);
2,372✔
86
        }
×
87
        case Type::Double: {
4,024✔
88
            return append_value(data.dnum);
4,024✔
89
        }
×
90
        case Type::Decimal: {
2,144✔
91
            return append_value(data.decimal);
2,144✔
92
        }
×
93
        case Type::ObjectId: {
13,384✔
94
            return append_value(data.object_id);
13,384✔
95
        }
×
96
        case Type::UUID: {
3,360✔
97
            return append_value(data.uuid);
3,360✔
98
        }
×
99
        case Type::Link: {
14,740✔
100
            return append_value(data.link);
14,740✔
101
        }
×
102
        case Type::Erased:
1,392✔
103
            [[fallthrough]];
1,392✔
104
        case Type::Set:
1,392✔
105
            [[fallthrough]];
1,392✔
106
        case Type::List:
1,392✔
107
            [[fallthrough]];
1,392✔
108
        case Type::Dictionary:
1,392✔
109
            [[fallthrough]];
1,392✔
110
        case Type::ObjectValue:
14,886✔
111
            [[fallthrough]];
14,886✔
112
        case Type::Null:
16,916✔
113
            // The payload type does not carry additional data.
8,360✔
114
            return;
16,916✔
115
    }
×
116
    REALM_TERMINATE("Invalid payload type.");
×
117
}
×
118

119
void ChangesetEncoder::append_value(Instruction::Payload::Type type)
120
{
2,670,670✔
121
    append_value(int64_t(type));
2,670,670✔
122
}
2,670,670✔
123

124
void ChangesetEncoder::append_value(Instruction::AddColumn::CollectionType type)
125
{
109,190✔
126
    append_value(uint8_t(type));
109,190✔
127
}
109,190✔
128

129
void ChangesetEncoder::append_value(const Instruction::Payload::Link& link)
130
{
14,740✔
131
    append_value(link.target_table);
14,740✔
132
    append_value(link.target);
14,740✔
133
}
14,740✔
134

135
void ChangesetEncoder::append_value(const Instruction::PrimaryKey& pk)
136
{
1,429,046✔
137
    using Type = Instruction::Payload::Type;
1,429,046✔
138
    auto append = util::overload{
1,429,046✔
139
        [&](mpark::monostate) {
692,112✔
140
            append_value(Type::Null);
340✔
141
        },
340✔
142
        [&](int64_t value) {
1,237,542✔
143
            append_value(Type::Int);
1,076,918✔
144
            append_value(value);
1,076,918✔
145
        },
1,076,918✔
146
        [&](InternString str) {
719,970✔
147
            // Note: Contextual difference. In payloads, Type::String denotes a
25,732✔
148
            // StringBufferRange, but here it denotes to an InternString.
25,732✔
149
            append_value(Type::String);
53,760✔
150
            append_value(str);
53,760✔
151
        },
53,760✔
152
        [&](GlobalKey key) {
691,942✔
UNCOV
153
            append_value(Type::GlobalKey);
×
UNCOV
154
            append_value(key);
×
UNCOV
155
        },
×
156
        [&](ObjectId id) {
855,182✔
157
            append_value(Type::ObjectId);
297,190✔
158
            append_value(id);
297,190✔
159
        },
297,190✔
160
        [&](UUID uuid) {
692,016✔
161
            append_value(Type::UUID);
148✔
162
            append_value(uuid);
148✔
163
        },
148✔
164
    };
1,429,046✔
165
    mpark::visit(std::move(append), pk);
1,429,046✔
166
}
1,429,046✔
167

168
void ChangesetEncoder::append_value(const Instruction::Path& path)
169
{
1,092,634✔
170
    append_value(uint32_t(path.size()));
1,092,634✔
171
    for (auto& element : path) {
744,488✔
172
        // Integer path elements are encoded as their integer values.
211,112✔
173
        // String path elements are encoded as [-1, intern_string_id].
211,112✔
174
        if (auto index = mpark::get_if<uint32_t>(&element)) {
423,368✔
175
            append_value(int64_t(*index));
276,566✔
176
        }
276,566✔
177
        else if (auto name = mpark::get_if<InternString>(&element)) {
146,802✔
178
            // Since indices cannot be negative, use -1 to indicate that the path element is a
73,260✔
179
            // string.
73,260✔
180
            append_value(int64_t(-1));
146,802✔
181
            append_value(*name);
146,802✔
182
        }
146,802✔
183
    }
423,368✔
184
}
1,092,634✔
185

186
void ChangesetEncoder::operator()(const Instruction::AddInteger& instr)
187
{
6,722✔
188
    append_path_instr(Instruction::Type::AddInteger, instr, instr.value);
6,722✔
189
}
6,722✔
190

191
void ChangesetEncoder::operator()(const Instruction::AddColumn& instr)
192
{
109,188✔
193
    bool is_dictionary = (instr.collection_type == Instruction::AddColumn::CollectionType::Dictionary);
109,188✔
194
    // Mixed columns are always nullable.
52,672✔
195
    REALM_ASSERT(instr.type != Instruction::Payload::Type::Null || instr.nullable || is_dictionary);
109,188!
196
    append(Instruction::Type::AddColumn, instr.table, instr.field, instr.type, instr.nullable, instr.collection_type);
109,188✔
197

52,672✔
198
    if (instr.type == Instruction::Payload::Type::Link) {
109,188✔
199
        append_value(instr.link_target_table);
8,868✔
200
    }
8,868✔
201
    if (is_dictionary) {
109,188✔
202
        append_value(instr.key_type);
11,900✔
203
    }
11,900✔
204
}
109,188✔
205

206
void ChangesetEncoder::operator()(const Instruction::EraseColumn& instr)
207
{
12✔
208
    append(Instruction::Type::EraseColumn, instr.table, instr.field);
12✔
209
}
12✔
210

211
void ChangesetEncoder::operator()(const Instruction::ArrayInsert& instr)
212
{
210,324✔
213
    append_path_instr(Instruction::Type::ArrayInsert, instr, instr.value, instr.prior_size);
210,324✔
214
}
210,324✔
215

216
void ChangesetEncoder::operator()(const Instruction::ArrayMove& instr)
217
{
372✔
218
    append_path_instr(Instruction::Type::ArrayMove, instr, instr.ndx_2, instr.prior_size);
372✔
219
}
372✔
220

221
void ChangesetEncoder::operator()(const Instruction::ArrayErase& instr)
222
{
13,380✔
223
    append_path_instr(Instruction::Type::ArrayErase, instr, instr.prior_size);
13,380✔
224
}
13,380✔
225

226
void ChangesetEncoder::operator()(const Instruction::Clear& instr)
227
{
802✔
228
    uint32_t prior_size = 0; // Ignored
802✔
229
    append_path_instr(Instruction::Type::Clear, instr, prior_size);
802✔
230
}
802✔
231

232
void ChangesetEncoder::operator()(const Instruction::SetInsert& instr)
233
{
19,560✔
234
    append_path_instr(Instruction::Type::SetInsert, instr, instr.value);
19,560✔
235
}
19,560✔
236

237
void ChangesetEncoder::operator()(const Instruction::SetErase& instr)
238
{
2,296✔
239
    append_path_instr(Instruction::Type::SetErase, instr, instr.value);
2,296✔
240
}
2,296✔
241

242
InternString ChangesetEncoder::intern_string(StringData str)
243
{
1,005,418✔
244
    auto it = m_intern_strings_rev.find(static_cast<std::string_view>(str));
1,005,418✔
245
    if (it == m_intern_strings_rev.end()) {
1,005,418✔
246
        size_t index = m_intern_strings_rev.size();
494,482✔
247
        // FIXME: Assert might be able to be removed after refactoring of changeset_parser types?
237,484✔
248
        REALM_ASSERT_RELEASE_EX(index <= std::numeric_limits<uint32_t>::max(), index);
494,482✔
249
        bool inserted;
494,482✔
250
        std::tie(it, inserted) = m_intern_strings_rev.insert({std::string{str}, uint32_t(index)});
494,482✔
251
        REALM_ASSERT_RELEASE_EX(inserted, str);
494,482✔
252

237,484✔
253
        StringBufferRange range = add_string_range(str);
494,482✔
254
        set_intern_string(uint32_t(index), range);
494,482✔
255
    }
494,482✔
256

489,896✔
257
    return InternString{it->second};
1,005,418✔
258
}
1,005,418✔
259

260
void ChangesetEncoder::set_intern_string(uint32_t index, StringBufferRange range)
261
{
1,096,782✔
262
    // Emit InternString metainstruction:
529,550✔
263
    append_int(uint64_t(InstrTypeInternString));
1,096,782✔
264
    append_int(index);
1,096,782✔
265
    append_string(range);
1,096,782✔
266
}
1,096,782✔
267

268
StringBufferRange ChangesetEncoder::add_string_range(StringData data)
269
{
1,021,456✔
270
    m_string_range = static_cast<std::string_view>(data);
1,021,456✔
271
    REALM_ASSERT(data.size() <= std::numeric_limits<uint32_t>::max());
1,021,456✔
272
    return StringBufferRange{0, uint32_t(data.size())};
1,021,456✔
273
}
1,021,456✔
274

275
void ChangesetEncoder::append_bytes(const void* bytes, size_t size)
276
{
17,234,916✔
277
    // FIXME: It would be better to move ownership of `m_buffer` to the caller,
8,338,496✔
278
    // potentially reducing the number of allocations to zero (amortized).
8,338,496✔
279
    m_buffer.reserve(1024); // lower the amount of reallocations
17,234,916✔
280
    m_buffer.append(static_cast<const char*>(bytes), size);
17,234,916✔
281
}
17,234,916✔
282

283
void ChangesetEncoder::append_string(StringBufferRange str)
284
{
1,350,238✔
285
    REALM_ASSERT(str.offset + str.size <= m_string_range.size());
1,350,238✔
286
    append_value(uint64_t(str.size));
1,350,238✔
287
    append_bytes(m_string_range.data() + str.offset, str.size);
1,350,238✔
288
}
1,350,238✔
289

290
template <class... Args>
291
void ChangesetEncoder::append(Instruction::Type t, Args&&... args)
292
{
488,904✔
293
    append_value(uint8_t(t));
488,904✔
294
    int unpack[] = {0, (append_value(args), 0)...};
488,904✔
295
    static_cast<void>(unpack);
488,904✔
296
}
488,904✔
297

298
template <class... Args>
299
void ChangesetEncoder::append_path_instr(Instruction::Type t, const Instruction::PathInstruction& instr,
300
                                         Args&&... args)
301
{
1,091,944✔
302
    append_value(uint8_t(t));
1,091,944✔
303
    append_value(instr.table);
1,091,944✔
304
    append_value(instr.object);
1,091,944✔
305
    append_value(instr.field);
1,091,944✔
306
    append_value(instr.path);
1,091,944✔
307
    (append_value(std::forward<Args>(args)), ...);
1,091,944✔
308
}
1,091,944✔
309

310
template <class T>
311
void ChangesetEncoder::append_int(T integer)
312
{
15,595,178✔
313
    char buffer[_impl::encode_int_max_bytes<T>()];
15,595,178✔
314
    std::size_t n = _impl::encode_int(buffer, integer);
15,595,178✔
315
    append_bytes(buffer, n);
15,595,178✔
316
}
15,595,178✔
317

318
void ChangesetEncoder::append_value(DataType type)
319
{
×
320
    append_value(uint64_t(type));
×
321
}
×
322

323
void ChangesetEncoder::append_value(bool v)
324
{
987,426✔
325
    // Reduce template instantiations of append_int
478,086✔
326
    append_value(uint8_t(v));
987,426✔
327
}
987,426✔
328

329
void ChangesetEncoder::append_value(uint8_t integer)
330
{
2,728,614✔
331
    // Reduce template instantiations of append_int
1,321,332✔
332
    append_value(uint64_t(integer));
2,728,614✔
333
}
2,728,614✔
334

335
void ChangesetEncoder::append_value(uint32_t integer)
336
{
4,388,622✔
337
    // Reduce template instantiations of append_int
2,136,238✔
338
    append_value(uint64_t(integer));
4,388,622✔
339
}
4,388,622✔
340

341
void ChangesetEncoder::append_value(uint64_t integer)
342
{
8,456,462✔
343
    append_int(integer);
8,456,462✔
344
}
8,456,462✔
345

346
void ChangesetEncoder::append_value(int64_t integer)
347
{
4,948,552✔
348
    append_int(integer);
4,948,552✔
349
}
4,948,552✔
350

351
void ChangesetEncoder::append_value(float number)
352
{
2,372✔
353
    append_bytes(&number, sizeof(number));
2,372✔
354
}
2,372✔
355

356
void ChangesetEncoder::append_value(double number)
357
{
4,024✔
358
    append_bytes(&number, sizeof(number));
4,024✔
359
}
4,024✔
360

361
void ChangesetEncoder::append_value(InternString str)
362
{
3,057,610✔
363
    REALM_ASSERT(str != InternString::npos);
3,057,610✔
364
    append_value(str.value);
3,057,610✔
365
}
3,057,610✔
366

367
void ChangesetEncoder::append_value(GlobalKey oid)
UNCOV
368
{
×
UNCOV
369
    append_value(oid.hi());
×
UNCOV
370
    append_value(oid.lo());
×
UNCOV
371
}
×
372

373
void ChangesetEncoder::append_value(Timestamp timestamp)
374
{
12,750✔
375
    append_value(timestamp.get_seconds());
12,750✔
376
    append_value(int64_t(timestamp.get_nanoseconds()));
12,750✔
377
}
12,750✔
378

379
void ChangesetEncoder::append_value(ObjectId id)
380
{
310,576✔
381
    append_bytes(&id, sizeof(id));
310,576✔
382
}
310,576✔
383

384
void ChangesetEncoder::append_value(UUID id)
385
{
3,508✔
386
    const auto bytes = id.to_bytes();
3,508✔
387
    append_bytes(bytes.data(), bytes.size());
3,508✔
388
}
3,508✔
389

390
void ChangesetEncoder::append_value(Decimal128 id)
391
{
2,144✔
392
    Decimal128::Bid128 cx;
2,144✔
393
    int exp;
2,144✔
394
    bool sign;
2,144✔
395
    id.unpack(cx, exp, sign);
2,144✔
396
    constexpr int max_bytes = 17; // 113 bits / 7
2,144✔
397
    char buffer[max_bytes];
2,144✔
398
    _impl::Bid128 tmp;
2,144✔
399
    memcpy(&tmp, &cx, sizeof(Decimal128::Bid128));
2,144✔
400
    auto n = _impl::encode_int(buffer, tmp);
2,144✔
401
    REALM_ASSERT(n <= max_bytes);
2,144✔
402
    append_bytes(buffer, n);
2,144✔
403
    append_value(int64_t(exp));
2,144✔
404
    append_value(sign);
2,144✔
405
}
2,144✔
406

407
auto ChangesetEncoder::release() noexcept -> Buffer
UNCOV
408
{
×
UNCOV
409
    m_intern_strings_rev.clear();
×
UNCOV
410
    return std::move(m_buffer);
×
UNCOV
411
}
×
412

413
void ChangesetEncoder::reset() noexcept
414
{
747,312✔
415
    m_intern_strings_rev.clear();
747,312✔
416
    m_buffer.clear();
747,312✔
417
}
747,312✔
418

419
void ChangesetEncoder::encode_single(const Changeset& log)
420
{
573,312✔
421
    // Checking if the log is empty avoids serialized interned strings in a
286,602✔
422
    // changeset where all meaningful instructions have been discarded due to
286,602✔
423
    // merge or compaction.
286,602✔
424
    if (!log.empty()) {
573,312✔
425
        add_string_range(log.string_data());
330,284✔
426
        const auto& strings = log.interned_strings();
330,284✔
427
        for (size_t i = 0; i < strings.size(); ++i) {
932,656✔
428
            set_intern_string(uint32_t(i), strings[i]); // Throws
602,372✔
429
        }
602,372✔
430
        for (auto instr : log) {
754,348✔
431
            if (!instr)
754,348✔
432
                continue;
2,116✔
433
            (*this)(*instr); // Throws
752,232✔
434
        }
752,232✔
435
    }
330,284✔
436
}
573,312✔
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