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

realm / realm-core / github_pull_request_301264

30 Jul 2024 07:11PM UTC coverage: 91.111% (+0.009%) from 91.102%
github_pull_request_301264

Pull #7936

Evergreen

web-flow
Add support for multi-process subscription state change notifications (#7862)

As with the other multi-process notifications, the core idea here is to
eliminate the in-memory state and produce notifications based entirely on the
current state of the Realm file.

SubscriptionStore::update_state() has been replaced with separate functions for
the specific legal state transitions, which also take a write transaction as a
parameter. These functions are called by PendingBootstrapStore inside the same
write transaction as the bootstrap updates which changed the subscription
state. This is both a minor performance optimization (due to fewer writes) and
eliminates a brief window between the two writes where the Realm file was in an
inconsistent state.

There's a minor functional change here: previously old subscription sets were
superseded when the new one reached the Completed state, and now they are
superseded on AwaitingMark. This aligns it with when the new subscription set
becomes the one which is returned by get_active().
Pull Request #7936: Fix connection callback crashes when reloading with React Native

102800 of 181570 branches covered (56.62%)

216840 of 237996 relevant lines covered (91.11%)

5918493.47 hits per line

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

82.05
/src/realm/string_data.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2017 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 "string_data.hpp"
20

21
#include <vector>
22

23
using namespace realm;
24

25
namespace {
26

27
template <bool has_alternate_pattern>
28
REALM_FORCEINLINE bool matchlike(const StringData& text, const StringData& pattern,
29
                                 const StringData* alternate_pattern = nullptr) noexcept
30
{
23,106✔
31
    // If alternate_pattern is provided, it is assumed to differ from `pattern` only in case.
32
    REALM_ASSERT_DEBUG(has_alternate_pattern == bool(alternate_pattern));
23,106✔
33
    REALM_ASSERT_DEBUG(!alternate_pattern || pattern.size() == alternate_pattern->size());
23,106!
34

35
    std::vector<size_t> textpos;
23,106✔
36
    std::vector<size_t> patternpos;
23,106✔
37
    size_t p1 = 0; // position in text (haystack)
23,106✔
38
    size_t p2 = 0; // position in pattern (needle)
23,106✔
39

40
    while (true) {
667,584✔
41
        if (p1 == text.size()) {
667,584✔
42
            // We're at the end of the text. This is a match if:
43
            // - we're also at the end of the pattern; or
44
            if (p2 == pattern.size())
19,464✔
45
                return true;
18,828✔
46

47
            // - we're at the last character of the pattern, and it's a multi-character wildcard.
48
            if (p2 == pattern.size() - 1 && pattern[p2] == '*')
636✔
49
                return true;
72✔
50

51
            goto no_match;
564✔
52
        }
636✔
53

54
        if (p2 == pattern.size()) {
648,120✔
55
            // We've hit the end of the pattern without matching the entirety of the text.
56
            goto no_match;
3,588✔
57
        }
3,588✔
58

59
        if (pattern[p2] == '*') {
644,532✔
60
            // Multi-character wildcard. Remember our position in case we need to backtrack.
61
            textpos.push_back(p1);
2,208✔
62
            patternpos.push_back(++p2);
2,208✔
63
            continue;
2,208✔
64
        }
2,208✔
65

66
        if (pattern[p2] == '?') {
642,324✔
67
            // utf-8 encoded characters may take up multiple bytes
68
            if ((text[p1] & 0x80) == 0) {
702✔
69
                ++p1;
630✔
70
                ++p2;
630✔
71
                continue;
630✔
72
            }
630✔
73
            else {
72✔
74
                size_t p = 1;
72✔
75
                while (p1 + p != text.size() && (text[p1 + p] & 0xc0) == 0x80)
180✔
76
                    ++p;
108✔
77
                p1 += p;
72✔
78
                ++p2;
72✔
79
                continue;
72✔
80
            }
72✔
81
        }
702✔
82

83
        if (pattern[p2] == text[p1]) {
641,622✔
84
            ++p1;
616,560✔
85
            ++p2;
616,560✔
86
            continue;
616,560✔
87
        }
616,560✔
88

89
        if (has_alternate_pattern && (*alternate_pattern)[p2] == text[p1]) {
25,062!
90
            ++p1;
19,644✔
91
            ++p2;
19,644✔
92
            continue;
19,644✔
93
        }
19,644✔
94

95
    no_match:
9,570✔
96
        if (textpos.empty()) {
9,570✔
97
            // We were performing the outermost level of matching, so if we made it here the text did not match.
98
            return false;
4,050✔
99
        }
4,050✔
100

101
        if (p1 == text.size()) {
5,520✔
102
            // We've hit the end of the text without a match, so backtrack.
103
            textpos.pop_back();
180✔
104
            patternpos.pop_back();
180✔
105
        }
180✔
106

107
        if (textpos.empty()) {
5,520✔
108
            // We finished our last backtrack attempt without finding a match, so the text did not match.
109
            return false;
156✔
110
        }
156✔
111

112
        // Reattempt the match from the next character.
113
        p1 = ++textpos.back();
5,364✔
114
        p2 = patternpos.back();
5,364✔
115
    }
5,364✔
116
}
23,106✔
117

118
} // unnamed namespace
119

120
bool StringData::matchlike(const realm::StringData& text, const realm::StringData& pattern) noexcept
121
{
10,620✔
122
    return ::matchlike<false>(text, pattern);
10,620✔
123
}
10,620✔
124

125
bool StringData::matchlike_ins(const StringData& text, const StringData& pattern_upper,
126
                               const StringData& pattern_lower) noexcept
127
{
12,486✔
128
    return ::matchlike<true>(text, pattern_upper, &pattern_lower);
12,486✔
129
}
12,486✔
130

131

132
namespace {
133
template <size_t = sizeof(void*)>
134
struct Murmur2OrCityHash;
135

136
template <>
137
struct Murmur2OrCityHash<8> {
138
    inline uint_least64_t operator()(const unsigned char* data, size_t len) const noexcept
139
    {
909,084✔
140
        return cityhash_64(data, len);
909,084✔
141
    }
909,084✔
142
};
143

144
template <>
145
struct Murmur2OrCityHash<4> {
146
    inline uint_least32_t operator()(const unsigned char* data, size_t len) const noexcept
147
    {
×
148
        return murmur2_32(data, len);
×
149
    }
×
150
};
151

152
inline uint_least32_t load4(const unsigned char* data)
153
{
14,112✔
154
    uint_least32_t word = 0;
14,112✔
155
    std::memcpy(&word, data, 4);
14,112✔
156
    return word;
14,112✔
157
}
14,112✔
158

159
inline uint_least64_t load8(const unsigned char* data)
160
{
1,773,456✔
161
    uint_least64_t word = 0;
1,773,456✔
162
    std::memcpy(&word, data, 8);
1,773,456✔
163
    return word;
1,773,456✔
164
}
1,773,456✔
165

166
} // unnamed namespace
167

168

169
size_t realm::murmur2_or_cityhash(const unsigned char* data, size_t len) noexcept
170
{
909,084✔
171
    return size_t(Murmur2OrCityHash<>{}(data, len));
909,084✔
172
}
909,084✔
173

174
uint_least32_t realm::murmur2_32(const unsigned char* data, size_t len) noexcept
175
{
6✔
176
    // This implementation is copied from libc++.
177
    // See: https://github.com/llvm-mirror/libcxx/blob/master/include/utility
178

179
    REALM_ASSERT_DEBUG(len <= std::numeric_limits<uint_least32_t>::max());
6✔
180

181
    const uint_least32_t m = 0x5bd1e995UL;
6✔
182
    const uint_least32_t r = 24;
6✔
183
    uint_least32_t h = uint_least32_t(len);
6✔
184

185
    for (; len >= 4; data += 4, len -= 4) {
24✔
186
        uint_least32_t k = load4(data);
18✔
187
        k *= m;
18✔
188
        k ^= k >> r;
18✔
189
        k *= m;
18✔
190
        h *= m;
18✔
191
        h ^= k;
18✔
192
    }
18✔
193

194
    switch (len) {
6✔
195
        case 3:
✔
196
            h ^= data[2] << 16;
×
197
            REALM_FALLTHROUGH;
×
198
        case 2:
✔
199
            h ^= data[1] << 8;
×
200
            REALM_FALLTHROUGH;
×
201
        case 1:
6✔
202
            h ^= data[0];
6✔
203
            h *= m;
6✔
204
    }
6✔
205
    h ^= h >> 13;
6✔
206
    h *= m;
6✔
207
    h ^= h >> 15;
6✔
208
    return h;
6✔
209
}
6✔
210

211
namespace {
212
struct CityHash64 {
213
    // This implementation is copied from libc++.
214
    // See: https://github.com/llvm-mirror/libcxx/blob/master/include/utility
215

216
    static const uint_least64_t k0 = 0xc3a5c85c97cb3127ULL;
217
    static const uint_least64_t k1 = 0xb492b66fbe98f273ULL;
218
    static const uint_least64_t k2 = 0x9ae16a3b2f90404fULL;
219
    static const uint_least64_t k3 = 0xc949d7c7509e6557ULL;
220
    using pair = std::pair<uint_least64_t, uint_least64_t>;
221

222
    uint_least64_t operator()(const unsigned char* data, size_t len) const noexcept
223
    {
909,174✔
224
        if (len <= 32) {
909,174✔
225
            if (len <= 16) {
909,135✔
226
                return hash_len_0_to_16(data, len);
909,123✔
227
            }
909,123✔
228
            else {
12✔
229
                return hash_len_17_to_32(data, len);
12✔
230
            }
12✔
231
        }
909,135✔
232
        else if (len <= 64) {
39✔
233
            return hash_len_33_to_64(data, len);
39✔
234
        }
39✔
235
        uint_least64_t x = load8(data + len - 40);
×
236
        uint_least64_t y = load8(data + len - 16) + load8(data + len - 56);
×
237
        uint_least64_t z = hash_len_16(load8(data + len - 48) + len, load8(data + len - 24));
×
238
        pair v = weak_hash_len_32_with_seeds(data + len - 64, len, z);
×
239
        pair w = weak_hash_len_32_with_seeds(data + len - 32, y + k1, x);
×
240
        x = x * k1 + load8(data);
×
241

242
        // Decrease len to the nearest multiple of 64, and operate on 64-byte
243
        // chunks.
244
        len = (len - 1) & ~static_cast<uint_least64_t>(63);
×
245
        do {
×
246
            x = rotate(x + y + v.first + load8(data + 8), 37) * k1;
×
247
            y = rotate(y + v.second + load8(data + 48), 42) * k1;
×
248
            x ^= w.second;
×
249
            y += v.first + load8(data + 40);
×
250
            z = rotate(z + w.first, 33) * k1;
×
251
            v = weak_hash_len_32_with_seeds(data, v.second * k1, x + w.first);
×
252
            w = weak_hash_len_32_with_seeds(data + 32, z + w.second, y + load8(data + 16));
×
253
            std::swap(z, x);
×
254
            data += 64;
×
255
            len -= 64;
×
256
        } while (len != 0);
×
257
        return hash_len_16(hash_len_16(v.first, w.first) + shift_mix(y) * k1 + z,
×
258
                           hash_len_16(v.second, w.second) + x);
×
259
    }
909,174✔
260

261
    static uint_least64_t hash_len_0_to_16(const unsigned char* data, size_t len) noexcept
262
    {
909,123✔
263
        if (len > 8) {
909,123✔
264
            const auto a = load8(data);
886,509✔
265
            const auto b = load8(data + len - 8);
886,509✔
266
            return hash_len_16(a, rotate_by_at_least_1(b + len, int(len))) ^ b;
886,509✔
267
        }
886,509✔
268
        if (len >= 4) {
22,614✔
269
            const auto a = load4(data);
7,047✔
270
            const auto b = load4(data + len - 4);
7,047✔
271
            return hash_len_16(len + (a << 3), b);
7,047✔
272
        }
7,047✔
273
        if (len > 0) {
15,567✔
274
            const auto a = data[0];
15,294✔
275
            const auto b = data[len >> 1];
15,294✔
276
            const auto c = data[len - 1];
15,294✔
277
            const auto y = static_cast<uint_least32_t>(a) + (static_cast<uint_least32_t>(b) << 8);
15,294✔
278
            const auto z = static_cast<uint_least32_t>(len) + (static_cast<uint_least32_t>(c) << 2);
15,294✔
279
            return shift_mix(y * k2 ^ z * k3) * k2;
15,294✔
280
        }
15,294✔
281
        return k2;
273✔
282
    }
15,567✔
283

284
    static uint_least64_t hash_len_17_to_32(const unsigned char* data, size_t len) noexcept
285
    {
12✔
286
        const auto a = load8(data) * k1;
12✔
287
        const auto b = load8(data + 8);
12✔
288
        const auto c = load8(data + len - 8) * k2;
12✔
289
        const auto d = load8(data + len - 16) * k0;
12✔
290
        return hash_len_16(rotate(a - b, 43) + rotate(c, 30) + d, a + rotate(b ^ k3, 20) - c + len);
12✔
291
    }
12✔
292

293
    static uint_least64_t hash_len_33_to_64(const unsigned char* data, size_t len) noexcept
294
    {
39✔
295
        uint_least64_t z = load8(data + 24);
39✔
296
        uint_least64_t a = load8(data) + (len + load8(data + len - 16)) * k0;
39✔
297
        uint_least64_t b = rotate(a + z, 52);
39✔
298
        uint_least64_t c = rotate(a, 37);
39✔
299
        a += load8(data + 8);
39✔
300
        c += rotate(a, 7);
39✔
301
        a += load8(data + 16);
39✔
302
        uint_least64_t vf = a + z;
39✔
303
        uint_least64_t vs = b + rotate(a, 31) + c;
39✔
304
        a = load8(data + 16) + load8(data + len - 32);
39✔
305
        z += load8(data + len - 8);
39✔
306
        b = rotate(a + z, 52);
39✔
307
        c = rotate(a, 37);
39✔
308
        a += load8(data + len - 24);
39✔
309
        c += rotate(a, 7);
39✔
310
        a += load8(data + len - 16);
39✔
311
        uint_least64_t wf = a + z;
39✔
312
        uint_least64_t ws = b + rotate(a, 31) + c;
39✔
313
        uint_least64_t r = shift_mix((vf + ws) * k2 + (wf + vs) * k0);
39✔
314
        return shift_mix(r * k0 + vs) * k2;
39✔
315
    }
39✔
316

317
    static uint_least64_t hash_len_16(uint_least64_t u, uint_least64_t v) noexcept
318
    {
893,568✔
319
        const uint_least64_t mul = 0x9ddfea08eb382d69ULL;
893,568✔
320
        uint_least64_t a = (u ^ v) * mul;
893,568✔
321
        a ^= (a >> 47);
893,568✔
322
        uint_least64_t b = (v ^ a) * mul;
893,568✔
323
        b ^= (b >> 47);
893,568✔
324
        b *= mul;
893,568✔
325
        return b;
893,568✔
326
    }
893,568✔
327

328
    static pair weak_hash_len_32_with_seeds(uint_least64_t w, uint_least64_t x, uint_least64_t y, uint_least64_t z,
329
                                            uint_least64_t a, uint_least64_t b) noexcept
330
    {
×
331
        a += w;
×
332
        b = rotate(b + a + z, 21);
×
333
        const uint_least64_t c = a;
×
334
        a += x;
×
335
        a += y;
×
336
        b += rotate(a, 44);
×
337
        return pair{a + z, b + c};
×
338
    }
×
339

340
    static pair weak_hash_len_32_with_seeds(const unsigned char* data, uint_least64_t a, uint_least64_t b) noexcept
341
    {
×
342
        return weak_hash_len_32_with_seeds(load8(data), load8(data + 8), load8(data + 16), load8(data + 24), a, b);
×
343
    }
×
344

345
    static inline uint_least64_t rotate(uint_least64_t val, int shift) noexcept
346
    {
348✔
347
        return shift == 0 ? val : rotate_by_at_least_1(val, shift);
348✔
348
    }
348✔
349

350
    static inline uint_least64_t rotate_by_at_least_1(uint_least64_t val, int shift) noexcept
351
    {
886,857✔
352
        return (val >> shift) | (val << (64 - shift));
886,857✔
353
    }
886,857✔
354

355
    static inline uint_least64_t shift_mix(uint_least64_t val) noexcept
356
    {
15,372✔
357
        return val ^ (val >> 47);
15,372✔
358
    }
15,372✔
359
};
360
} // unnamed namespace
361

362
uint_least64_t realm::cityhash_64(const unsigned char* data, size_t len) noexcept
363
{
909,174✔
364
    return CityHash64{}(data, len);
909,174✔
365
}
909,174✔
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