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

realm / realm-core / jorgen.edelbo_391

13 Aug 2024 07:57AM UTC coverage: 91.091% (-0.02%) from 91.107%
jorgen.edelbo_391

Pull #7826

Evergreen

web-flow
Merge pull request #7979 from realm/test-upgrade-files

Create test file in file-format 24
Pull Request #7826: Merge Next major

103486 of 182216 branches covered (56.79%)

3157 of 3519 new or added lines in 54 files covered. (89.71%)

191 existing lines in 22 files now uncovered.

219971 of 241486 relevant lines covered (91.09%)

6652643.8 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
// Does v2 contain v1?
35
struct Contains {
36
    bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const
37
    {
×
38
        return v2.contains(v1);
×
39
    }
×
40
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
41
    {
10,146✔
42
        return v2.contains(v1);
10,146✔
43
    }
10,146✔
44
    bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const
45
    {
5,370✔
46
        return v2.contains(v1);
5,370✔
47
    }
5,370✔
48
    bool operator()(StringData v1, const std::array<uint8_t, 256>& charmap, StringData v2) const
49
    {
33,384✔
50
        return v2.contains(v1, charmap);
33,384✔
51
    }
33,384✔
52

53
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
54
    {
15,324✔
55
        if (m1.is_null())
15,324✔
56
            return !m2.is_null();
36✔
57
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
15,288✔
58
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
10,146✔
59
        }
10,146✔
60
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
5,142✔
61
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
648✔
62
        }
648✔
63
        return false;
4,494✔
64
    }
5,142✔
65

66
    template <class A, class B>
67
    bool operator()(A, B) const
68
    {
69
        REALM_ASSERT(false);
70
        return false;
71
    }
72
    template <class A, class B, class C, class D>
73
    bool operator()(A, B, C, D) const
74
    {
×
75
        REALM_ASSERT(false);
×
76
        return false;
×
77
    }
×
78
    bool operator()(int64_t, int64_t, bool, bool) const
79
    {
×
80
        REALM_ASSERT(false);
×
81
        return false;
×
82
    }
×
83

84
    static std::string description()
85
    {
276✔
86
        return "CONTAINS";
276✔
87
    }
276✔
88

89
    static const int condition = -1;
90
};
91

92
// Does v2 contain something like v1 (wildcard matching)?
93
struct Like {
94
    bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const
95
    {
32,964✔
96
        return v2.like(v1);
32,964✔
97
    }
32,964✔
98
    bool operator()(BinaryData b1, const char*, const char*, BinaryData b2, bool = false, bool = false) const
99
    {
×
100
        StringData s1(b1.data(), b1.size());
×
101
        StringData s2(b2.data(), b2.size());
×
102
        return s2.like(s1);
×
103
    }
×
104
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
105
    {
9,546✔
106
        return v2.like(v1);
9,546✔
107
    }
9,546✔
108
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
109
    {
768✔
110
        StringData s1(b1.data(), b1.size());
768✔
111
        StringData s2(b2.data(), b2.size());
768✔
112
        return s2.like(s1);
768✔
113
    }
768✔
114

115
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
116
    {
14,586✔
117
        if (m1.is_null() && m2.is_null())
14,586✔
118
            return true;
36✔
119
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
14,550✔
120
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
9,546✔
121
        }
9,546✔
122
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
5,004✔
123
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
648✔
124
        }
648✔
125
        return false;
4,356✔
126
    }
5,004✔
127

128
    template <class A, class B>
129
    bool operator()(A, B) const
130
    {
131
        REALM_ASSERT(false);
132
        return false;
133
    }
134

135
    template <class A, class B, class C, class D>
136
    bool operator()(A, B, C, D) const
137
    {
×
138
        REALM_ASSERT(false);
×
139
        return false;
×
140
    }
×
141

142
    bool operator()(int64_t, int64_t, bool, bool) const
143
    {
×
144
        REALM_ASSERT(false);
×
145
        return false;
×
146
    }
×
147

148
    static std::string description()
149
    {
156✔
150
        return "LIKE";
156✔
151
    }
156✔
152

153
    static const int condition = -1;
154
};
155

156
// Does v2 begin with v1?
157
struct BeginsWith {
158
    bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const
159
    {
32,970✔
160
        return v2.begins_with(v1);
32,970✔
161
    }
32,970✔
162
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
163
    {
×
164
        return v2.begins_with(v1);
×
165
    }
×
166
    bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const
167
    {
4,578✔
168
        return v2.begins_with(v1);
4,578✔
169
    }
4,578✔
170

171
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
172
    {
19,806✔
173
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
19,806✔
174
            StringData s1 = m1.get<StringData>();
11,292✔
175
            StringData s2 = m2.get<StringData>();
11,292✔
176
            return s2.begins_with(s1);
11,292✔
177
        }
11,292✔
178
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
8,514✔
179
            BinaryData b1 = m1.get<BinaryData>();
672✔
180
            BinaryData b2 = m2.get<BinaryData>();
672✔
181
            return b2.begins_with(b1);
672✔
182
        }
672✔
183
        return false;
7,842✔
184
    }
8,514✔
185

186
    template <class A, class B, class C, class D>
187
    bool operator()(A, B, C, D) const
188
    {
×
189
        REALM_ASSERT(false);
×
190
        return false;
×
191
    }
×
192
    template <class A, class B>
193
    bool operator()(A, B) const
194
    {
195
        REALM_ASSERT(false);
196
        return false;
197
    }
198

199
    static std::string description()
200
    {
168✔
201
        return "BEGINSWITH";
168✔
202
    }
168✔
203

204
    static const int condition = -1;
205
};
206

207
// Does v2 end with v1?
208
struct EndsWith {
209
    bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const
210
    {
32,970✔
211
        return v2.ends_with(v1);
32,970✔
212
    }
32,970✔
213
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
214
    {
10,224✔
215
        return v2.ends_with(v1);
10,224✔
216
    }
10,224✔
217
    bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const
218
    {
5,274✔
219
        return v2.ends_with(v1);
5,274✔
220
    }
5,274✔
221

222
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
223
    {
21,810✔
224

225
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
21,810✔
226
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
10,224✔
227
        }
10,224✔
228
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
11,586✔
229
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
696✔
230
        }
696✔
231
        return false;
10,890✔
232
    }
11,586✔
233

234
    template <class A, class B>
235
    bool operator()(A, B) const
236
    {
237
        REALM_ASSERT(false);
238
        return false;
239
    }
240
    template <class A, class B, class C, class D>
241
    bool operator()(A, B, C, D) const
242
    {
×
243
        REALM_ASSERT(false);
×
244
        return false;
×
245
    }
×
246

247
    static std::string description()
248
    {
174✔
249
        return "ENDSWITH";
174✔
250
    }
174✔
251

252
    static const int condition = -1;
253
};
254

255
struct Equal {
256
    static const int avx = 0x00; // _CMP_EQ_OQ
257
    //    bool operator()(const bool v1, const bool v2, bool v1null = false, bool v2null = false) const { return v1 ==
258
    //    v2; }
259
    bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const
260
    {
×
261
        return v1 == v2;
×
262
    }
×
263
    bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const
264
    {
2,481,726✔
265
        return v1 == v2;
2,481,726✔
266
    }
2,481,726✔
267

268
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
269
    {
8,037,996✔
270
        return (m1.is_null() && m2.is_null()) || (Mixed::types_are_comparable(m1, m2) && (m1 == m2));
8,037,996✔
271
    }
8,037,996✔
272

273
    template <class T>
274
    bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const
275
    {
3,202,217✔
276
        return (v1null && v2null) || (!v1null && !v2null && v1 == v2);
3,202,217✔
277
    }
3,202,217✔
278
    static const int condition = cond_Equal;
279
    bool can_match(int64_t v, int64_t lbound, int64_t ubound)
280
    {
16,758,267✔
281
        return (v >= lbound && v <= ubound);
17,129,412✔
282
    }
16,758,267✔
283
    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
284
    {
15,190,803✔
285
        return (v == 0 && ubound == 0 && lbound == 0);
15,190,803✔
286
    }
15,190,803✔
287
    bool operator()(int64_t v1, int64_t v2) const
288
    {
1,305,213✔
289
        return v1 == v2;
1,305,213✔
290
    }
1,305,213✔
291

292
    static std::string description()
293
    {
37,209✔
294
        return "==";
37,209✔
295
    }
37,209✔
296
};
297

298
struct NotEqual {
299
    static const int avx = 0x0B; // _CMP_FALSE_OQ
300
    bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const
301
    {
118,764✔
302
        return v1 != v2;
118,764✔
303
    }
118,764✔
304
    // bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const { return v1 != v2; }
305

306
    template <class T>
307
    bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const
308
    {
19,753,533✔
309
        if (!v1null && !v2null)
19,753,533✔
310
            return v1 != v2;
19,700,649✔
311

312
        if (v1null && v2null)
52,884!
313
            return false;
20,458✔
314

315
        return true;
32,426✔
316
    }
52,884✔
317

318
    bool operator()(const QueryValue& m1, const Mixed& m2) const
319
    {
×
320
        return !Equal()(m1, m2);
×
321
    }
×
322

323

324
    static const int condition = cond_NotEqual;
325
    bool can_match(int64_t v, int64_t lbound, int64_t ubound)
326
    {
334,251✔
327
        return !(v == 0 && ubound == 0 && lbound == 0);
334,251✔
328
    }
334,251✔
329
    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
330
    {
332,652✔
331
        return (v > ubound || v < lbound);
332,652✔
332
    }
332,652✔
333
    bool operator()(int64_t v1, int64_t v2) const
334
    {
23,739✔
335
        return v1 != v2;
23,739✔
336
    }
23,739✔
337

338
    template <class A, class B, class C, class D>
339
    bool operator()(A, B, C, D) const = delete;
340

341
    static std::string description()
342
    {
2,898✔
343
        return "!=";
2,898✔
344
    }
2,898✔
345
};
346

347
// Does v2 contain v1?
348
struct ContainsIns {
349
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false,
350
                    bool = false) const
351
    {
×
352
        if (v2.is_null() && !v1.is_null())
×
353
            return false;
×
354

×
355
        if (v1.size() == 0 && !v2.is_null())
×
356
            return true;
×
357

×
358
        return search_case_fold(v2, v1_upper, v1_lower, v1.size()) != v2.size();
×
359
    }
×
360

361
    // Slow version, used if caller hasn't stored an upper and lower case version
362
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
363
    {
17,562✔
364
        if (v2.is_null() && !v1.is_null())
17,562✔
365
            return false;
24✔
366

367
        if (v1.size() == 0 && !v2.is_null())
17,538✔
368
            return true;
8,220✔
369

370
        std::string v1_upper = case_map(v1, true, IgnoreErrors);
9,318✔
371
        std::string v1_lower = case_map(v1, false, IgnoreErrors);
9,318✔
372
        return search_case_fold(v2, v1_upper.c_str(), v1_lower.c_str(), v1.size()) != v2.size();
9,318✔
373
    }
17,538✔
374
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
375
    {
192✔
376
        StringData s1(b1.data(), b1.size());
192✔
377
        StringData s2(b2.data(), b2.size());
192✔
378
        return this->operator()(s1, s2, false, false);
192✔
379
    }
192✔
380

381
    // Case insensitive Boyer-Moore version
382
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower,
383
                    const std::array<uint8_t, 256>& charmap, StringData v2) const
384
    {
7,470✔
385
        if (v2.is_null() && !v1.is_null())
7,470✔
386
            return false;
24✔
387

388
        if (v1.size() == 0 && !v2.is_null())
7,446✔
389
            return true;
24✔
390

391
        return contains_ins(v2, v1_upper, v1_lower, v1.size(), charmap);
7,422✔
392
    }
7,446✔
393

394
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
395
    {
23,202✔
396
        if (m1.is_null())
23,202✔
397
            return !m2.is_null();
66✔
398
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
23,136✔
399
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
17,370✔
400
        }
17,370✔
401
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
5,766✔
402
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
72✔
403
        }
72✔
404
        return false;
5,694✔
405
    }
5,766✔
406

407
    template <class A, class B>
408
    bool operator()(A, B) const
409
    {
410
        REALM_ASSERT(false);
411
        return false;
412
    }
413
    template <class A, class B, class C, class D>
414
    bool operator()(A, B, C, D) const
415
    {
×
416
        REALM_ASSERT(false);
×
417
        return false;
×
418
    }
×
419
    bool operator()(int64_t, int64_t, bool, bool) const
420
    {
×
421
        REALM_ASSERT(false);
×
422
        return false;
×
423
    }
×
424

425
    static std::string description()
426
    {
402✔
427
        return "CONTAINS[c]";
402✔
428
    }
402✔
429

430
    static const int condition = -1;
431
};
432

433
// Does v2 contain something like v1 (wildcard matching)?
434
struct LikeIns {
435
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false,
436
                    bool = false) const
437
    {
24,858✔
438
        if (v2.is_null() || v1.is_null()) {
24,858✔
439
            return (v2.is_null() && v1.is_null());
24,612✔
440
        }
24,612✔
441

442
        return string_like_ins(v2, v1_lower, v1_upper);
246✔
443
    }
24,858✔
444
    bool operator()(BinaryData b1, const char* b1_upper, const char* b1_lower, BinaryData b2, bool = false,
445
                    bool = false) const
446
    {
×
447
        if (b2.is_null() || b1.is_null()) {
×
448
            return (b2.is_null() && b1.is_null());
×
449
        }
×
450
        StringData s2(b2.data(), b2.size());
×
451

×
452
        return string_like_ins(s2, b1_lower, b1_upper);
×
453
    }
×
454

455
    // Slow version, used if caller hasn't stored an upper and lower case version
456
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
457
    {
11,952✔
458
        if (v2.is_null() || v1.is_null()) {
11,952✔
459
            return (v2.is_null() && v1.is_null());
×
460
        }
×
461

462
        std::string v1_upper = case_map(v1, true, IgnoreErrors);
11,952✔
463
        std::string v1_lower = case_map(v1, false, IgnoreErrors);
11,952✔
464
        return string_like_ins(v2, v1_lower, v1_upper);
11,952✔
465
    }
11,952✔
466
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
467
    {
180✔
468
        if (b2.is_null() || b1.is_null()) {
180✔
469
            return (b2.is_null() && b1.is_null());
84✔
470
        }
84✔
471
        StringData s1(b1.data(), b1.size());
96✔
472
        StringData s2(b2.data(), b2.size());
96✔
473

474
        std::string s1_upper = case_map(s1, true, IgnoreErrors);
96✔
475
        std::string s1_lower = case_map(s1, false, IgnoreErrors);
96✔
476
        return string_like_ins(s2, s1_lower, s1_upper);
96✔
477
    }
180✔
478

479
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
480
    {
14,028✔
481
        if (m1.is_null() && m2.is_null())
14,028✔
482
            return true;
42✔
483
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
13,986✔
484
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
11,952✔
485
        }
11,952✔
486
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
2,034✔
487
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
60✔
488
        }
60✔
489
        return false;
1,974✔
490
    }
2,034✔
491

492
    template <class A, class B>
493
    bool operator()(A, B) const
494
    {
495
        REALM_ASSERT(false);
496
        return false;
497
    }
498
    template <class A, class B, class C, class D>
499
    bool operator()(A, B, C, D) const
500
    {
×
501
        REALM_ASSERT(false);
×
502
        return false;
×
503
    }
×
504
    bool operator()(int64_t, int64_t, bool, bool) const
505
    {
×
506
        REALM_ASSERT(false);
×
507
        return false;
×
508
    }
×
509

510
    static std::string description()
511
    {
228✔
512
        return "LIKE[c]";
228✔
513
    }
228✔
514

515
    static const int condition = -1;
516
};
517

518
// Does v2 begin with v1?
519
struct BeginsWithIns {
520
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false,
521
                    bool = false) const
522
    {
24,714✔
523
        if (v2.is_null() && !v1.is_null())
24,714✔
524
            return false;
18✔
525
        return v1.size() <= v2.size() && equal_case_fold(v2.prefix(v1.size()), v1_upper, v1_lower);
24,696✔
526
    }
24,714✔
527

528
    // Slow version, used if caller hasn't stored an upper and lower case version
529
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
530
    {
12,336✔
531
        if (v2.is_null() && !v1.is_null())
12,336✔
532
            return false;
24✔
533

534
        if (v1.size() > v2.size())
12,312✔
535
            return false;
396✔
536
        std::string v1_upper = case_map(v1, true, IgnoreErrors);
11,916✔
537
        std::string v1_lower = case_map(v1, false, IgnoreErrors);
11,916✔
538
        return equal_case_fold(v2.prefix(v1.size()), v1_upper.c_str(), v1_lower.c_str());
11,916✔
539
    }
12,312✔
540
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
541
    {
192✔
542
        StringData s1(b1.data(), b1.size());
192✔
543
        StringData s2(b2.data(), b2.size());
192✔
544
        return this->operator()(s1, s2, false, false);
192✔
545
    }
192✔
546

547
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
548
    {
15,582✔
549
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
15,582✔
550
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
12,144✔
551
        }
12,144✔
552
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
3,438✔
553
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
72✔
554
        }
72✔
555
        return false;
3,366✔
556
    }
3,438✔
557

558
    template <class A, class B>
559
    bool operator()(A, B) const
560
    {
561
        REALM_ASSERT(false);
562
        return false;
563
    }
564
    template <class A, class B, class C, class D>
565
    bool operator()(A, B, C, D) const
566
    {
×
567
        REALM_ASSERT(false);
×
568
        return false;
×
569
    }
×
570
    bool operator()(int64_t, int64_t, bool, bool) const
571
    {
×
572
        REALM_ASSERT(false);
×
573
        return false;
×
574
    }
×
575

576
    static std::string description()
577
    {
252✔
578
        return "BEGINSWITH[c]";
252✔
579
    }
252✔
580

581
    static const int condition = -1;
582
};
583

584
// Does v2 end with v1?
585
struct EndsWithIns {
586
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false,
587
                    bool = false) const
588
    {
24,792✔
589
        if (v2.is_null() && !v1.is_null())
24,792✔
590
            return false;
18✔
591

592
        return v1.size() <= v2.size() && equal_case_fold(v2.suffix(v1.size()), v1_upper, v1_lower);
24,774✔
593
    }
24,792✔
594

595
    // Slow version, used if caller hasn't stored an upper and lower case version
596
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
597
    {
12,102✔
598
        if (v2.is_null() && !v1.is_null())
12,102✔
599
            return false;
24✔
600

601
        if (v1.size() > v2.size())
12,078✔
602
            return false;
336✔
603
        std::string v1_upper = case_map(v1, true, IgnoreErrors);
11,742✔
604
        std::string v1_lower = case_map(v1, false, IgnoreErrors);
11,742✔
605
        return equal_case_fold(v2.suffix(v1.size()), v1_upper.c_str(), v1_lower.c_str());
11,742✔
606
    }
12,078✔
607
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
608
    {
180✔
609
        StringData s1(b1.data(), b1.size());
180✔
610
        StringData s2(b2.data(), b2.size());
180✔
611
        return this->operator()(s1, s2, false, false);
180✔
612
    }
180✔
613

614
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
615
    {
13,812✔
616
        if (m1.is_type(type_String) && m2.is_type(type_String)) {
13,812✔
617
            return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
11,922✔
618
        }
11,922✔
619
        if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
1,890✔
620
            return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
60✔
621
        }
60✔
622
        return false;
1,830✔
623
    }
1,890✔
624

625
    template <class A, class B>
626
    bool operator()(A, B) const
627
    {
628
        REALM_ASSERT(false);
629
        return false;
630
    }
631
    template <class A, class B, class C, class D>
632
    bool operator()(A, B, C, D) const
633
    {
×
634
        REALM_ASSERT(false);
×
635
        return false;
×
636
    }
×
637
    bool operator()(int64_t, int64_t, bool, bool) const
638
    {
×
639
        REALM_ASSERT(false);
×
640
        return false;
×
641
    }
×
642

643
    static std::string description()
644
    {
234✔
645
        return "ENDSWITH[c]";
234✔
646
    }
234✔
647

648
    static const int condition = -1;
649
};
650

651
struct EqualIns {
652
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false,
653
                    bool = false) const
654
    {
25,674✔
655
        if (v1.is_null() != v2.is_null())
25,674✔
656
            return false;
24,582✔
657

658
        return v1.size() == v2.size() && equal_case_fold(v2, v1_upper, v1_lower);
1,092✔
659
    }
25,674✔
660

661
    // Slow version, used if caller hasn't stored an upper and lower case version
662
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
663
    {
18,120✔
664
        if (v1.is_null() != v2.is_null())
18,120✔
665
            return false;
×
666

667
        if (v1.size() != v2.size())
18,120✔
668
            return false;
1,128✔
669
        std::string v1_upper = case_map(v1, true, IgnoreErrors);
16,992✔
670
        std::string v1_lower = case_map(v1, false, IgnoreErrors);
16,992✔
671
        return equal_case_fold(v2, v1_upper.c_str(), v1_lower.c_str());
16,992✔
672
    }
18,120✔
673
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
674
    {
264✔
675
        StringData s1(b1.data(), b1.size());
264✔
676
        StringData s2(b2.data(), b2.size());
264✔
677
        return this->operator()(s1, s2, false, false);
264✔
678
    }
264✔
679

680
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
681
    {
25,776✔
682
        if (m1.is_null() && m2.is_null()) {
25,776✔
683
            return true;
96✔
684
        }
96✔
685
        if (Mixed::types_are_comparable(m1, m2)) {
25,680✔
686
            if (m1.is_type(type_String) && m2.is_type(type_String)) {
18,564✔
687
                return operator()(m1.get<StringData>(), m2.get<StringData>(), false, false);
17,856✔
688
            }
17,856✔
689
            if (m1.is_type(type_Binary) && m2.is_type(type_Binary)) {
708✔
690
                return operator()(m1.get<BinaryData>(), m2.get<BinaryData>(), false, false);
264✔
691
            }
264✔
692
            return m1 == m2;
444✔
693
        }
708✔
694
        return false;
7,116✔
695
    }
25,680✔
696

697
    template <class A, class B>
698
    bool operator()(A, B) const
699
    {
700
        REALM_ASSERT(false);
701
        return false;
702
    }
703
    template <class A, class B, class C, class D>
704
    bool operator()(A, B, C, D) const
705
    {
×
706
        REALM_ASSERT(false);
×
707
        return false;
×
708
    }
×
709
    bool operator()(int64_t, int64_t, bool, bool) const
710
    {
×
711
        REALM_ASSERT(false);
×
712
        return false;
×
713
    }
×
714

715
    static std::string description()
716
    {
114✔
717
        return "==[c]";
114✔
718
    }
114✔
719

720
    static const int condition = -1;
721
};
722

723
struct NotEqualIns {
724
    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false,
725
                    bool = false) const
726
    {
24,624✔
727
        if (v1.is_null() != v2.is_null())
24,624✔
728
            return true;
24,522✔
729
        return v1.size() != v2.size() || !equal_case_fold(v2, v1_upper, v1_lower);
102✔
730
    }
24,624✔
731

732
    // Slow version, used if caller hasn't stored an upper and lower case version
733
    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
734
    {
×
735
        if (v1.is_null() != v2.is_null())
×
736
            return true;
×
737

738
        if (v1.size() != v2.size())
×
739
            return true;
×
740
        std::string v1_upper = case_map(v1, true, IgnoreErrors);
×
741
        std::string v1_lower = case_map(v1, false, IgnoreErrors);
×
742
        return !equal_case_fold(v2, v1_upper.c_str(), v1_lower.c_str());
×
743
    }
×
744
    bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const
745
    {
×
746
        StringData s1(b1.data(), b1.size());
×
747
        StringData s2(b2.data(), b2.size());
×
748
        return this->operator()(s1, s2, false, false);
×
749
    }
×
750

751
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
752
    {
14,556✔
753
        return !EqualIns()(m1, m2);
14,556✔
754
    }
14,556✔
755

756
    template <class A, class B>
757
    bool operator()(A, B) const
758
    {
759
        REALM_ASSERT(false);
760
        return false;
761
    }
762
    template <class A, class B, class C, class D>
763
    bool operator()(A, B, C, D) const
764
    {
×
765
        REALM_ASSERT(false);
×
766
        return false;
×
767
    }
×
768

769
    static std::string description()
770
    {
54✔
771
        return "!=[c]";
54✔
772
    }
54✔
773

774
    static const int condition = -1;
775
};
776

777
struct Greater {
778
    static const int avx = 0x1E; // _CMP_GT_OQ
779
    template <class T>
780
    bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const
781
    {
103,860✔
782
        if (v1null || v2null)
103,860✔
783
            return false;
172✔
784

785
        return v1 > v2;
103,688✔
786
    }
103,860✔
787
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
788
    {
1,016,865✔
789
        return Mixed::types_are_comparable(m1, m2) && (m1 > m2);
1,016,865✔
790
    }
1,016,865✔
791
    static const int condition = cond_Greater;
792
    template <class A, class B, class C, class D>
793
    bool operator()(A, B, C, D) const
794
    {
795
        REALM_ASSERT(false);
796
        return false;
797
    }
798

799
    bool can_match(int64_t v, int64_t lbound, int64_t ubound)
800
    {
355,509✔
801
        static_cast<void>(lbound);
355,509✔
802
        return ubound > v;
355,509✔
803
    }
355,509✔
804
    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
805
    {
342,414✔
806
        static_cast<void>(ubound);
342,414✔
807
        return lbound > v;
342,414✔
808
    }
342,414✔
809
    bool operator()(int64_t v1, int64_t v2) const
810
    {
130,527✔
811
        return v1 > v2;
130,527✔
812
    }
130,527✔
813

814
    static std::string description()
815
    {
2,559✔
816
        return ">";
2,559✔
817
    }
2,559✔
818
};
819

820
struct None {
821
    template <class T>
822
    bool operator()(const T&, const T&, bool = false, bool = false) const
823
    {
×
824
        return true;
×
825
    }
×
826
    static const int condition = cond_None;
827
    template <class A, class B, class C, class D>
828
    bool operator()(A, B, C, D) const
829
    {
830
        REALM_ASSERT(false);
831
        return false;
832
    }
833
    bool can_match(int64_t v, int64_t lbound, int64_t ubound)
834
    {
×
835
        static_cast<void>(lbound);
×
836
        static_cast<void>(ubound);
×
837
        static_cast<void>(v);
×
838
        return true;
×
839
    }
×
840
    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
841
    {
×
842
        static_cast<void>(lbound);
×
843
        static_cast<void>(ubound);
×
844
        static_cast<void>(v);
×
845
        return true;
×
846
    }
×
847

848
    static std::string description()
849
    {
×
850
        return "none";
×
851
    }
×
852
};
853

854
struct NotNull {
855
    template <class T>
856
    bool operator()(const T&, const T&, bool v = false, bool = false) const
857
    {
×
858
        return !v;
×
859
    }
×
860
    static const int condition = cond_LeftNotNull;
861
    template <class A, class B, class C, class D>
862
    bool operator()(A, B, C, D) const
863
    {
864
        REALM_ASSERT(false);
865
        return false;
866
    }
867
    bool can_match(int64_t v, int64_t lbound, int64_t ubound)
868
    {
×
869
        static_cast<void>(lbound);
×
870
        static_cast<void>(ubound);
×
871
        static_cast<void>(v);
×
872
        return true;
×
873
    }
×
874
    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
875
    {
×
876
        static_cast<void>(lbound);
×
877
        static_cast<void>(ubound);
×
878
        static_cast<void>(v);
×
879
        return true;
×
880
    }
×
881
    static std::string description()
882
    {
×
883
        return "!= NULL";
×
884
    }
×
885
};
886

887
struct Less {
888
    static const int avx = 0x11; // _CMP_LT_OQ
889
    template <class T>
890
    bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const
891
    {
91,326✔
892
        if (v1null || v2null)
91,326✔
893
            return false;
118✔
894

895
        return v1 < v2;
91,208✔
896
    }
91,326✔
897

898
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
899
    {
373,701✔
900
        return Mixed::types_are_comparable(m1, m2) && (m1 < m2);
373,701✔
901
    }
373,701✔
902

903
    bool operator()(int64_t v1, int64_t v2) const
904
    {
13,530✔
905
        return v1 < v2;
13,530✔
906
    }
13,530✔
907

908
    template <class A, class B, class C, class D>
909
    bool operator()(A, B, C, D) const
910
    {
911
        REALM_ASSERT(false);
912
        return false;
913
    }
914
    static const int condition = cond_Less;
915
    bool can_match(int64_t v, int64_t lbound, int64_t)
916
    {
251,202✔
917
        return lbound < v;
251,202✔
918
    }
251,202✔
919
    bool will_match(int64_t v, int64_t, int64_t ubound)
920
    {
243,255✔
921
        return ubound < v;
243,255✔
922
    }
243,255✔
923
    static std::string description()
924
    {
519✔
925
        return "<";
519✔
926
    }
519✔
927
};
928

929
struct LessEqual {
930
    static const int avx = 0x12; // _CMP_LE_OQ
931
    template <class T>
932
    bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const
933
    {
740,468✔
934
        if (v1null && v2null)
740,468✔
935
            return true;
10✔
936

937
        return (!v1null && !v2null && v1 <= v2);
740,458✔
938
    }
740,468✔
939
    bool operator()(const util::Optional<bool>& v1, const util::Optional<bool>& v2, bool v1null, bool v2null) const
940
    {
×
941
        if (v1null && v2null)
×
942
            return false;
×
943

944
        return (!v1null && !v2null && *v1 <= *v2);
×
945
    }
×
946

947
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
948
    {
74,964✔
949
        return (m1.is_null() && m2.is_null()) || (Mixed::types_are_comparable(m1, m2) && (m1 <= m2));
74,964✔
950
    }
74,964✔
951
    bool operator()(int64_t v1, int64_t v2) const
952
    {
24✔
953
        return v1 <= v2;
24✔
954
    }
24✔
955

956
    template <class A, class B, class C, class D>
957
    bool operator()(A, B, C, D) const
958
    {
959
        REALM_ASSERT(false);
960
        return false;
961
    }
962
    static std::string description()
963
    {
330✔
964
        return "<=";
330✔
965
    }
330✔
966
    static const int condition = -1;
967
};
968

969
struct GreaterEqual {
970
    static const int avx = 0x1D; // _CMP_GE_OQ
971
    template <class T>
972
    bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const
973
    {
186,134✔
974
        if (v1null && v2null)
186,134!
975
            return true;
10✔
976

977
        return (!v1null && !v2null && v1 >= v2);
186,124✔
978
    }
186,134✔
979
    bool operator()(const util::Optional<bool>& v1, const util::Optional<bool>& v2, bool v1null, bool v2null) const
980
    {
×
981
        if (v1null && v2null)
×
982
            return false;
×
983

984
        return (!v1null && !v2null && *v1 >= *v2);
×
985
    }
×
986

987
    bool operator()(const QueryValue& m1, const QueryValue& m2) const
988
    {
74,586✔
989
        return (m1.is_null() && m2.is_null()) || (Mixed::types_are_comparable(m1, m2) && (m1 >= m2));
74,586✔
990
    }
74,586✔
991
    bool operator()(int64_t v1, int64_t v2) const
992
    {
24✔
993
        return v1 >= v2;
24✔
994
    }
24✔
995

996
    template <class A, class B, class C, class D>
997
    bool operator()(A, B, C, D) const
998
    {
999
        REALM_ASSERT(false);
1000
        return false;
1001
    }
1002
    static std::string description()
1003
    {
642✔
1004
        return ">=";
642✔
1005
    }
642✔
1006
    static const int condition = -1;
1007
};
1008

1009
/* Unsigned LT.
1010

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

1018
 Unsigned LT is also used to find all zero fields or all non-zero fields, so it is
1019
 the backbone of all comparisons returning vectors.
1020
 */
1021

1022
// compute the overflows in unsigned trial subtraction A-B. The overflows
1023
// will be marked by 1 in the sign bit of each field in the result. Other
1024
// bits in the result are zero.
1025
// Overflow are detected for each field pair where A is less than B.
1026
inline uint64_t unsigned_LT_vector(uint64_t MSBs, uint64_t A, uint64_t B)
1027
{
102,867,663✔
1028
    // 1. compute borrow from most significant bit
1029
    // Isolate bitfields inside A and B before subtraction (prevent carries from spilling over)
1030
    // do this by clamping most significant bit in A to 1, and msb in B to 0
1031
    auto A_isolated = A | MSBs;                              // 1 op
102,867,663✔
1032
    auto B_isolated = B & ~MSBs;                             // 2 ops
102,867,663✔
1033
    auto borrows_into_sign_bit = ~(A_isolated - B_isolated); // 2 ops (total latency 4)
102,867,663✔
1034

1035
    // 2. determine what subtraction against most significant bit would give:
1036
    // A B borrow-in:   (A-B-borrow-in)
1037
    // 0 0 0            (0-0-0) = 0
1038
    // 0 0 1            (0-0-1) = 1 + borrow-out
1039
    // 0 1 0            (0-1-0) = 1 + borrow-out
1040
    // 0 1 1            (0-1-1) = 0 + borrow-out
1041
    // 1 0 0            (1-0-0) = 1
1042
    // 1 0 1            (1-0-1) = 0
1043
    // 1 1 0            (1-1-0) = 0
1044
    // 1 1 1            (1-1-1) = 1 + borrow-out
1045
    // borrow-out = (~A & B) | (~A & borrow-in) | (A & B & borrow-in)
1046
    // The overflows are simply the borrow-out, now encoded into the sign bits of each field.
1047
    auto overflows = (~A & B) | (~A & borrows_into_sign_bit) | (A & B & borrows_into_sign_bit);
102,867,663✔
1048
    // ^ 6 ops, total latency 6 (4+2)
1049
    return overflows & MSBs; // 1 op, total latency 7
102,867,663✔
1050
    // total of 12 ops and a latency of 7. On a beefy CPU 3-4 of those can run in parallel
1051
    // and still reach a combined latency of 10 or less.
1052
}
102,867,663✔
1053

1054
template <typename Cond>
1055
uint64_t find_all_fields_unsigned(uint64_t MSBs, uint64_t A, uint64_t B);
1056

1057
template <typename Cond>
1058
uint64_t find_all_fields(uint64_t MSBs, uint64_t A, uint64_t B);
1059

1060
template <>
1061
inline uint64_t find_all_fields<NotEqual>(uint64_t MSBs, uint64_t A, uint64_t B)
1062
{
90,877,287✔
1063
    // 0 != A^B, same as asking 0 - (A^B) overflows.
1064
    return unsigned_LT_vector(MSBs, 0, A ^ B);
90,877,287✔
1065
}
90,877,287✔
1066

1067
template <>
1068
inline uint64_t find_all_fields<Equal>(uint64_t MSBs, uint64_t A, uint64_t B)
1069
{
79,253,802✔
1070
    // get the fields which are EQ and negate the result
1071
    auto all_fields_NE = find_all_fields<NotEqual>(MSBs, A, B);
79,253,802✔
1072
    auto all_fields_NE_negated = ~all_fields_NE;
79,253,802✔
1073
    // must filter the negated vector so only MSB are left.
1074
    return MSBs & all_fields_NE_negated;
79,253,802✔
1075
}
79,253,802✔
1076

1077
template <>
1078
inline uint64_t find_all_fields_unsigned<Equal>(uint64_t MSBs, uint64_t A, uint64_t B)
1079
{
162✔
1080
    return find_all_fields<Equal>(MSBs, A, B);
162✔
1081
}
162✔
1082

1083
template <>
1084
inline uint64_t find_all_fields_unsigned<NotEqual>(uint64_t MSBs, uint64_t A, uint64_t B)
1085
{
2,124✔
1086
    return find_all_fields<NotEqual>(MSBs, A, B);
2,124✔
1087
}
2,124✔
1088

1089
template <>
1090
inline uint64_t find_all_fields_unsigned<Less>(uint64_t MSBs, uint64_t A, uint64_t B)
1091
{
10,386✔
1092
    return unsigned_LT_vector(MSBs, A, B);
10,386✔
1093
}
10,386✔
1094

1095
template <>
1096
inline uint64_t find_all_fields_unsigned<LessEqual>(uint64_t MSBs, uint64_t A, uint64_t B)
1097
{
3,642✔
1098
    // Now A <= B is the same as !(A > B) so...
1099
    // reverse A and B to turn (A>B) --> (B<A)
1100
    auto GT = unsigned_LT_vector(MSBs, B, A);
3,642✔
1101
    // Negate the matches
1102
    auto GT_negated = ~GT;
3,642✔
1103
    // and since this negates all bits, filter so we only have MSBs again
1104
    return MSBs & GT_negated;
3,642✔
1105
}
3,642✔
1106

1107
template <>
1108
inline uint64_t find_all_fields_unsigned<GreaterEqual>(uint64_t MSBs, uint64_t A, uint64_t B)
1109
{
3,282✔
1110
    return find_all_fields_unsigned<LessEqual>(MSBs, B, A);
3,282✔
1111
}
3,282✔
1112

1113
template <>
1114
inline uint64_t find_all_fields_unsigned<Greater>(uint64_t MSBs, uint64_t A, uint64_t B)
NEW
1115
{
×
NEW
1116
    return find_all_fields_unsigned<Less>(MSBs, B, A);
×
NEW
1117
}
×
1118

1119
/*
1120
 Handling signed values
1121

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

1127
 */
1128

1129

1130
template <>
1131
inline uint64_t find_all_fields<Less>(uint64_t MSBs, uint64_t A, uint64_t B)
1132
{
12,435,876✔
1133
    auto sign_bits = MSBs;
12,435,876✔
1134
    return unsigned_LT_vector(MSBs, A ^ sign_bits, B ^ sign_bits);
12,435,876✔
1135
}
12,435,876✔
1136

1137
template <>
1138
inline uint64_t find_all_fields<LessEqual>(uint64_t MSBs, uint64_t A, uint64_t B)
1139
{
360✔
1140
    auto sign_bits = MSBs;
360✔
1141
    return find_all_fields_unsigned<LessEqual>(MSBs, A ^ sign_bits, B ^ sign_bits);
360✔
1142
}
360✔
1143

1144
template <>
1145
inline uint64_t find_all_fields<Greater>(uint64_t MSBs, uint64_t A, uint64_t B)
1146
{
6,170,739✔
1147
    // A > B is the same as B < A
1148
    return find_all_fields<Less>(MSBs, B, A);
6,170,739✔
1149
}
6,170,739✔
1150

1151
template <>
1152
inline uint64_t find_all_fields<GreaterEqual>(uint64_t MSBs, uint64_t A, uint64_t B)
1153
{
360✔
1154
    // A >= B is the same as B <= A
1155
    return find_all_fields<LessEqual>(MSBs, B, A);
360✔
1156
}
360✔
1157

1158
} // namespace realm
1159

1160
#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