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

realm / realm-core / github_pull_request_285284

21 Nov 2023 01:56PM UTC coverage: 91.664% (-0.03%) from 91.689%
github_pull_request_285284

Pull #7123

Evergreen

jedelbo
Merge branch 'master' into jf/mql
Pull Request #7123: PoC: Add MQL translation skeleton

92364 of 169228 branches covered (0.0%)

264 of 308 new or added lines in 5 files covered. (85.71%)

102 existing lines in 21 files now uncovered.

231504 of 252558 relevant lines covered (91.66%)

5988933.97 hits per line

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

91.07
/src/realm/util/bson/bson.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2020 Realm Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 * http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expreout or implied.
14
 * See the License for the specific language governing permioutions and
15
 * limitations under the License.
16
 *
17
 **************************************************************************/
18

19
#include <realm/util/bson/bson.hpp>
20
#include <realm/util/base64.hpp>
21
#include <realm/exceptions.hpp>
22
#include <external/json/json.hpp>
23
#include <sstream>
24
#include <algorithm>
25

26
namespace realm {
27
namespace bson {
28

29
Bson::~Bson() noexcept
30
{
1,640,944✔
31
    switch (m_type) {
1,640,944✔
32
        case Type::String:
552,863✔
33
            string_val.~basic_string();
552,863✔
34
            break;
552,863✔
35
        case Type::Binary:
68✔
36
            binary_val.~vector<char>();
68✔
37
            break;
68✔
38
        case Type::RegularExpression:
88✔
39
            regex_val.~RegularExpression();
88✔
40
            break;
88✔
41
        case Type::Document:
126,082✔
42
            document_val.reset();
126,082✔
43
            break;
126,082✔
44
        case Type::Array:
62,420✔
45
            array_val.reset();
62,420✔
46
            break;
62,420✔
47
        default:
899,423✔
48
            break;
899,423✔
49
    }
1,640,944✔
50
}
1,640,944✔
51

52
Bson::Bson(const Bson& v)
53
{
502,445✔
54
    m_type = Type::Null;
502,445✔
55
    *this = v;
502,445✔
56
}
502,445✔
57

58
Bson::Bson(Bson&& v) noexcept
59
{
48,871✔
60
    m_type = Type::Null;
48,871✔
61
    *this = std::move(v);
48,871✔
62
}
48,871✔
63

64
Bson& Bson::operator=(Bson&& v) noexcept
65
{
185,208✔
66
    if (this == &v)
185,208✔
67
        return *this;
×
68

91,099✔
69
    this->~Bson();
185,208✔
70

91,099✔
71
    m_type = v.m_type;
185,208✔
72

91,099✔
73
    switch (v.m_type) {
185,208✔
74
        case Type::Null:
1,946✔
75
            break;
1,946✔
76
        case Type::Int32:
413✔
77
            int32_val = v.int32_val;
413✔
78
            break;
413✔
79
        case Type::Int64:
32,176✔
80
            int64_val = v.int64_val;
32,176✔
81
            break;
32,176✔
82
        case Type::Bool:
170✔
83
            bool_val = v.bool_val;
170✔
84
            break;
170✔
85
        case Type::Double:
32✔
86
            double_val = v.double_val;
32✔
87
            break;
32✔
88
        case Type::Timestamp:
8✔
89
            time_val = v.time_val;
8✔
90
            break;
8✔
91
        case Type::Datetime:
22✔
92
            date_val = v.date_val;
22✔
93
            break;
22✔
94
        case Type::ObjectId:
2,480✔
95
            oid_val = v.oid_val;
2,480✔
96
            break;
2,480✔
97
        case Type::Decimal128:
10✔
98
            decimal_val = v.decimal_val;
10✔
99
            break;
10✔
100
        case Type::MaxKey:
4✔
101
            max_key_val = v.max_key_val;
4✔
102
            break;
4✔
103
        case Type::MinKey:
4✔
104
            min_key_val = v.min_key_val;
4✔
105
            break;
4✔
106
        case Type::Binary:
10✔
107
            new (&binary_val) std::vector<char>(std::move(v.binary_val));
10✔
108
            break;
10✔
109
        case Type::RegularExpression:
16✔
110
            new (&regex_val) RegularExpression(std::move(v.regex_val));
16✔
111
            break;
16✔
112
        case Type::String:
107,462✔
113
            new (&string_val) std::string(std::move(v.string_val));
107,462✔
114
            break;
107,462✔
115
        case Type::Document:
25,270✔
116
            new (&document_val) std::unique_ptr<BsonDocument>{std::move(v.document_val)};
25,270✔
117
            break;
25,270✔
118
        case Type::Array:
15,181✔
119
            new (&array_val) std::unique_ptr<BsonArray>{std::move(v.array_val)};
15,181✔
120
            break;
15,181✔
121
        case Type::Uuid:
4✔
122
            uuid_val = v.uuid_val;
4✔
123
            break;
4✔
124
    }
185,208✔
125

91,099✔
126
    return *this;
185,208✔
127
}
185,208✔
128

129
Bson& Bson::operator=(const Bson& v)
130
{
510,288✔
131
    if (&v == this)
510,288✔
132
        return *this;
×
133

248,999✔
134
    this->~Bson();
510,288✔
135

248,999✔
136
    m_type = v.m_type;
510,288✔
137

248,999✔
138
    switch (v.m_type) {
510,288✔
139
        case Type::Null:
3,930✔
140
            break;
3,930✔
141
        case Type::Int32:
2,124✔
142
            int32_val = v.int32_val;
2,124✔
143
            break;
2,124✔
144
        case Type::Int64:
64,004✔
145
            int64_val = v.int64_val;
64,004✔
146
            break;
64,004✔
147
        case Type::Bool:
562✔
148
            bool_val = v.bool_val;
562✔
149
            break;
562✔
150
        case Type::Double:
4,942✔
151
            double_val = v.double_val;
4,942✔
152
            break;
4,942✔
153
        case Type::Timestamp:
34✔
154
            time_val = v.time_val;
34✔
155
            break;
34✔
156
        case Type::Datetime:
110✔
157
            date_val = v.date_val;
110✔
158
            break;
110✔
159
        case Type::ObjectId:
10,064✔
160
            oid_val = v.oid_val;
10,064✔
161
            break;
10,064✔
162
        case Type::Decimal128:
40✔
163
            decimal_val = v.decimal_val;
40✔
164
            break;
40✔
165
        case Type::MaxKey:
18✔
166
            max_key_val = v.max_key_val;
18✔
167
            break;
18✔
168
        case Type::MinKey:
18✔
169
            min_key_val = v.min_key_val;
18✔
170
            break;
18✔
171
        case Type::Binary:
44✔
172
            new (&binary_val) std::vector<char>(v.binary_val);
44✔
173
            break;
44✔
174
        case Type::RegularExpression:
54✔
175
            new (&regex_val) RegularExpression(v.regex_val);
54✔
176
            break;
54✔
177
        case Type::String:
337,717✔
178
            new (&string_val) std::string(v.string_val);
337,717✔
179
            break;
337,717✔
180
        case Type::Document:
54,473✔
181
            new (&document_val) std::unique_ptr<BsonDocument>(new BsonDocument(*v.document_val));
54,473✔
182
            break;
54,473✔
183
        case Type::Array: {
32,118✔
184
            new (&array_val) std::unique_ptr<BsonArray>(new BsonArray(*v.array_val));
32,118✔
185
            break;
32,118✔
186
        }
×
187
        case Type::Uuid:
36✔
188
            uuid_val = v.uuid_val;
36✔
189
            break;
36✔
190
    }
510,288✔
191

248,999✔
192
    return *this;
510,288✔
193
}
510,288✔
194

195
Bson::Type Bson::type() const noexcept
196
{
47,411✔
197
    return m_type;
47,411✔
198
}
47,411✔
199

200
std::string Bson::to_string() const
201
{
9,908✔
202
    std::stringstream ss;
9,908✔
203
    ss << *this;
9,908✔
204
    return ss.str();
9,908✔
205
}
9,908✔
206

207
bool Bson::operator==(const Bson& other) const
208
{
43,865✔
209
    if (m_type != other.m_type) {
43,865✔
210
        return false;
16✔
211
    }
16✔
212

21,925✔
213
    switch (m_type) {
43,849✔
214
        case Type::Null:
2✔
215
            return true;
2✔
216
        case Type::Int32:
20✔
217
            return int32_val == other.int32_val;
20✔
218
        case Type::Int64:
72✔
219
            return int64_val == other.int64_val;
72✔
220
        case Type::Bool:
10✔
221
            return bool_val == other.bool_val;
10✔
222
        case Type::Double:
2✔
223
            return double_val == other.double_val;
2✔
224
        case Type::Datetime:
6✔
225
            return date_val == other.date_val;
6✔
226
        case Type::Timestamp:
2✔
227
            return time_val == other.time_val;
2✔
228
        case Type::ObjectId:
43,109✔
229
            return oid_val == other.oid_val;
43,109✔
230
        case Type::Decimal128:
✔
231
            return decimal_val == other.decimal_val;
×
232
        case Type::MaxKey:
2✔
233
            return max_key_val == other.max_key_val;
2✔
234
        case Type::MinKey:
2✔
235
            return min_key_val == other.min_key_val;
2✔
236
        case Type::String:
516✔
237
            return string_val == other.string_val;
516✔
238
        case Type::RegularExpression:
2✔
239
            return regex_val == other.regex_val;
2✔
240
        case Type::Binary:
4✔
241
            return binary_val == other.binary_val;
4✔
242
        case Type::Document:
80✔
243
            return *document_val == *other.document_val;
80✔
244
        case Type::Array:
14✔
245
            return *array_val == *other.array_val;
14✔
246
        case Type::Uuid:
6✔
247
            return uuid_val == other.uuid_val;
6✔
248
    }
×
249

250
    return false;
×
251
}
×
252

253
bool Bson::operator!=(const Bson& other) const
254
{
16✔
255
    return !(*this == other);
16✔
256
}
16✔
257

258
template <>
259
bool holds_alternative<util::None>(const Bson& bson)
260
{
44✔
261
    return bson.m_type == Bson::Type::Null;
44✔
262
}
44✔
263

264
template <>
265
bool holds_alternative<int32_t>(const Bson& bson)
266
{
10✔
267
    return bson.m_type == Bson::Type::Int32;
10✔
268
}
10✔
269

270
template <>
271
bool holds_alternative<int64_t>(const Bson& bson)
272
{
10✔
273
    return bson.m_type == Bson::Type::Int64;
10✔
274
}
10✔
275

276
template <>
277
bool holds_alternative<bool>(const Bson& bson)
278
{
36✔
279
    return bson.m_type == Bson::Type::Bool;
36✔
280
}
36✔
281

282
template <>
283
bool holds_alternative<double>(const Bson& bson)
284
{
24✔
285
    return bson.m_type == Bson::Type::Double;
24✔
286
}
24✔
287

288
template <>
289
bool holds_alternative<std::string>(const Bson& bson)
290
{
8,480✔
291
    return bson.m_type == Bson::Type::String;
8,480✔
292
}
8,480✔
293

294
template <>
295
bool holds_alternative<std::vector<char>>(const Bson& bson)
296
{
6✔
297
    return bson.m_type == Bson::Type::Binary;
6✔
298
}
6✔
299

300
template <>
301
bool holds_alternative<Timestamp>(const Bson& bson)
302
{
8✔
303
    return bson.m_type == Bson::Type::Datetime;
8✔
304
}
8✔
305

306
template <>
307
bool holds_alternative<ObjectId>(const Bson& bson)
308
{
6✔
309
    return bson.m_type == Bson::Type::ObjectId;
6✔
310
}
6✔
311

312
template <>
313
bool holds_alternative<Decimal128>(const Bson& bson)
314
{
10✔
315
    return bson.m_type == Bson::Type::Decimal128;
10✔
316
}
10✔
317

318
template <>
319
bool holds_alternative<RegularExpression>(const Bson& bson)
320
{
14✔
321
    return bson.m_type == Bson::Type::RegularExpression;
14✔
322
}
14✔
323

324
template <>
325
bool holds_alternative<MinKey>(const Bson& bson)
326
{
2✔
327
    return bson.m_type == Bson::Type::MinKey;
2✔
328
}
2✔
329

330
template <>
331
bool holds_alternative<MaxKey>(const Bson& bson)
332
{
2✔
333
    return bson.m_type == Bson::Type::MaxKey;
2✔
334
}
2✔
335

336
template <>
337
bool holds_alternative<IndexedMap<Bson>>(const Bson& bson)
338
{
4,769✔
339
    return bson.m_type == Bson::Type::Document;
4,769✔
340
}
4,769✔
341

342
template <>
343
bool holds_alternative<std::vector<Bson>>(const Bson& bson)
344
{
1,052✔
345
    return bson.m_type == Bson::Type::Array;
1,052✔
346
}
1,052✔
347

348
template <>
349
bool holds_alternative<MongoTimestamp>(const Bson& bson)
350
{
6✔
351
    return bson.m_type == Bson::Type::Timestamp;
6✔
352
}
6✔
353

354
template <>
355
bool holds_alternative<UUID>(const Bson& bson)
356
{
2✔
357
    return bson.m_type == Bson::Type::Uuid;
2✔
358
}
2✔
359

360
struct PrecisionGuard {
361
    PrecisionGuard(std::ostream& stream, std::streamsize new_precision)
362
        : stream(stream)
363
        , old_precision(stream.precision(new_precision))
364
    {
226✔
365
    }
226✔
366

367
    ~PrecisionGuard()
368
    {
226✔
369
        stream.precision(old_precision);
226✔
370
    }
226✔
371

372
    std::ostream& stream;
373
    std::streamsize old_precision;
374
};
375

376
std::ostream& operator<<(std::ostream& out, const Bson& b)
377
{
44,303✔
378
    switch (b.type()) {
44,303✔
379
        case Bson::Type::Null:
60✔
380
            out << "null";
60✔
381
            break;
60✔
382
        case Bson::Type::Int32:
537✔
383
            out << "{"
537✔
384
                << "\"$numberInt\""
537✔
385
                << ":" << '"' << static_cast<int32_t>(b) << '"' << "}";
537✔
386
            break;
537✔
387
        case Bson::Type::Int64:
300✔
388
            out << "{"
300✔
389
                << "\"$numberLong\""
300✔
390
                << ":" << '"' << static_cast<int64_t>(b) << '"' << "}";
300✔
391
            break;
300✔
392
        case Bson::Type::Bool:
130✔
393
            out << (b ? "true" : "false");
103✔
394
            break;
130✔
395
        case Bson::Type::Double: {
232✔
396
            double d = static_cast<double>(b);
232✔
397
            out << "{"
232✔
398
                << "\"$numberDouble\""
232✔
399
                << ":" << '"';
232✔
400
            if (std::isnan(d)) {
232✔
401
                out << "NaN";
2✔
402
            }
2✔
403
            else if (d == std::numeric_limits<double>::infinity()) {
230✔
404
                out << "Infinity";
2✔
405
            }
2✔
406
            else if (d == (-1 * std::numeric_limits<double>::infinity())) {
228✔
407
                out << "-Infinity";
2✔
408
            }
2✔
409
            else {
226✔
410
                PrecisionGuard precision_guard(out, std::numeric_limits<double>::max_digits10);
226✔
411
                out << d;
226✔
412
            }
226✔
413
            out << '"' << "}";
232✔
414
            break;
232✔
415
        }
×
416
        case Bson::Type::String:
30,528✔
417
            out << nlohmann::json(b.operator const std::string&()).dump();
30,528✔
418
            break;
30,528✔
419
        case Bson::Type::Binary: {
10✔
420
            const std::vector<char>& vec = static_cast<std::vector<char>>(b);
10✔
421
            out << "{\"$binary\":{\"base64\":\"" << std::string(vec.begin(), vec.end()) << "\",\"subType\":\"00\"}}";
10✔
422
            break;
10✔
423
        }
×
424
        case Bson::Type::Timestamp: {
8✔
425
            const MongoTimestamp& t = static_cast<MongoTimestamp>(b);
8✔
426
            out << "{\"$timestamp\":{\"t\":" << t.seconds << ",\"i\":" << t.increment << "}}";
8✔
427
            break;
8✔
428
        }
×
429
        case Bson::Type::Datetime: {
22✔
430
            auto d = static_cast<realm::Timestamp>(b);
22✔
431

7✔
432
            out << "{\"$date\":{\"$numberLong\":\"" << ((d.get_seconds() * 1000) + d.get_nanoseconds() / 1000000)
22✔
433
                << "\"}}";
22✔
434
            break;
22✔
435
        }
×
436
        case Bson::Type::ObjectId: {
2,510✔
437
            const ObjectId& oid = static_cast<ObjectId>(b);
2,510✔
438
            out << "{"
2,510✔
439
                << "\"$oid\""
2,510✔
440
                << ":" << '"' << oid << '"' << "}";
2,510✔
441
            break;
2,510✔
442
        }
×
443
        case Bson::Type::Decimal128: {
10✔
444
            const Decimal128& d = static_cast<Decimal128>(b);
10✔
445
            out << "{"
10✔
446
                << "\"$numberDecimal\""
10✔
447
                << ":" << '"';
10✔
448
            if (d.is_nan()) {
10✔
449
                out << "NaN";
2✔
450
            }
2✔
451
            else if (d == Decimal128("Infinity")) {
8✔
452
                out << "Infinity";
2✔
453
            }
2✔
454
            else if (d == Decimal128("-Infinity")) {
6✔
455
                out << "-Infinity";
2✔
456
            }
2✔
457
            else {
4✔
458
                out << d;
4✔
459
            }
4✔
460
            out << '"' << "}";
10✔
461
            break;
10✔
462
        }
×
463
        case Bson::Type::RegularExpression: {
12✔
464
            const RegularExpression& regex = static_cast<RegularExpression>(b);
12✔
465
            out << "{\"$regularExpression\":{\"pattern\":\"" << regex.pattern() << "\",\"options\":\""
12✔
466
                << regex.options() << "\"}}";
12✔
467
            break;
12✔
468
        }
×
469
        case Bson::Type::MaxKey:
4✔
470
            out << "{\"$maxKey\":1}";
4✔
471
            break;
4✔
472
        case Bson::Type::MinKey:
4✔
473
            out << "{\"$minKey\":1}";
4✔
474
            break;
4✔
475
        case Bson::Type::Document: {
9,623✔
476
            const BsonDocument& doc = static_cast<BsonDocument>(b);
9,623✔
477
            out << "{";
9,623✔
478
            bool first = true;
9,623✔
479
            for (auto const& pair : doc) {
29,755✔
480
                if (!first)
29,755✔
481
                    out << ',';
20,464✔
482
                first = false;
29,755✔
483
                out << nlohmann::json(pair.first).dump() << ':' << pair.second;
29,755✔
484
            }
29,755✔
485
            out << "}";
9,623✔
486
            break;
9,623✔
487
        }
×
488
        case Bson::Type::Array: {
291✔
489
            const BsonArray& arr = static_cast<BsonArray>(b);
291✔
490
            out << "[";
291✔
491
            bool first = true;
291✔
492
            for (auto const& b : arr) {
2,832✔
493
                if (!first)
2,832✔
494
                    out << ',';
2,557✔
495
                first = false;
2,832✔
496
                out << b;
2,832✔
497
            }
2,832✔
498
            out << "]";
291✔
499
            break;
291✔
500
        }
×
501
        case Bson::Type::Uuid: {
22✔
502
            const UUID& u = static_cast<UUID>(b);
22✔
503
            out << "{\"$binary\":{\"base64\":\"";
22✔
504
            out << u.to_base64();
22✔
505
            out << "\",\"subType\":\"04\"}}";
22✔
506
            break;
22✔
507
        }
44,303✔
508
    }
44,303✔
509
    return out;
44,303✔
510
}
44,303✔
511

512
std::string Bson::toJson() const
513
{
628✔
514
    std::stringstream s;
628✔
515
    s << *this;
628✔
516
    return s.str();
628✔
517
}
628✔
518

519
namespace {
520

521
struct BsonError : public std::runtime_error {
522
    BsonError(std::string message)
523
        : std::runtime_error(std::move(message))
524
    {
×
525
    }
×
526
};
527

528
namespace {
529
// This implements just enough of the map API to support nlohmann's DOM apis that we use.
530
template <typename K, typename V, typename... Ignored>
531
struct LinearMap {
532
    using key_type = K;
533
    using mapped_type = V;
534
    using value_type = std::pair<const K, V>;
535
    using storage_type = std::vector<value_type>;
536
    using iterator = typename storage_type::iterator;
537
    using const_iterator = typename storage_type::const_iterator;
538
    using key_compare = std::equal_to<K>;
539

540
    auto begin()
541
    {
133,172✔
542
        return _elems.begin();
133,172✔
543
    }
133,172✔
544
    auto begin() const
545
    {
546
        return _elems.begin();
547
    }
548
    auto end()
549
    {
252,907✔
550
        return _elems.end();
252,907✔
551
    }
252,907✔
552
    auto end() const
553
    {
554
        return _elems.end();
555
    }
556
    auto size() const
557
    {
115,009✔
558
        return _elems.size();
115,009✔
559
    }
115,009✔
560
    auto max_size() const
561
    {
×
562
        return _elems.max_size();
×
563
    }
×
564
    auto clear()
565
    {
24,331✔
566
        return _elems.clear();
24,331✔
567
    }
24,331✔
568
    V& operator[](const K& k)
569
    {
122,900✔
570
        // assume this is only used for adding a new element.
60,407✔
571
        return _elems.emplace_back(k, V()).second;
122,900✔
572
    }
122,900✔
573

574
    template <typename... Args>
575
    std::pair<iterator, bool> emplace(Args&&... args)
576
    {
577
        // assume this is only used for adding a new element.
578
        _elems.emplace_back(std::forward<Args>(args)...);
579
        return {--_elems.end(), true};
580
    }
581

582
    iterator erase(iterator)
583
    {
×
584
        // This is only used when mutating the DOM which we don't do.
585
        REALM_TERMINATE("LinearMap::erase() should never be called");
586
    }
×
587

588
    storage_type _elems;
589
};
590

591
using Json = nlohmann::basic_json<LinearMap>;
592

593
Bson dom_obj_to_bson(const Json& json);
594

595
Bson dom_elem_to_bson(const Json& json)
596
{
170,302✔
597
    switch (json.type()) {
170,302✔
598
        case Json::value_t::null:
1,940✔
599
            return Bson();
1,940✔
600
        case Json::value_t::string:
78,900✔
601
            return Bson(json.get<std::string>());
78,900✔
602
        case Json::value_t::boolean:
52✔
603
            return Bson(json.get<bool>());
52✔
604
        case Json::value_t::binary: {
✔
605
            std::vector<char> out;
×
606
            for (auto&& elem : json.get_binary()) {
×
607
                out.push_back(elem);
×
608
            }
×
609
            return Bson(std::move(out));
×
610
        }
×
611
        case Json::value_t::number_integer:
✔
612
            return Bson(json.get<int64_t>());
×
613
        case Json::value_t::number_unsigned: {
31,908✔
614
            uint64_t val = json.get<uint64_t>();
31,908✔
615
            if (val <= uint64_t(std::numeric_limits<int64_t>::max()))
31,908✔
616
                return Bson(int64_t(val));
31,908✔
617
            return Bson(double(val));
×
618
        }
×
619
        case Json::value_t::number_float:
✔
620
            return Bson(json.get<double>());
×
621
        case Json::value_t::object:
42,587✔
622
            return dom_obj_to_bson(json);
42,587✔
623
        case Json::value_t::array: {
14,915✔
624
            std::vector<Bson> out;
14,915✔
625
            for (auto&& elem : json) {
31,040✔
626
                out.push_back(dom_elem_to_bson(elem));
31,040✔
627
            }
31,040✔
628
            return Bson(std::move(out));
14,915✔
629
        }
×
630
        case Json::value_t::discarded:
✔
631
            REALM_TERMINATE("should never see discarded");
632
    }
170,302✔
633
    REALM_TERMINATE("unknown json value type");
634
}
×
635

636
// This works around the deleted rvalue constructor in StringData
637
inline StringData tosd(const std::string& s)
638
{
10✔
639
    return s;
10✔
640
}
10✔
641

642
// Keep these sorted by key. This is checked so you can't forget.
643
using FancyParser = Bson (*)(const Json& json);
644
static constexpr std::pair<std::string_view, FancyParser> bson_fancy_parsers[] = {
645
    {"$binary",
646
     +[](const Json& json) {
18✔
647
         util::Optional<std::vector<char>> base64;
18✔
648
         util::Optional<uint8_t> subType;
18✔
649
         if (json.size() != 2)
18✔
650
             throw BsonError("invalid extended json $binary");
×
651
         for (auto&& [k, v] : json.items()) {
36✔
652
             if (k == "base64") {
36✔
653
                 const std::string& str = v.get<std::string>();
18✔
654
                 base64.emplace(str.begin(), str.end());
18✔
655
             }
18✔
656
             else if (k == "subType") {
18✔
657
                 subType = uint8_t(std::stoul(v.get<std::string>(), nullptr, 16));
18✔
658
             }
18✔
659
         }
36✔
660
         if (!base64 || !subType)
18✔
661
             throw BsonError("invalid extended json $binary");
×
662
         if (subType == 0x04) { // UUID
18✔
663
             auto stringData = StringData(reinterpret_cast<const char*>(base64->data()), base64->size());
8✔
664
             util::Optional<std::vector<char>> uuidChrs = util::base64_decode_to_vector(stringData);
8✔
665
             if (!uuidChrs)
8✔
666
                 throw BsonError("Invalid base64 in $binary");
×
667
             UUID::UUIDBytes bytes{};
8✔
668
             std::copy_n(uuidChrs->data(), bytes.size(), bytes.begin());
8✔
669
             return Bson(UUID(bytes));
8✔
670
         }
8✔
671
         else {
10✔
672
             return Bson(std::move(*base64)); // TODO don't throw away the subType.
10✔
673
         }
10✔
674
     }},
18✔
675
    {"$date",
676
     +[](const Json& json) {
22✔
677
         int64_t millis_since_epoch = dom_elem_to_bson(json).operator int64_t();
22✔
678
         return Bson(realm::Timestamp(millis_since_epoch / 1000,
22✔
679
                                      (millis_since_epoch % 1000) * 1'000'000)); // ms -> ns
22✔
680
     }},
22✔
681
    {"$maxKey",
682
     +[](const Json&) {
4✔
683
         return Bson(MaxKey());
4✔
684
     }},
4✔
685
    {"$minKey",
686
     +[](const Json&) {
4✔
687
         return Bson(MinKey());
4✔
688
     }},
4✔
689
    {"$numberDecimal",
690
     +[](const Json& json) {
10✔
691
         return Bson(Decimal128(tosd(json.get<std::string>())));
10✔
692
     }},
10✔
693
    {"$numberDouble",
694
     +[](const Json& json) {
26✔
695
         return Bson(std::stod(json.get<std::string>()));
26✔
696
     }},
26✔
697
    {"$numberInt",
698
     +[](const Json& json) {
391✔
699
         return Bson(int32_t(std::stoi(json.get<std::string>())));
391✔
700
     }},
391✔
701
    {"$numberLong",
702
     +[](const Json& json) {
240✔
703
         return Bson(int64_t(std::stoll(json.get<std::string>())));
240✔
704
     }},
240✔
705
    {"$oid",
706
     +[](const Json& json) {
2,418✔
707
         return Bson(ObjectId(json.get<std::string>().c_str()));
2,418✔
708
     }},
2,418✔
709
    {"$regularExpression",
710
     +[](const Json& json) {
16✔
711
         util::Optional<std::string> pattern;
16✔
712
         util::Optional<std::string> options;
16✔
713
         if (json.size() != 2)
16✔
714
             throw BsonError("invalid extended json $binary");
×
715
         for (auto&& [k, v] : json.items()) {
32✔
716
             if (k == "pattern") {
32✔
717
                 pattern = v.get<std::string>();
16✔
718
             }
16✔
719
             else if (k == "options") {
16✔
720
                 options = v.get<std::string>();
16✔
721
             }
16✔
722
         }
32✔
723
         if (!pattern || !options)
16✔
724
             throw BsonError("invalid extended json $binary");
×
725
         return Bson(RegularExpression(std::move(*pattern), std::move(*options)));
16✔
726
     }},
16✔
727
    {"$timestamp",
728
     +[](const Json& json) {
8✔
729
         util::Optional<uint32_t> t;
8✔
730
         util::Optional<uint32_t> i;
8✔
731
         if (json.size() != 2)
8✔
732
             throw BsonError("invalid extended json $timestamp");
×
733
         for (auto&& [k, v] : json.items()) {
16✔
734
             if (k == "t") {
16✔
735
                 t = v.get<uint32_t>();
8✔
736
             }
8✔
737
             else if (k == "i") {
8✔
738
                 i = v.get<uint32_t>();
8✔
739
             }
8✔
740
         }
16✔
741
         if (!t || !i)
8✔
742
             throw BsonError("invalid extended json $timestamp");
×
743
         return Bson(MongoTimestamp(*t, *i));
8✔
744
     }},
8✔
745
    {"$uuid",
746
     +[](const Json& json) {
2✔
747
         std::string uuid = json.get<std::string>();
2✔
748
         return Bson(UUID(uuid));
2✔
749
     }},
2✔
750
};
751

752
constexpr auto parser_comp = [](const std::pair<std::string_view, FancyParser>& lhs,
753
                                const std::pair<std::string_view, FancyParser>& rhs) {
12,423✔
754
    return lhs.first < rhs.first;
12,423✔
755
};
12,423✔
756

757
// TODO do this instead in C++20
758
// static_assert(std::ranges::is_sorted(bson_fancy_parsers, parser_comp));
759
#if REALM_DEBUG
760
[[maybe_unused]] bool check_sort_on_startup = [] {
4✔
761
    REALM_ASSERT(std::is_sorted(std::begin(bson_fancy_parsers), std::end(bson_fancy_parsers), parser_comp));
4✔
762
    return false;
4✔
763
}();
4✔
764
#endif
765

766
Bson dom_obj_to_bson(const Json& json)
767
{
42,587✔
768
    if (json.size() == 1) {
42,587✔
769
        const auto& [key, value] = json.items().begin();
18,552✔
770
        if (key[0] == '$') {
18,552✔
771
            auto it = std::lower_bound(std::begin(bson_fancy_parsers), std::end(bson_fancy_parsers),
3,197✔
772
                                       std::pair<std::string_view, FancyParser>(key, nullptr), parser_comp);
3,197✔
773
            if (it != std::end(bson_fancy_parsers) && it->first == key) {
3,197✔
774
                return it->second(value);
3,159✔
775
            }
3,159✔
776
        }
24,035✔
777
    }
24,035✔
778
    else if (json.size() == 2) {
24,035✔
779
        const auto& [key, value] = json.items().begin();
2,474✔
780
        if (key[0] == '$') {
2,474✔
781
            auto it = std::lower_bound(std::begin(bson_fancy_parsers), std::end(bson_fancy_parsers),
×
782
                                       std::pair<std::string_view, FancyParser>(key, nullptr), parser_comp);
×
783
            if (it != std::end(bson_fancy_parsers) && it->first == key) {
×
784
                return it->second(json);
×
785
            }
×
786
        }
39,428✔
787
    }
2,474✔
788

19,426✔
789
    BsonDocument out;
39,428✔
790
    for (auto&& [k, v] : json.items()) {
119,651✔
791
        out[k] = dom_elem_to_bson(v);
119,651✔
792
    }
119,651✔
793
    return out;
39,428✔
794
}
39,428✔
795
} // namespace
796

797
} // anonymous namespace
798

799
Bson parse(const std::string_view& json)
800
{
19,613✔
801
    try {
19,613✔
802
        return dom_elem_to_bson(Json::parse(json));
19,613✔
803
    }
19,613✔
804
    catch (const std::exception& e) {
24✔
805
        throw query_parser::SyntaxError(e.what());
24✔
806
    }
24✔
NEW
807
    return {};
×
UNCOV
808
}
×
809

810
} // namespace bson
811
} // namespace realm
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