• 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

85.64
/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
{
11,248,038✔
62
    if (a == b)
11,248,038✔
63
        return 0;
9,878,751✔
64
    return utf8_compare(a, b) ? -1 : 1;
1,369,287✔
65
}
1,369,287✔
66

67
int compare_binary(BinaryData a, BinaryData b)
68
{
19,008✔
69
    size_t asz = a.size();
19,008✔
70
    size_t bsz = b.size();
19,008✔
71
    size_t min_sz = std::min(asz, bsz);
19,008✔
72
    int ret = memcmp(a.data(), b.data(), min_sz);
19,008✔
73
    if (ret == 0) {
19,008✔
74
        if (asz > bsz)
11,457✔
75
            ret = 1;
513✔
76
        else if (asz < bsz)
10,944✔
77
            ret = -1;
546✔
78
    }
11,457✔
79
    return ret;
19,008✔
80
}
19,008✔
81

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

101
template <typename Float>
102
int compare_float(Float a_raw, Float b_raw)
103
{
1,909,881✔
104
    bool a_nan = std::isnan(a_raw);
1,909,881✔
105
    bool b_nan = std::isnan(b_raw);
1,909,881✔
106
    if (!a_nan && !b_nan) {
1,909,881✔
107
        // Just compare as IEEE floats
866,733✔
108
        return a_raw == b_raw ? 0 : a_raw < b_raw ? -1 : 1;
1,453,458✔
109
    }
1,683,105✔
110
    if (a_nan && b_nan) {
226,776✔
111
        // Compare the nan values as unsigned
19,098✔
112
        using IntType = typename IntTypeForSize<sizeof(Float)>::type;
38,280✔
113
        IntType a = 0, b = 0;
38,280✔
114
        memcpy(&a, &a_raw, sizeof(Float));
38,280✔
115
        memcpy(&b, &b_raw, sizeof(Float));
38,280✔
116
        return a == b ? 0 : a < b ? -1 : 1;
29,112✔
117
    }
38,280✔
118
    // One is nan, the other is not
94,926✔
119
    // nans are treated as being less than all non-nan values
94,926✔
120
    return a_nan ? -1 : 1;
188,496✔
121
}
188,496✔
122

123
template <typename T>
124
int compare_generic(T lhs, T rhs)
125
{
36,821,712✔
126
    return lhs == rhs ? 0 : lhs < rhs ? -1 : 1;
35,628,372✔
127
}
36,821,712✔
128

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

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

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

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

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

167
bool Mixed::types_are_comparable(const Mixed& lhs, const Mixed& rhs)
168
{
2,857,815✔
169
    if (lhs.m_type == rhs.m_type)
2,857,815✔
170
        return lhs.m_type != 0;
2,136,588✔
171

370,209✔
172
    if (lhs.is_null() || rhs.is_null())
721,227✔
173
        return false;
341,349✔
174

199,515✔
175
    DataType l_type = lhs.get_type();
379,878✔
176
    DataType r_type = rhs.get_type();
379,878✔
177
    return data_types_are_comparable(l_type, r_type);
379,878✔
178
}
379,878✔
179

180
bool Mixed::data_types_are_comparable(DataType l_type, DataType r_type)
181
{
1,208,316✔
182
    if (l_type == r_type)
1,208,316✔
183
        return true;
799,158✔
184

214,827✔
185
    if (is_numeric(l_type, r_type)) {
409,158✔
186
        return true;
350,679✔
187
    }
350,679✔
188
    if (l_type == type_Mixed || r_type == type_Mixed) {
58,479✔
189
        return true; // Mixed is comparable with any type
786✔
190
    }
786✔
191
    return false;
57,693✔
192
}
57,693✔
193

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

226
int Mixed::compare(const Mixed& b) const noexcept
227
{
51,610,605✔
228
    // Observe! Changing this function breaks the file format for Set<Mixed>
25,845,654✔
229

25,845,654✔
230
    if (is_null()) {
51,610,605✔
231
        return b.is_null() ? 0 : -1;
734,820✔
232
    }
885,081✔
233
    if (b.is_null())
50,725,524✔
234
        return 1;
376,320✔
235

25,215,108✔
236
    // None is null
25,215,108✔
237
    auto type = get_type();
50,349,204✔
238
    switch (type) {
50,349,204✔
239
        case type_Bool: {
1,254,537✔
240
            if (b.get_type() == type_Bool) {
1,254,537✔
241
                return compare_generic(bool_val, b.bool_val);
1,253,748✔
242
            }
1,253,748✔
243
            break;
789✔
244
        }
789✔
245
        case type_Int:
34,659,777✔
246
            switch (b.get_type()) {
34,659,777✔
247
                case type_Int:
34,411,722✔
248
                    return compare_generic(int_val, b.int_val);
34,411,722✔
249
                case type_Float:
79,770✔
250
                    return compare_long_to_double(int_val, b.float_val);
79,770✔
251
                case type_Double:
156,714✔
252
                    return compare_long_to_double(int_val, b.double_val);
156,714✔
253
                case type_Decimal:
5,088✔
254
                    return Decimal128(int_val).compare(b.decimal_val);
5,088✔
255
                default:
7,617✔
256
                    break;
7,617✔
257
            }
7,617✔
258
            break;
7,617✔
259
        case type_String:
11,241,066✔
260
            if (b.get_type() == type_String)
11,241,066✔
261
                return compare_string(get<StringData>(), b.get<StringData>());
11,226,435✔
262
            break;
14,631✔
263
        case type_Binary:
23,304✔
264
            if (b.get_type() == type_Binary)
23,304✔
265
                return compare_binary(get<BinaryData>(), b.get<BinaryData>());
19,008✔
266
            break;
4,296✔
267
        case type_Float:
743,646✔
268
            switch (b.get_type()) {
743,646✔
269
                case type_Int:
10,221✔
270
                    return -compare_long_to_double(b.int_val, float_val);
10,221✔
271
                case type_Float:
719,802✔
272
                    return compare_float(float_val, b.float_val);
719,802✔
273
                case type_Double:
5,343✔
274
                    return compare_float(double(float_val), b.double_val);
5,343✔
275
                case type_Decimal:
4,095✔
276
                    return Decimal128(float_val).compare(b.decimal_val);
4,095✔
277
                default:
4,386✔
278
                    break;
4,386✔
279
            }
4,386✔
280
            break;
4,386✔
281
        case type_Double:
949,650✔
282
            switch (b.get_type()) {
949,650✔
283
                case type_Int:
100,116✔
284
                    return -compare_long_to_double(b.int_val, double_val);
100,116✔
285
                case type_Float:
6,225✔
286
                    return compare_float(double_val, double(b.float_val));
6,225✔
287
                case type_Double:
832,443✔
288
                    return compare_float(double_val, b.double_val);
832,443✔
289
                case type_Decimal:
5,685✔
290
                    return Decimal128(double_val).compare(b.decimal_val);
5,685✔
291
                default:
5,379✔
292
                    break;
5,379✔
293
            }
5,379✔
294
            break;
5,379✔
295
        case type_Timestamp:
596,889✔
296
            if (b.get_type() == type_Timestamp) {
596,889✔
297
                return compare_generic(date_val, b.date_val);
596,523✔
298
            }
596,523✔
299
            break;
366✔
300
        case type_ObjectId:
90,795✔
301
            if (b.get_type() == type_ObjectId) {
90,795✔
302
                return compare_generic(id_val, b.id_val);
84,411✔
303
            }
84,411✔
304
            break;
6,384✔
305
        case type_Decimal:
456,015✔
306
            switch (b.get_type()) {
456,015✔
307
                case type_Int:
13,665✔
308
                    return decimal_val.compare(Decimal128(b.int_val));
13,665✔
309
                case type_Float:
4,557✔
310
                    return decimal_val.compare(Decimal128(b.float_val));
4,557✔
311
                case type_Double:
5,655✔
312
                    return decimal_val.compare(Decimal128(b.double_val));
5,655✔
313
                case type_Decimal:
428,010✔
314
                    return decimal_val.compare(b.decimal_val);
428,010✔
315
                default:
4,128✔
316
                    break;
4,128✔
317
            }
4,128✔
318
            break;
4,128✔
319
        case type_Link:
5,541✔
320
            if (b.get_type() == type_Link) {
5,541✔
321
                return compare_generic(int_val, b.int_val);
5,541✔
322
            }
5,541✔
UNCOV
323
            break;
×
324
        case type_TypedLink:
110,706✔
325
            if (b.is_type(type_TypedLink)) {
110,706✔
326
                return compare_generic(link_val, b.link_val);
110,580✔
327
            }
110,580✔
328
            break;
126✔
329
        case type_UUID:
320,691✔
330
            if (b.get_type() == type_UUID) {
320,691✔
331
                return compare_generic(uuid_val, b.uuid_val);
318,213✔
332
            }
318,213✔
333
            break;
2,478✔
334
        default:
62,334✔
335
            if (type == type_TypeOfValue && b.get_type() == type_TypeOfValue) {
62,334✔
336
                return TypeOfValue(int_val).matches(TypeOfValue(b.int_val)) ? 0 : compare_generic(int_val, b.int_val);
51,672✔
337
            }
61,548✔
338
            if ((type == type_List || type == type_Dictionary || type == type_Set)) {
786✔
339
                return m_type == b.m_type ? 0 : m_type < b.m_type ? -1 : 1;
693✔
340
            }
786✔
UNCOV
341
            REALM_ASSERT_RELEASE(false && "Compare not supported for this column type");
×
UNCOV
342
            break;
×
343
    }
45,750✔
344

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

22,395✔
350
    // Observe! Changing this function breaks the file format for Set<Mixed>
22,395✔
351
}
45,750✔
352

353
int Mixed::compare_signed(const Mixed& b) const noexcept
354
{
22,773✔
355
    if (is_type(type_String) && b.is_type(type_String)) {
22,773✔
356
        auto a_val = get_string();
22,605✔
357
        auto b_val = b.get_string();
22,605✔
358
        return a_val == b_val ? 0 : a_val < b_val ? -1 : 1;
22,593✔
359
    }
22,605✔
360
    return compare(b);
168✔
361
}
168✔
362

363
template <>
364
int64_t Mixed::export_to_type() const noexcept
365
{
16,655,484✔
366
    // If the common type is Int, then both values must be Int
10,505,130✔
367
    REALM_ASSERT(get_type() == type_Int);
16,655,484✔
368
    return int_val;
16,655,484✔
369
}
16,655,484✔
370

371
template <>
372
float Mixed::export_to_type() const noexcept
373
{
1,452✔
374
    // If the common type is Float, then values must be either Int or Float
726✔
375
    REALM_ASSERT(m_type);
1,452✔
376
    switch (get_type()) {
1,452✔
377
        case type_Int:
678✔
378
            return float(int_val);
678✔
379
        case type_Float:
774✔
380
            return float_val;
774✔
381
        default:
✔
382
            REALM_ASSERT(false);
×
UNCOV
383
            break;
×
UNCOV
384
    }
×
UNCOV
385
    return 0.;
×
UNCOV
386
}
×
387

388
template <>
389
double Mixed::export_to_type() const noexcept
390
{
2,592✔
391
    // If the common type is Double, then values must be either Int, Float or Double
1,296✔
392
    REALM_ASSERT(m_type);
2,592✔
393
    switch (get_type()) {
2,592✔
394
        case type_Int:
1,296✔
395
            return double(int_val);
1,296✔
396
        case type_Float:
✔
397
            return double(float_val);
×
398
        case type_Double:
1,296✔
399
            return double_val;
1,296✔
400
        default:
✔
401
            REALM_ASSERT(false);
×
UNCOV
402
            break;
×
UNCOV
403
    }
×
UNCOV
404
    return 0.;
×
UNCOV
405
}
×
406

407
template <>
408
Decimal128 Mixed::export_to_type() const noexcept
409
{
192✔
410
    REALM_ASSERT(m_type);
192✔
411
    switch (get_type()) {
192✔
412
        case type_Int:
96✔
413
            return Decimal128(int_val);
96✔
UNCOV
414
        case type_Float:
✔
UNCOV
415
            return Decimal128(float_val);
×
416
        case type_Double:
✔
417
            return Decimal128(double_val);
×
418
        case type_Decimal:
96✔
419
            return decimal_val;
96✔
420
        default:
✔
421
            REALM_ASSERT(false);
×
UNCOV
422
            break;
×
UNCOV
423
    }
×
UNCOV
424
    return {};
×
UNCOV
425
}
×
426

427
template <>
428
StringData Mixed::export_to_type() const noexcept
429
{
1,476✔
430
    REALM_ASSERT(m_type);
1,476✔
431
    if (is_type(type_String)) {
1,476✔
432
        return string_val;
1,440✔
433
    }
1,440✔
434
    if (is_type(type_Binary)) {
36✔
435
        return StringData(binary_val.data(), binary_val.size());
36✔
436
    }
36✔
437
    return {};
×
UNCOV
438
}
×
439

440
template <>
441
BinaryData Mixed::export_to_type() const noexcept
442
{
1,296✔
443
    REALM_ASSERT(m_type);
1,296✔
444
    if (is_type(type_String)) {
1,296✔
445
        return BinaryData(string_val.data(), string_val.size());
768✔
446
    }
768✔
447
    if (is_type(type_Binary)) {
528✔
448
        return binary_val;
528✔
449
    }
528✔
UNCOV
450
    return {};
×
UNCOV
451
}
×
452

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

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

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

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

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

498
template <>
499
util::Optional<UUID> Mixed::get<util::Optional<UUID>>() const noexcept
500
{
96✔
501
    if (is_null()) {
96✔
UNCOV
502
        return {};
×
UNCOV
503
    }
×
504
    return get<UUID>();
96✔
505
}
96✔
506

507
static DataType get_common_type(DataType t1, DataType t2) noexcept
508
{
8,329,908✔
509
    // It might be by accident that this works, but it finds the most advanced type
5,253,648✔
510
    DataType common = std::max(t1, t2);
8,329,908✔
511
    return common;
8,329,908✔
512
}
8,329,908✔
513

514
Mixed Mixed::operator+(const Mixed& rhs) const noexcept
515
{
4,405,620✔
516
    if (!is_null() && !rhs.is_null()) {
4,405,620✔
517
        auto common_type = get_common_type(get_type(), rhs.get_type());
4,405,536✔
518
        switch (common_type) {
4,405,536✔
519
            case type_Int:
4,405,002✔
520
                return export_to_type<Int>() + rhs.export_to_type<Int>();
4,405,002✔
521
            case type_Float:
372✔
522
                return export_to_type<float>() + rhs.export_to_type<float>();
372✔
523
            case type_Double:
114✔
524
                return export_to_type<double>() + rhs.export_to_type<double>();
114✔
UNCOV
525
            case type_Decimal:
✔
UNCOV
526
                return export_to_type<Decimal128>() + rhs.export_to_type<Decimal128>();
×
527
            default:
48✔
528
                break;
48✔
529
        }
132✔
530
    }
132✔
531
    return {};
132✔
532
}
132✔
533

534
Mixed Mixed::operator-(const Mixed& rhs) const noexcept
535
{
2,177,676✔
536
    if (!is_null() && !rhs.is_null()) {
2,177,676✔
537
        auto common_type = get_common_type(get_type(), rhs.get_type());
2,177,676✔
538
        switch (common_type) {
2,177,676✔
539
            case type_Int:
2,177,580✔
540
                return export_to_type<Int>() - rhs.export_to_type<Int>();
2,177,580✔
541
            case type_Float:
96✔
542
                return export_to_type<float>() - rhs.export_to_type<float>();
96✔
UNCOV
543
            case type_Double:
✔
UNCOV
544
                return export_to_type<double>() - rhs.export_to_type<double>();
×
UNCOV
545
            case type_Decimal:
✔
UNCOV
546
                return export_to_type<Decimal128>() - rhs.export_to_type<Decimal128>();
×
UNCOV
547
            default:
✔
UNCOV
548
                break;
×
UNCOV
549
        }
×
UNCOV
550
    }
×
UNCOV
551
    return {};
×
UNCOV
552
}
×
553

554
Mixed Mixed::operator*(const Mixed& rhs) const noexcept
555
{
873,966✔
556
    if (!is_null() && !rhs.is_null()) {
873,966✔
557
        auto common_type = get_common_type(get_type(), rhs.get_type());
873,966✔
558
        switch (common_type) {
873,966✔
559
            case type_Int:
873,042✔
560
                return export_to_type<Int>() * rhs.export_to_type<Int>();
873,042✔
UNCOV
561
            case type_Float:
✔
UNCOV
562
                return export_to_type<float>() * rhs.export_to_type<float>();
×
563
            case type_Double:
924✔
564
                return export_to_type<double>() * rhs.export_to_type<double>();
924✔
UNCOV
565
            case type_Decimal:
✔
UNCOV
566
                return export_to_type<Decimal128>() * rhs.export_to_type<Decimal128>();
×
567
            default:
✔
568
                break;
×
569
        }
×
570
    }
×
571
    return {};
×
UNCOV
572
}
×
573

574
Mixed Mixed::operator/(const Mixed& rhs) const noexcept
575
{
872,730✔
576
    if (!is_null() && !rhs.is_null()) {
872,730✔
577
        auto common_type = get_common_type(get_type(), rhs.get_type());
872,730✔
578
        switch (common_type) {
872,730✔
579
            case type_Int: {
872,118✔
580
                auto dividend = export_to_type<Int>();
872,118✔
581
                auto divisor = rhs.export_to_type<Int>();
872,118✔
582
                // We don't want to throw here. This is usually used as part of a query
557,025✔
583
                // and in this case we would just expect a no match
557,025✔
584
                if (divisor == 0)
872,118✔
585
                    return dividend < 0 ? std::numeric_limits<int64_t>::min() : std::numeric_limits<int64_t>::max();
624✔
586
                return dividend / divisor;
871,494✔
587
            }
871,494✔
588
            case type_Float:
556,842✔
589
                static_assert(std::numeric_limits<float>::is_iec559); // Infinity is supported
258✔
590
                return export_to_type<float>() / rhs.export_to_type<float>();
258✔
591
            case type_Double:
556,842✔
592
                static_assert(std::numeric_limits<double>::is_iec559); // Infinity is supported
258✔
593
                return export_to_type<double>() / rhs.export_to_type<double>();
258✔
594
            case type_Decimal:
556,761✔
595
                return export_to_type<Decimal128>() / rhs.export_to_type<Decimal128>();
96✔
596
            default:
556,713✔
UNCOV
597
                break;
×
UNCOV
598
        }
×
UNCOV
599
    }
×
UNCOV
600
    return {};
×
UNCOV
601
}
×
602

603
size_t Mixed::hash() const
604
{
41,727✔
605
    if (is_null())
41,727✔
606
        return 0;
4,014✔
607

19,398✔
608
    size_t hash = 0;
37,713✔
609
    switch (get_type()) {
37,713✔
610
        case type_Int:
2,946✔
611
            hash = size_t(int_val);
2,946✔
612
            break;
2,946✔
613
        case type_Bool:
1,746✔
614
            hash = bool_val ? 0xdeadbeef : 0xcafebabe;
1,320✔
615
            break;
1,746✔
616
        case type_Float: {
✔
617
            auto unsigned_data = reinterpret_cast<const unsigned char*>(&float_val);
×
618
            hash = murmur2_or_cityhash(unsigned_data, sizeof(float));
×
619
            break;
×
UNCOV
620
        }
×
621
        case type_Double: {
5,862✔
622
            auto unsigned_data = reinterpret_cast<const unsigned char*>(&double_val);
5,862✔
623
            hash = murmur2_or_cityhash(unsigned_data, sizeof(double));
5,862✔
624
            break;
5,862✔
625
        }
×
626
        case type_String:
11,556✔
627
            hash = get<StringData>().hash();
11,556✔
628
            break;
11,556✔
629
        case type_Binary: {
4,500✔
630
            auto bin = get<BinaryData>();
4,500✔
631
            StringData str(bin.data(), bin.size());
4,500✔
632
            hash = str.hash();
4,500✔
633
            break;
4,500✔
UNCOV
634
        }
×
635
        case type_Timestamp:
2,850✔
636
            hash = get<Timestamp>().hash();
2,850✔
637
            break;
2,850✔
638
        case type_ObjectId:
2,751✔
639
            hash = get<ObjectId>().hash();
2,751✔
640
            break;
2,751✔
641
        case type_UUID: {
2,751✔
642
            hash = get<UUID>().hash();
2,751✔
643
            break;
2,751✔
UNCOV
644
        }
×
UNCOV
645
        case type_TypedLink: {
✔
UNCOV
646
            auto unsigned_data = reinterpret_cast<const unsigned char*>(&link_val);
×
647
            hash = murmur2_or_cityhash(unsigned_data, 12);
×
UNCOV
648
            break;
×
UNCOV
649
        }
×
650
        case type_Decimal: {
2,751✔
651
            auto value = get<Decimal128>();
2,751✔
652
            auto unsigned_data = reinterpret_cast<const unsigned char*>(value.raw());
2,751✔
653
            hash = murmur2_or_cityhash(unsigned_data, sizeof(Decimal128::Bid128));
2,751✔
654
            break;
2,751✔
UNCOV
655
        }
×
656
        case type_Mixed:
✔
657
        case type_Link:
✔
UNCOV
658
        case type_LinkList:
✔
UNCOV
659
            REALM_ASSERT_RELEASE(false && "Hash not supported for this column type");
×
UNCOV
660
            break;
×
661
    }
37,713✔
662

19,398✔
663
    return hash;
37,713✔
664
}
37,713✔
665

666
StringData Mixed::get_index_data(std::array<char, 16>& buffer) const noexcept
667
{
6,465,165✔
668
    if (is_null()) {
6,465,165✔
669
        return {};
455,952✔
670
    }
455,952✔
671
    switch (get_type()) {
6,009,213✔
672
        case type_Int: {
3,750,903✔
673
            int64_t i = get_int();
3,750,903✔
674
            const char* c = reinterpret_cast<const char*>(&i);
3,750,903✔
675
            realm::safe_copy_n(c, sizeof(int64_t), buffer.data());
3,750,903✔
676
            return StringData{buffer.data(), sizeof(int64_t)};
3,750,903✔
UNCOV
677
        }
×
678
        case type_Bool: {
34,929✔
679
            int64_t i = get_bool() ? 1 : 0;
26,406✔
680
            return Mixed(i).get_index_data(buffer);
34,929✔
UNCOV
681
        }
×
682
        case type_Float: {
24✔
683
            auto v2 = get_float();
24✔
684
            int i = int(v2);
24✔
685
            if (i == v2) {
24✔
UNCOV
686
                return Mixed(i).get_index_data(buffer);
×
UNCOV
687
            }
×
688
            const char* src = reinterpret_cast<const char*>(&v2);
24✔
689
            realm::safe_copy_n(src, sizeof(float), buffer.data());
24✔
690
            return StringData{buffer.data(), sizeof(float)};
24✔
691
        }
24✔
692
        case type_Double: {
48✔
693
            auto v2 = get_double();
48✔
694
            int i = int(v2);
48✔
695
            if (i == v2) {
48✔
696
                return Mixed(i).get_index_data(buffer);
24✔
697
            }
24✔
698
            const char* src = reinterpret_cast<const char*>(&v2);
24✔
699
            realm::safe_copy_n(src, sizeof(double), buffer.data());
24✔
700
            return StringData{buffer.data(), sizeof(double)};
24✔
701
        }
24✔
702
        case type_String:
1,295,670✔
703
            return get_string();
1,295,670✔
704
        case type_Binary: {
30✔
705
            auto bin = get_binary();
30✔
706
            return {bin.data(), bin.size()};
30✔
707
        }
24✔
708
        case type_Timestamp: {
47,271✔
709
            auto dt = get<Timestamp>();
47,271✔
710
            int64_t s = dt.get_seconds();
47,271✔
711
            int32_t ns = dt.get_nanoseconds();
47,271✔
712
            constexpr size_t index_size = sizeof(s) + sizeof(ns);
47,271✔
713
            const char* s_buf = reinterpret_cast<const char*>(&s);
47,271✔
714
            const char* ns_buf = reinterpret_cast<const char*>(&ns);
47,271✔
715
            realm::safe_copy_n(s_buf, sizeof(s), buffer.data());
47,271✔
716
            realm::safe_copy_n(ns_buf, sizeof(ns), buffer.data() + sizeof(s));
47,271✔
717
            return StringData{buffer.data(), index_size};
47,271✔
718
        }
24✔
719
        case type_ObjectId: {
858,378✔
720
            auto id = get<ObjectId>();
858,378✔
721
            memcpy(&buffer, &id, sizeof(ObjectId));
858,378✔
722
            return StringData{buffer.data(), sizeof(ObjectId)};
858,378✔
723
        }
24✔
724
        case type_Decimal: {
30✔
725
            auto v2 = this->get_decimal();
30✔
726
            int64_t i;
30✔
727
            if (v2.to_int(i)) {
30✔
728
                return Mixed(i).get_index_data(buffer);
18✔
729
            }
18✔
730
            const char* src = reinterpret_cast<const char*>(&v2);
12✔
731
            realm::safe_copy_n(src, sizeof(v2), buffer.data());
12✔
732
            return StringData{buffer.data(), sizeof(v2)};
12✔
733
        }
12✔
734
        case type_UUID: {
25,482✔
735
            auto id = get<UUID>();
25,482✔
736
            const auto bytes = id.to_bytes();
25,482✔
737
            std::copy_n(bytes.data(), bytes.size(), buffer.begin());
25,482✔
738
            return StringData{buffer.data(), bytes.size()};
25,482✔
739
        }
12✔
740
        case type_TypedLink: {
54✔
741
            auto link = get<ObjLink>();
54✔
742
            uint32_t k1 = link.get_table_key().value;
54✔
743
            int64_t k2 = link.get_obj_key().value;
54✔
744
            const char* src = reinterpret_cast<const char*>(&k1);
54✔
745
            realm::safe_copy_n(src, sizeof(k1), buffer.data());
54✔
746
            src = reinterpret_cast<const char*>(&k2);
54✔
747
            realm::safe_copy_n(src, sizeof(k2), buffer.data() + sizeof(k1));
54✔
748
            return StringData{buffer.data(), sizeof(k1) + sizeof(k2)};
54✔
749
        }
12✔
750
        case type_Mixed:
6✔
UNCOV
751
        case type_Link:
✔
UNCOV
752
        case type_LinkList:
✔
UNCOV
753
            break;
×
UNCOV
754
    }
×
UNCOV
755
    REALM_ASSERT_RELEASE(false && "Index not supported for this column type");
×
UNCOV
756
    return {};
×
UNCOV
757
}
×
758

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

3✔
782
        auto sz = binary_val.size();
6✔
783
        bool capped = false;
6✔
784
        size_t out_size = 0;
6✔
785

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

810
void Mixed::use_buffer(std::string& buf) noexcept
811
{
52,452✔
812
    if (is_null()) {
52,452✔
813
        return;
14,820✔
814
    }
14,820✔
815
    switch (get_type()) {
37,632✔
816
        case type_String:
7,272✔
817
            buf = std::string(string_val);
7,272✔
818
            string_val = StringData(buf);
7,272✔
819
            break;
7,272✔
820
        case type_Binary:
246✔
821
            buf = std::string(binary_val);
246✔
822
            binary_val = BinaryData(buf);
246✔
823
            break;
246✔
824
        default:
30,114✔
825
            break;
30,114✔
826
    }
37,632✔
827
}
37,632✔
828

829
// LCOV_EXCL_START
830
std::ostream& operator<<(std::ostream& out, const Mixed& m)
831
{
473,655✔
832
    if (m.is_null()) {
473,655✔
833
        out << "null";
1,044✔
834
    }
1,044✔
835
    else {
472,611✔
836
        switch (m.get_type()) {
472,611✔
837
            case type_Int:
305,517✔
838
                out << m.int_val;
305,517✔
839
                break;
305,517✔
840
            case type_Bool:
1,320✔
841
                out << (m.bool_val ? "true" : "false");
1,023✔
842
                break;
1,320✔
843
            case type_Float:
1,506✔
844
                out << m.float_val;
1,506✔
845
                break;
1,506✔
846
            case type_Double:
2,106✔
847
                out << m.double_val;
2,106✔
848
                break;
2,106✔
849
            case type_String:
13,524✔
850
                out << util::serializer::print_value(m.string_val);
13,524✔
851
                break;
13,524✔
852
            case type_Binary:
1,500✔
853
                out << util::serializer::print_value(m.binary_val);
1,500✔
854
                break;
1,500✔
855
            case type_Timestamp:
1,374✔
856
                out << util::serializer::print_value(m.date_val);
1,374✔
857
                break;
1,374✔
858
            case type_Decimal:
1,464✔
859
                out << m.decimal_val;
1,464✔
860
                break;
1,464✔
861
            case type_ObjectId:
138,126✔
862
                out << util::serializer::print_value(m.id_val);
138,126✔
863
                break;
138,126✔
864
            case type_Link:
3,744✔
865
                out << util::serializer::print_value(ObjKey(m.int_val));
3,744✔
866
                break;
3,744✔
867
            case type_TypedLink:
654✔
868
                out << util::serializer::print_value(m.link_val);
654✔
869
                break;
654✔
870
            case type_UUID:
1,758✔
871
                out << util::serializer::print_value(m.uuid_val);
1,758✔
872
                break;
1,758✔
UNCOV
873
            case type_Mixed:
✔
UNCOV
874
            case type_LinkList:
✔
UNCOV
875
                REALM_ASSERT(false);
×
876
            default:
18✔
877
                if (m.is_type(type_List)) {
18✔
878
                    out << "list";
6✔
879
                }
6✔
880
                else if (m.is_type(type_Set)) {
12✔
881
                    out << "set";
6✔
882
                }
6✔
883
                else if (m.is_type(type_Dictionary)) {
6✔
884
                    out << "dictionary";
6✔
885
                }
6✔
886
                break;
18✔
887
        }
473,655✔
888
    }
473,655✔
889
    return out;
473,655✔
890
}
473,655✔
891
// LCOV_EXCL_STOP
892

893

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

© 2026 Coveralls, Inc