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

realm / realm-core / github_pull_request_281750

30 Oct 2023 03:37PM UTC coverage: 90.528% (-1.0%) from 91.571%
github_pull_request_281750

Pull #6073

Evergreen

jedelbo
Log free space and history sizes when opening file
Pull Request #6073: Merge next-major

95488 of 175952 branches covered (0.0%)

8973 of 12277 new or added lines in 149 files covered. (73.09%)

622 existing lines in 51 files now uncovered.

233503 of 257934 relevant lines covered (90.53%)

6533720.56 hits per line

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

85.5
/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
{
4,795,266✔
62
    // Observe! Changing these values breaks the file format for Set<Mixed> and the StringIndex
2,403,525✔
63
    if (a == b)
4,795,266✔
64
        return 0;
3,252,726✔
65
    return a < b ? -1 : 1;
1,542,540✔
66
}
1,542,540✔
67

68
int compare_binary(BinaryData a, BinaryData b)
69
{
19,608✔
70
    size_t asz = a.size();
19,608✔
71
    size_t bsz = b.size();
19,608✔
72
    size_t min_sz = std::min(asz, bsz);
19,608✔
73
    int ret = memcmp(a.data(), b.data(), min_sz);
19,608✔
74
    if (ret == 0) {
19,608✔
75
        if (asz > bsz)
11,583✔
76
            ret = 1;
513✔
77
        else if (asz < bsz)
11,070✔
78
            ret = -1;
546✔
79
    }
11,583✔
80
    return ret;
19,608✔
81
}
19,608✔
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,899,690✔
105
    bool a_nan = std::isnan(a_raw);
1,899,690✔
106
    bool b_nan = std::isnan(b_raw);
1,899,690✔
107
    if (!a_nan && !b_nan) {
1,899,690✔
108
        // Just compare as IEEE floats
849,654✔
109
        return a_raw == b_raw ? 0 : a_raw < b_raw ? -1 : 1;
1,438,728✔
110
    }
1,672,443✔
111
    if (a_nan && b_nan) {
227,247✔
112
        // Compare the nan values as unsigned
19,098✔
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;
29,112✔
118
    }
38,280✔
119
    // One is nan, the other is not
95,106✔
120
    // nans are treated as being less than all non-nan values
95,106✔
121
    return a_nan ? -1 : 1;
188,967✔
122
}
188,967✔
123

124
template <typename T>
125
int compare_generic(T lhs, T rhs)
126
{
11,750,931✔
127
    return lhs == rhs ? 0 : lhs < rhs ? -1 : 1;
10,405,092✔
128
}
11,750,931✔
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
{
335,691✔
138
    // All Longs are > NaN
166,431✔
139
    if (std::isnan(rhs))
335,691✔
140
        return 1;
6✔
141

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

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

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

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

168
bool Mixed::types_are_comparable(const Mixed& lhs, const Mixed& rhs)
169
{
2,964,075✔
170
    if (lhs.m_type == rhs.m_type)
2,964,075✔
171
        return lhs.m_type != 0;
2,252,274✔
172

354,366✔
173
    if (lhs.is_null() || rhs.is_null())
711,801✔
174
        return false;
341,325✔
175

183,639✔
176
    DataType l_type = lhs.get_type();
370,476✔
177
    DataType r_type = rhs.get_type();
370,476✔
178
    return data_types_are_comparable(l_type, r_type);
370,476✔
179
}
370,476✔
180

181
bool Mixed::data_types_are_comparable(DataType l_type, DataType r_type)
182
{
1,181,595✔
183
    if (l_type == r_type)
1,181,595✔
184
        return true;
783,603✔
185

197,583✔
186
    if (is_numeric(l_type, r_type)) {
397,992✔
187
        return true;
339,576✔
188
    }
339,576✔
189
    if (l_type == type_Mixed || r_type == type_Mixed) {
58,416✔
190
        return true; // Mixed is comparable with any type
786✔
191
    }
786✔
192
    return false;
57,630✔
193
}
57,630✔
194

195
bool Mixed::accumulate_numeric_to(Decimal128& destination) const noexcept
196
{
27,876✔
197
    bool did_accumulate = false;
27,876✔
198
    if (!is_null()) {
27,876✔
199
        switch (get_type()) {
27,300✔
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,164✔
205
                destination += Decimal128(get_double());
1,164✔
206
                did_accumulate = true;
1,164✔
207
                break;
1,164✔
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
        }
27,876✔
223
    }
27,876✔
224
    return did_accumulate;
27,876✔
225
}
27,876✔
226

227
int Mixed::compare(const Mixed& b) const noexcept
228
{
20,096,898✔
229
    // Observe! Changing this function breaks the file format for Set<Mixed> and the StringIndex
9,987,117✔
230

9,987,117✔
231
    if (is_null()) {
20,096,898✔
232
        return b.is_null() ? 0 : -1;
614,481✔
233
    }
764,373✔
234
    if (b.is_null())
19,332,525✔
235
        return 1;
376,776✔
236

9,416,463✔
237
    // None is null
9,416,463✔
238
    auto type = get_type();
18,955,749✔
239
    switch (type) {
18,955,749✔
240
        case type_Bool: {
1,087,545✔
241
            if (b.get_type() == type_Bool) {
1,087,545✔
242
                return compare_generic(bool_val, b.bool_val);
1,086,849✔
243
            }
1,086,849✔
244
            break;
696✔
245
        }
696✔
246
        case type_Int:
9,820,332✔
247
            switch (b.get_type()) {
9,820,332✔
248
                case type_Int:
9,583,146✔
249
                    return compare_generic(int_val, b.int_val);
9,583,146✔
250
                case type_Float:
68,616✔
251
                    return compare_long_to_double(int_val, b.float_val);
68,616✔
252
                case type_Double:
156,741✔
253
                    return compare_long_to_double(int_val, b.double_val);
156,741✔
254
                case type_Decimal:
5,088✔
255
                    return Decimal128(int_val).compare(b.decimal_val);
5,088✔
256
                default:
7,632✔
257
                    break;
7,632✔
258
            }
7,632✔
259
            break;
7,632✔
260
        case type_String:
4,805,565✔
261
            if (b.get_type() == type_String)
4,805,565✔
262
                return compare_string(get<StringData>(), b.get<StringData>());
4,793,970✔
263
            break;
11,595✔
264
        case type_Binary:
24,363✔
265
            if (b.get_type() == type_Binary)
24,363✔
266
                return compare_binary(get<BinaryData>(), b.get<BinaryData>());
19,608✔
267
            break;
4,755✔
268
        case type_Float:
743,760✔
269
            switch (b.get_type()) {
743,760✔
270
                case type_Int:
10,221✔
271
                    return -compare_long_to_double(b.int_val, float_val);
10,221✔
272
                case type_Float:
719,727✔
273
                    return compare_float(float_val, b.float_val);
719,727✔
274
                case type_Double:
5,340✔
275
                    return compare_float(double(float_val), b.double_val);
5,340✔
276
                case type_Decimal:
4,092✔
277
                    return Decimal128(float_val).compare(b.decimal_val);
4,092✔
278
                default:
4,380✔
279
                    break;
4,380✔
280
            }
4,380✔
281
            break;
4,380✔
282
        case type_Double:
950,418✔
283
            switch (b.get_type()) {
950,418✔
284
                case type_Int:
100,128✔
285
                    return -compare_long_to_double(b.int_val, double_val);
100,128✔
286
                case type_Float:
6,231✔
287
                    return compare_float(double_val, double(b.float_val));
6,231✔
288
                case type_Double:
832,824✔
289
                    return compare_float(double_val, b.double_val);
832,824✔
290
                case type_Decimal:
5,685✔
291
                    return Decimal128(double_val).compare(b.decimal_val);
5,685✔
292
                default:
5,559✔
293
                    break;
5,559✔
294
            }
5,559✔
295
            break;
5,559✔
296
        case type_Timestamp:
541,008✔
297
            if (b.get_type() == type_Timestamp) {
541,008✔
298
                return compare_generic(date_val, b.date_val);
540,615✔
299
            }
540,615✔
300
            break;
393✔
301
        case type_ObjectId:
88,275✔
302
            if (b.get_type() == type_ObjectId) {
88,275✔
303
                return compare_generic(id_val, b.id_val);
81,861✔
304
            }
81,861✔
305
            break;
6,414✔
306
        case type_Decimal:
456,033✔
307
            switch (b.get_type()) {
456,033✔
308
                case type_Int:
13,671✔
309
                    return decimal_val.compare(Decimal128(b.int_val));
13,671✔
310
                case type_Float:
4,557✔
311
                    return decimal_val.compare(Decimal128(b.float_val));
4,557✔
312
                case type_Double:
5,649✔
313
                    return decimal_val.compare(Decimal128(b.double_val));
5,649✔
314
                case type_Decimal:
428,010✔
315
                    return decimal_val.compare(b.decimal_val);
428,010✔
316
                default:
4,146✔
317
                    break;
4,146✔
318
            }
4,146✔
319
            break;
4,146✔
320
        case type_Link:
5,541✔
321
            if (b.get_type() == type_Link) {
5,541✔
322
                return compare_generic(int_val, b.int_val);
5,541✔
323
            }
5,541✔
324
            break;
×
325
        case type_TypedLink:
95,127✔
326
            if (b.is_type(type_TypedLink)) {
95,127✔
327
                return compare_generic(link_val, b.link_val);
95,022✔
328
            }
95,022✔
329
            break;
105✔
330
        case type_UUID:
320,664✔
331
            if (b.get_type() == type_UUID) {
320,664✔
332
                return compare_generic(uuid_val, b.uuid_val);
318,141✔
333
            }
318,141✔
334
            break;
2,523✔
335
        default:
61,602✔
336
            if (type == type_TypeOfValue && b.get_type() == type_TypeOfValue) {
61,602✔
337
                return TypeOfValue(int_val).matches(TypeOfValue(b.int_val)) ? 0 : compare_generic(int_val, b.int_val);
51,672✔
338
            }
61,548✔
339
            if ((type == type_List || type == type_Dictionary || type == type_Set)) {
54✔
340
                return m_type == b.m_type ? 0 : m_type < b.m_type ? -1 : 1;
45✔
341
            }
54✔
342
            REALM_ASSERT_RELEASE(false && "Compare not supported for this column type");
×
343
            break;
×
344
    }
47,997✔
345

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

23,418✔
351
    // Observe! Changing this function breaks the file format for Set<Mixed> and the StringIndex
23,418✔
352
}
47,997✔
353

354
template <>
355
int64_t Mixed::export_to_type() const noexcept
356
{
22,732,536✔
357
    // If the common type is Int, then both values must be Int
8,864,502✔
358
    REALM_ASSERT(get_type() == type_Int);
22,732,536✔
359
    return int_val;
22,732,536✔
360
}
22,732,536✔
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
726✔
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
    }
×
376
    return 0.;
×
377
}
×
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
1,296✔
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
    }
×
395
    return 0.;
×
396
}
×
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
    }
×
415
    return {};
×
416
}
×
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✔
NEW
428
    return {};
×
NEW
429
}
×
430

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

444
template <>
445
util::Optional<int64_t> Mixed::get<util::Optional<int64_t>>() const noexcept
446
{
408✔
447
    if (is_null()) {
408✔
448
        return {};
×
449
    }
×
450
    return get<int64_t>();
408✔
451
}
408✔
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
{
120✔
465
    if (is_null()) {
120✔
466
        return {};
×
467
    }
×
468
    return get<float>();
120✔
469
}
120✔
470

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

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

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

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

505
Mixed Mixed::operator+(const Mixed& rhs) const noexcept
506
{
5,924,883✔
507
    if (!is_null() && !rhs.is_null()) {
5,924,883✔
508
        auto common_type = get_common_type(get_type(), rhs.get_type());
5,924,799✔
509
        switch (common_type) {
5,924,799✔
510
            case type_Int:
5,924,265✔
511
                return export_to_type<Int>() + rhs.export_to_type<Int>();
5,924,265✔
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
        }
132✔
521
    }
132✔
522
    return {};
132✔
523
}
132✔
524

525
Mixed Mixed::operator-(const Mixed& rhs) const noexcept
526
{
3,021,711✔
527
    if (!is_null() && !rhs.is_null()) {
3,021,711✔
528
        auto common_type = get_common_type(get_type(), rhs.get_type());
3,021,711✔
529
        switch (common_type) {
3,021,711✔
530
            case type_Int:
3,021,615✔
531
                return export_to_type<Int>() - rhs.export_to_type<Int>();
3,021,615✔
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
        }
×
541
    }
×
542
    return {};
×
543
}
×
544

545
Mixed Mixed::operator*(const Mixed& rhs) const noexcept
546
{
1,211,580✔
547
    if (!is_null() && !rhs.is_null()) {
1,211,580✔
548
        auto common_type = get_common_type(get_type(), rhs.get_type());
1,211,580✔
549
        switch (common_type) {
1,211,580✔
550
            case type_Int:
1,210,656✔
551
                return export_to_type<Int>() * rhs.export_to_type<Int>();
1,210,656✔
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
        }
×
561
    }
×
562
    return {};
×
563
}
×
564

565
Mixed Mixed::operator/(const Mixed& rhs) const noexcept
566
{
1,210,344✔
567
    if (!is_null() && !rhs.is_null()) {
1,210,344✔
568
        auto common_type = get_common_type(get_type(), rhs.get_type());
1,210,344✔
569
        switch (common_type) {
1,210,344✔
570
            case type_Int: {
1,209,732✔
571
                auto dividend = export_to_type<Int>();
1,209,732✔
572
                auto divisor = rhs.export_to_type<Int>();
1,209,732✔
573
                // We don't want to throw here. This is usually used as part of a query
465,879✔
574
                // and in this case we would just expect a no match
465,879✔
575
                if (divisor == 0)
1,209,732✔
576
                    return dividend < 0 ? std::numeric_limits<int64_t>::min() : std::numeric_limits<int64_t>::max();
624✔
577
                return dividend / divisor;
1,209,108✔
578
            }
1,209,108✔
579
            case type_Float:
465,696✔
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:
465,696✔
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:
465,615✔
586
                return export_to_type<Decimal128>() / rhs.export_to_type<Decimal128>();
96✔
587
            default:
465,567✔
588
                break;
×
589
        }
×
590
    }
×
591
    return {};
×
592
}
×
593

594
size_t Mixed::hash() const
595
{
41,727✔
596
    if (is_null())
41,727✔
597
        return 0;
4,014✔
598

19,398✔
599
    size_t hash = 0;
37,713✔
600
    switch (get_type()) {
37,713✔
601
        case type_Int:
2,946✔
602
            hash = size_t(int_val);
2,946✔
603
            break;
2,946✔
604
        case type_Bool:
1,746✔
605
            hash = bool_val ? 0xdeadbeef : 0xcafebabe;
1,320✔
606
            break;
1,746✔
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,862✔
613
            auto unsigned_data = reinterpret_cast<const unsigned char*>(&double_val);
5,862✔
614
            hash = murmur2_or_cityhash(unsigned_data, sizeof(double));
5,862✔
615
            break;
5,862✔
616
        }
×
617
        case type_String:
11,556✔
618
            hash = get<StringData>().hash();
11,556✔
619
            break;
11,556✔
620
        case type_Binary: {
4,500✔
621
            auto bin = get<BinaryData>();
4,500✔
622
            StringData str(bin.data(), bin.size());
4,500✔
623
            hash = str.hash();
4,500✔
624
            break;
4,500✔
625
        }
×
626
        case type_Timestamp:
2,850✔
627
            hash = get<Timestamp>().hash();
2,850✔
628
            break;
2,850✔
629
        case type_ObjectId:
2,751✔
630
            hash = get<ObjectId>().hash();
2,751✔
631
            break;
2,751✔
632
        case type_UUID: {
2,751✔
633
            hash = get<UUID>().hash();
2,751✔
634
            break;
2,751✔
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,751✔
642
            auto value = get<Decimal128>();
2,751✔
643
            auto unsigned_data = reinterpret_cast<const unsigned char*>(value.raw());
2,751✔
644
            hash = murmur2_or_cityhash(unsigned_data, sizeof(Decimal128::Bid128));
2,751✔
645
            break;
2,751✔
646
        }
×
647
        case type_Mixed:
✔
648
        case type_Link:
✔
649
        case type_LinkList:
✔
650
            REALM_ASSERT_RELEASE(false && "Hash not supported for this column type");
×
651
            break;
×
652
    }
37,713✔
653

19,398✔
654
    return hash;
37,713✔
655
}
37,713✔
656

657
StringData Mixed::get_index_data(std::array<char, 16>& buffer) const noexcept
658
{
6,455,280✔
659
    if (is_null()) {
6,455,280✔
660
        return {};
457,335✔
661
    }
457,335✔
662
    switch (get_type()) {
5,997,945✔
663
        case type_Int: {
3,751,908✔
664
            int64_t i = get_int();
3,751,908✔
665
            const char* c = reinterpret_cast<const char*>(&i);
3,751,908✔
666
            realm::safe_copy_n(c, sizeof(int64_t), buffer.data());
3,751,908✔
667
            return StringData{buffer.data(), sizeof(int64_t)};
3,751,908✔
668
        }
×
669
        case type_Bool: {
35,487✔
670
            int64_t i = get_bool() ? 1 : 0;
28,056✔
671
            return Mixed(i).get_index_data(buffer);
35,487✔
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: {
54✔
684
            auto v2 = get_double();
54✔
685
            int i = int(v2);
54✔
686
            if (i == v2) {
54✔
687
                return Mixed(i).get_index_data(buffer);
30✔
688
            }
30✔
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
        }
24✔
693
        case type_String:
1,287,045✔
694
            return get_string();
1,287,045✔
695
        case type_Binary: {
72✔
696
            auto bin = get_binary();
72✔
697
            return {bin.data(), bin.size()};
72✔
698
        }
24✔
699
        case type_Timestamp: {
46,179✔
700
            auto dt = get<Timestamp>();
46,179✔
701
            int64_t s = dt.get_seconds();
46,179✔
702
            int32_t ns = dt.get_nanoseconds();
46,179✔
703
            constexpr size_t index_size = sizeof(s) + sizeof(ns);
46,179✔
704
            const char* s_buf = reinterpret_cast<const char*>(&s);
46,179✔
705
            const char* ns_buf = reinterpret_cast<const char*>(&ns);
46,179✔
706
            realm::safe_copy_n(s_buf, sizeof(s), buffer.data());
46,179✔
707
            realm::safe_copy_n(ns_buf, sizeof(ns), buffer.data() + sizeof(s));
46,179✔
708
            return StringData{buffer.data(), index_size};
46,179✔
709
        }
24✔
710
        case type_ObjectId: {
853,590✔
711
            auto id = get<ObjectId>();
853,590✔
712
            memcpy(&buffer, &id, sizeof(ObjectId));
853,590✔
713
            return StringData{buffer.data(), sizeof(ObjectId)};
853,590✔
714
        }
24✔
715
        case type_Decimal: {
30✔
716
            auto v2 = this->get_decimal();
30✔
717
            int64_t i;
30✔
718
            if (v2.to_int(i)) {
30✔
719
                return Mixed(i).get_index_data(buffer);
18✔
720
            }
18✔
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
        }
12✔
725
        case type_UUID: {
25,488✔
726
            auto id = get<UUID>();
25,488✔
727
            const auto bytes = id.to_bytes();
25,488✔
728
            std::copy_n(bytes.data(), bytes.size(), buffer.begin());
25,488✔
729
            return StringData{buffer.data(), bytes.size()};
25,488✔
730
        }
12✔
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
        }
12✔
741
        case type_Mixed:
6✔
742
        case type_Link:
✔
743
        case type_LinkList:
✔
744
            break;
×
745
    }
×
746
    REALM_ASSERT_RELEASE(false && "Index not supported for this column type");
×
747
    return {};
×
748
}
×
749

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

3✔
773
        auto sz = binary_val.size();
6✔
774
        bool capped = false;
6✔
775
        size_t out_size = 0;
6✔
776

3✔
777
        ostr << '"';
6✔
778
        for (size_t n = 0; n < sz; n++) {
162✔
779
            out_size += size_one_hex_out;
162✔
780
            if (out_size > max_size) {
162✔
781
                capped = true;
6✔
782
                break;
6✔
783
            }
6✔
784
            out_hex(binary_val[n]);
156✔
785
        }
156✔
786
        if (capped) {
6✔
787
            ostr << "...";
6✔
788
        }
6✔
789
        ostr << '"';
6✔
790
    }
6✔
791
    else if (is_type(type_Timestamp)) {
36✔
792
        char buffer[32];
6✔
793
        return date_val.to_string(buffer);
6✔
794
    }
6✔
795
    else {
30✔
796
        ostr << *this;
30✔
797
    }
30✔
798
    return ostr.str();
45✔
799
}
54✔
800

801
void Mixed::use_buffer(std::string& buf) noexcept
802
{
52,560✔
803
    if (is_null()) {
52,560✔
804
        return;
14,820✔
805
    }
14,820✔
806
    switch (get_type()) {
37,740✔
807
        case type_String:
7,380✔
808
            buf = std::string(string_val);
7,380✔
809
            string_val = StringData(buf);
7,380✔
810
            break;
7,380✔
811
        case type_Binary:
246✔
812
            buf = std::string(binary_val);
246✔
813
            binary_val = BinaryData(buf);
246✔
814
            break;
246✔
815
        default:
30,114✔
816
            break;
30,114✔
817
    }
37,740✔
818
}
37,740✔
819

820
// LCOV_EXCL_START
821
std::ostream& operator<<(std::ostream& out, const Mixed& m)
822
{
335,928✔
823
    if (m.is_null()) {
335,928✔
824
        out << "null";
864✔
825
    }
864✔
826
    else {
335,064✔
827
        switch (m.get_type()) {
335,064✔
828
            case type_Int:
258,546✔
829
                out << m.int_val;
258,546✔
830
                break;
258,546✔
831
            case type_Bool:
1,320✔
832
                out << (m.bool_val ? "true" : "false");
1,023✔
833
                break;
1,320✔
834
            case type_Float:
1,506✔
835
                out << m.float_val;
1,506✔
836
                break;
1,506✔
837
            case type_Double:
2,106✔
838
                out << m.double_val;
2,106✔
839
                break;
2,106✔
840
            case type_String:
10,974✔
841
                out << util::serializer::print_value(m.string_val);
10,974✔
842
                break;
10,974✔
843
            case type_Binary:
1,500✔
844
                out << util::serializer::print_value(m.binary_val);
1,500✔
845
                break;
1,500✔
846
            case type_Timestamp:
1,374✔
847
                out << util::serializer::print_value(m.date_val);
1,374✔
848
                break;
1,374✔
849
            case type_Decimal:
1,464✔
850
                out << m.decimal_val;
1,464✔
851
                break;
1,464✔
852
            case type_ObjectId:
50,118✔
853
                out << util::serializer::print_value(m.id_val);
50,118✔
854
                break;
50,118✔
855
            case type_Link:
3,744✔
856
                out << util::serializer::print_value(ObjKey(m.int_val));
3,744✔
857
                break;
3,744✔
858
            case type_TypedLink:
654✔
859
                out << util::serializer::print_value(m.link_val);
654✔
860
                break;
654✔
861
            case type_UUID:
1,740✔
862
                out << util::serializer::print_value(m.uuid_val);
1,740✔
863
                break;
1,740✔
864
            case type_Mixed:
✔
865
            case type_LinkList:
✔
866
                REALM_ASSERT(false);
×
867
            default:
18✔
868
                if (m.is_type(type_List)) {
18✔
869
                    out << "list";
6✔
870
                }
6✔
871
                else if (m.is_type(type_Set)) {
12✔
872
                    out << "set";
6✔
873
                }
6✔
874
                else if (m.is_type(type_Dictionary)) {
6✔
875
                    out << "dictionary";
6✔
876
                }
6✔
877
                break;
18✔
878
        }
335,928✔
879
    }
335,928✔
880
    return out;
335,928✔
881
}
335,928✔
882
// LCOV_EXCL_STOP
883

884

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