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

realm / realm-core / jorgen.edelbo_138

13 Mar 2024 08:41AM UTC coverage: 91.77% (-0.3%) from 92.078%
jorgen.edelbo_138

Pull #7356

Evergreen

jedelbo
Add ability to get path to modified collections in object notifications
Pull Request #7356: Add ability to get path to modified collections in object notifications

94532 of 174642 branches covered (54.13%)

118 of 163 new or added lines in 16 files covered. (72.39%)

765 existing lines in 41 files now uncovered.

242808 of 264584 relevant lines covered (91.77%)

5878961.32 hits per line

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

91.22
/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
#include <realm/object-store/keypath_helpers.hpp>
4

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

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

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

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

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

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

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

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

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

67
std::optional<KeyPathArray> build_key_path_array(realm_key_path_array_t* key_path_array)
68
{
36✔
69
    std::optional<KeyPathArray> ret;
36✔
70
    if (key_path_array && key_path_array->size()) {
36✔
71
        ret.emplace(std::move(*key_path_array));
10✔
72
    }
10✔
73
    return ret;
36✔
74
}
36✔
75

76
} // namespace
77

78
RLM_API realm_key_path_array_t* realm_create_key_path_array(const realm_t* realm,
79
                                                            const realm_class_key_t object_class_key,
80
                                                            size_t num_key_paths, const char** user_key_paths)
81
{
14✔
82
    return wrap_err([&]() {
14✔
83
        KeyPathArray ret;
14✔
84
        if (user_key_paths) {
14✔
85
            ret = (*realm)->create_key_path_array(TableKey(object_class_key), num_key_paths, user_key_paths);
14✔
86
        }
14✔
87
        return new realm_key_path_array_t(std::move(ret));
14✔
88
    });
14✔
89
}
14✔
90

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

106
RLM_API bool realm_object_changes_is_deleted(const realm_object_changes_t* changes)
107
{
4✔
108
    return !changes->deletions.empty();
4✔
109
}
4✔
110

111
RLM_API size_t realm_object_changes_get_num_modified_properties(const realm_object_changes_t* changes)
112
{
2✔
113
    return changes->columns.size();
2✔
114
}
2✔
115

116
RLM_API size_t realm_object_changes_get_num_modified_paths(const realm_object_changes_t* changes)
NEW
117
{
×
NEW
118
    return changes->modified_paths.size();
×
NEW
119
}
×
120

121
RLM_API size_t realm_object_changes_get_modified_properties(const realm_object_changes_t* changes,
122
                                                            realm_property_key_t* out_properties, size_t max)
123
{
8✔
124
    if (!out_properties)
8✔
125
        return changes->columns.size();
2✔
126

3✔
127
    size_t i = 0;
6✔
128
    for (const auto& [col_key_val, index_set] : changes->columns) {
8✔
129
        if (i >= max) {
8✔
130
            break;
2✔
131
        }
2✔
132
        out_properties[i] = col_key_val;
6✔
133
        ++i;
6✔
134
    }
6✔
135
    return i;
6✔
136
}
6✔
137

138
RLM_API size_t realm_object_changes_get_modified_paths(const realm_object_changes_t* const_changes,
139
                                                       realm_string_t* out_paths, size_t max)
140
{
6✔
141
    if (!out_paths)
6✔
NEW
142
        return const_changes->modified_paths.size();
×
143

3✔
144
    realm_object_changes_t* changes = const_cast<realm_object_changes_t*>(const_changes);
6✔
145
    changes->path_buffer.resize(changes->modified_paths.size());
6✔
146
    size_t i = 0;
6✔
147
    for (const auto& p : changes->modified_paths) {
4✔
148
        if (i >= max) {
2✔
NEW
149
            break;
×
NEW
150
        }
×
151
        std::string& path = changes->path_buffer[i];
2✔
152
        path = p[0].get_key();
2✔
153
        for (auto path_elem = p.begin() + 1; path_elem != p.end(); ++path_elem) {
4✔
154
            if (path_elem->is_key()) {
2✔
155
                path += '.';
2✔
156
                path += path_elem->get_key();
2✔
157
            }
2✔
NEW
158
            else {
×
NEW
159
                char buffer[10];
×
NEW
160
                sprintf(buffer, "[%u]", unsigned(path_elem->get_ndx()));
×
NEW
161
                path += buffer;
×
NEW
162
            }
×
163
        }
2✔
164
        out_paths[i].data = path.data();
2✔
165
        out_paths[i].size = path.size();
2✔
166
        ++i;
2✔
167
    }
2✔
168
    return i;
6✔
169
}
6✔
170

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

186
RLM_API realm_notification_token_t* realm_set_add_notification_callback(realm_set_t* set, realm_userdata_t userdata,
187
                                                                        realm_free_userdata_func_t free,
188
                                                                        realm_key_path_array_t* key_path_array,
189
                                                                        realm_on_collection_change_func_t on_change)
190
{
4✔
191
    return wrap_err([&]() {
4✔
192
        CollectionNotificationsCallback cb;
4✔
193
        cb.m_userdata = UserdataPtr{userdata, free};
4✔
194
        cb.m_on_change = on_change;
4✔
195
        auto token = set->add_notification_callback(std::move(cb), build_key_path_array(key_path_array));
4✔
196
        return new realm_notification_token_t{std::move(token)};
4✔
197
    });
4✔
198
}
4✔
199

200
RLM_API realm_notification_token_t*
201
realm_dictionary_add_notification_callback(realm_dictionary_t* dict, realm_userdata_t userdata,
202
                                           realm_free_userdata_func_t free, realm_key_path_array_t* key_path_array,
203
                                           realm_on_dictionary_change_func_t on_change)
204
{
6✔
205
    return wrap_err([&]() {
6✔
206
        DictionaryNotificationsCallback cb;
6✔
207
        cb.m_userdata = UserdataPtr{userdata, free};
6✔
208
        cb.m_on_change = on_change;
6✔
209
        auto token = dict->add_key_based_notification_callback(std::move(cb), build_key_path_array(key_path_array));
6✔
210
        return new realm_notification_token_t{std::move(token)};
6✔
211
    });
6✔
212
}
6✔
213

214
RLM_API realm_notification_token_t*
215
realm_results_add_notification_callback(realm_results_t* results, realm_userdata_t userdata,
216
                                        realm_free_userdata_func_t free, realm_key_path_array_t* key_path_array,
217
                                        realm_on_collection_change_func_t on_change)
218
{
2✔
219
    return wrap_err([&]() {
2✔
220
        CollectionNotificationsCallback cb;
2✔
221
        cb.m_userdata = UserdataPtr{userdata, free};
2✔
222
        cb.m_on_change = on_change;
2✔
223
        auto token = results->add_notification_callback(std::move(cb), build_key_path_array(key_path_array));
2✔
224
        return new realm_notification_token_t{std::move(token)};
2✔
225
    });
2✔
226
}
2✔
227

228
RLM_API void realm_collection_changes_get_num_ranges(const realm_collection_changes_t* changes,
229
                                                     size_t* out_num_deletion_ranges,
230
                                                     size_t* out_num_insertion_ranges,
231
                                                     size_t* out_num_modification_ranges, size_t* out_num_moves)
232
{
6✔
233
    // FIXME: `std::distance()` has O(n) performance here, which seems ridiculous.
3✔
234

3✔
235
    if (out_num_deletion_ranges)
6✔
236
        *out_num_deletion_ranges = std::distance(changes->deletions.begin(), changes->deletions.end());
6✔
237
    if (out_num_insertion_ranges)
6✔
238
        *out_num_insertion_ranges = std::distance(changes->insertions.begin(), changes->insertions.end());
6✔
239
    if (out_num_modification_ranges)
6✔
240
        *out_num_modification_ranges = std::distance(changes->modifications.begin(), changes->modifications.end());
6✔
241
    if (out_num_moves)
6✔
242
        *out_num_moves = changes->moves.size();
6✔
243
}
6✔
244

245
RLM_API void realm_collection_changes_get_num_changes(const realm_collection_changes_t* changes,
246
                                                      size_t* out_num_deletions, size_t* out_num_insertions,
247
                                                      size_t* out_num_modifications, size_t* out_num_moves,
248
                                                      bool* out_collection_was_cleared,
249
                                                      bool* out_collection_was_deleted)
250
{
12✔
251
    // FIXME: This has O(n) performance, which seems ridiculous.
6✔
252

6✔
253
    if (out_num_deletions)
12✔
254
        *out_num_deletions = changes->deletions.count();
12✔
255
    if (out_num_insertions)
12✔
256
        *out_num_insertions = changes->insertions.count();
12✔
257
    if (out_num_modifications)
12✔
258
        *out_num_modifications = changes->modifications.count();
12✔
259
    if (out_num_moves)
12✔
260
        *out_num_moves = changes->moves.size();
6✔
261
    if (out_collection_was_cleared)
12✔
262
        *out_collection_was_cleared = changes->collection_was_cleared;
6✔
263
    if (out_collection_was_deleted)
12✔
264
        *out_collection_was_deleted = changes->collection_root_was_deleted;
6✔
265
}
12✔
266

267
static inline void copy_index_ranges(const IndexSet& index_set, realm_index_range_t* out_ranges, size_t max)
268
{
14✔
269
    size_t i = 0;
14✔
270
    for (auto [from, to] : index_set) {
14✔
271
        if (i >= max)
14✔
272
            return;
×
273
        out_ranges[i++] = realm_index_range_t{from, to};
14✔
274
    }
14✔
275
}
14✔
276

277
RLM_API void realm_collection_changes_get_ranges(
278
    const realm_collection_changes_t* changes, realm_index_range_t* out_deletion_ranges, size_t max_deletion_ranges,
279
    realm_index_range_t* out_insertion_ranges, size_t max_insertion_ranges,
280
    realm_index_range_t* out_modification_ranges, size_t max_modification_ranges,
281
    realm_index_range_t* out_modification_ranges_after, size_t max_modification_ranges_after,
282
    realm_collection_move_t* out_moves, size_t max_moves)
283
{
6✔
284
    if (out_deletion_ranges) {
6✔
285
        copy_index_ranges(changes->deletions, out_deletion_ranges, max_deletion_ranges);
4✔
286
    }
4✔
287
    if (out_insertion_ranges) {
6✔
288
        copy_index_ranges(changes->insertions, out_insertion_ranges, max_insertion_ranges);
6✔
289
    }
6✔
290
    if (out_modification_ranges) {
6✔
291
        copy_index_ranges(changes->modifications, out_modification_ranges, max_modification_ranges);
2✔
292
    }
2✔
293
    if (out_modification_ranges_after) {
6✔
294
        copy_index_ranges(changes->modifications_new, out_modification_ranges_after, max_modification_ranges_after);
2✔
295
    }
2✔
296
    if (out_moves) {
6✔
297
        size_t i = 0;
2✔
298
        for (auto [from, to] : changes->moves) {
1✔
299
            if (i >= max_moves)
×
300
                break;
×
301
            out_moves[i] = realm_collection_move_t{from, to};
×
302
            ++i;
×
303
        }
×
304
    }
2✔
305
}
6✔
306

307
RLM_API void realm_dictionary_get_changes(const realm_dictionary_changes_t* changes, size_t* out_deletions_size,
308
                                          size_t* out_insertion_size, size_t* out_modification_size,
309
                                          bool* out_was_deleted)
310
{
8✔
311
    if (out_deletions_size)
8✔
312
        *out_deletions_size = changes->deletions.size();
8✔
313
    if (out_insertion_size)
8✔
314
        *out_insertion_size = changes->insertions.size();
8✔
315
    if (out_modification_size)
8✔
316
        *out_modification_size = changes->modifications.size();
8✔
317
    if (out_was_deleted)
8✔
318
        *out_was_deleted = changes->collection_root_was_deleted;
6✔
319
}
8✔
320

321
RLM_API void realm_dictionary_get_changed_keys(const realm_dictionary_changes_t* changes,
322
                                               realm_value_t* deletion_keys, size_t* deletions_size,
323
                                               realm_value_t* insertion_keys, size_t* insertions_size,
324
                                               realm_value_t* modification_keys, size_t* modifications_size,
325
                                               bool* collection_was_cleared)
326
{
4✔
327
    auto fill = [](const auto& collection, realm_value_t* out, size_t* n) {
12✔
328
        if (!out || !n)
12✔
329
            return;
4✔
330
        if (collection.size() == 0 || *n < collection.size()) {
8✔
331
            *n = 0;
4✔
332
            return;
4✔
333
        }
4✔
334
        size_t i = 0;
4✔
335
        for (auto val : collection)
4✔
336
            out[i++] = to_capi(val);
6✔
337
        *n = i;
4✔
338
    };
4✔
339

2✔
340
    fill(changes->deletions, deletion_keys, deletions_size);
4✔
341
    fill(changes->insertions, insertion_keys, insertions_size);
4✔
342
    fill(changes->modifications, modification_keys, modifications_size);
4✔
343
    if (collection_was_cleared)
4✔
344
        *collection_was_cleared = changes->collection_was_cleared;
4✔
345
}
4✔
346

347
static inline void copy_indices(const IndexSet& index_set, size_t* out_indices, size_t max)
348
{
8✔
349
    size_t i = 0;
8✔
350
    for (auto index : index_set.as_indexes()) {
10✔
351
        if (i >= max)
10✔
352
            return;
×
353
        out_indices[i] = index;
10✔
354
        ++i;
10✔
355
    }
10✔
356
}
8✔
357

358
RLM_API void realm_collection_changes_get_changes(const realm_collection_changes_t* changes, size_t* out_deletions,
359
                                                  size_t max_deletions, size_t* out_insertions, size_t max_insertions,
360
                                                  size_t* out_modifications, size_t max_modifications,
361
                                                  size_t* out_modifications_after, size_t max_modifications_after,
362
                                                  realm_collection_move_t* out_moves, size_t max_moves)
363
{
2✔
364
    if (out_deletions) {
2✔
365
        copy_indices(changes->deletions, out_deletions, max_deletions);
2✔
366
    }
2✔
367
    if (out_insertions) {
2✔
368
        copy_indices(changes->insertions, out_insertions, max_insertions);
2✔
369
    }
2✔
370
    if (out_modifications) {
2✔
371
        copy_indices(changes->modifications, out_modifications, max_modifications);
2✔
372
    }
2✔
373
    if (out_modifications_after) {
2✔
374
        copy_indices(changes->modifications_new, out_modifications_after, max_modifications_after);
2✔
375
    }
2✔
376
    if (out_moves) {
2✔
377
        size_t i = 0;
2✔
378
        for (auto [from, to] : changes->moves) {
1✔
379
            if (i >= max_moves)
×
380
                break;
×
381
            out_moves[i] = realm_collection_move_t{from, to};
×
382
            ++i;
×
383
        }
×
384
    }
2✔
385
}
2✔
386

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