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

realm / realm-core / nicola.cabiddu_1040

26 Sep 2023 05:08PM UTC 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

94.05
/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
{
53,548✔
9
    auto spec = mpark::get_if<Instruction::AddTable::TopLevelTable>(&instr.type);
53,548✔
10
    const bool is_embedded = (spec == nullptr);
53,548✔
11
    Table::Type table_type;
53,548✔
12
    if (!is_embedded) {
53,548✔
13
        if (spec->is_asymmetric) {
51,684✔
14
            table_type = Table::Type::TopLevelAsymmetric;
96✔
15
        }
96✔
16
        else {
51,588✔
17
            table_type = Table::Type::TopLevel;
51,588✔
18
        }
51,588✔
19
    }
51,684✔
20
    else {
1,864✔
21
        table_type = Table::Type::Embedded;
1,864✔
22
    }
1,864✔
23
    auto table_type_int = static_cast<uint8_t>(table_type);
53,548✔
24
    append(Instruction::Type::AddTable, instr.table, table_type_int);
53,548✔
25
    if (!is_embedded) {
53,548✔
26
        append_value(spec->pk_field);
51,684✔
27
        append_value(spec->pk_type);
51,684✔
28
        append_value(spec->pk_nullable);
51,684✔
29
    }
51,684✔
30
}
53,548✔
31

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

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

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

47
void ChangesetEncoder::operator()(const Instruction::Update& instr)
48
{
840,362✔
49
    if (instr.is_array_update()) {
840,362✔
50
        append_path_instr(Instruction::Type::Update, instr, instr.value, instr.prior_size);
14,442✔
51
    }
14,442✔
52
    else {
825,920✔
53
        append_path_instr(Instruction::Type::Update, instr, instr.value, instr.is_default);
825,920✔
54
    }
825,920✔
55
}
840,362✔
56

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

524,138✔
62
    append_value(payload.type);
1,069,922✔
63
    const auto& data = payload.data;
1,069,922✔
64

524,138✔
65
    switch (payload.type) {
1,069,922✔
66
        case Type::GlobalKey: {
✔
67
            return append_value(data.key);
×
68
        }
×
69
        case Type::Int: {
742,290✔
70
            return append_value(data.integer);
742,290✔
71
        }
×
72
        case Type::Bool: {
2,150✔
73
            return append_value(data.boolean);
2,150✔
74
        }
×
75
        case Type::String: {
236,068✔
76
            return append_string(data.str);
236,068✔
77
        }
×
78
        case Type::Binary: {
19,584✔
79
            return append_string(data.binary);
19,584✔
80
        }
×
81
        case Type::Timestamp: {
12,578✔
82
            return append_value(data.timestamp);
12,578✔
83
        }
×
84
        case Type::Float: {
2,362✔
85
            return append_value(data.fnum);
2,362✔
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,732✔
100
            return append_value(data.link);
14,732✔
101
        }
×
102
        case Type::Erased:
1,416✔
103
            [[fallthrough]];
1,416✔
104
        case Type::Set:
1,464✔
105
            [[fallthrough]];
1,464✔
106
        case Type::List:
2,072✔
107
            [[fallthrough]];
2,072✔
108
        case Type::Dictionary:
2,280✔
109
            [[fallthrough]];
2,280✔
110
        case Type::ObjectValue:
15,748✔
111
            [[fallthrough]];
15,748✔
112
        case Type::Null:
17,836✔
113
            // The payload type does not carry additional data.
8,908✔
114
            return;
17,836✔
UNCOV
115
    }
×
UNCOV
116
    REALM_TERMINATE("Invalid payload type.");
×
UNCOV
117
}
×
118

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

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

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

135
void ChangesetEncoder::append_value(const Instruction::PrimaryKey& pk)
136
{
1,427,928✔
137
    using Type = Instruction::Payload::Type;
1,427,928✔
138
    auto append = util::overload{
1,427,928✔
139
        [&](mpark::monostate) {
695,670✔
140
            append_value(Type::Null);
340✔
141
        },
340✔
142
        [&](int64_t value) {
1,235,296✔
143
            append_value(Type::Int);
1,070,878✔
144
            append_value(value);
1,070,878✔
145
        },
1,070,878✔
146
        [&](InternString str) {
723,350✔
147
            // Note: Contextual difference. In payloads, Type::String denotes a
27,684✔
148
            // StringBufferRange, but here it denotes to an InternString.
27,684✔
149
            append_value(Type::String);
55,534✔
150
            append_value(str);
55,534✔
151
        },
55,534✔
152
        [&](GlobalKey key) {
695,554✔
153
            append_value(Type::GlobalKey);
108✔
154
            append_value(key);
108✔
155
        },
108✔
156
        [&](ObjectId id) {
859,894✔
157
            append_value(Type::ObjectId);
299,886✔
158
            append_value(id);
299,886✔
159
        },
299,886✔
160
        [&](UUID uuid) {
695,574✔
161
            append_value(Type::UUID);
148✔
162
            append_value(uuid);
148✔
163
        },
148✔
164
    };
1,427,928✔
165
    mpark::visit(std::move(append), pk);
1,427,928✔
166
}
1,427,928✔
167

168
void ChangesetEncoder::append_value(const Instruction::Path& path)
169
{
1,090,968✔
170
    append_value(uint32_t(path.size()));
1,090,968✔
171
    for (auto& element : path) {
741,810✔
172
        // Integer path elements are encoded as their integer values.
213,646✔
173
        // String path elements are encoded as [-1, intern_string_id].
213,646✔
174
        if (auto index = mpark::get_if<uint32_t>(&element)) {
420,030✔
175
            append_value(int64_t(*index));
272,756✔
176
        }
272,756✔
177
        else if (auto name = mpark::get_if<InternString>(&element)) {
147,274✔
178
            // Since indices cannot be negative, use -1 to indicate that the path element is a
73,516✔
179
            // string.
73,516✔
180
            append_value(int64_t(-1));
147,274✔
181
            append_value(*name);
147,274✔
182
        }
147,274✔
183
    }
420,030✔
184
}
1,090,968✔
185

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

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

53,132✔
198
    if (instr.type == Instruction::Payload::Type::Link) {
109,792✔
199
        append_value(instr.link_target_table);
8,652✔
200
    }
8,652✔
201
    if (is_dictionary) {
109,792✔
202
        append_value(instr.key_type);
11,812✔
203
    }
11,812✔
204
}
109,792✔
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
{
208,248✔
213
    append_path_instr(Instruction::Type::ArrayInsert, instr, instr.value, instr.prior_size);
208,248✔
214
}
208,248✔
215

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

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

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

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

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

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

239,990✔
253
        StringBufferRange range = add_string_range(str);
499,484✔
254
        set_intern_string(uint32_t(index), range);
499,484✔
255
    }
499,484✔
256

492,890✔
257
    return InternString{it->second};
1,011,502✔
258
}
1,011,502✔
259

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

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

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

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

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

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

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

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

323
void ChangesetEncoder::append_value(bool v)
324
{
991,592✔
325
    // Reduce template instantiations of append_int
479,932✔
326
    append_value(uint8_t(v));
991,592✔
327
}
991,592✔
328

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

335
void ChangesetEncoder::append_value(uint32_t integer)
336
{
4,381,936✔
337
    // Reduce template instantiations of append_int
2,149,070✔
338
    append_value(uint64_t(integer));
4,381,936✔
339
}
4,381,936✔
340

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

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

351
void ChangesetEncoder::append_value(float number)
352
{
2,362✔
353
    append_bytes(&number, sizeof(number));
2,362✔
354
}
2,362✔
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,058,968✔
363
    REALM_ASSERT(str != InternString::npos);
3,058,968✔
364
    append_value(str.value);
3,058,968✔
365
}
3,058,968✔
366

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

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

379
void ChangesetEncoder::append_value(ObjectId id)
380
{
313,272✔
381
    append_bytes(&id, sizeof(id));
313,272✔
382
}
313,272✔
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
408
{
8✔
409
    m_intern_strings_rev.clear();
8✔
410
    return std::move(m_buffer);
8✔
411
}
8✔
412

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

419
void ChangesetEncoder::encode_single(const Changeset& log)
420
{
568,514✔
421
    // Checking if the log is empty avoids serialized interned strings in a
286,174✔
422
    // changeset where all meaningful instructions have been discarded due to
286,174✔
423
    // merge or compaction.
286,174✔
424
    if (!log.empty()) {
568,514✔
425
        add_string_range(log.string_data());
325,754✔
426
        const auto& strings = log.interned_strings();
325,754✔
427
        for (size_t i = 0; i < strings.size(); ++i) {
921,162✔
428
            set_intern_string(uint32_t(i), strings[i]); // Throws
595,408✔
429
        }
595,408✔
430
        for (auto instr : log) {
750,012✔
431
            if (!instr)
750,012✔
432
                continue;
1,646✔
433
            (*this)(*instr); // Throws
748,366✔
434
        }
748,366✔
435
    }
325,754✔
436
}
568,514✔
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