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

realm / realm-core / jorgen.edelbo_337

03 Jul 2024 01:04PM UTC coverage: 90.864% (-0.1%) from 90.984%
jorgen.edelbo_337

Pull #7826

Evergreen

nicola-cab
Merge branch 'master' of github.com:realm/realm-core into next-major
Pull Request #7826: Merge Next major

102968 of 181176 branches covered (56.83%)

3131 of 3738 new or added lines in 54 files covered. (83.76%)

106 existing lines in 23 files now uncovered.

217725 of 239616 relevant lines covered (90.86%)

6844960.2 hits per line

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

71.91
/src/realm/query_conditions.hpp
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
#ifndef REALM_QUERY_CONDITIONS_HPP
20
#define REALM_QUERY_CONDITIONS_HPP
21

22
#include <cstdint>
23
#include <string>
24

25
#include <realm/query_state.hpp>
26
#include <realm/unicode.hpp>
27
#include <realm/binary_data.hpp>
28
#include <realm/query_value.hpp>
29
#include <realm/mixed.hpp>
30
#include <realm/utilities.hpp>
31

32
namespace realm {
33

34
// Quick hack to make "Queries with Integer null columns" able to compile in Visual Studio 2015 which doesn't full
35
// support sfinae
36
// (real cause hasn't been investigated yet, cannot exclude that we don't obey c++11 standard)
37
struct HackClass {
38
    template <class A, class B, class C>
39
    bool can_match(A, B, C)
40
    {
41
        REALM_ASSERT(false);
42
        return false;
43
    }
44
    template <class A, class B, class C>
45
    bool will_match(A, B, C)
46
    {
47
        REALM_ASSERT(false);
48
        return false;
49
    }
50
};
51

52
// Does v2 contain v1?
53
struct Contains : public HackClass {
54
    bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const
55
    {
×
56
        return v2.contains(v1);
×
57
    }
×
58
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
59
    {
10,146✔
60
        return v2.contains(v1);
10,146✔
61
    }
10,146✔
62
    bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const
63
    {
5,370✔
64
        return v2.contains(v1);
5,370✔
65
    }
5,370✔
66
    bool operator()(StringData v1, const std::array<uint8_t, 256>& charmap, StringData v2) const
67
    {
33,384✔
68
        return v2.contains(v1, charmap);
33,384✔
69
    }
33,384✔
70

71
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
72
    {
15,324✔
73
        if (m1.is_null())
15,324✔
74
            return !m2.is_null();
36✔
75
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
15,288✔
76
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
10,146✔
77
        }
10,146✔
78
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
5,142✔
79
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
648✔
80
        }
648✔
81
        return false;
4,494✔
82
    }
5,142✔
83

84
    template <class A, class B>
85
    bool operator()(A, B) const
86
    {
87
        REALM_ASSERT(false);
88
        return false;
89
    }
90
    template <class A, class B, class C, class D>
91
    bool operator()(A, B, C, D) const
92
    {
×
93
        REALM_ASSERT(false);
×
94
        return false;
×
95
    }
×
96
    bool operator()(int64_t, int64_t, bool, bool) const
97
    {
×
98
        REALM_ASSERT(false);
×
99
        return false;
×
100
    }
×
101

102
    static std::string description()
103
    {
276✔
104
        return "CONTAINS";
276✔
105
    }
276✔
106

107
    static const int condition = -1;
108
};
109

110
// Does v2 contain something like v1 (wildcard matching)?
111
struct Like : public HackClass {
112
    bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const
113
    {
32,964✔
114
        return v2.like(v1);
32,964✔
115
    }
32,964✔
116
    bool operator()(BinaryData b1, const char*, const char*, BinaryData b2, bool = false, bool = false) const
117
    {
×
118
        StringData s1(b1.data(), b1.size());
×
119
        StringData s2(b2.data(), b2.size());
×
120
        return s2.like(s1);
×
121
    }
×
122
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
123
    {
9,546✔
124
        return v2.like(v1);
9,546✔
125
    }
9,546✔
126
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
127
    {
768✔
128
        StringData s1(b1.data(), b1.size());
768✔
129
        StringData s2(b2.data(), b2.size());
768✔
130
        return s2.like(s1);
768✔
131
    }
768✔
132

133
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
134
    {
14,586✔
135
        if (m1.is_null() && m2.is_null())
14,586✔
136
            return true;
36✔
137
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
14,550✔
138
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
9,546✔
139
        }
9,546✔
140
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
5,004✔
141
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
648✔
142
        }
648✔
143
        return false;
4,356✔
144
    }
5,004✔
145

146
    template <class A, class B>
147
    bool operator()(A, B) const
148
    {
149
        REALM_ASSERT(false);
150
        return false;
151
    }
152

153
    template <class A, class B, class C, class D>
154
    bool operator()(A, B, C, D) const
155
    {
×
156
        REALM_ASSERT(false);
×
157
        return false;
×
158
    }
×
159

160
    bool operator()(int64_t, int64_t, bool, bool) const
161
    {
×
162
        REALM_ASSERT(false);
×
163
        return false;
×
164
    }
×
165

166
    static std::string description()
167
    {
156✔
168
        return "LIKE";
156✔
169
    }
156✔
170

171
    static const int condition = -1;
172
};
173

174
// Does v2 begin with v1?
175
struct BeginsWith : public HackClass {
176
    bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const
177
    {
32,970✔
178
        return v2.begins_with(v1);
32,970✔
179
    }
32,970✔
180
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
181
    {
×
182
        return v2.begins_with(v1);
×
183
    }
×
184
    bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const
185
    {
4,578✔
186
        return v2.begins_with(v1);
4,578✔
187
    }
4,578✔
188

189
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
190
    {
19,806✔
191
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
19,806✔
192
            StringData s1 = m1.get<StringData>();
11,292✔
193
            StringData s2 = m2.get<StringData>();
11,292✔
194
            return s2.begins_with(s1);
11,292✔
195
        }
11,292✔
196
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
8,514✔
197
            BinaryData b1 = m1.get<BinaryData>();
672✔
198
            BinaryData b2 = m2.get<BinaryData>();
672✔
199
            return b2.begins_with(b1);
672✔
200
        }
672✔
201
        return false;
7,842✔
202
    }
8,514✔
203

204
    template <class A, class B, class C, class D>
205
    bool operator()(A, B, C, D) const
206
    {
×
207
        REALM_ASSERT(false);
×
208
        return false;
×
209
    }
×
210
    template <class A, class B>
211
    bool operator()(A, B) const
212
    {
213
        REALM_ASSERT(false);
214
        return false;
215
    }
216

217
    static std::string description()
218
    {
168✔
219
        return "BEGINSWITH";
168✔
220
    }
168✔
221

222
    static const int condition = -1;
223
};
224

225
// Does v2 end with v1?
226
struct EndsWith : public HackClass {
227
    bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const
228
    {
32,970✔
229
        return v2.ends_with(v1);
32,970✔
230
    }
32,970✔
231
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
232
    {
10,224✔
233
        return v2.ends_with(v1);
10,224✔
234
    }
10,224✔
235
    bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const
236
    {
5,274✔
237
        return v2.ends_with(v1);
5,274✔
238
    }
5,274✔
239

240
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
241
    {
21,810✔
242

243
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
21,810✔
244
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
10,224✔
245
        }
10,224✔
246
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
11,586✔
247
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
696✔
248
        }
696✔
249
        return false;
10,890✔
250
    }
11,586✔
251

252
    template <class A, class B>
253
    bool operator()(A, B) const
254
    {
255
        REALM_ASSERT(false);
256
        return false;
257
    }
258
    template <class A, class B, class C, class D>
259
    bool operator()(A, B, C, D) const
260
    {
×
261
        REALM_ASSERT(false);
×
262
        return false;
×
263
    }
×
264

265
    static std::string description()
266
    {
174✔
267
        return "ENDSWITH";
174✔
268
    }
174✔
269

270
    static const int condition = -1;
271
};
272

273
struct Equal {
274
    static const int avx = 0x00; // _CMP_EQ_OQ
275
    //    bool operator()(const bool v1, const bool v2, bool v1null = false, bool v2null = false) const { return v1 ==
276
    //    v2; }
277
    bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const
278
    {
×
279
        return v1 == v2;
×
280
    }
×
281
    bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const
282
    {
2,481,726✔
283
        return v1 == v2;
2,481,726✔
284
    }
2,481,726✔
285

286
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
287
    {
8,038,173✔
288
        return (m1.is_null() && m2.is_null()) || (Mixed::types_are_comparable(m1, m2) && (m1 == m2));
8,038,173✔
289
    }
8,038,173✔
290

291
    template <class T>
292
    bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const
293
    {
3,227,000✔
294
        return (v1null && v2null) || (!v1null && !v2null && v1 == v2);
3,227,000✔
295
    }
3,227,000✔
296
    static const int condition = cond_Equal;
297
    bool can_match(int64_t v, int64_t lbound, int64_t ubound)
298
    {
16,228,791✔
299
        return (v >= lbound && v <= ubound);
17,037,183✔
300
    }
16,228,791✔
301
    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
302
    {
15,200,310✔
303
        return (v == 0 && ubound == 0 && lbound == 0);
15,200,310✔
304
    }
15,200,310✔
305
    bool operator()(int64_t v1, int64_t v2) const
306
    {
1,356,141✔
307
        return v1 == v2;
1,356,141✔
308
    }
1,356,141✔
309

310
    static std::string description()
311
    {
37,371✔
312
        return "==";
37,371✔
313
    }
37,371✔
314
};
315

316
struct NotEqual {
317
    static const int avx = 0x0B; // _CMP_FALSE_OQ
318
    bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const
319
    {
118,764✔
320
        return v1 != v2;
118,764✔
321
    }
118,764✔
322
    // bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const { return v1 != v2; }
323

324
    template <class T>
325
    bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const
326
    {
19,753,653✔
327
        if (!v1null && !v2null)
19,753,653✔
328
            return v1 != v2;
19,700,757✔
329

330
        if (v1null && v2null)
52,896!
331
            return false;
20,458✔
332

333
        return true;
32,438✔
334
    }
52,896✔
335

336
    bool operator()(const QueryValue& m1, const Mixed& m2) const
337
    {
×
338
        return !Equal()(m1, m2);
×
339
    }
×
340

341

342
    static const int condition = cond_NotEqual;
343
    bool can_match(int64_t v, int64_t lbound, int64_t ubound)
344
    {
332,337✔
345
        return !(v == 0 && ubound == 0 && lbound == 0);
332,337✔
346
    }
332,337✔
347
    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
348
    {
330,738✔
349
        return (v > ubound || v < lbound);
330,738✔
350
    }
330,738✔
351
    bool operator()(int64_t v1, int64_t v2) const
352
    {
23,502✔
353
        return v1 != v2;
23,502✔
354
    }
23,502✔
355

356
    template <class A, class B, class C, class D>
357
    bool operator()(A, B, C, D) const = delete;
358

359
    static std::string description()
360
    {
2,898✔
361
        return "!=";
2,898✔
362
    }
2,898✔
363
};
364

365
// Does v2 contain v1?
366
struct ContainsIns : public HackClass {
367
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false,
368
                    bool = false) const
369
    {
×
370
        if (v2.is_null() && !v1.is_null())
×
371
            return false;
×
372

×
373
        if (v1.size() == 0 && !v2.is_null())
×
374
            return true;
×
375

×
376
        return search_case_fold(v2, v1_upper, v1_lower, v1.size()) != v2.size();
×
377
    }
×
378

379
    // Slow version, used if caller hasn't stored an upper and lower case version
380
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
381
    {
17,562✔
382
        if (v2.is_null() && !v1.is_null())
17,562✔
383
            return false;
24✔
384

385
        if (v1.size() == 0 && !v2.is_null())
17,538✔
386
            return true;
8,220✔
387

388
        std::string v1_upper = case_map(v1, true, IgnoreErrors);
9,318✔
389
        std::string v1_lower = case_map(v1, false, IgnoreErrors);
9,318✔
390
        return search_case_fold(v2, v1_upper.c_str(), v1_lower.c_str(), v1.size()) != v2.size();
9,318✔
391
    }
17,538✔
392
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
393
    {
192✔
394
        StringData s1(b1.data(), b1.size());
192✔
395
        StringData s2(b2.data(), b2.size());
192✔
396
        return this->operator()(s1, s2, false, false);
192✔
397
    }
192✔
398

399
    // Case insensitive Boyer-Moore version
400
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower,
401
                    const std::array<uint8_t, 256>& charmap, StringData v2) const
402
    {
7,470✔
403
        if (v2.is_null() && !v1.is_null())
7,470✔
404
            return false;
24✔
405

406
        if (v1.size() == 0 && !v2.is_null())
7,446✔
407
            return true;
24✔
408

409
        return contains_ins(v2, v1_upper, v1_lower, v1.size(), charmap);
7,422✔
410
    }
7,446✔
411

412
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
413
    {
23,202✔
414
        if (m1.is_null())
23,202✔
415
            return !m2.is_null();
66✔
416
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
23,136✔
417
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
17,370✔
418
        }
17,370✔
419
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
5,766✔
420
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
72✔
421
        }
72✔
422
        return false;
5,694✔
423
    }
5,766✔
424

425
    template <class A, class B>
426
    bool operator()(A, B) const
427
    {
428
        REALM_ASSERT(false);
429
        return false;
430
    }
431
    template <class A, class B, class C, class D>
432
    bool operator()(A, B, C, D) const
433
    {
×
434
        REALM_ASSERT(false);
×
435
        return false;
×
436
    }
×
437
    bool operator()(int64_t, int64_t, bool, bool) const
438
    {
×
439
        REALM_ASSERT(false);
×
440
        return false;
×
441
    }
×
442

443
    static std::string description()
444
    {
402✔
445
        return "CONTAINS[c]";
402✔
446
    }
402✔
447

448
    static const int condition = -1;
449
};
450

451
// Does v2 contain something like v1 (wildcard matching)?
452
struct LikeIns : public HackClass {
453
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false,
454
                    bool = false) const
455
    {
24,858✔
456
        if (v2.is_null() || v1.is_null()) {
24,858✔
457
            return (v2.is_null() && v1.is_null());
24,612✔
458
        }
24,612✔
459

460
        return string_like_ins(v2, v1_lower, v1_upper);
246✔
461
    }
24,858✔
462
    bool operator()(BinaryData b1, const char* b1_upper, const char* b1_lower, BinaryData b2, bool = false,
463
                    bool = false) const
464
    {
×
465
        if (b2.is_null() || b1.is_null()) {
×
466
            return (b2.is_null() && b1.is_null());
×
467
        }
×
468
        StringData s2(b2.data(), b2.size());
×
469

×
470
        return string_like_ins(s2, b1_lower, b1_upper);
×
471
    }
×
472

473
    // Slow version, used if caller hasn't stored an upper and lower case version
474
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
475
    {
11,952✔
476
        if (v2.is_null() || v1.is_null()) {
11,952✔
477
            return (v2.is_null() && v1.is_null());
×
478
        }
×
479

480
        std::string v1_upper = case_map(v1, true, IgnoreErrors);
11,952✔
481
        std::string v1_lower = case_map(v1, false, IgnoreErrors);
11,952✔
482
        return string_like_ins(v2, v1_lower, v1_upper);
11,952✔
483
    }
11,952✔
484
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
485
    {
180✔
486
        if (b2.is_null() || b1.is_null()) {
180✔
487
            return (b2.is_null() && b1.is_null());
84✔
488
        }
84✔
489
        StringData s1(b1.data(), b1.size());
96✔
490
        StringData s2(b2.data(), b2.size());
96✔
491

492
        std::string s1_upper = case_map(s1, true, IgnoreErrors);
96✔
493
        std::string s1_lower = case_map(s1, false, IgnoreErrors);
96✔
494
        return string_like_ins(s2, s1_lower, s1_upper);
96✔
495
    }
180✔
496

497
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
498
    {
14,028✔
499
        if (m1.is_null() && m2.is_null())
14,028✔
500
            return true;
42✔
501
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
13,986✔
502
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
11,952✔
503
        }
11,952✔
504
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
2,034✔
505
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
60✔
506
        }
60✔
507
        return false;
1,974✔
508
    }
2,034✔
509

510
    template <class A, class B>
511
    bool operator()(A, B) const
512
    {
513
        REALM_ASSERT(false);
514
        return false;
515
    }
516
    template <class A, class B, class C, class D>
517
    bool operator()(A, B, C, D) const
518
    {
×
519
        REALM_ASSERT(false);
×
520
        return false;
×
521
    }
×
522
    bool operator()(int64_t, int64_t, bool, bool) const
523
    {
×
524
        REALM_ASSERT(false);
×
525
        return false;
×
526
    }
×
527

528
    static std::string description()
529
    {
228✔
530
        return "LIKE[c]";
228✔
531
    }
228✔
532

533
    static const int condition = -1;
534
};
535

536
// Does v2 begin with v1?
537
struct BeginsWithIns : public HackClass {
538
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false,
539
                    bool = false) const
540
    {
24,714✔
541
        if (v2.is_null() && !v1.is_null())
24,714✔
542
            return false;
18✔
543
        return v1.size() <= v2.size() && equal_case_fold(v2.prefix(v1.size()), v1_upper, v1_lower);
24,696✔
544
    }
24,714✔
545

546
    // Slow version, used if caller hasn't stored an upper and lower case version
547
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
548
    {
12,336✔
549
        if (v2.is_null() && !v1.is_null())
12,336✔
550
            return false;
24✔
551

552
        if (v1.size() > v2.size())
12,312✔
553
            return false;
396✔
554
        std::string v1_upper = case_map(v1, true, IgnoreErrors);
11,916✔
555
        std::string v1_lower = case_map(v1, false, IgnoreErrors);
11,916✔
556
        return equal_case_fold(v2.prefix(v1.size()), v1_upper.c_str(), v1_lower.c_str());
11,916✔
557
    }
12,312✔
558
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
559
    {
192✔
560
        StringData s1(b1.data(), b1.size());
192✔
561
        StringData s2(b2.data(), b2.size());
192✔
562
        return this->operator()(s1, s2, false, false);
192✔
563
    }
192✔
564

565
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
566
    {
15,582✔
567
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
15,582✔
568
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
12,144✔
569
        }
12,144✔
570
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
3,438✔
571
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
72✔
572
        }
72✔
573
        return false;
3,366✔
574
    }
3,438✔
575

576
    template <class A, class B>
577
    bool operator()(A, B) const
578
    {
579
        REALM_ASSERT(false);
580
        return false;
581
    }
582
    template <class A, class B, class C, class D>
583
    bool operator()(A, B, C, D) const
584
    {
×
585
        REALM_ASSERT(false);
×
586
        return false;
×
587
    }
×
588
    bool operator()(int64_t, int64_t, bool, bool) const
589
    {
×
590
        REALM_ASSERT(false);
×
591
        return false;
×
592
    }
×
593

594
    static std::string description()
595
    {
252✔
596
        return "BEGINSWITH[c]";
252✔
597
    }
252✔
598

599
    static const int condition = -1;
600
};
601

602
// Does v2 end with v1?
603
struct EndsWithIns : public HackClass {
604
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false,
605
                    bool = false) const
606
    {
24,792✔
607
        if (v2.is_null() && !v1.is_null())
24,792✔
608
            return false;
18✔
609

610
        return v1.size() <= v2.size() && equal_case_fold(v2.suffix(v1.size()), v1_upper, v1_lower);
24,774✔
611
    }
24,792✔
612

613
    // Slow version, used if caller hasn't stored an upper and lower case version
614
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
615
    {
12,102✔
616
        if (v2.is_null() && !v1.is_null())
12,102✔
617
            return false;
24✔
618

619
        if (v1.size() > v2.size())
12,078✔
620
            return false;
336✔
621
        std::string v1_upper = case_map(v1, true, IgnoreErrors);
11,742✔
622
        std::string v1_lower = case_map(v1, false, IgnoreErrors);
11,742✔
623
        return equal_case_fold(v2.suffix(v1.size()), v1_upper.c_str(), v1_lower.c_str());
11,742✔
624
    }
12,078✔
625
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
626
    {
180✔
627
        StringData s1(b1.data(), b1.size());
180✔
628
        StringData s2(b2.data(), b2.size());
180✔
629
        return this->operator()(s1, s2, false, false);
180✔
630
    }
180✔
631

632
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
633
    {
13,812✔
634
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
13,812✔
635
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
11,922✔
636
        }
11,922✔
637
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
1,890✔
638
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
60✔
639
        }
60✔
640
        return false;
1,830✔
641
    }
1,890✔
642

643
    template <class A, class B>
644
    bool operator()(A, B) const
645
    {
646
        REALM_ASSERT(false);
647
        return false;
648
    }
649
    template <class A, class B, class C, class D>
650
    bool operator()(A, B, C, D) const
651
    {
×
652
        REALM_ASSERT(false);
×
653
        return false;
×
654
    }
×
655
    bool operator()(int64_t, int64_t, bool, bool) const
656
    {
×
657
        REALM_ASSERT(false);
×
658
        return false;
×
659
    }
×
660

661
    static std::string description()
662
    {
234✔
663
        return "ENDSWITH[c]";
234✔
664
    }
234✔
665

666
    static const int condition = -1;
667
};
668

669
struct EqualIns : public HackClass {
670
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false,
671
                    bool = false) const
672
    {
25,674✔
673
        if (v1.is_null() != v2.is_null())
25,674✔
674
            return false;
24,582✔
675

676
        return v1.size() == v2.size() && equal_case_fold(v2, v1_upper, v1_lower);
1,092✔
677
    }
25,674✔
678

679
    // Slow version, used if caller hasn't stored an upper and lower case version
680
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
681
    {
18,120✔
682
        if (v1.is_null() != v2.is_null())
18,120✔
683
            return false;
×
684

685
        if (v1.size() != v2.size())
18,120✔
686
            return false;
1,128✔
687
        std::string v1_upper = case_map(v1, true, IgnoreErrors);
16,992✔
688
        std::string v1_lower = case_map(v1, false, IgnoreErrors);
16,992✔
689
        return equal_case_fold(v2, v1_upper.c_str(), v1_lower.c_str());
16,992✔
690
    }
18,120✔
691
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
692
    {
264✔
693
        StringData s1(b1.data(), b1.size());
264✔
694
        StringData s2(b2.data(), b2.size());
264✔
695
        return this->operator()(s1, s2, false, false);
264✔
696
    }
264✔
697

698
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
699
    {
25,776✔
700
        if (m1.is_null() && m2.is_null()) {
25,776✔
701
            return true;
96✔
702
        }
96✔
703
        if (Mixed::types_are_comparable(m1, m2)) {
25,680✔
704
            if (m1.is_type(type_String) && m2.is_type(type_String)) {
18,564✔
705
                return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
17,856✔
706
            }
17,856✔
707
            if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
708✔
708
                return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
264✔
709
            }
264✔
710
            return m1 == m2;
444✔
711
        }
708✔
712
        return false;
7,116✔
713
    }
25,680✔
714

715
    template <class A, class B>
716
    bool operator()(A, B) const
717
    {
718
        REALM_ASSERT(false);
719
        return false;
720
    }
721
    template <class A, class B, class C, class D>
722
    bool operator()(A, B, C, D) const
723
    {
×
724
        REALM_ASSERT(false);
×
725
        return false;
×
726
    }
×
727
    bool operator()(int64_t, int64_t, bool, bool) const
728
    {
×
729
        REALM_ASSERT(false);
×
730
        return false;
×
731
    }
×
732

733
    static std::string description()
734
    {
114✔
735
        return "==[c]";
114✔
736
    }
114✔
737

738
    static const int condition = -1;
739
};
740

741
struct NotEqualIns : public HackClass {
742
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false,
743
                    bool = false) const
744
    {
24,624✔
745
        if (v1.is_null() != v2.is_null())
24,624✔
746
            return true;
24,522✔
747
        return v1.size() != v2.size() || !equal_case_fold(v2, v1_upper, v1_lower);
102✔
748
    }
24,624✔
749

750
    // Slow version, used if caller hasn't stored an upper and lower case version
751
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
752
    {
×
753
        if (v1.is_null() != v2.is_null())
×
754
            return true;
×
755

756
        if (v1.size() != v2.size())
×
757
            return true;
×
758
        std::string v1_upper = case_map(v1, true, IgnoreErrors);
×
759
        std::string v1_lower = case_map(v1, false, IgnoreErrors);
×
760
        return !equal_case_fold(v2, v1_upper.c_str(), v1_lower.c_str());
×
761
    }
×
762
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
763
    {
×
764
        StringData s1(b1.data(), b1.size());
×
765
        StringData s2(b2.data(), b2.size());
×
766
        return this->operator()(s1, s2, false, false);
×
767
    }
×
768

769
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
770
    {
14,556✔
771
        return !EqualIns()(m1, m2);
14,556✔
772
    }
14,556✔
773

774
    template <class A, class B>
775
    bool operator()(A, B) const
776
    {
777
        REALM_ASSERT(false);
778
        return false;
779
    }
780
    template <class A, class B, class C, class D>
781
    bool operator()(A, B, C, D) const
782
    {
×
783
        REALM_ASSERT(false);
×
784
        return false;
×
785
    }
×
786

787
    static std::string description()
788
    {
54✔
789
        return "!=[c]";
54✔
790
    }
54✔
791

792
    static const int condition = -1;
793
};
794

795
struct Greater {
796
    static const int avx = 0x1E; // _CMP_GT_OQ
797
    template <class T>
798
    bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const
799
    {
103,860✔
800
        if (v1null || v2null)
103,860✔
801
            return false;
172✔
802

803
        return v1 > v2;
103,688✔
804
    }
103,860✔
805
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
806
    {
1,247,886✔
807
        return Mixed::types_are_comparable(m1, m2) && (m1 > m2);
1,247,886✔
808
    }
1,247,886✔
809
    static const int condition = cond_Greater;
810
    template <class A, class B, class C, class D>
811
    bool operator()(A, B, C, D) const
812
    {
813
        REALM_ASSERT(false);
814
        return false;
815
    }
816

817
    bool can_match(int64_t v, int64_t lbound, int64_t ubound)
818
    {
349,923✔
819
        static_cast<void>(lbound);
349,923✔
820
        return ubound > v;
349,923✔
821
    }
349,923✔
822
    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
823
    {
338,766✔
824
        static_cast<void>(ubound);
338,766✔
825
        return lbound > v;
338,766✔
826
    }
338,766✔
827
    bool operator()(int64_t v1, int64_t v2) const
828
    {
123,336✔
829
        return v1 > v2;
123,336✔
830
    }
123,336✔
831

832
    static std::string description()
833
    {
2,535✔
834
        return ">";
2,535✔
835
    }
2,535✔
836
};
837

838
struct None {
839
    template <class T>
840
    bool operator()(const T&, const T&, bool = false, bool = false) const
841
    {
×
842
        return true;
×
843
    }
×
844
    static const int condition = cond_None;
845
    template <class A, class B, class C, class D>
846
    bool operator()(A, B, C, D) const
847
    {
848
        REALM_ASSERT(false);
849
        return false;
850
    }
851
    bool can_match(int64_t v, int64_t lbound, int64_t ubound)
852
    {
×
853
        static_cast<void>(lbound);
×
854
        static_cast<void>(ubound);
×
855
        static_cast<void>(v);
×
856
        return true;
×
857
    }
×
858
    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
859
    {
×
860
        static_cast<void>(lbound);
×
861
        static_cast<void>(ubound);
×
862
        static_cast<void>(v);
×
863
        return true;
×
864
    }
×
865

866
    static std::string description()
867
    {
×
868
        return "none";
×
869
    }
×
870
};
871

872
struct NotNull {
873
    template <class T>
874
    bool operator()(const T&, const T&, bool v = false, bool = false) const
875
    {
×
876
        return !v;
×
877
    }
×
878
    static const int condition = cond_LeftNotNull;
879
    template <class A, class B, class C, class D>
880
    bool operator()(A, B, C, D) const
881
    {
882
        REALM_ASSERT(false);
883
        return false;
884
    }
885
    bool can_match(int64_t v, int64_t lbound, int64_t ubound)
886
    {
×
887
        static_cast<void>(lbound);
×
888
        static_cast<void>(ubound);
×
889
        static_cast<void>(v);
×
890
        return true;
×
891
    }
×
892
    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
893
    {
×
894
        static_cast<void>(lbound);
×
895
        static_cast<void>(ubound);
×
896
        static_cast<void>(v);
×
897
        return true;
×
898
    }
×
899
    static std::string description()
900
    {
×
901
        return "!= NULL";
×
902
    }
×
903
};
904

905
struct Less {
906
    static const int avx = 0x11; // _CMP_LT_OQ
907
    template <class T>
908
    bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const
909
    {
91,326✔
910
        if (v1null || v2null)
91,326✔
911
            return false;
118✔
912

913
        return v1 < v2;
91,208✔
914
    }
91,326✔
915

916
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
917
    {
373,665✔
918
        return Mixed::types_are_comparable(m1, m2) && (m1 < m2);
373,665✔
919
    }
373,665✔
920

921
    bool operator()(int64_t v1, int64_t v2) const
922
    {
13,530✔
923
        return v1 < v2;
13,530✔
924
    }
13,530✔
925

926
    template <class A, class B, class C, class D>
927
    bool operator()(A, B, C, D) const
928
    {
929
        REALM_ASSERT(false);
930
        return false;
931
    }
932
    static const int condition = cond_Less;
933
    bool can_match(int64_t v, int64_t lbound, int64_t)
934
    {
250,581✔
935
        return lbound < v;
250,581✔
936
    }
250,581✔
937
    bool will_match(int64_t v, int64_t, int64_t ubound)
938
    {
242,904✔
939
        return ubound < v;
242,904✔
940
    }
242,904✔
941
    static std::string description()
942
    {
507✔
943
        return "<";
507✔
944
    }
507✔
945
};
946

947
struct LessEqual : public HackClass {
948
    static const int avx = 0x12; // _CMP_LE_OQ
949
    template <class T>
950
    bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const
951
    {
740,468✔
952
        if (v1null && v2null)
740,468✔
953
            return true;
10✔
954

955
        return (!v1null && !v2null && v1 <= v2);
740,458✔
956
    }
740,468✔
957
    bool operator()(const util::Optional<bool>& v1, const util::Optional<bool>& v2, bool v1null, bool v2null) const
958
    {
×
959
        if (v1null && v2null)
×
960
            return false;
×
961

962
        return (!v1null && !v2null && *v1 <= *v2);
×
963
    }
×
964

965
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
966
    {
60,540✔
967
        return (m1.is_null() && m2.is_null()) || (Mixed::types_are_comparable(m1, m2) && (m1 <= m2));
60,540✔
968
    }
60,540✔
969
    bool operator()(int64_t v1, int64_t v2) const
970
    {
24✔
971
        return v1 <= v2;
24✔
972
    }
24✔
973

974
    template <class A, class B, class C, class D>
975
    bool operator()(A, B, C, D) const
976
    {
977
        REALM_ASSERT(false);
978
        return false;
979
    }
980
    static std::string description()
981
    {
294✔
982
        return "<=";
294✔
983
    }
294✔
984
    static const int condition = -1;
985
};
986

987
struct GreaterEqual : public HackClass {
988
    static const int avx = 0x1D; // _CMP_GE_OQ
989
    template <class T>
990
    bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const
991
    {
186,134✔
992
        if (v1null && v2null)
186,134!
993
            return true;
10✔
994

995
        return (!v1null && !v2null && v1 >= v2);
186,124✔
996
    }
186,134✔
997
    bool operator()(const util::Optional<bool>& v1, const util::Optional<bool>& v2, bool v1null, bool v2null) const
998
    {
×
999
        if (v1null && v2null)
×
1000
            return false;
×
1001

1002
        return (!v1null && !v2null && *v1 >= *v2);
×
1003
    }
×
1004

1005
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
1006
    {
66,666✔
1007
        return (m1.is_null() && m2.is_null()) || (Mixed::types_are_comparable(m1, m2) && (m1 >= m2));
66,666✔
1008
    }
66,666✔
1009
    bool operator()(int64_t v1, int64_t v2) const
1010
    {
24✔
1011
        return v1 >= v2;
24✔
1012
    }
24✔
1013

1014
    template <class A, class B, class C, class D>
1015
    bool operator()(A, B, C, D) const
1016
    {
1017
        REALM_ASSERT(false);
1018
        return false;
1019
    }
1020
    static std::string description()
1021
    {
606✔
1022
        return ">=";
606✔
1023
    }
606✔
1024
    static const int condition = -1;
1025
};
1026

1027
/* Unsigned LT.
1028

1029
 This can be determined by trial subtaction. However, some care must be exercised
1030
 since simply subtracting one vector from another will allow carries from one
1031
 bitfield to flow into the next one. To avoid this, we isolate bitfields by clamping
1032
 the MSBs to 1 in A and 0 in B before subtraction. After the subtraction the MSBs in
1033
 the result indicate borrows from the MSB. We then compute overflow (borrow OUT of MSB)
1034
 using boolean logic as described below.
1035

1036
 Unsigned LT is also used to find all zero fields or all non-zero fields, so it is
1037
 the backbone of all comparisons returning vectors.
1038
 */
1039

1040
// compute the overflows in unsigned trial subtraction A-B. The overflows
1041
// will be marked by 1 in the sign bit of each field in the result. Other
1042
// bits in the result are zero.
1043
// Overflow are detected for each field pair where A is less than B.
1044
inline uint64_t unsigned_LT_vector(uint64_t MSBs, uint64_t A, uint64_t B)
1045
{
96,740,574✔
1046
    // 1. compute borrow from most significant bit
1047
    // Isolate bitfields inside A and B before subtraction (prevent carries from spilling over)
1048
    // do this by clamping most significant bit in A to 1, and msb in B to 0
1049
    auto A_isolated = A | MSBs;                              // 1 op
96,740,574✔
1050
    auto B_isolated = B & ~MSBs;                             // 2 ops
96,740,574✔
1051
    auto borrows_into_sign_bit = ~(A_isolated - B_isolated); // 2 ops (total latency 4)
96,740,574✔
1052

1053
    // 2. determine what subtraction against most significant bit would give:
1054
    // A B borrow-in:   (A-B-borrow-in)
1055
    // 0 0 0            (0-0-0) = 0
1056
    // 0 0 1            (0-0-1) = 1 + borrow-out
1057
    // 0 1 0            (0-1-0) = 1 + borrow-out
1058
    // 0 1 1            (0-1-1) = 0 + borrow-out
1059
    // 1 0 0            (1-0-0) = 1
1060
    // 1 0 1            (1-0-1) = 0
1061
    // 1 1 0            (1-1-0) = 0
1062
    // 1 1 1            (1-1-1) = 1 + borrow-out
1063
    // borrow-out = (~A & B) | (~A & borrow-in) | (A & B & borrow-in)
1064
    // The overflows are simply the borrow-out, now encoded into the sign bits of each field.
1065
    auto overflows = (~A & B) | (~A & borrows_into_sign_bit) | (A & B & borrows_into_sign_bit);
96,740,574✔
1066
    // ^ 6 ops, total latency 6 (4+2)
1067
    return overflows & MSBs; // 1 op, total latency 7
96,740,574✔
1068
    // total of 12 ops and a latency of 7. On a beefy CPU 3-4 of those can run in parallel
1069
    // and still reach a combined latency of 10 or less.
1070
}
96,740,574✔
1071

1072
template <typename Cond>
1073
uint64_t find_all_fields_unsigned(uint64_t MSBs, uint64_t A, uint64_t B);
1074

1075
template <typename Cond>
1076
uint64_t find_all_fields(uint64_t MSBs, uint64_t A, uint64_t B);
1077

1078
template <>
1079
inline uint64_t find_all_fields<NotEqual>(uint64_t MSBs, uint64_t A, uint64_t B)
1080
{
87,109,386✔
1081
    // 0 != A^B, same as asking 0 - (A^B) overflows.
1082
    return unsigned_LT_vector(MSBs, 0, A ^ B);
87,109,386✔
1083
}
87,109,386✔
1084

1085
template <>
1086
inline uint64_t find_all_fields<Equal>(uint64_t MSBs, uint64_t A, uint64_t B)
1087
{
76,824,621✔
1088
    // get the fields which are EQ and negate the result
1089
    auto all_fields_NE = find_all_fields<NotEqual>(MSBs, A, B);
76,824,621✔
1090
    auto all_fields_NE_negated = ~all_fields_NE;
76,824,621✔
1091
    // must filter the negated vector so only MSB are left.
1092
    return MSBs & all_fields_NE_negated;
76,824,621✔
1093
}
76,824,621✔
1094

1095
template <>
1096
inline uint64_t find_all_fields_unsigned<Equal>(uint64_t MSBs, uint64_t A, uint64_t B)
1097
{
162✔
1098
    return find_all_fields<Equal>(MSBs, A, B);
162✔
1099
}
162✔
1100

1101
template <>
1102
inline uint64_t find_all_fields_unsigned<NotEqual>(uint64_t MSBs, uint64_t A, uint64_t B)
1103
{
2,124✔
1104
    return find_all_fields<NotEqual>(MSBs, A, B);
2,124✔
1105
}
2,124✔
1106

1107
template <>
1108
inline uint64_t find_all_fields_unsigned<Less>(uint64_t MSBs, uint64_t A, uint64_t B)
1109
{
10,386✔
1110
    return unsigned_LT_vector(MSBs, A, B);
10,386✔
1111
}
10,386✔
1112

1113
template <>
1114
inline uint64_t find_all_fields_unsigned<LessEqual>(uint64_t MSBs, uint64_t A, uint64_t B)
1115
{
3,642✔
1116
    // Now A <= B is the same as !(A > B) so...
1117
    // reverse A and B to turn (A>B) --> (B<A)
1118
    auto GT = unsigned_LT_vector(MSBs, B, A);
3,642✔
1119
    // Negate the matches
1120
    auto GT_negated = ~GT;
3,642✔
1121
    // and since this negates all bits, filter so we only have MSBs again
1122
    return MSBs & GT_negated;
3,642✔
1123
}
3,642✔
1124

1125
template <>
1126
inline uint64_t find_all_fields_unsigned<GreaterEqual>(uint64_t MSBs, uint64_t A, uint64_t B)
1127
{
3,282✔
1128
    return find_all_fields_unsigned<LessEqual>(MSBs, B, A);
3,282✔
1129
}
3,282✔
1130

1131
template <>
1132
inline uint64_t find_all_fields_unsigned<Greater>(uint64_t MSBs, uint64_t A, uint64_t B)
NEW
1133
{
×
NEW
1134
    return find_all_fields_unsigned<Less>(MSBs, B, A);
×
NEW
1135
}
×
1136

1137
/*
1138
 Handling signed values
1139

1140
 Trial subtraction only works as is for unsigned. We simply transform signed into unsigned
1141
 by pusing all values up by 1<<(field_width-1). This makes all negative values positive and positive
1142
 values remain positive, although larger. Any overflow during the push can be ignored.
1143
 After that transformation Trial subtraction should correctly detect the LT condition.
1144

1145
 */
1146

1147

1148
template <>
1149
inline uint64_t find_all_fields<Less>(uint64_t MSBs, uint64_t A, uint64_t B)
1150
{
12,424,593✔
1151
    auto sign_bits = MSBs;
12,424,593✔
1152
    return unsigned_LT_vector(MSBs, A ^ sign_bits, B ^ sign_bits);
12,424,593✔
1153
}
12,424,593✔
1154

1155
template <>
1156
inline uint64_t find_all_fields<LessEqual>(uint64_t MSBs, uint64_t A, uint64_t B)
1157
{
360✔
1158
    auto sign_bits = MSBs;
360✔
1159
    return find_all_fields_unsigned<LessEqual>(MSBs, A ^ sign_bits, B ^ sign_bits);
360✔
1160
}
360✔
1161

1162
template <>
1163
inline uint64_t find_all_fields<Greater>(uint64_t MSBs, uint64_t A, uint64_t B)
1164
{
6,158,655✔
1165
    // A > B is the same as B < A
1166
    return find_all_fields<Less>(MSBs, B, A);
6,158,655✔
1167
}
6,158,655✔
1168

1169
template <>
1170
inline uint64_t find_all_fields<GreaterEqual>(uint64_t MSBs, uint64_t A, uint64_t B)
1171
{
360✔
1172
    // A >= B is the same as B <= A
1173
    return find_all_fields<LessEqual>(MSBs, B, A);
360✔
1174
}
360✔
1175

1176
} // namespace realm
1177

1178
#endif // REALM_QUERY_CONDITIONS_HPP
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