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

realm / realm-core / 1839

14 Nov 2023 02:09PM UTC coverage: 91.678% (+0.001%) from 91.677%
1839

push

Evergreen

web-flow
Merge pull request #7130 from realm/release/13.23.4

Release/13.23.4

92152 of 168858 branches covered (0.0%)

0 of 2 new or added lines in 1 file covered. (0.0%)

83 existing lines in 14 files now uncovered.

231131 of 252112 relevant lines covered (91.68%)

6623106.44 hits per line

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

93.57
/src/realm/set.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2020 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/set.hpp"
20

21
#include "realm/array_basic.hpp"
22
#include "realm/array_binary.hpp"
23
#include "realm/array_bool.hpp"
24
#include "realm/array_decimal128.hpp"
25
#include "realm/array_fixed_bytes.hpp"
26
#include "realm/array_integer.hpp"
27
#include "realm/array_mixed.hpp"
28
#include "realm/array_string.hpp"
29
#include "realm/array_timestamp.hpp"
30
#include "realm/array_typed_link.hpp"
31
#include "realm/replication.hpp"
32

33
#include <numeric> // std::iota
34

35
namespace realm {
36

37
template <typename T>
38
UpdateStatus Set<T>::update_if_needed() const
39
{
909,774✔
40
    auto status = Base::update_if_needed();
909,774✔
41
    switch (status) {
909,774✔
42
        case UpdateStatus::Detached: {
✔
43
            m_tree.reset();
×
44
            return UpdateStatus::Detached;
×
45
        }
×
46
        case UpdateStatus::NoChange:
741,990✔
47
            if (m_tree && m_tree->is_attached()) {
741,990✔
48
                return UpdateStatus::NoChange;
707,811✔
49
            }
707,811✔
50
            // The tree has not been initialized yet for this accessor, so
16,962✔
51
            // perform lazy initialization by treating it as an update.
16,962✔
52
            [[fallthrough]];
34,179✔
53
        case UpdateStatus::Updated: {
201,963✔
54
            bool attached = init_from_parent(false);
201,963✔
55
            return attached ? UpdateStatus::Updated : UpdateStatus::Detached;
166,002✔
56
        }
×
57
    }
×
58
    REALM_UNREACHABLE();
59
}
×
60

61
template <typename T>
62
UpdateStatus Set<T>::ensure_created()
63
{
90,600✔
64
    auto status = Base::ensure_created();
90,600✔
65
    switch (status) {
90,600✔
66
        case UpdateStatus::Detached:
✔
67
            break; // Not possible (would have thrown earlier).
×
68
        case UpdateStatus::NoChange: {
90,600✔
69
            if (m_tree && m_tree->is_attached()) {
90,600✔
70
                return UpdateStatus::NoChange;
66,918✔
71
            }
66,918✔
72
            // The tree has not been initialized yet for this accessor, so
11,805✔
73
            // perform lazy initialization by treating it as an update.
11,805✔
74
            [[fallthrough]];
23,682✔
75
        }
23,682✔
76
        case UpdateStatus::Updated: {
23,682✔
77
            bool attached = init_from_parent(true);
23,682✔
78
            REALM_ASSERT(attached);
23,682✔
79
            return attached ? UpdateStatus::Updated : UpdateStatus::Detached;
23,682✔
80
        }
×
81
    }
×
82

83
    REALM_UNREACHABLE();
84
}
×
85

86
static bool do_init_from_parent(BPlusTreeBase& tree, bool allow_create)
87
{
225,645✔
88
    if (tree.init_from_parent()) {
225,645✔
89
        // All is well
65,223✔
90
        return true;
130,545✔
91
    }
130,545✔
92

47,262✔
93
    if (!allow_create) {
95,100✔
94
        return false;
71,418✔
95
    }
71,418✔
96

11,805✔
97
    // The ref in the column was NULL, create the tree in place.
11,805✔
98
    tree.create();
23,682✔
99
    REALM_ASSERT(tree.is_attached());
23,682✔
100
    return true;
23,682✔
101
}
23,682✔
102

103
template <typename T>
104
bool Set<T>::init_from_parent(bool allow_create) const
105
{
225,645✔
106
    if (!m_tree) {
225,645✔
107
        m_tree.reset(new BPlusTree<T>(m_obj.get_alloc()));
136,380✔
108
        const ArrayParent* parent = this;
136,380✔
109
        m_tree->set_parent(const_cast<ArrayParent*>(parent), 0);
136,380✔
110
    }
136,380✔
111
    return do_init_from_parent(*m_tree, allow_create);
225,645✔
112
}
225,645✔
113

114
SetBasePtr Obj::get_setbase_ptr(ColKey col_key) const
115
{
117,021✔
116
    auto attr = get_table()->get_column_attr(col_key);
117,021✔
117
    REALM_ASSERT(attr.test(col_attr_Set));
117,021✔
118
    bool nullable = attr.test(col_attr_Nullable);
117,021✔
119

58,335✔
120
    switch (get_table()->get_column_type(col_key)) {
117,021✔
121
        case type_Int:
7,947✔
122
            if (nullable)
7,947✔
123
                return std::make_unique<Set<util::Optional<Int>>>(*this, col_key);
3,663✔
124
            return std::make_unique<Set<Int>>(*this, col_key);
4,284✔
125
        case type_Bool:
5,904✔
126
            if (nullable)
5,904✔
127
                return std::make_unique<Set<util::Optional<Bool>>>(*this, col_key);
3,108✔
128
            return std::make_unique<Set<Bool>>(*this, col_key);
2,796✔
129
        case type_Float:
6,672✔
130
            if (nullable)
6,672✔
131
                return std::make_unique<Set<util::Optional<Float>>>(*this, col_key);
3,492✔
132
            return std::make_unique<Set<Float>>(*this, col_key);
3,180✔
133
        case type_Double:
6,672✔
134
            if (nullable)
6,672✔
135
                return std::make_unique<Set<util::Optional<Double>>>(*this, col_key);
3,492✔
136
            return std::make_unique<Set<Double>>(*this, col_key);
3,180✔
137
        case type_String:
6,672✔
138
            return std::make_unique<Set<String>>(*this, col_key);
6,672✔
139
        case type_Binary:
6,528✔
140
            return std::make_unique<Set<Binary>>(*this, col_key);
6,528✔
141
        case type_Timestamp:
6,600✔
142
            return std::make_unique<Set<Timestamp>>(*this, col_key);
6,600✔
143
        case type_Decimal:
6,198✔
144
            return std::make_unique<Set<Decimal128>>(*this, col_key);
6,198✔
145
        case type_ObjectId:
14,772✔
146
            if (nullable)
14,772✔
147
                return std::make_unique<Set<util::Optional<ObjectId>>>(*this, col_key);
11,976✔
148
            return std::make_unique<Set<ObjectId>>(*this, col_key);
2,796✔
149
        case type_UUID:
5,904✔
150
            if (nullable)
5,904✔
151
                return std::make_unique<Set<util::Optional<UUID>>>(*this, col_key);
3,108✔
152
            return std::make_unique<Set<UUID>>(*this, col_key);
2,796✔
153
        case type_TypedLink:
1,398✔
154
            return std::make_unique<Set<ObjLink>>(*this, col_key);
×
155
        case type_Mixed:
10,146✔
156
            return std::make_unique<Set<Mixed>>(*this, col_key);
10,146✔
157
        case type_Link:
33,006✔
158
            return std::make_unique<LnkSet>(*this, col_key);
33,006✔
159
        default:
1,398✔
160
            REALM_TERMINATE("Unsupported column type.");
161
    }
117,021✔
162
}
117,021✔
163

164
void SetBase::insert_repl(Replication* repl, size_t index, Mixed value) const
165
{
60,600✔
166
    repl->set_insert(*this, index, value);
60,600✔
167
}
60,600✔
168

169
void SetBase::erase_repl(Replication* repl, size_t index, Mixed value) const
170
{
9,456✔
171
    repl->set_erase(*this, index, value);
9,456✔
172
}
9,456✔
173

174
void SetBase::clear_repl(Replication* repl) const
175
{
2,412✔
176
    repl->set_clear(*this);
2,412✔
177
}
2,412✔
178

179
static std::vector<Mixed> convert_to_set(const CollectionBase& rhs)
180
{
426✔
181
    std::vector<Mixed> mixed(rhs.begin(), rhs.end());
426✔
182
    std::sort(mixed.begin(), mixed.end(), SetElementLessThan<Mixed>());
426✔
183
    mixed.erase(std::unique(mixed.begin(), mixed.end()), mixed.end());
426✔
184
    return mixed;
426✔
185
}
426✔
186

187
bool SetBase::is_subset_of(const CollectionBase& rhs) const
188
{
174✔
189
    if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
174✔
190
        return is_subset_of(other_set->begin(), other_set->end());
48✔
191
    }
48✔
192
    auto other_set = convert_to_set(rhs);
126✔
193
    return is_subset_of(other_set.begin(), other_set.end());
126✔
194
}
126✔
195

196
template <class It1, class It2>
197
bool SetBase::is_subset_of(It1 first, It2 last) const
198
{
330✔
199
    return std::includes(first, last, begin(), end(), SetElementLessThan<Mixed>{});
330✔
200
}
330✔
201

202
bool SetBase::is_strict_subset_of(const CollectionBase& rhs) const
203
{
30✔
204
    if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
30✔
205
        return size() != rhs.size() && is_subset_of(other_set->begin(), other_set->end());
6!
206
    }
6✔
207
    auto other_set = convert_to_set(rhs);
24✔
208
    return size() != other_set.size() && is_subset_of(other_set.begin(), other_set.end());
24✔
209
}
24✔
210

211
bool SetBase::is_superset_of(const CollectionBase& rhs) const
212
{
60✔
213
    if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
60✔
214
        return is_superset_of(other_set->begin(), other_set->end());
48✔
215
    }
48✔
216
    auto other_set = convert_to_set(rhs);
12✔
217
    return is_superset_of(other_set.begin(), other_set.end());
12✔
218
}
12✔
219

220
template <class It1, class It2>
221
bool SetBase::is_superset_of(It1 first, It2 last) const
222
{
78✔
223
    return std::includes(begin(), end(), first, last, SetElementLessThan<Mixed>{});
78✔
224
}
78✔
225

226
bool SetBase::is_strict_superset_of(const CollectionBase& rhs) const
227
{
30✔
228
    if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
30✔
229
        return size() != rhs.size() && is_superset_of(other_set->begin(), other_set->end());
6!
230
    }
6✔
231
    auto other_set = convert_to_set(rhs);
24✔
232
    return size() != other_set.size() && is_superset_of(other_set.begin(), other_set.end());
24✔
233
}
24✔
234

235
bool SetBase::intersects(const CollectionBase& rhs) const
236
{
72✔
237
    if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
72✔
238
        return intersects(other_set->begin(), other_set->end());
60✔
239
    }
60✔
240
    auto other_set = convert_to_set(rhs);
12✔
241
    return intersects(other_set.begin(), other_set.end());
12✔
242
}
12✔
243

244
template <class It1, class It2>
245
bool SetBase::intersects(It1 first, It2 last) const
246
{
72✔
247
    SetElementLessThan<Mixed> less;
72✔
248
    auto it = begin();
72✔
249
    while (it != end() && first != last) {
192✔
250
        if (less(*it, *first)) {
180✔
251
            ++it;
24✔
252
        }
24✔
253
        else if (less(*first, *it)) {
156✔
254
            ++first;
96✔
255
        }
96✔
256
        else {
60✔
257
            return true;
60✔
258
        }
60✔
259
    }
180✔
260
    return false;
42✔
261
}
72✔
262

263
bool SetBase::set_equals(const CollectionBase& rhs) const
264
{
162✔
265
    if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
162✔
266
        return size() == rhs.size() && is_subset_of(other_set->begin(), other_set->end());
6✔
267
    }
6✔
268
    auto other_set = convert_to_set(rhs);
156✔
269
    return size() == other_set.size() && is_subset_of(other_set.begin(), other_set.end());
156✔
270
}
156✔
271

272
void SetBase::assign_union(const CollectionBase& rhs)
273
{
60✔
274
    if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
60✔
275
        return assign_union(other_set->begin(), other_set->end());
36✔
276
    }
36✔
277
    auto other_set = convert_to_set(rhs);
24✔
278
    return assign_union(other_set.begin(), other_set.end());
24✔
279
}
24✔
280

281
template <class It1, class It2>
282
void SetBase::assign_union(It1 first, It2 last)
283
{
60✔
284
    std::vector<Mixed> the_diff;
60✔
285
    std::set_difference(first, last, begin(), end(), std::back_inserter(the_diff), SetElementLessThan<Mixed>{});
60✔
286
    // 'the_diff' now contains all the elements that are in foreign set, but not in 'this'
30✔
287
    // Now insert those elements
30✔
288
    for (auto&& value : the_diff) {
120✔
289
        insert_any(value);
120✔
290
    }
120✔
291
}
60✔
292

293
void SetBase::assign_intersection(const CollectionBase& rhs)
294
{
42✔
295
    if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
42✔
296
        return assign_intersection(other_set->begin(), other_set->end());
30✔
297
    }
30✔
298
    auto other_set = convert_to_set(rhs);
12✔
299
    return assign_intersection(other_set.begin(), other_set.end());
12✔
300
}
12✔
301

302
template <class It1, class It2>
303
void SetBase::assign_intersection(It1 first, It2 last)
304
{
42✔
305
    std::vector<Mixed> intersection;
42✔
306
    std::set_intersection(first, last, begin(), end(), std::back_inserter(intersection), SetElementLessThan<Mixed>{});
42✔
307
    clear();
42✔
308
    // Elements in intersection comes from foreign set, so ok to use here
21✔
309
    for (auto&& value : intersection) {
90✔
310
        insert_any(value);
90✔
311
    }
90✔
312
}
42✔
313

314
void SetBase::assign_difference(const CollectionBase& rhs)
315
{
36✔
316
    if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
36✔
317
        return assign_difference(other_set->begin(), other_set->end());
18✔
318
    }
18✔
319
    auto other_set = convert_to_set(rhs);
18✔
320
    return assign_difference(other_set.begin(), other_set.end());
18✔
321
}
18✔
322

323
template <class It1, class It2>
324
void SetBase::assign_difference(It1 first, It2 last)
325
{
36✔
326
    std::vector<Mixed> intersection;
36✔
327
    std::set_intersection(first, last, begin(), end(), std::back_inserter(intersection), SetElementLessThan<Mixed>{});
36✔
328
    // 'intersection' now contains all the elements that are in both foreign set and 'this'.
18✔
329
    // Remove those elements. The elements comes from the foreign set, so ok to refer to.
18✔
330
    for (auto&& value : intersection) {
102✔
331
        erase_any(value);
102✔
332
    }
102✔
333
}
36✔
334

335
void SetBase::assign_symmetric_difference(const CollectionBase& rhs)
336
{
30✔
337
    if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
30✔
338
        return assign_symmetric_difference(other_set->begin(), other_set->end());
12✔
339
    }
12✔
340
    auto other_set = convert_to_set(rhs);
18✔
341
    return assign_symmetric_difference(other_set.begin(), other_set.end());
18✔
342
}
18✔
343

344
template <class It1, class It2>
345
void SetBase::assign_symmetric_difference(It1 first, It2 last)
346
{
30✔
347
    std::vector<Mixed> difference;
30✔
348
    std::set_difference(first, last, begin(), end(), std::back_inserter(difference), SetElementLessThan<Mixed>{});
30✔
349
    std::vector<Mixed> intersection;
30✔
350
    std::set_intersection(first, last, begin(), end(), std::back_inserter(intersection), SetElementLessThan<Mixed>{});
30✔
351
    // Now remove the common elements and add the differences
15✔
352
    for (auto&& value : intersection) {
48✔
353
        erase_any(value);
48✔
354
    }
48✔
355
    for (auto&& value : difference) {
48✔
356
        insert_any(value);
48✔
357
    }
48✔
358
}
30✔
359

360
template <>
361
void Set<ObjKey>::do_insert(size_t ndx, ObjKey target_key)
362
{
17,046✔
363
    auto origin_table = m_obj.get_table();
17,046✔
364
    auto target_table_key = origin_table->get_opposite_table_key(m_col_key);
17,046✔
365
    m_obj.set_backlink(m_col_key, {target_table_key, target_key});
17,046✔
366
    tree().insert(ndx, target_key);
17,046✔
367
    if (target_key.is_unresolved()) {
17,046✔
368
        m_tree->set_context_flag(true);
54✔
369
    }
54✔
370
}
17,046✔
371

372
template <>
373
void Set<ObjKey>::do_erase(size_t ndx)
374
{
5,592✔
375
    auto origin_table = m_obj.get_table();
5,592✔
376
    auto target_table_key = origin_table->get_opposite_table_key(m_col_key);
5,592✔
377
    ObjKey old_key = get(ndx);
5,592✔
378
    CascadeState state(old_key.is_unresolved() ? CascadeState::Mode::All : CascadeState::Mode::Strong);
5,586✔
379

2,796✔
380
    bool recurse = m_obj.remove_backlink(m_col_key, {target_table_key, old_key}, state);
5,592✔
381

2,796✔
382
    m_tree->erase(ndx);
5,592✔
383

2,796✔
384
    if (recurse) {
5,592✔
385
        _impl::TableFriend::remove_recursive(*origin_table, state); // Throws
×
386
    }
×
387
    if (old_key.is_unresolved()) {
5,592✔
388
        // We might have removed the last unresolved link - check it
6✔
389

6✔
390
        // FIXME: Exploit the fact that the values are sorted and unresolved
6✔
391
        // keys have a negative value.
6✔
392
        _impl::check_for_last_unresolved(&tree());
12✔
393
    }
12✔
394
}
5,592✔
395

396
template <>
397
void Set<ObjKey>::do_clear()
398
{
924✔
399
    size_t ndx = size();
924✔
400
    while (ndx--) {
4,752✔
401
        do_erase(ndx);
3,828✔
402
    }
3,828✔
403

462✔
404
    m_tree->set_context_flag(false);
924✔
405
}
924✔
406

407
template <>
408
void Set<ObjKey>::migrate()
NEW
409
{
×
NEW
410
}
×
411

412
template class Set<ObjKey>;
413

414
template <>
415
void Set<ObjLink>::do_insert(size_t ndx, ObjLink target_link)
416
{
30✔
417
    m_obj.set_backlink(m_col_key, target_link);
30✔
418
    tree().insert(ndx, target_link);
30✔
419
}
30✔
420

421
template <>
422
void Set<ObjLink>::do_erase(size_t ndx)
423
{
6✔
424
    ObjLink old_link = get(ndx);
6✔
425
    CascadeState state(old_link.get_obj_key().is_unresolved() ? CascadeState::Mode::All : CascadeState::Mode::Strong);
6✔
426

3✔
427
    bool recurse = m_obj.remove_backlink(m_col_key, old_link, state);
6✔
428

3✔
429
    m_tree->erase(ndx);
6✔
430

3✔
431
    if (recurse) {
6✔
432
        auto table = m_obj.get_table();
×
433
        _impl::TableFriend::remove_recursive(*table, state); // Throws
×
434
    }
×
435
}
6✔
436

437
template <>
438
void Set<Mixed>::do_insert(size_t ndx, Mixed value)
439
{
8,478✔
440
    if (value.is_type(type_TypedLink)) {
8,478✔
441
        auto target_link = value.get<ObjLink>();
4,416✔
442
        m_obj.get_table()->get_parent_group()->validate(target_link);
4,416✔
443
        m_obj.set_backlink(m_col_key, target_link);
4,416✔
444
    }
4,416✔
445
    tree().insert(ndx, value);
8,478✔
446
}
8,478✔
447

448
template <>
449
void Set<Mixed>::do_erase(size_t ndx)
450
{
3,618✔
451
    if (Mixed old_value = get(ndx); old_value.is_type(type_TypedLink)) {
3,618✔
452
        auto old_link = old_value.get<ObjLink>();
1,032✔
453

516✔
454
        CascadeState state(old_link.get_obj_key().is_unresolved() ? CascadeState::Mode::All
516✔
455
                                                                  : CascadeState::Mode::Strong);
1,032✔
456
        bool recurse = m_obj.remove_backlink(m_col_key, old_link, state);
1,032✔
457

516✔
458
        m_tree->erase(ndx);
1,032✔
459

516✔
460
        if (recurse) {
1,032✔
461
            auto table = m_obj.get_table();
×
462
            _impl::TableFriend::remove_recursive(*table, state); // Throws
×
463
        }
×
464
    }
1,032✔
465
    else {
2,586✔
466
        m_tree->erase(ndx);
2,586✔
467
    }
2,586✔
468
}
3,618✔
469

470
template <>
471
void Set<Mixed>::do_clear()
472
{
252✔
473
    size_t ndx = size();
252✔
474
    while (ndx--) {
2,292✔
475
        do_erase(ndx);
2,040✔
476
    }
2,040✔
477
}
252✔
478

479
template <>
480
void Set<Mixed>::migrate()
481
{
6✔
482
    // We should just move all string values to be before the binary values
3✔
483
    size_t first_binary = size();
6✔
484
    for (size_t n = 0; n < size(); n++) {
24✔
485
        if (tree().get(n).is_type(type_Binary)) {
24✔
486
            first_binary = n;
6✔
487
            break;
6✔
488
        }
6✔
489
    }
24✔
490

3✔
491
    for (size_t n = first_binary; n < size(); n++) {
36✔
492
        if (tree().get(n).is_type(type_String)) {
30✔
493
            tree().insert(first_binary, Mixed());
12✔
494
            tree().swap(n + 1, first_binary);
12✔
495
            m_tree->erase(n + 1);
12✔
496
            first_binary++;
12✔
497
        }
12✔
498
    }
30✔
499
}
6✔
500

501
void LnkSet::remove_target_row(size_t link_ndx)
502
{
×
503
    // Deleting the object will automatically remove all links
504
    // to it. So we do not have to manually remove the deleted link
505
    ObjKey k = get(link_ndx);
×
506
    get_target_table()->remove_object(k);
×
507
}
×
508

509
void LnkSet::remove_all_target_rows()
510
{
510✔
511
    if (m_set.update()) {
510✔
512
        _impl::TableFriend::batch_erase_rows(*get_target_table(), m_set.tree());
510✔
513
    }
510✔
514
}
510✔
515

516
void set_sorted_indices(size_t sz, std::vector<size_t>& indices, bool ascending)
517
{
31,878✔
518
    indices.resize(sz);
31,878✔
519
    if (ascending) {
31,878✔
520
        std::iota(indices.begin(), indices.end(), 0);
31,626✔
521
    }
31,626✔
522
    else {
252✔
523
        std::iota(indices.rbegin(), indices.rend(), 0);
252✔
524
    }
252✔
525
}
31,878✔
526

527
template <typename Iterator>
528
static bool partition_points(const Set<Mixed>& set, std::vector<size_t>& indices, Iterator& first_string,
529
                             Iterator& first_binary, Iterator& end)
530
{
1,782✔
531
    first_string = std::partition_point(indices.begin(), indices.end(), [&](size_t i) {
1,734✔
532
        return set.get(i).is_type(type_Bool, type_Int, type_Float, type_Double, type_Decimal);
1,686✔
533
    });
1,686✔
534
    if (first_string == indices.end() || !set.get(*first_string).is_type(type_String))
1,782✔
535
        return false;
1,716✔
536
    first_binary = std::partition_point(first_string + 1, indices.end(), [&](size_t i) {
150✔
537
        return set.get(i).is_type(type_String);
150✔
538
    });
150✔
539
    if (first_binary == indices.end() || !set.get(*first_binary).is_type(type_Binary))
66✔
540
        return false;
30✔
541
    end = std::partition_point(first_binary + 1, indices.end(), [&](size_t i) {
72✔
542
        return set.get(i).is_type(type_Binary);
72✔
543
    });
72✔
544
    return true;
36✔
545
}
36✔
546

547
template <>
548
void Set<Mixed>::sort(std::vector<size_t>& indices, bool ascending) const
549
{
1,782✔
550
    set_sorted_indices(size(), indices, true);
1,782✔
551

891✔
552
    // The on-disk order is bool -> numbers -> string -> binary -> others
891✔
553
    // We want to merge the string and binary sections to match the sort order
891✔
554
    // of other collections. To do this we find the three partition points
891✔
555
    // where the first string occurs, the first binary occurs, and the first
891✔
556
    // non-binary after binaries occurs. If there's no strings or binaries we
891✔
557
    // don't have to do anything. If they're both non-empty, we perform an
891✔
558
    // in-place merge on the strings and binaries.
891✔
559
    std::vector<size_t>::iterator first_string, first_binary, end;
1,782✔
560
    if (partition_points(*this, indices, first_string, first_binary, end)) {
1,782✔
561
        std::inplace_merge(first_string, first_binary, end, [&](auto a, auto b) {
120✔
562
            return get(a) < get(b);
120✔
563
        });
120✔
564
    }
36✔
565
    if (!ascending) {
1,782✔
566
        std::reverse(indices.begin(), indices.end());
36✔
567
    }
36✔
568
}
1,782✔
569

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