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

realm / realm-core / nicola.cabiddu_1042

27 Sep 2023 06:04PM CUT coverage: 91.085% (-1.8%) from 92.915%
nicola.cabiddu_1042

Pull #6766

Evergreen

nicola-cab
Fix logic for dictionaries
Pull Request #6766: Client Reset for collections in mixed / nested collections

97276 of 178892 branches covered (0.0%)

1994 of 2029 new or added lines in 7 files covered. (98.28%)

4556 existing lines in 112 files now uncovered.

237059 of 260260 relevant lines covered (91.09%)

6321099.55 hits per line

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

90.83
/src/realm/object-store/c_api/notifications.cpp
1
#include <realm/object-store/c_api/types.hpp>
2
#include <realm/object-store/c_api/util.hpp>
3

4
namespace realm::c_api {
5
namespace {
6
struct ObjectNotificationsCallback {
7
    UserdataPtr m_userdata;
8
    realm_on_object_change_func_t m_on_change = nullptr;
9

10
    ObjectNotificationsCallback() = default;
6✔
11
    ObjectNotificationsCallback(ObjectNotificationsCallback&& other)
12
        : m_userdata(std::exchange(other.m_userdata, nullptr))
13
        , m_on_change(std::exchange(other.m_on_change, nullptr))
14
    {
24✔
15
    }
24✔
16

17
    void operator()(const CollectionChangeSet& changes)
18
    {
12✔
19
        if (m_on_change) {
12✔
20
            realm_object_changes_t c{changes};
12✔
21
            m_on_change(m_userdata.get(), &c);
12✔
22
        }
12✔
23
    }
12✔
24
};
25

26
struct CollectionNotificationsCallback {
27
    UserdataPtr m_userdata;
28
    realm_on_collection_change_func_t m_on_change = nullptr;
29

30
    CollectionNotificationsCallback() = default;
14✔
31
    CollectionNotificationsCallback(CollectionNotificationsCallback&& other)
32
        : m_userdata(std::exchange(other.m_userdata, nullptr))
33
        , m_on_change(std::exchange(other.m_on_change, nullptr))
34
    {
56✔
35
    }
56✔
36

37
    void operator()(const CollectionChangeSet& changes)
38
    {
26✔
39
        if (m_on_change) {
26✔
40
            realm_collection_changes_t c{changes};
26✔
41
            m_on_change(m_userdata.get(), &c);
26✔
42
        }
26✔
43
    }
26✔
44
};
45

46
struct DictionaryNotificationsCallback {
47
    UserdataPtr m_userdata;
48
    realm_on_dictionary_change_func_t m_on_change = nullptr;
49

50
    DictionaryNotificationsCallback() = default;
6✔
51
    DictionaryNotificationsCallback(DictionaryNotificationsCallback&& other)
52
        : m_userdata(std::exchange(other.m_userdata, nullptr))
53
        , m_on_change(std::exchange(other.m_on_change, nullptr))
54
    {
6✔
55
    }
6✔
56

57
    void operator()(const DictionaryChangeSet& changes)
58
    {
12✔
59
        if (m_on_change) {
12✔
60
            realm_dictionary_changes_t c{changes};
12✔
61
            m_on_change(m_userdata.get(), &c);
12✔
62
        }
12✔
63
    }
12✔
64
};
65

66
std::optional<KeyPathArray> build_key_path_array(realm_key_path_array_t* key_path_array)
67
{
26✔
68
    if (key_path_array) {
26✔
69
        KeyPathArray ret;
4✔
70
        for (size_t i = 0; i < key_path_array->nb_elements; i++) {
8✔
71
            realm_key_path_t* key_path = key_path_array->paths + i;
4✔
72
            ret.emplace_back();
4✔
73
            KeyPath& kp = ret.back();
4✔
74
            for (size_t j = 0; j < key_path->nb_elements; j++) {
8✔
75
                realm_key_path_elem_t* path_elem = key_path->path_elements + j;
4✔
76
                kp.emplace_back(TableKey(path_elem->object), ColKey(path_elem->property));
4✔
77
            }
4✔
78
        }
4✔
79
        return ret;
4✔
80
    }
4✔
81
    return std::nullopt;
22✔
82
}
22✔
83

84
} // namespace
85

86
RLM_API realm_notification_token_t* realm_object_add_notification_callback(realm_object_t* obj,
87
                                                                           realm_userdata_t userdata,
88
                                                                           realm_free_userdata_func_t free,
89
                                                                           realm_key_path_array_t* key_path_array,
90
                                                                           realm_on_object_change_func_t on_change)
91
{
6✔
92
    return wrap_err([&]() {
6✔
93
        ObjectNotificationsCallback cb;
6✔
94
        cb.m_userdata = UserdataPtr{userdata, free};
6✔
95
        cb.m_on_change = on_change;
6✔
96
        auto token = obj->add_notification_callback(std::move(cb), build_key_path_array(key_path_array));
6✔
97
        return new realm_notification_token_t{std::move(token)};
6✔
98
    });
6✔
99
}
6✔
100

101
RLM_API bool realm_object_changes_is_deleted(const realm_object_changes_t* changes)
102
{
4✔
103
    return !changes->deletions.empty();
4✔
104
}
4✔
105

106
RLM_API size_t realm_object_changes_get_num_modified_properties(const realm_object_changes_t* changes)
107
{
2✔
108
    return changes->columns.size();
2✔
109
}
2✔
110

111
RLM_API size_t realm_object_changes_get_modified_properties(const realm_object_changes_t* changes,
112
                                                            realm_property_key_t* out_properties, size_t max)
113
{
8✔
114
    if (!out_properties)
8✔
115
        return changes->columns.size();
2✔
116

3✔
117
    size_t i = 0;
6✔
118
    for (const auto& [col_key_val, index_set] : changes->columns) {
8✔
119
        if (i >= max) {
8✔
120
            break;
2✔
121
        }
2✔
122
        out_properties[i] = col_key_val;
6✔
123
        ++i;
6✔
124
    }
6✔
125
    return i;
6✔
126
}
6✔
127

128
RLM_API realm_notification_token_t* realm_list_add_notification_callback(realm_list_t* list,
129
                                                                         realm_userdata_t userdata,
130
                                                                         realm_free_userdata_func_t free,
131
                                                                         realm_key_path_array_t* key_path_array,
132
                                                                         realm_on_collection_change_func_t on_change)
133
{
10✔
134
    return wrap_err([&]() {
10✔
135
        CollectionNotificationsCallback cb;
10✔
136
        cb.m_userdata = UserdataPtr{userdata, free};
10✔
137
        cb.m_on_change = on_change;
10✔
138
        auto token = list->add_notification_callback(std::move(cb), build_key_path_array(key_path_array));
10✔
139
        return new realm_notification_token_t{std::move(token)};
10✔
140
    });
10✔
141
}
10✔
142

143
RLM_API realm_notification_token_t* realm_set_add_notification_callback(realm_set_t* set, realm_userdata_t userdata,
144
                                                                        realm_free_userdata_func_t free,
145
                                                                        realm_key_path_array_t* key_path_array,
146
                                                                        realm_on_collection_change_func_t on_change)
147
{
4✔
148
    return wrap_err([&]() {
4✔
149
        CollectionNotificationsCallback cb;
4✔
150
        cb.m_userdata = UserdataPtr{userdata, free};
4✔
151
        cb.m_on_change = on_change;
4✔
152
        auto token = set->add_notification_callback(std::move(cb), build_key_path_array(key_path_array));
4✔
153
        return new realm_notification_token_t{std::move(token)};
4✔
154
    });
4✔
155
}
4✔
156

157
RLM_API realm_notification_token_t*
158
realm_dictionary_add_notification_callback(realm_dictionary_t* dict, realm_userdata_t userdata,
159
                                           realm_free_userdata_func_t free, realm_key_path_array_t* key_path_array,
160
                                           realm_on_dictionary_change_func_t on_change)
161
{
6✔
162
    return wrap_err([&]() {
6✔
163
        DictionaryNotificationsCallback cb;
6✔
164
        cb.m_userdata = UserdataPtr{userdata, free};
6✔
165
        cb.m_on_change = on_change;
6✔
166
        auto token = dict->add_key_based_notification_callback(std::move(cb), build_key_path_array(key_path_array));
6✔
167
        return new realm_notification_token_t{std::move(token)};
6✔
168
    });
6✔
169
}
6✔
170

171
RLM_API realm_notification_token_t*
172
realm_results_add_notification_callback(realm_results_t* results, realm_userdata_t userdata,
173
                                        realm_free_userdata_func_t free, realm_key_path_array_t* key_path_array,
174
                                        realm_on_collection_change_func_t on_change)
175
{
×
176
    return wrap_err([&]() {
×
177
        CollectionNotificationsCallback cb;
×
178
        cb.m_userdata = UserdataPtr{userdata, free};
×
179
        cb.m_on_change = on_change;
×
180
        auto token = results->add_notification_callback(std::move(cb), build_key_path_array(key_path_array));
×
181
        return new realm_notification_token_t{std::move(token)};
×
182
    });
×
183
}
×
184

185
RLM_API void realm_collection_changes_get_num_ranges(const realm_collection_changes_t* changes,
186
                                                     size_t* out_num_deletion_ranges,
187
                                                     size_t* out_num_insertion_ranges,
188
                                                     size_t* out_num_modification_ranges, size_t* out_num_moves)
189
{
6✔
190
    // FIXME: `std::distance()` has O(n) performance here, which seems ridiculous.
3✔
191

3✔
192
    if (out_num_deletion_ranges)
6✔
193
        *out_num_deletion_ranges = std::distance(changes->deletions.begin(), changes->deletions.end());
6✔
194
    if (out_num_insertion_ranges)
6✔
195
        *out_num_insertion_ranges = std::distance(changes->insertions.begin(), changes->insertions.end());
6✔
196
    if (out_num_modification_ranges)
6✔
197
        *out_num_modification_ranges = std::distance(changes->modifications.begin(), changes->modifications.end());
6✔
198
    if (out_num_moves)
6✔
199
        *out_num_moves = changes->moves.size();
6✔
200
}
6✔
201

202
RLM_API void realm_collection_changes_get_num_changes(const realm_collection_changes_t* changes,
203
                                                      size_t* out_num_deletions, size_t* out_num_insertions,
204
                                                      size_t* out_num_modifications, size_t* out_num_moves,
205
                                                      bool* out_collection_was_cleared,
206
                                                      bool* out_collection_was_deleted)
207
{
12✔
208
    // FIXME: This has O(n) performance, which seems ridiculous.
6✔
209

6✔
210
    if (out_num_deletions)
12✔
211
        *out_num_deletions = changes->deletions.count();
12✔
212
    if (out_num_insertions)
12✔
213
        *out_num_insertions = changes->insertions.count();
12✔
214
    if (out_num_modifications)
12✔
215
        *out_num_modifications = changes->modifications.count();
12✔
216
    if (out_num_moves)
12✔
217
        *out_num_moves = changes->moves.size();
6✔
218
    if (out_collection_was_cleared)
12✔
219
        *out_collection_was_cleared = changes->collection_was_cleared;
6✔
220
    if (out_collection_was_deleted)
12✔
221
        *out_collection_was_deleted = changes->collection_root_was_deleted;
6✔
222
}
12✔
223

224
static inline void copy_index_ranges(const IndexSet& index_set, realm_index_range_t* out_ranges, size_t max)
225
{
14✔
226
    size_t i = 0;
14✔
227
    for (auto [from, to] : index_set) {
14✔
228
        if (i >= max)
14✔
UNCOV
229
            return;
×
230
        out_ranges[i++] = realm_index_range_t{from, to};
14✔
231
    }
14✔
232
}
14✔
233

234
RLM_API void realm_collection_changes_get_ranges(
235
    const realm_collection_changes_t* changes, realm_index_range_t* out_deletion_ranges, size_t max_deletion_ranges,
236
    realm_index_range_t* out_insertion_ranges, size_t max_insertion_ranges,
237
    realm_index_range_t* out_modification_ranges, size_t max_modification_ranges,
238
    realm_index_range_t* out_modification_ranges_after, size_t max_modification_ranges_after,
239
    realm_collection_move_t* out_moves, size_t max_moves)
240
{
6✔
241
    if (out_deletion_ranges) {
6✔
242
        copy_index_ranges(changes->deletions, out_deletion_ranges, max_deletion_ranges);
4✔
243
    }
4✔
244
    if (out_insertion_ranges) {
6✔
245
        copy_index_ranges(changes->insertions, out_insertion_ranges, max_insertion_ranges);
6✔
246
    }
6✔
247
    if (out_modification_ranges) {
6✔
248
        copy_index_ranges(changes->modifications, out_modification_ranges, max_modification_ranges);
2✔
249
    }
2✔
250
    if (out_modification_ranges_after) {
6✔
251
        copy_index_ranges(changes->modifications_new, out_modification_ranges_after, max_modification_ranges_after);
2✔
252
    }
2✔
253
    if (out_moves) {
6✔
254
        size_t i = 0;
2✔
255
        for (auto [from, to] : changes->moves) {
1✔
256
            if (i >= max_moves)
×
257
                break;
×
UNCOV
258
            out_moves[i] = realm_collection_move_t{from, to};
×
UNCOV
259
            ++i;
×
UNCOV
260
        }
×
261
    }
2✔
262
}
6✔
263

264
RLM_API void realm_dictionary_get_changes(const realm_dictionary_changes_t* changes, size_t* out_deletions_size,
265
                                          size_t* out_insertion_size, size_t* out_modification_size,
266
                                          bool* out_was_deleted)
267
{
8✔
268
    if (out_deletions_size)
8✔
269
        *out_deletions_size = changes->deletions.size();
8✔
270
    if (out_insertion_size)
8✔
271
        *out_insertion_size = changes->insertions.size();
8✔
272
    if (out_modification_size)
8✔
273
        *out_modification_size = changes->modifications.size();
8✔
274
    if (out_was_deleted)
8✔
275
        *out_was_deleted = changes->collection_root_was_deleted;
6✔
276
}
8✔
277

278
RLM_API void realm_dictionary_get_changed_keys(const realm_dictionary_changes_t* changes,
279
                                               realm_value_t* deletion_keys, size_t* deletions_size,
280
                                               realm_value_t* insertion_keys, size_t* insertions_size,
281
                                               realm_value_t* modification_keys, size_t* modifications_size,
282
                                               bool* collection_was_cleared)
283
{
4✔
284
    auto fill = [](const auto& collection, realm_value_t* out, size_t* n) {
12✔
285
        if (!out || !n)
12✔
286
            return;
4✔
287
        if (collection.size() == 0 || *n < collection.size()) {
8✔
288
            *n = 0;
4✔
289
            return;
4✔
290
        }
4✔
291
        size_t i = 0;
4✔
292
        for (auto val : collection)
4✔
293
            out[i++] = to_capi(val);
6✔
294
        *n = i;
4✔
295
    };
4✔
296

2✔
297
    fill(changes->deletions, deletion_keys, deletions_size);
4✔
298
    fill(changes->insertions, insertion_keys, insertions_size);
4✔
299
    fill(changes->modifications, modification_keys, modifications_size);
4✔
300
    if (collection_was_cleared)
4✔
301
        *collection_was_cleared = changes->collection_was_cleared;
4✔
302
}
4✔
303

304
static inline void copy_indices(const IndexSet& index_set, size_t* out_indices, size_t max)
305
{
8✔
306
    size_t i = 0;
8✔
307
    for (auto index : index_set.as_indexes()) {
10✔
308
        if (i >= max)
10✔
UNCOV
309
            return;
×
310
        out_indices[i] = index;
10✔
311
        ++i;
10✔
312
    }
10✔
313
}
8✔
314

315
RLM_API void realm_collection_changes_get_changes(const realm_collection_changes_t* changes, size_t* out_deletions,
316
                                                  size_t max_deletions, size_t* out_insertions, size_t max_insertions,
317
                                                  size_t* out_modifications, size_t max_modifications,
318
                                                  size_t* out_modifications_after, size_t max_modifications_after,
319
                                                  realm_collection_move_t* out_moves, size_t max_moves)
320
{
2✔
321
    if (out_deletions) {
2✔
322
        copy_indices(changes->deletions, out_deletions, max_deletions);
2✔
323
    }
2✔
324
    if (out_insertions) {
2✔
325
        copy_indices(changes->insertions, out_insertions, max_insertions);
2✔
326
    }
2✔
327
    if (out_modifications) {
2✔
328
        copy_indices(changes->modifications, out_modifications, max_modifications);
2✔
329
    }
2✔
330
    if (out_modifications_after) {
2✔
331
        copy_indices(changes->modifications_new, out_modifications_after, max_modifications_after);
2✔
332
    }
2✔
333
    if (out_moves) {
2✔
334
        size_t i = 0;
2✔
335
        for (auto [from, to] : changes->moves) {
1✔
UNCOV
336
            if (i >= max_moves)
×
UNCOV
337
                break;
×
UNCOV
338
            out_moves[i] = realm_collection_move_t{from, to};
×
UNCOV
339
            ++i;
×
UNCOV
340
        }
×
341
    }
2✔
342
}
2✔
343

344
} // namespace realm::c_api
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