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

realm / realm-core / thomas.goyne_491

09 Aug 2024 04:34PM UTC coverage: 89.577% (-1.5%) from 91.087%
thomas.goyne_491

Pull #7967

Evergreen

tgoyne
Actually check for unuplaoded changes in no_pending_local_changes()

We can have local changesets stored which have already been uploaded and
acknoledged by the server, so checking all of the changesets is incorrect. We
need to instead only check changesets for versions after the current position
of the upload cursor.
Pull Request #7967: RCORE-2232 Actually check for unuploaded changes in no_pending_local_changes()

90956 of 164876 branches covered (55.17%)

37 of 38 new or added lines in 2 files covered. (97.37%)

42 existing lines in 8 files now uncovered.

145956 of 162940 relevant lines covered (89.58%)

8094301.68 hits per line

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

86.46
/src/realm/mixed.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2016 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 express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 **************************************************************************/
18

19
#include <realm/mixed.hpp>
20
#include <realm/decimal128.hpp>
21
#include <realm/unicode.hpp>
22
#include <realm/column_type_traits.hpp>
23
#include <realm/obj.hpp>
24
#include <realm/table.hpp>
25
#include <realm/query_value.hpp>
26
#include <realm/util/serializer.hpp>
27

28
namespace realm {
29
namespace {
30
static const int sorting_rank[] = {
31
    // Observe! Changing these values breaks the file format for Set<Mixed>
32

33
    -1, // null
34
    1,  // type_Int = 0,
35
    0,  // type_Bool = 1,
36
    2,  // type_String = 2,
37
    -1,
38
    3,  // type_Binary = 4,
39
    -1, // type_OldTable = 5,
40
    -1, // type_Mixed = 6,
41
    -1, // type_OldDateTime = 7,
42
    4,  // type_Timestamp = 8,
43
    1,  // type_Float = 9,
44
    1,  // type_Double = 10,
45
    1,  // type_Decimal = 11,
46
    8,  // type_Link = 12,
47
    -1, // type_LinkList = 13,
48
    -1,
49
    5,  // type_ObjectId = 15,
50
    7,  // type_TypedLink = 16
51
    6,  // type_UUID = 17
52
    8,  // type_TypeOfValue = 18
53
    9,  // type_List = 19
54
    10, // type_Set = 20
55
    11, // type_Dictionary = 21
56

57
    // Observe! Changing these values breaks the file format for Set<Mixed>
58
};
59

60
int compare_string(StringData a, StringData b)
61
{
5,209,698✔
62
    // Observe! Changing these values breaks the file format for Set<Mixed> and the StringIndex
63
    if (a == b)
5,209,698✔
64
        return 0;
3,459,009✔
65
    return a < b ? -1 : 1;
1,750,689✔
66
}
5,209,698✔
67

68
int compare_binary(BinaryData a, BinaryData b)
69
{
20,478✔
70
    size_t asz = a.size();
20,478✔
71
    size_t bsz = b.size();
20,478✔
72
    size_t min_sz = std::min(asz, bsz);
20,478✔
73
    int ret = memcmp(a.data(), b.data(), min_sz);
20,478✔
74
    if (ret == 0) {
20,478✔
75
        if (asz > bsz)
12,015✔
76
            ret = 1;
501✔
77
        else if (asz < bsz)
11,514✔
78
            ret = -1;
546✔
79
    }
12,015✔
80
    return ret;
20,478✔
81
}
20,478✔
82

83
template <int>
84
struct IntTypeForSize;
85
template <>
86
struct IntTypeForSize<1> {
87
    using type = uint8_t;
88
};
89
template <>
90
struct IntTypeForSize<2> {
91
    using type = uint16_t;
92
};
93
template <>
94
struct IntTypeForSize<4> {
95
    using type = uint32_t;
96
};
97
template <>
98
struct IntTypeForSize<8> {
99
    using type = uint64_t;
100
};
101

102
template <typename Float>
103
int compare_float(Float a_raw, Float b_raw)
104
{
1,919,178✔
105
    bool a_nan = std::isnan(a_raw);
1,919,178✔
106
    bool b_nan = std::isnan(b_raw);
1,919,178✔
107
    if (!a_nan && !b_nan) {
1,919,178✔
108
        // Just compare as IEEE floats
109
        return a_raw == b_raw ? 0 : a_raw < b_raw ? -1 : 1;
1,691,928✔
110
    }
1,691,928✔
111
    if (a_nan && b_nan) {
227,250✔
112
        // Compare the nan values as unsigned
113
        using IntType = typename IntTypeForSize<sizeof(Float)>::type;
38,280✔
114
        IntType a = 0, b = 0;
38,280✔
115
        memcpy(&a, &a_raw, sizeof(Float));
38,280✔
116
        memcpy(&b, &b_raw, sizeof(Float));
38,280✔
117
        return a == b ? 0 : a < b ? -1 : 1;
38,280✔
118
    }
38,280✔
119
    // One is nan, the other is not
120
    // nans are treated as being less than all non-nan values
121
    return a_nan ? -1 : 1;
188,970✔
122
}
227,250✔
123

124
template <typename T>
125
int compare_generic(T lhs, T rhs)
126
{
19,199,892✔
127
    return lhs == rhs ? 0 : lhs < rhs ? -1 : 1;
19,199,892✔
128
}
19,199,892✔
129

130
// This is the tricky one. Needs to support the following cases:
131
// * Doubles with a fractional component.
132
// * Longs that can't be precisely represented as a double.
133
// * Doubles outside of the range of Longs (including +/- Inf).
134
// * NaN (defined by us as less than all Longs)
135
// * Return value is always -1, 0, or 1 to ensure it is safe to negate.
136
int compare_long_to_double(int64_t lhs, double rhs)
137
{
345,048✔
138
    // All Longs are > NaN
139
    if (std::isnan(rhs))
345,048✔
140
        return 1;
6✔
141

142
    // Ints with magnitude <= 2**53 can be precisely represented as doubles.
143
    // Additionally, doubles outside of this range can't have a fractional component.
144
    static const int64_t kEndOfPreciseDoubles = 1ll << 53;
345,042✔
145
    if (lhs <= kEndOfPreciseDoubles && lhs >= -kEndOfPreciseDoubles) {
345,042✔
146
        return compare_float(double(lhs), rhs);
345,006✔
147
    }
345,006✔
148

149
    // Large magnitude doubles (including +/- Inf) are strictly > or < all Longs.
150
    static const double kBoundOfLongRange = -static_cast<double>(LLONG_MIN); // positive 2**63
36✔
151
    if (rhs >= kBoundOfLongRange)
36✔
152
        return -1; // Can't be represented in a Long.
6✔
153
    if (rhs < -kBoundOfLongRange)
30✔
154
        return 1; // Can be represented in a Long.
6✔
155

156
    // Remaining Doubles can have their integer component precisely represented as long longs.
157
    // If they have a fractional component, they must be strictly > or < lhs even after
158
    // truncation of the fractional component since low-magnitude lhs were handled above.
159
    return compare_generic(lhs, int64_t(rhs));
24✔
160
}
30✔
161
} // anonymous namespace
162

163
Mixed::Mixed(const Obj& obj) noexcept
164
    : Mixed(ObjLink(obj.get_table()->get_key(), obj.get_key()))
66✔
165
{
132✔
166
}
132✔
167

168
bool Mixed::types_are_comparable(const Mixed& lhs, const Mixed& rhs)
169
{
9,431,961✔
170
    if (lhs.m_type == rhs.m_type)
9,431,961✔
171
        return lhs.m_type != 0;
8,719,710✔
172

173
    if (lhs.is_null() || rhs.is_null())
712,251✔
174
        return false;
347,016✔
175

176
    DataType l_type = lhs.get_type();
365,235✔
177
    DataType r_type = rhs.get_type();
365,235✔
178
    return data_types_are_comparable(l_type, r_type);
365,235✔
179
}
712,251✔
180

181
bool Mixed::data_types_are_comparable(DataType l_type, DataType r_type)
182
{
2,405,997✔
183
    if (l_type == r_type)
2,405,997✔
184
        return true;
1,579,023✔
185

186
    if (is_numeric(l_type, r_type)) {
826,974✔
187
        return true;
382,569✔
188
    }
382,569✔
189
    if (l_type == type_Mixed || r_type == type_Mixed) {
444,405✔
190
        return true; // Mixed is comparable with any type
1,062✔
191
    }
1,062✔
192
    return false;
443,343✔
193
}
444,405✔
194

195
bool Mixed::accumulate_numeric_to(Decimal128& destination) const noexcept
196
{
28,692✔
197
    bool did_accumulate = false;
28,692✔
198
    if (!is_null()) {
28,692✔
199
        switch (get_type()) {
28,116✔
200
            case type_Int:
17,820✔
201
                destination += Decimal128(get_int());
17,820✔
202
                did_accumulate = true;
17,820✔
203
                break;
17,820✔
204
            case type_Double:
1,980✔
205
                destination += Decimal128(get_double());
1,980✔
206
                did_accumulate = true;
1,980✔
207
                break;
1,980✔
208
            case type_Float:
1,764✔
209
                destination += Decimal128(get_float());
1,764✔
210
                did_accumulate = true;
1,764✔
211
                break;
1,764✔
212
            case type_Decimal: {
1,752✔
213
                auto val = get_decimal();
1,752✔
214
                if (!val.is_nan()) {
1,752✔
215
                    destination += val;
1,116✔
216
                    did_accumulate = true;
1,116✔
217
                }
1,116✔
218
                break;
1,752✔
219
            }
×
220
            default:
4,800✔
221
                break;
4,800✔
222
        }
28,116✔
223
    }
28,116✔
224
    return did_accumulate;
28,692✔
225
}
28,692✔
226

227
int Mixed::compare(const Mixed& b) const noexcept
228
{
28,406,097✔
229
    // Observe! Changing this function breaks the file format for Set<Mixed> and the StringIndex
230

231
    if (is_null()) {
28,406,097✔
232
        return b.is_null() ? 0 : -1;
1,135,998✔
233
    }
1,135,998✔
234
    if (b.is_null())
27,270,099✔
235
        return 1;
379,311✔
236

237
    // None is null
238
    auto type = get_type();
26,890,788✔
239
    switch (type) {
26,890,788✔
240
        case type_Bool: {
1,100,670✔
241
            if (b.get_type() == type_Bool) {
1,100,670✔
242
                return compare_generic(bool_val, b.bool_val);
1,100,079✔
243
            }
1,100,079✔
244
            break;
591✔
245
        }
1,100,670✔
246
        case type_Int:
17,048,064✔
247
            switch (b.get_type()) {
17,048,064✔
248
                case type_Int:
16,803,765✔
249
                    return compare_generic(int_val, b.int_val);
16,803,765✔
250
                case type_Float:
62,331✔
251
                    return compare_long_to_double(int_val, b.float_val);
62,331✔
252
                case type_Double:
164,100✔
253
                    return compare_long_to_double(int_val, b.double_val);
164,100✔
254
                case type_Decimal:
12,594✔
255
                    return Decimal128(int_val).compare(b.decimal_val);
12,594✔
256
                default:
5,883✔
257
                    break;
5,883✔
258
            }
17,048,064✔
259
            break;
5,883✔
260
        case type_String:
5,223,669✔
261
            if (b.get_type() == type_String)
5,223,669✔
262
                return compare_string(get<StringData>(), b.get<StringData>());
5,210,895✔
263
            break;
12,774✔
264
        case type_Binary:
25,413✔
265
            if (b.get_type() == type_Binary)
25,413✔
266
                return compare_binary(get<BinaryData>(), b.get<BinaryData>());
20,478✔
267
            break;
4,935✔
268
        case type_Float:
744,501✔
269
            switch (b.get_type()) {
744,501✔
270
                case type_Int:
10,221✔
271
                    return -compare_long_to_double(b.int_val, float_val);
10,221✔
272
                case type_Float:
720,447✔
273
                    return compare_float(float_val, b.float_val);
720,447✔
274
                case type_Double:
5,346✔
275
                    return compare_float(double(float_val), b.double_val);
5,346✔
276
                case type_Decimal:
4,089✔
277
                    return Decimal128(float_val).compare(b.decimal_val);
4,089✔
278
                default:
4,398✔
279
                    break;
4,398✔
280
            }
744,501✔
281
            break;
4,398✔
282
        case type_Double:
975,399✔
283
            switch (b.get_type()) {
975,399✔
284
                case type_Int:
108,399✔
285
                    return -compare_long_to_double(b.int_val, double_val);
108,399✔
286
                case type_Float:
6,219✔
287
                    return compare_float(double_val, double(b.float_val));
6,219✔
288
                case type_Double:
842,175✔
289
                    return compare_float(double_val, b.double_val);
842,175✔
290
                case type_Decimal:
13,182✔
291
                    return Decimal128(double_val).compare(b.decimal_val);
13,182✔
292
                default:
5,439✔
293
                    break;
5,439✔
294
            }
975,399✔
295
            break;
5,439✔
296
        case type_Timestamp:
559,512✔
297
            if (b.get_type() == type_Timestamp) {
559,512✔
298
                return compare_generic(date_val, b.date_val);
559,098✔
299
            }
559,098✔
300
            break;
414✔
301
        case type_ObjectId:
180,453✔
302
            if (b.get_type() == type_ObjectId) {
180,453✔
303
                return compare_generic(id_val, b.id_val);
174,021✔
304
            }
174,021✔
305
            break;
6,432✔
306
        case type_Decimal:
485,355✔
307
            switch (b.get_type()) {
485,355✔
308
                case type_Int:
21,918✔
309
                    return decimal_val.compare(Decimal128(b.int_val));
21,918✔
310
                case type_Float:
4,557✔
311
                    return decimal_val.compare(Decimal128(b.float_val));
4,557✔
312
                case type_Double:
13,221✔
313
                    return decimal_val.compare(Decimal128(b.double_val));
13,221✔
314
                case type_Decimal:
441,528✔
315
                    return decimal_val.compare(b.decimal_val);
441,528✔
316
                default:
4,128✔
317
                    break;
4,128✔
318
            }
485,355✔
319
            break;
4,128✔
320
        case type_Link:
6,531✔
321
            if (b.get_type() == type_Link) {
6,531✔
322
                return compare_generic(int_val, b.int_val);
6,531✔
323
            }
6,531✔
324
            break;
×
325
        case type_TypedLink:
182,592✔
326
            if (b.is_type(type_TypedLink)) {
182,592✔
327
                return compare_generic(link_val, b.link_val);
182,478✔
328
            }
182,478✔
329
            break;
114✔
330
        case type_UUID:
329,682✔
331
            if (b.get_type() == type_UUID) {
329,682✔
332
                return compare_generic(uuid_val, b.uuid_val);
327,162✔
333
            }
327,162✔
334
            break;
2,520✔
335
        default:
70,554✔
336
            if (type == type_TypeOfValue && b.get_type() == type_TypeOfValue) {
70,554✔
337
                return TypeOfValue(int_val).matches(TypeOfValue(b.int_val)) ? 0 : compare_generic(int_val, b.int_val);
67,188✔
338
            }
67,188✔
339
            if ((type == type_List || type == type_Dictionary || type == type_Set)) {
3,366!
340
                return m_type == b.m_type ? 0 : m_type < b.m_type ? -1 : 1;
3,366✔
341
            }
3,366✔
342
            REALM_ASSERT_RELEASE(false && "Compare not supported for this column type");
×
343
            break;
×
344
    }
26,890,788✔
345

346
    // Comparing rank of types as a fallback makes it possible to sort of a list of Mixed
347
    REALM_ASSERT(sorting_rank[m_type] != sorting_rank[b.m_type]);
46,188✔
348
    // Using rank table will ensure that all numeric values are kept together
349
    return (sorting_rank[m_type] > sorting_rank[b.m_type]) ? 1 : -1;
46,188✔
350

351
    // Observe! Changing this function breaks the file format for Set<Mixed> and the StringIndex
352
}
26,890,788✔
353

354
template <>
355
int64_t Mixed::export_to_type() const noexcept
356
{
36,396,027✔
357
    // If the common type is Int, then both values must be Int
358
    REALM_ASSERT(get_type() == type_Int);
36,396,027✔
359
    return int_val;
36,396,027✔
360
}
36,396,027✔
361

362
template <>
363
float Mixed::export_to_type() const noexcept
364
{
1,452✔
365
    // If the common type is Float, then values must be either Int or Float
366
    REALM_ASSERT(m_type);
1,452✔
367
    switch (get_type()) {
1,452✔
368
        case type_Int:
678✔
369
            return float(int_val);
678✔
370
        case type_Float:
774✔
371
            return float_val;
774✔
372
        default:
✔
373
            REALM_ASSERT(false);
×
374
            break;
×
375
    }
1,452✔
376
    return 0.;
×
377
}
1,452✔
378

379
template <>
380
double Mixed::export_to_type() const noexcept
381
{
2,592✔
382
    // If the common type is Double, then values must be either Int, Float or Double
383
    REALM_ASSERT(m_type);
2,592✔
384
    switch (get_type()) {
2,592✔
385
        case type_Int:
1,296✔
386
            return double(int_val);
1,296✔
387
        case type_Float:
✔
388
            return double(float_val);
×
389
        case type_Double:
1,296✔
390
            return double_val;
1,296✔
391
        default:
✔
392
            REALM_ASSERT(false);
×
393
            break;
×
394
    }
2,592✔
395
    return 0.;
×
396
}
2,592✔
397

398
template <>
399
Decimal128 Mixed::export_to_type() const noexcept
400
{
192✔
401
    REALM_ASSERT(m_type);
192✔
402
    switch (get_type()) {
192✔
403
        case type_Int:
96✔
404
            return Decimal128(int_val);
96✔
405
        case type_Float:
✔
406
            return Decimal128(float_val);
×
407
        case type_Double:
✔
408
            return Decimal128(double_val);
×
409
        case type_Decimal:
96✔
410
            return decimal_val;
96✔
411
        default:
✔
412
            REALM_ASSERT(false);
×
413
            break;
×
414
    }
192✔
415
    return {};
×
416
}
192✔
417

418
template <>
419
StringData Mixed::export_to_type() const noexcept
420
{
1,476✔
421
    REALM_ASSERT(m_type);
1,476✔
422
    if (is_type(type_String)) {
1,476✔
423
        return string_val;
1,440✔
424
    }
1,440✔
425
    if (is_type(type_Binary)) {
36✔
426
        return StringData(binary_val.data(), binary_val.size());
36✔
427
    }
36✔
428
    return {};
×
429
}
36✔
430

431
template <>
432
BinaryData Mixed::export_to_type() const noexcept
433
{
3,036✔
434
    REALM_ASSERT(m_type);
3,036✔
435
    if (is_type(type_String)) {
3,036✔
436
        return BinaryData(string_val.data(), string_val.size());
2,508✔
437
    }
2,508✔
438
    if (is_type(type_Binary)) {
528✔
439
        return binary_val;
528✔
440
    }
528✔
441
    return {};
×
442
}
528✔
443

444
template <>
445
util::Optional<int64_t> Mixed::get<util::Optional<int64_t>>() const noexcept
446
{
72✔
447
    if (is_null()) {
72✔
448
        return {};
×
449
    }
×
450
    return get<int64_t>();
72✔
451
}
72✔
452

453
template <>
454
util::Optional<bool> Mixed::get<util::Optional<bool>>() const noexcept
455
{
48✔
456
    if (is_null()) {
48✔
457
        return {};
×
458
    }
×
459
    return get<bool>();
48✔
460
}
48✔
461

462
template <>
463
util::Optional<float> Mixed::get<util::Optional<float>>() const noexcept
464
{
72✔
465
    if (is_null()) {
72✔
466
        return {};
×
467
    }
×
468
    return get<float>();
72✔
469
}
72✔
470

471
template <>
472
util::Optional<double> Mixed::get<util::Optional<double>>() const noexcept
473
{
72✔
474
    if (is_null()) {
72✔
475
        return {};
×
476
    }
×
477
    return get<double>();
72✔
478
}
72✔
479

480
template <>
481
util::Optional<ObjectId> Mixed::get<util::Optional<ObjectId>>() const noexcept
482
{
48✔
483
    if (is_null()) {
48✔
484
        return {};
×
485
    }
×
486
    return get<ObjectId>();
48✔
487
}
48✔
488

489
template <>
490
util::Optional<UUID> Mixed::get<util::Optional<UUID>>() const noexcept
491
{
48✔
492
    if (is_null()) {
48✔
493
        return {};
×
494
    }
×
495
    return get<UUID>();
48✔
496
}
48✔
497

498
static DataType get_common_type(DataType t1, DataType t2) noexcept
499
{
18,200,175✔
500
    // It might be by accident that this works, but it finds the most advanced type
501
    DataType common = std::max(t1, t2);
18,200,175✔
502
    return common;
18,200,175✔
503
}
18,200,175✔
504

505
Mixed Mixed::operator+(const Mixed& rhs) const noexcept
506
{
9,340,761✔
507
    if (!is_null() && !rhs.is_null()) {
9,340,761✔
508
        auto common_type = get_common_type(get_type(), rhs.get_type());
9,340,677✔
509
        switch (common_type) {
9,340,677✔
510
            case type_Int:
9,340,143✔
511
                return export_to_type<Int>() + rhs.export_to_type<Int>();
9,340,143✔
512
            case type_Float:
372✔
513
                return export_to_type<float>() + rhs.export_to_type<float>();
372✔
514
            case type_Double:
114✔
515
                return export_to_type<double>() + rhs.export_to_type<double>();
114✔
516
            case type_Decimal:
✔
517
                return export_to_type<Decimal128>() + rhs.export_to_type<Decimal128>();
×
518
            default:
48✔
519
                break;
48✔
520
        }
9,340,677✔
521
    }
9,340,677✔
522
    return {};
132✔
523
}
9,340,761✔
524

525
Mixed Mixed::operator-(const Mixed& rhs) const noexcept
526
{
4,919,421✔
527
    if (!is_null() && !rhs.is_null()) {
4,919,421✔
528
        auto common_type = get_common_type(get_type(), rhs.get_type());
4,919,421✔
529
        switch (common_type) {
4,919,421✔
530
            case type_Int:
4,919,325✔
531
                return export_to_type<Int>() - rhs.export_to_type<Int>();
4,919,325✔
532
            case type_Float:
96✔
533
                return export_to_type<float>() - rhs.export_to_type<float>();
96✔
534
            case type_Double:
✔
535
                return export_to_type<double>() - rhs.export_to_type<double>();
×
536
            case type_Decimal:
✔
537
                return export_to_type<Decimal128>() - rhs.export_to_type<Decimal128>();
×
538
            default:
✔
539
                break;
×
540
        }
4,919,421✔
541
    }
4,919,421✔
UNCOV
542
    return {};
×
543
}
4,919,421✔
544

545
Mixed Mixed::operator*(const Mixed& rhs) const noexcept
546
{
1,970,655✔
547
    if (!is_null() && !rhs.is_null()) {
1,970,655✔
548
        auto common_type = get_common_type(get_type(), rhs.get_type());
1,970,655✔
549
        switch (common_type) {
1,970,655✔
550
            case type_Int:
1,969,740✔
551
                return export_to_type<Int>() * rhs.export_to_type<Int>();
1,969,740✔
552
            case type_Float:
✔
553
                return export_to_type<float>() * rhs.export_to_type<float>();
×
554
            case type_Double:
924✔
555
                return export_to_type<double>() * rhs.export_to_type<double>();
924✔
556
            case type_Decimal:
✔
557
                return export_to_type<Decimal128>() * rhs.export_to_type<Decimal128>();
×
558
            default:
✔
559
                break;
×
560
        }
1,970,655✔
561
    }
1,970,655✔
UNCOV
562
    return {};
×
563
}
1,970,655✔
564

565
Mixed Mixed::operator/(const Mixed& rhs) const noexcept
566
{
1,969,428✔
567
    if (!is_null() && !rhs.is_null()) {
1,969,428✔
568
        auto common_type = get_common_type(get_type(), rhs.get_type());
1,969,428✔
569
        switch (common_type) {
1,969,428✔
570
            case type_Int: {
1,968,816✔
571
                auto dividend = export_to_type<Int>();
1,968,816✔
572
                auto divisor = rhs.export_to_type<Int>();
1,968,816✔
573
                // We don't want to throw here. This is usually used as part of a query
574
                // and in this case we would just expect a no match
575
                if (divisor == 0)
1,968,816✔
576
                    return dividend < 0 ? std::numeric_limits<int64_t>::min() : std::numeric_limits<int64_t>::max();
624✔
577
                return dividend / divisor;
1,968,192✔
578
            }
1,968,816✔
579
            case type_Float:
258✔
580
                static_assert(std::numeric_limits<float>::is_iec559); // Infinity is supported
258✔
581
                return export_to_type<float>() / rhs.export_to_type<float>();
258✔
582
            case type_Double:
258✔
583
                static_assert(std::numeric_limits<double>::is_iec559); // Infinity is supported
258✔
584
                return export_to_type<double>() / rhs.export_to_type<double>();
258✔
585
            case type_Decimal:
96✔
586
                return export_to_type<Decimal128>() / rhs.export_to_type<Decimal128>();
96✔
587
            default:
✔
588
                break;
×
589
        }
1,969,428✔
590
    }
1,969,428✔
UNCOV
591
    return {};
×
592
}
1,969,428✔
593

594
size_t Mixed::hash() const
595
{
41,247✔
596
    if (is_null())
41,247✔
597
        return 0;
3,948✔
598

599
    size_t hash = 0;
37,299✔
600
    switch (get_type()) {
37,299✔
601
        case type_Int:
2,934✔
602
            hash = size_t(int_val);
2,934✔
603
            break;
2,934✔
604
        case type_Bool:
1,734✔
605
            hash = bool_val ? 0xdeadbeef : 0xcafebabe;
1,734✔
606
            break;
1,734✔
607
        case type_Float: {
✔
608
            auto unsigned_data = reinterpret_cast<const unsigned char*>(&float_val);
×
609
            hash = murmur2_or_cityhash(unsigned_data, sizeof(float));
×
610
            break;
×
611
        }
×
612
        case type_Double: {
5,838✔
613
            auto unsigned_data = reinterpret_cast<const unsigned char*>(&double_val);
5,838✔
614
            hash = murmur2_or_cityhash(unsigned_data, sizeof(double));
5,838✔
615
            break;
5,838✔
616
        }
×
617
        case type_String:
11,298✔
618
            hash = get<StringData>().hash();
11,298✔
619
            break;
11,298✔
620
        case type_Binary: {
4,440✔
621
            auto bin = get<BinaryData>();
4,440✔
622
            StringData str(bin.data(), bin.size());
4,440✔
623
            hash = str.hash();
4,440✔
624
            break;
4,440✔
625
        }
×
626
        case type_Timestamp:
2,838✔
627
            hash = get<Timestamp>().hash();
2,838✔
628
            break;
2,838✔
629
        case type_ObjectId:
2,739✔
630
            hash = get<ObjectId>().hash();
2,739✔
631
            break;
2,739✔
632
        case type_UUID: {
2,739✔
633
            hash = get<UUID>().hash();
2,739✔
634
            break;
2,739✔
635
        }
×
636
        case type_TypedLink: {
✔
637
            auto unsigned_data = reinterpret_cast<const unsigned char*>(&link_val);
×
638
            hash = murmur2_or_cityhash(unsigned_data, 12);
×
639
            break;
×
640
        }
×
641
        case type_Decimal: {
2,739✔
642
            auto value = get<Decimal128>();
2,739✔
643
            auto unsigned_data = reinterpret_cast<const unsigned char*>(value.raw());
2,739✔
644
            hash = murmur2_or_cityhash(unsigned_data, sizeof(Decimal128::Bid128));
2,739✔
645
            break;
2,739✔
646
        }
×
647
        case type_Mixed:
✔
648
        case type_Link:
✔
649
            REALM_ASSERT_RELEASE(false && "Hash not supported for this column type");
×
650
            break;
×
651
    }
37,299✔
652

653
    return hash;
37,299✔
654
}
37,299✔
655

656
StringData Mixed::get_index_data(std::array<char, 16>& buffer) const noexcept
657
{
6,997,476✔
658
    if (is_null()) {
6,997,476✔
659
        return {};
459,891✔
660
    }
459,891✔
661
    auto type = get_type();
6,537,585✔
662
    switch (type) {
6,537,585✔
663
        case type_Int: {
3,450,852✔
664
            int64_t i = get_int();
3,450,852✔
665
            const char* c = reinterpret_cast<const char*>(&i);
3,450,852✔
666
            realm::safe_copy_n(c, sizeof(int64_t), buffer.data());
3,450,852✔
667
            return StringData{buffer.data(), sizeof(int64_t)};
3,450,852✔
668
        }
×
669
        case type_Bool: {
36,432✔
670
            int64_t i = get_bool() ? 1 : 0;
36,432✔
671
            return Mixed(i).get_index_data(buffer);
36,432✔
672
        }
×
673
        case type_Float: {
24✔
674
            auto v2 = get_float();
24✔
675
            int i = int(v2);
24✔
676
            if (i == v2) {
24✔
677
                return Mixed(i).get_index_data(buffer);
×
678
            }
×
679
            const char* src = reinterpret_cast<const char*>(&v2);
24✔
680
            realm::safe_copy_n(src, sizeof(float), buffer.data());
24✔
681
            return StringData{buffer.data(), sizeof(float)};
24✔
682
        }
24✔
683
        case type_Double: {
504✔
684
            auto v2 = get_double();
504✔
685
            int i = int(v2);
504✔
686
            if (i == v2) {
504✔
687
                return Mixed(i).get_index_data(buffer);
480✔
688
            }
480✔
689
            const char* src = reinterpret_cast<const char*>(&v2);
24✔
690
            realm::safe_copy_n(src, sizeof(double), buffer.data());
24✔
691
            return StringData{buffer.data(), sizeof(double)};
24✔
692
        }
504✔
693
        case type_String:
2,629,353✔
694
            return get_string();
2,629,353✔
695
        case type_Binary: {
72✔
696
            auto bin = get_binary();
72✔
697
            return {bin.data(), bin.size()};
72✔
698
        }
504✔
699
        case type_Timestamp: {
52,365✔
700
            auto dt = get<Timestamp>();
52,365✔
701
            int64_t s = dt.get_seconds();
52,365✔
702
            int32_t ns = dt.get_nanoseconds();
52,365✔
703
            constexpr size_t index_size = sizeof(s) + sizeof(ns);
52,365✔
704
            const char* s_buf = reinterpret_cast<const char*>(&s);
52,365✔
705
            const char* ns_buf = reinterpret_cast<const char*>(&ns);
52,365✔
706
            realm::safe_copy_n(s_buf, sizeof(s), buffer.data());
52,365✔
707
            realm::safe_copy_n(ns_buf, sizeof(ns), buffer.data() + sizeof(s));
52,365✔
708
            return StringData{buffer.data(), index_size};
52,365✔
709
        }
504✔
710
        case type_ObjectId: {
339,981✔
711
            auto id = get<ObjectId>();
339,981✔
712
            memcpy(&buffer, &id, sizeof(ObjectId));
339,981✔
713
            return StringData{buffer.data(), sizeof(ObjectId)};
339,981✔
714
        }
504✔
715
        case type_Decimal: {
480✔
716
            auto v2 = this->get_decimal();
480✔
717
            int64_t i;
480✔
718
            if (v2.to_int(i)) {
480✔
719
                return Mixed(i).get_index_data(buffer);
468✔
720
            }
468✔
721
            const char* src = reinterpret_cast<const char*>(&v2);
12✔
722
            realm::safe_copy_n(src, sizeof(v2), buffer.data());
12✔
723
            return StringData{buffer.data(), sizeof(v2)};
12✔
724
        }
480✔
725
        case type_UUID: {
29,538✔
726
            auto id = get<UUID>();
29,538✔
727
            const auto bytes = id.to_bytes();
29,538✔
728
            std::copy_n(bytes.data(), bytes.size(), buffer.begin());
29,538✔
729
            return StringData{buffer.data(), bytes.size()};
29,538✔
730
        }
480✔
731
        case type_TypedLink: {
54✔
732
            auto link = get<ObjLink>();
54✔
733
            uint32_t k1 = link.get_table_key().value;
54✔
734
            int64_t k2 = link.get_obj_key().value;
54✔
735
            const char* src = reinterpret_cast<const char*>(&k1);
54✔
736
            realm::safe_copy_n(src, sizeof(k1), buffer.data());
54✔
737
            src = reinterpret_cast<const char*>(&k2);
54✔
738
            realm::safe_copy_n(src, sizeof(k2), buffer.data() + sizeof(k1));
54✔
739
            return StringData{buffer.data(), sizeof(k1) + sizeof(k2)};
54✔
740
        }
480✔
741
        case type_Mixed:
✔
742
        case type_Link:
✔
743
            break;
×
744
        default:
12✔
745
            if (type == type_Dictionary) {
12✔
746
                return "__Dictionary__";
12✔
747
            }
12✔
748
            if (type == type_List) {
×
749
                return "__List__";
×
750
            }
×
751
            break;
×
752
    }
6,537,585✔
753
    REALM_ASSERT_RELEASE(false && "Index not supported for this column type");
×
754
    return {};
×
755
}
6,537,585✔
756

757
std::string Mixed::to_string(size_t max_size) const noexcept
758
{
42✔
759
    std::ostringstream ostr;
42✔
760
    if (is_type(type_String)) {
42✔
761
        std::string ret = "\"";
12✔
762
        if (string_val.size() <= max_size) {
12✔
763
            ret += std::string(string_val);
6✔
764
        }
6✔
765
        else {
6✔
766
            ret += std::string(StringData(string_val.data(), max_size)) + " ...";
6✔
767
        }
6✔
768
        ret += "\"";
12✔
769
        return ret;
12✔
770
    }
12✔
771
    else if (is_type(type_Binary)) {
30✔
772
        static constexpr int size_one_hex_out = 3;
6✔
773
        static char hex_chars[] = "0123456789ABCDEF";
6✔
774
        auto out_hex = [&ostr](char c) {
156✔
775
            ostr << hex_chars[c >> 4];
156✔
776
            ostr << hex_chars[c & 0xF];
156✔
777
            ostr << ' ';
156✔
778
        };
156✔
779

780
        auto sz = binary_val.size();
6✔
781
        bool capped = false;
6✔
782
        size_t out_size = 0;
6✔
783

784
        ostr << '"';
6✔
785
        for (size_t n = 0; n < sz; n++) {
162✔
786
            out_size += size_one_hex_out;
162✔
787
            if (out_size > max_size) {
162✔
788
                capped = true;
6✔
789
                break;
6✔
790
            }
6✔
791
            out_hex(binary_val[n]);
156✔
792
        }
156✔
793
        if (capped) {
6✔
794
            ostr << "...";
6✔
795
        }
6✔
796
        ostr << '"';
6✔
797
    }
6✔
798
    else if (is_type(type_Timestamp)) {
24✔
799
        std::array<char, 32> buffer{};
6✔
800
        return date_val.to_string(buffer);
6✔
801
    }
6✔
802
    else {
18✔
803
        ostr << *this;
18✔
804
    }
18✔
805
    return ostr.str();
24✔
806
}
42✔
807

808
void Mixed::use_buffer(std::string& buf) noexcept
809
{
56,442✔
810
    if (is_null()) {
56,442✔
811
        return;
14,826✔
812
    }
14,826✔
813
    switch (get_type()) {
41,616✔
814
        case type_String:
6,906✔
815
            buf = std::string(string_val);
6,906✔
816
            string_val = StringData(buf);
6,906✔
817
            break;
6,906✔
818
        case type_Binary:
246✔
819
            buf = std::string(binary_val);
246✔
820
            binary_val = BinaryData(buf);
246✔
821
            break;
246✔
822
        default:
34,464✔
823
            break;
34,464✔
824
    }
41,616✔
825
}
41,616✔
826

827
// LCOV_EXCL_START
828
std::ostream& operator<<(std::ostream& out, const Mixed& m)
829
{
955,200✔
830
    if (m.is_null()) {
955,200✔
831
        out << "null";
408✔
832
    }
408✔
833
    else {
954,792✔
834
        switch (m.get_type()) {
954,792✔
835
            case type_Int:
131,748✔
836
                out << m.int_val;
131,748✔
837
                break;
131,748✔
838
            case type_Bool:
1,356✔
839
                out << (m.bool_val ? "true" : "false");
1,356✔
840
                break;
1,356✔
841
            case type_Float:
1,596✔
842
                out << m.float_val;
1,596✔
843
                break;
1,596✔
844
            case type_Double:
2,310✔
845
                out << m.double_val;
2,310✔
846
                break;
2,310✔
847
            case type_String:
3,597✔
848
                out << util::serializer::print_value(m.string_val);
3,597✔
849
                break;
3,597✔
850
            case type_Binary:
1,188✔
851
                out << util::serializer::print_value(m.binary_val);
1,188✔
852
                break;
1,188✔
853
            case type_Timestamp:
1,530✔
854
                out << util::serializer::print_value(m.date_val);
1,530✔
855
                break;
1,530✔
856
            case type_Decimal:
1,986✔
857
                out << m.decimal_val;
1,986✔
858
                break;
1,986✔
859
            case type_ObjectId:
807,711✔
860
                out << util::serializer::print_value(m.id_val);
807,711✔
861
                break;
807,711✔
862
            case type_Link:
84✔
863
                out << util::serializer::print_value(ObjKey(m.int_val));
84✔
864
                break;
84✔
865
            case type_TypedLink:
✔
866
                out << util::serializer::print_value(m.link_val);
×
867
                break;
×
868
            case type_UUID:
1,674✔
869
                out << util::serializer::print_value(m.uuid_val);
1,674✔
870
                break;
1,674✔
871
            case type_Mixed:
✔
872
                REALM_FALLTHROUGH;
×
873
            default:
12✔
874
                if (m.is_type(type_List)) {
12✔
875
                    out << "list";
6✔
876
                }
6✔
877
                else if (m.is_type(type_Set)) {
6✔
878
                    out << "set";
×
879
                }
×
880
                else if (m.is_type(type_Dictionary)) {
6✔
881
                    out << "dictionary";
6✔
882
                }
6✔
883
                break;
12✔
884
        }
954,792✔
885
    }
954,792✔
886
    return out;
955,200✔
887
}
955,200✔
888
// LCOV_EXCL_STOP
889

890

891
} // 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