• 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

40.89
/src/realm/sync/changeset.cpp
1
#include <realm/sync/changeset.hpp>
2

3
#if REALM_DEBUG
4
#include <iostream>
5
#include <iomanip>
6
#include <sstream>
7
#endif // REALM_DEBUG
8

9
using namespace realm;
10
using namespace realm::sync;
11
using namespace realm::util;
12

13
InternString Changeset::intern_string(StringData str)
14
{
212✔
15
    if (InternString interned = find_string(str))
212✔
16
        return interned;
×
17

106✔
18
    REALM_ASSERT(m_string_buffer.size() < std::numeric_limits<uint32_t>::max());
212✔
19
    REALM_ASSERT(m_strings.size() < std::numeric_limits<uint32_t>::max());
212✔
20
    REALM_ASSERT(str.size() < std::numeric_limits<uint32_t>::max());
212✔
21

106✔
22
    // FIXME: Very slow.
106✔
23
    uint32_t size = uint32_t(str.size());
212✔
24
    uint32_t offset = uint32_t(m_string_buffer.size());
212✔
25
    m_string_buffer.append(str.data(), size);
212✔
26
    uint32_t index = uint32_t(m_strings.size());
212✔
27
    m_strings.push_back(StringBufferRange{offset, size});
212✔
28
    return InternString{index};
212✔
29
}
212✔
30

31

32
InternString Changeset::find_string(StringData string) const noexcept
33
{
212✔
34
    // FIXME: Linear search can be very expensive as changesets can be very big
106✔
35
    std::size_t n = m_strings.size();
212✔
36
    for (std::size_t i = 0; i < n; ++i) {
428✔
37
        const auto& range = m_strings[i];
216✔
38
        StringData string_2{m_string_buffer.data() + range.offset, range.size};
216✔
39
        if (string_2 == string)
216✔
40
            return InternString{std::uint_least32_t(i)};
×
41
    }
216✔
42
    return InternString{};
212✔
43
}
212✔
44

45
PrimaryKey Changeset::get_key(const Instruction::PrimaryKey& key) const noexcept
46
{
15,077,536✔
47
    // we do not use the expected `mpark::visit(overload...` because in MSVC 2019 this
7,517,798✔
48
    // code produces a segfault for something that works on other compilers.
7,517,798✔
49
    // See https://github.com/realm/realm-core/issues/4624
7,517,798✔
50
    if (const auto int64_ptr = mpark::get_if<int64_t>(&key)) {
15,077,536✔
51
        return *int64_ptr;
12,409,162✔
52
    }
12,409,162✔
53
    else if (const auto intern_string_ptr = mpark::get_if<InternString>(&key)) {
2,668,374✔
54
        return this->get_string(*intern_string_ptr);
2,633,506✔
55
    }
2,633,506✔
56
    else if (const auto monostate_ptr = mpark::get_if<mpark::monostate>(&key)) {
34,868✔
57
        return *monostate_ptr;
×
58
    }
×
59
    else if (const auto global_key_ptr = mpark::get_if<GlobalKey>(&key)) {
34,868✔
60
        return *global_key_ptr;
×
61
    }
×
62
    else if (const auto oid_ptr = mpark::get_if<ObjectId>(&key)) {
34,868✔
63
        return *oid_ptr;
34,058✔
64
    }
34,058✔
65
    else if (const auto uuid_ptr = mpark::get_if<UUID>(&key)) {
810✔
66
        return *uuid_ptr;
100✔
67
    }
100✔
68
    else {
710✔
69
        REALM_UNREACHABLE(); // unhandled primary key type
710✔
70
    }
710✔
71
}
15,077,536✔
72

73
bool Changeset::operator==(const Changeset& that) const noexcept
74
{
60✔
75
    if (m_instructions == that.m_instructions) {
60✔
76
        return m_strings == that.m_strings;
60✔
77
    }
60✔
78
    return false;
×
79
}
×
80

81
std::ostream& Changeset::print_value(std::ostream& os, const Instruction::Payload& value) const noexcept
82
{
12✔
83
    using Type = Instruction::Payload::Type;
12✔
84

6✔
85
    os << get_type_name(value.type) << "(";
12✔
86
    auto& data = value.data;
12✔
87
    switch (value.type) {
12✔
88
        case Type::ObjectValue:
✔
89
            break;
×
90
        case Type::GlobalKey:
✔
91
            os << data.key;
×
92
            break;
×
93
        case Type::Erased:
✔
94
            break;
×
95
        case Type::Set:
✔
96
            break;
×
97
        case Type::List:
✔
98
            break;
×
99
        case Type::Dictionary:
✔
100
            break;
×
101
        case Type::Null:
✔
102
            break;
×
103
        case Type::Int:
✔
104
            os << data.integer;
×
105
            break;
×
106
        case Type::Bool:
✔
107
            os << data.boolean;
×
108
            break;
×
109
        case Type::String:
✔
110
            os << "\"" << get_string(data.str) << "\"";
×
111
            break;
×
112
        case Type::Binary:
✔
113
            os << "...";
×
114
            break;
×
115
        case Type::Timestamp:
✔
116
            os << data.timestamp;
×
117
            break;
×
118
        case Type::Float:
✔
119
            os << data.fnum;
×
120
            break;
×
121
        case Type::Double:
✔
122
            os << data.dnum;
×
123
            break;
×
124
        case Type::Decimal:
✔
125
            os << data.decimal;
×
UNCOV
126
            break;
×
UNCOV
127
        case Type::UUID:
✔
UNCOV
128
            os << data.uuid;
×
UNCOV
129
            break;
×
130
        case Type::Link: {
12✔
131
            os << "target_table = " << get_string(data.link.target_table) << ", "
12✔
132
               << "target = " << format_pk(get_key(data.link.target));
12✔
133
            break;
12✔
UNCOV
134
        };
×
UNCOV
135
        case Type::ObjectId:
✔
UNCOV
136
            os << data.object_id;
×
UNCOV
137
            break;
×
138
    }
12✔
139
    return os << ")";
12✔
140
}
12✔
141

142
std::ostream& Changeset::print_path(std::ostream& os, const Instruction::Path& path) const noexcept
143
{
×
144
    bool first = true;
×
145
    for (auto& element : path) {
×
146
        if (!first) {
×
147
            os << '.';
×
148
        }
×
149
        first = false;
×
150
        auto print = overload{
×
151
            [&](uint32_t index) {
×
152
                os << index;
×
153
            },
×
154
            [&](InternString str) {
×
155
                os << get_string(str);
×
156
            },
×
157
        };
×
UNCOV
158
        mpark::visit(print, element);
×
UNCOV
159
    }
×
UNCOV
160
    return os;
×
UNCOV
161
}
×
162

163
std::ostream& Changeset::print_path(std::ostream& os, InternString table, const Instruction::PrimaryKey& pk,
164
                                    util::Optional<InternString> field, const Instruction::Path* path) const
165
{
28✔
166
    os << get_string(table) << "[" << format_pk(get_key(pk)) << "]";
28✔
167
    if (field) {
28✔
168
        os << "." << get_string(*field);
12✔
169
    }
12✔
170
    if (path) {
28✔
171
        for (auto& element : *path) {
12✔
172
            if (auto subfield = mpark::get_if<InternString>(&element)) {
12✔
UNCOV
173
                os << "." << get_string(*subfield);
×
174
            }
×
175
            else if (auto index = mpark::get_if<uint32_t>(&element)) {
12✔
176
                os << "[" << *index << "]";
12✔
177
            }
12✔
UNCOV
178
            else {
×
UNCOV
179
                REALM_TERMINATE("Invalid path");
×
UNCOV
180
            }
×
181
        }
12✔
182
    }
12✔
183
    return os;
28✔
184
}
28✔
185

186
std::ostream& realm::sync::operator<<(std::ostream& os, const Changeset& changeset)
187
{
8✔
188
#if REALM_DEBUG // LCOV_EXCL_START
4✔
189
    changeset.print(os);
8✔
190
    return os;
8✔
191
#else
192
    return os << "[changeset with " << changeset.size() << " instructions]";
193
#endif
194
}
8✔
195

196

197
#if REALM_DEBUG // LCOV_EXCL_START
198
void Changeset::print(std::ostream& os) const
199
{
8✔
200
    Changeset::Printer printer{os};
8✔
201
    Changeset::Reflector reflector{printer, *this};
8✔
202
    os << std::left << std::setw(16) << "InternStrings";
8✔
203
    for (size_t i = 0; i < m_strings.size(); ++i) {
48✔
204
        os << i << "=\"" << get_string(m_strings.at(i)) << '"';
40✔
205
        if (i + 1 != m_strings.size())
40✔
206
            os << ", ";
32✔
207
    }
40✔
208
    os << "\n";
8✔
209

4✔
210
    reflector.visit_all();
8✔
211
}
8✔
212

213
void Changeset::print() const
UNCOV
214
{
×
UNCOV
215
    print(std::cerr);
×
216
}
×
217

218

219
void Changeset::verify() const
220
{
×
221
    for (size_t i = 0; i < m_strings.size(); ++i) {
×
222
        auto& range = m_strings.at(i);
×
223
        REALM_ASSERT(range.offset <= m_string_buffer.size());
×
224
        REALM_ASSERT(range.offset + range.size <= m_string_buffer.size());
×
225
    }
×
226

227
    auto verify_string_range = [&](StringBufferRange range) {
×
228
        REALM_ASSERT(range.offset <= m_string_buffer.size());
×
229
        REALM_ASSERT(range.offset + range.size <= m_string_buffer.size());
×
230
    };
×
231

232
    auto verify_intern_string = [&](InternString str) {
×
233
        auto range = get_intern_string(str);
×
234
        verify_string_range(range);
×
235
    };
×
236

237
    auto verify_key = [&](const Instruction::PrimaryKey& key) {
×
238
        mpark::visit(util::overload{[&](InternString str) {
×
239
                                        verify_intern_string(str);
×
240
                                    },
×
241
                                    [](auto&&) {}},
×
242
                     key);
×
243
    };
×
244

245
    auto verify_payload = [&](const Instruction::Payload& payload) {
×
246
        using Type = Instruction::Payload::Type;
×
247
        switch (payload.type) {
×
248
            case Type::String: {
×
249
                return verify_string_range(payload.data.str);
×
250
            }
×
251
            case Type::Binary: {
×
252
                return verify_string_range(payload.data.binary);
×
253
            }
×
254
            case Type::Link: {
×
255
                verify_intern_string(payload.data.link.target_table);
×
256
                return verify_key(payload.data.link.target);
×
257
            }
×
258
            default:
×
259
                return;
×
260
        }
×
261
    };
×
262

263
    auto verify_path = [&](const Instruction::Path& path) {
×
264
        for (auto& element : path) {
×
265
            mpark::visit(util::overload{[&](InternString str) {
×
266
                                            verify_intern_string(str);
×
267
                                        },
×
268
                                        [](auto&&) {}},
×
269
                         element);
×
270
        }
×
271
    };
×
272

273
    for (auto instr : *this) {
×
274
        if (!instr)
×
275
            continue;
×
276

277
        if (auto table_instr = instr->get_if<Instruction::TableInstruction>()) {
×
278
            verify_intern_string(table_instr->table);
×
279
            if (auto object_instr = instr->get_if<Instruction::ObjectInstruction>()) {
×
280
                verify_key(object_instr->object);
×
281

282
                if (auto path_instr = instr->get_if<Instruction::PathInstruction>()) {
×
283
                    verify_path(path_instr->path);
×
284
                }
×
285

286
                if (auto set_instr = instr->get_if<Instruction::Update>()) {
×
287
                    verify_payload(set_instr->value);
×
288
                }
×
289
                else if (auto insert_instr = instr->get_if<Instruction::ArrayInsert>()) {
×
290
                    verify_payload(insert_instr->value);
×
291
                }
×
292
            }
×
293
            else if (auto add_table_instr = instr->get_if<Instruction::AddTable>()) {
×
294
                mpark::visit(util::overload{
×
295
                                 [&](const Instruction::AddTable::TopLevelTable& spec) {
×
296
                                     REALM_ASSERT(is_valid_key_type(spec.pk_type));
×
297
                                     verify_intern_string(spec.pk_field);
×
298
                                 },
×
299
                                 [](const Instruction::AddTable::EmbeddedTable&) {},
×
300
                             },
×
301
                             add_table_instr->type);
×
302
            }
×
303
            else if (auto add_column_instr = instr->get_if<Instruction::AddColumn>()) {
×
304
                verify_intern_string(add_column_instr->field);
×
305
                if (add_column_instr->type == Instruction::Payload::Type::Link) {
×
306
                    verify_intern_string(add_column_instr->link_target_table);
×
307
                }
×
308
            }
×
309
            else if (auto erase_column_instr = instr->get_if<Instruction::EraseColumn>()) {
×
310
                verify_intern_string(erase_column_instr->field);
×
311
            }
×
312
        }
×
313
        else {
×
UNCOV
314
            REALM_TERMINATE("Corrupt instruction type");
×
UNCOV
315
        }
×
UNCOV
316
    }
×
UNCOV
317
}
×
318

319
void Changeset::Reflector::operator()(const Instruction::AddTable& p) const
320
{
8✔
321
    m_tracer.name("AddTable");
8✔
322
    table_instr(p);
8✔
323
    auto trace = util::overload{
8✔
324
        [&](const Instruction::AddTable::TopLevelTable& spec) {
8✔
325
            m_tracer.field("pk_field", spec.pk_field);
8✔
326
            m_tracer.field("pk_type", spec.pk_type);
8✔
327
            m_tracer.field("pk_nullable", spec.pk_nullable);
8✔
328
            m_tracer.field("is_asymmetric", spec.is_asymmetric);
8✔
329
        },
8✔
330
        [&](const Instruction::AddTable::EmbeddedTable&) {
4✔
UNCOV
331
            m_tracer.field("embedded", true);
×
UNCOV
332
        },
×
333
    };
8✔
334
    mpark::visit(trace, p.type);
8✔
335
}
8✔
336

337
void Changeset::Reflector::operator()(const Instruction::EraseTable& p) const
UNCOV
338
{
×
UNCOV
339
    m_tracer.name("EraseTable");
×
340
    table_instr(p);
×
341
}
×
342

343
void Changeset::Reflector::operator()(const Instruction::Update& p) const
344
{
×
345
    m_tracer.name("Update");
×
346
    path_instr(p);
×
347
    m_tracer.field("value", p.value);
×
348
    if (p.is_array_update()) {
×
349
        m_tracer.field("prior_size", p.prior_size);
×
350
    }
×
UNCOV
351
    else {
×
UNCOV
352
        m_tracer.field("default", p.is_default);
×
353
    }
×
354
}
×
355

356
void Changeset::Reflector::operator()(const Instruction::AddInteger& p) const
357
{
×
UNCOV
358
    m_tracer.name("AddInteger");
×
UNCOV
359
    path_instr(p);
×
UNCOV
360
    m_tracer.field("value", Instruction::Payload{p.value});
×
UNCOV
361
}
×
362

363
void Changeset::Reflector::operator()(const Instruction::CreateObject& p) const
364
{
16✔
365
    m_tracer.name("CreateObject");
16✔
366
    object_instr(p);
16✔
367
}
16✔
368

369
void Changeset::Reflector::operator()(const Instruction::EraseObject& p) const
UNCOV
370
{
×
UNCOV
371
    m_tracer.name("EraseObject");
×
UNCOV
372
    object_instr(p);
×
UNCOV
373
}
×
374

375
void Changeset::Reflector::operator()(const Instruction::ArrayInsert& p) const
376
{
12✔
377
    m_tracer.name("ArrayInsert");
12✔
378
    path_instr(p);
12✔
379
    m_tracer.field("value", p.value);
12✔
380
    m_tracer.field("prior_size", p.prior_size);
12✔
381
}
12✔
382

383
void Changeset::Reflector::operator()(const Instruction::ArrayMove& p) const
384
{
×
385
    m_tracer.name("ArrayMove");
×
UNCOV
386
    path_instr(p);
×
UNCOV
387
    m_tracer.field("ndx_2", p.ndx_2);
×
388
    m_tracer.field("prior_size", p.prior_size);
×
389
}
×
390

391
void Changeset::Reflector::operator()(const Instruction::ArrayErase& p) const
392
{
×
UNCOV
393
    m_tracer.name("ArrayErase");
×
UNCOV
394
    path_instr(p);
×
395
    m_tracer.field("prior_size", p.prior_size);
×
396
}
×
397

398
void Changeset::Reflector::operator()(const Instruction::Clear& p) const
UNCOV
399
{
×
UNCOV
400
    m_tracer.name("Clear");
×
401
    path_instr(p);
×
402
}
×
403

404
void Changeset::Reflector::operator()(const Instruction::SetInsert& p) const
405
{
×
UNCOV
406
    m_tracer.name("SetInsert");
×
UNCOV
407
    path_instr(p);
×
408
    m_tracer.field("value", p.value);
×
409
}
×
410

411
void Changeset::Reflector::operator()(const Instruction::SetErase& p) const
412
{
×
UNCOV
413
    m_tracer.name("SetErase");
×
UNCOV
414
    path_instr(p);
×
UNCOV
415
    m_tracer.field("value", p.value);
×
UNCOV
416
}
×
417

418
void Changeset::Reflector::operator()(const Instruction::AddColumn& p) const
419
{
4✔
420
    m_tracer.name("AddColumn");
4✔
421
    m_tracer.field("table", p.table);
4✔
422
    m_tracer.field("field", p.field);
4✔
423
    if (p.type != Instruction::Payload::Type::Null) {
4✔
424
        m_tracer.field("type", p.type);
4✔
425
    }
4✔
UNCOV
426
    else {
×
UNCOV
427
        m_tracer.field("type", Instruction::Payload::Type::Null);
×
UNCOV
428
    }
×
429
    m_tracer.field("nullable", p.nullable);
4✔
430
    m_tracer.field("collection_type", p.collection_type);
4✔
431
    if (p.type == Instruction::Payload::Type::Link) {
4✔
432
        m_tracer.field("target_table", p.link_target_table);
4✔
433
    }
4✔
434
    if (p.collection_type == Instruction::AddColumn::CollectionType::Dictionary) {
4✔
UNCOV
435
        m_tracer.field("key_type", p.key_type);
×
436
    }
×
437
}
4✔
438

439
void Changeset::Reflector::operator()(const Instruction::EraseColumn& p) const
440
{
×
UNCOV
441
    m_tracer.name("EraseColumn");
×
UNCOV
442
    m_tracer.field("table", p.table);
×
UNCOV
443
    m_tracer.field("field", p.field);
×
UNCOV
444
}
×
445

446
void Changeset::Reflector::table_instr(const Instruction::TableInstruction& p) const
447
{
8✔
448
    m_tracer.field("path", p.table);
8✔
449
}
8✔
450

451
void Changeset::Reflector::object_instr(const Instruction::ObjectInstruction& p) const
452
{
16✔
453
    m_tracer.path("path", p.table, p.object, util::none, nullptr);
16✔
454
}
16✔
455

456
void Changeset::Reflector::path_instr(const Instruction::PathInstruction& p) const
457
{
12✔
458
    m_tracer.path("path", p.table, p.object, p.field, &p.path);
12✔
459
}
12✔
460

461
void Changeset::Reflector::visit_all() const
462
{
8✔
463
    m_tracer.set_changeset(&m_changeset);
8✔
464
    for (auto instr : m_changeset) {
40✔
465
        if (!instr)
40✔
UNCOV
466
            continue;
×
467
        m_tracer.before_each();
40✔
468
        instr->visit(*this);
40✔
469
        m_tracer.after_each();
40✔
470
    }
40✔
471
    m_tracer.set_changeset(nullptr);
8✔
472
}
8✔
473

474
void Changeset::Printer::name(StringData n)
475
{
40✔
476
    pad_or_ellipsis(n, 16);
40✔
477
}
40✔
478

479
void Changeset::Printer::print_field(StringData name, std::string value)
480
{
116✔
481
    if (!m_first) {
116✔
482
        m_out << ", ";
76✔
483
    }
76✔
484
    m_first = false;
116✔
485
    m_out << name << "=" << value;
116✔
486
}
116✔
487

488
void Changeset::Printer::path(StringData name, InternString table, const Instruction::PrimaryKey& pk,
489
                              util::Optional<InternString> field, const Instruction::Path* path)
490
{
28✔
491
    std::stringstream ss;
28✔
492
    m_changeset->print_path(ss, table, pk, field, path);
28✔
493
    print_field(name, ss.str());
28✔
494
}
28✔
495

496
void Changeset::Printer::field(StringData n, InternString value)
497
{
28✔
498
    std::stringstream ss;
28✔
499
    ss << "\"" << m_changeset->get_string(value) << "\"";
28✔
500
    print_field(n, ss.str());
28✔
501
}
28✔
502

503
void Changeset::Printer::field(StringData n, Instruction::Payload::Type type)
504
{
12✔
505
    print_field(n, get_type_name(type));
12✔
506
}
12✔
507

508
void Changeset::Printer::field(StringData n, Instruction::AddColumn::CollectionType type)
509
{
4✔
510
    print_field(n, get_collection_type(type));
4✔
511
}
4✔
512

513
std::string Changeset::Printer::primary_key_to_string(const Instruction::PrimaryKey& key)
514
{
×
515
    auto convert = overload{
×
516
        [&](const mpark::monostate&) {
×
517
            return std::string("NULL");
×
518
        },
×
519
        [&](int64_t value) {
×
520
            std::stringstream ss;
×
521
            ss << value;
×
522
            return ss.str();
×
523
        },
×
524
        [&](InternString str) {
×
525
            std::stringstream ss;
×
526
            ss << "\"" << m_changeset->get_string(str) << "\"";
×
527
            return ss.str();
×
528
        },
×
529
        [&](GlobalKey key) {
×
530
            std::stringstream ss;
×
531
            ss << key;
×
532
            return ss.str();
×
533
        },
×
534
        [&](ObjectId id) {
×
535
            std::stringstream ss;
×
536
            ss << id;
×
537
            return ss.str();
×
538
        },
×
539
        [&](UUID uuid) {
×
540
            return uuid.to_string();
×
UNCOV
541
        },
×
UNCOV
542
    };
×
543
    return mpark::visit(convert, key);
×
544
}
×
545

546
void Changeset::Printer::field(StringData n, const Instruction::PrimaryKey& key)
547
{
×
UNCOV
548
    std::stringstream ss;
×
UNCOV
549
    ss << format_pk(m_changeset->get_key(key));
×
UNCOV
550
    print_field(n, ss.str());
×
UNCOV
551
}
×
552

553
void Changeset::Printer::field(StringData n, const Instruction::Payload& value)
554
{
12✔
555
    std::stringstream ss;
12✔
556
    m_changeset->print_value(ss, value);
12✔
557
    print_field(n, ss.str());
12✔
558
}
12✔
559

560
void Changeset::Printer::field(StringData n, const Instruction::Path& path)
561
{
×
562
    std::stringstream ss;
×
563
    ss << "[";
×
564
    bool first = true;
×
565
    for (auto& element : path) {
×
566
        if (!first) {
×
567
            ss << ".";
×
568
        }
×
569
        first = false;
×
570

571
        auto print = util::overload{
×
572
            [&](InternString field) {
×
573
                ss << m_changeset->get_string(field);
×
574
            },
×
575
            [&](uint32_t index) {
×
576
                ss << index;
×
577
            },
×
578
        };
×
579
        mpark::visit(print, element);
×
UNCOV
580
    }
×
UNCOV
581
    ss << "]";
×
UNCOV
582
    print_field(n, ss.str());
×
UNCOV
583
}
×
584

585
void Changeset::Printer::field(StringData n, uint32_t value)
586
{
32✔
587
    std::stringstream ss;
32✔
588
    ss << value;
32✔
589
    print_field(n, ss.str());
32✔
590
}
32✔
591

592
void Changeset::Printer::after_each()
593
{
40✔
594
    m_out << "\n";
40✔
595
    m_first = true;
40✔
596
}
40✔
597

598
void Changeset::Printer::pad_or_ellipsis(StringData s, int width) const
599
{
40✔
600
    // FIXME: Does not work with UTF-8.
20✔
601
    std::string str = s; // FIXME: StringData doesn't work with iomanip because it calls ios_base::write() directly
40✔
602
    if (str.size() > size_t(width)) {
40✔
UNCOV
603
        m_out << str.substr(0, width - 1) << "~";
×
UNCOV
604
    }
×
605
    else {
40✔
606
        m_out << std::left << std::setw(width) << str;
40✔
607
    }
40✔
608
}
40✔
609

610
#endif // REALM_DEBUG LCOV_EXCL_STOP
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