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

realm / realm-core / jonathan.reams_3577

21 Jan 2025 05:41PM UTC coverage: 91.105% (-0.02%) from 91.124%
jonathan.reams_3577

Pull #8064

Evergreen

jbreams
fix test
Pull Request #8064: Sync access token refreshes shouldn't extend SyncSession lifetime

102714 of 181514 branches covered (56.59%)

73 of 73 new or added lines in 3 files covered. (100.0%)

109 existing lines in 18 files now uncovered.

217338 of 238558 relevant lines covered (91.1%)

5558106.05 hits per line

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

90.43
/src/realm/util/serializer.cpp
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
#include <realm/util/serializer.hpp>
20

21
#include <realm/binary_data.hpp>
22
#include <realm/geospatial.hpp>
23
#include <realm/keys.hpp>
24
#include <realm/null.hpp>
25
#include <realm/query_expression.hpp>
26
#include <realm/string_data.hpp>
27
#include <realm/table.hpp>
28
#include <realm/timestamp.hpp>
29
#include <realm/util/base64.hpp>
30

31
#include <cctype>
32
#include <cmath>
33
#include <iomanip>
34

35
namespace realm {
36

37
/* Uses Fliegel & Van Flandern algorithm */
38
static constexpr long date_to_julian(int y, int m, int d)
39
{
×
40
    return (1461 * (y + 4800 + (m - 14) / 12)) / 4 + (367 * (m - 2 - 12 * ((m - 14) / 12))) / 12 -
×
41
           (3 * ((y + 4900 + (m - 14) / 12) / 100)) / 4 + d - 32075;
×
42
}
×
43

44
static void julian_to_date(int jd, int* y, int* m, int* d)
45
{
329,814✔
46
    uint64_t L = jd + 68569;
329,814✔
47
    uint64_t n = (4 * L) / 146097;
329,814✔
48
    uint64_t i, j;
329,814✔
49

50
    L = L - (146097 * n + 3) / 4;
329,814✔
51
    i = (4000 * (L + 1)) / 1461001;
329,814✔
52
    L = L - (1461 * i) / 4 + 31;
329,814✔
53
    j = (80 * L) / 2447;
329,814✔
54
    *d = static_cast<int>(L - (2447 * j) / 80);
329,814✔
55
    L = j / 11;
329,814✔
56
    *m = static_cast<int>(j + 2 - (12 * L));
329,814✔
57
    *y = static_cast<int>(100 * (n - 49) + i + L);
329,814✔
58
}
329,814✔
59

60
// Confirmed to work for all val < 16389
61
static void out_dec(char** buffer, unsigned val, int width)
62
{
1,978,884✔
63
    int w = width;
1,978,884✔
64
    char* p = *buffer;
1,978,884✔
65
    while (width > 0) {
6,596,280✔
66
        width--;
4,617,396✔
67
        unsigned div10 = (val * 6554) >> 16;
4,617,396✔
68
        p[width] = char(val - div10 * 10) + '0';
4,617,396✔
69
        val = div10;
4,617,396✔
70
    }
4,617,396✔
71
    *buffer += w;
1,978,884✔
72
}
1,978,884✔
73

74
static constexpr long epoc_julian_days = date_to_julian(1970, 1, 1); // 2440588
75
static constexpr int seconds_in_a_day = 24 * 60 * 60;
76

77
const char* Timestamp::to_string(std::array<char, 32>& buffer) const
78
{
329,814✔
79
    if (is_null()) {
329,814✔
80
        return "null";
×
81
    }
×
82
    int64_t seconds = get_seconds();
329,814✔
83
    int32_t nano = get_nanoseconds();
329,814✔
84
    if (nano < 0) {
329,814✔
85
        nano += Timestamp::nanoseconds_per_second;
6✔
86
        seconds--;
6✔
87
    }
6✔
88

89
    int64_t days = seconds / seconds_in_a_day;
329,814✔
90
    int julian_days = int(days + epoc_julian_days);
329,814✔
91

92
    int seconds_in_day = int(seconds - days * seconds_in_a_day);
329,814✔
93
    if (seconds_in_day < 0) {
329,814✔
94
        seconds_in_day += seconds_in_a_day;
153,420✔
95
        julian_days--;
153,420✔
96
    }
153,420✔
97

98
    int year;
329,814✔
99
    int month;
329,814✔
100
    int day;
329,814✔
101
    int hours = seconds_in_day / 3600;
329,814✔
102
    int remainingSeconds = seconds_in_day - hours * 3600;
329,814✔
103
    int minutes = remainingSeconds / 60;
329,814✔
104
    int secs = remainingSeconds - minutes * 60;
329,814✔
105

106
    julian_to_date(julian_days, &year, &month, &day);
329,814✔
107

108
    char* p = buffer.data();
329,814✔
109
    if (year < 0) {
329,814✔
110
        *p++ = '-';
6✔
111
        year = -year;
6✔
112
    }
6✔
113
    out_dec(&p, year, 4);
329,814✔
114
    *p++ = '-';
329,814✔
115
    out_dec(&p, month, 2);
329,814✔
116
    *p++ = '-';
329,814✔
117
    out_dec(&p, day, 2);
329,814✔
118
    *p++ = ' ';
329,814✔
119
    out_dec(&p, hours, 2);
329,814✔
120
    *p++ = ':';
329,814✔
121
    out_dec(&p, minutes, 2);
329,814✔
122
    *p++ = ':';
329,814✔
123
    out_dec(&p, secs, 2);
329,814✔
124
    *p = '\0';
329,814✔
125
    if (nano) {
329,814✔
126
        snprintf(p, 32 - (p - buffer.data()), ".%09d", nano);
972✔
127
    }
972✔
128
    return buffer.data();
329,814✔
129
}
329,814✔
130

131
namespace util {
132
namespace serializer {
133

134
template <>
135
std::string print_value<>(BinaryData data)
136
{
3,738✔
137
    if (data.is_null()) {
3,738✔
138
        return "NULL";
114✔
139
    }
114✔
140
    return util::format("binary(%1)", print_value<StringData>(StringData(data.data(), data.size())));
3,624✔
141
}
3,738✔
142

143
template <>
144
std::string print_value<>(bool b)
145
{
864✔
146
    if (b) {
864✔
147
        return "true";
306✔
148
    }
306✔
149
    return "false";
558✔
150
}
864✔
151

152
template <typename T>
153
inline std::string print_with_nan_check(T val)
154
{
2,298✔
155
    // we standardize NaN because some implementations (windows) will
156
    // print the different types of NaN such as "nan(ind)" to indicate "indefinite"
157
    if (std::isnan(val)) {
2,298✔
158
        // preserving the sign of nan is not strictly required but is good etiquette
159
        if (std::signbit(val)) {
48✔
160
            return "-nan";
×
161
        }
×
162
        return "nan";
48✔
163
    }
48✔
164
    std::stringstream ss;
2,250✔
165
    ss << std::setprecision(std::numeric_limits<T>::max_digits10) << val;
2,250✔
166
    return ss.str();
2,250✔
167
}
2,298✔
168

169
template <>
170
std::string print_value<>(float val)
171
{
696✔
172
    return print_with_nan_check(val);
696✔
173
}
696✔
174

175
template <>
176
std::string print_value<>(double val)
177
{
1,602✔
178
    return print_with_nan_check(val);
1,602✔
179
}
1,602✔
180

181
template <>
182
std::string print_value<>(realm::null)
183
{
×
184
    return "NULL";
×
185
}
×
186

187
static bool contains_invalids(StringData data)
188
{
17,454✔
189
    // the custom whitelist is different from std::isprint because it doesn't include quotations
190
    const static std::string whitelist = " {|}~:;<=>?@!#$%&()*+,-./[]^_`";
17,454✔
191
    const char* ptr = data.data();
17,454✔
192
    const char* end = ptr + data.size();
17,454✔
193
    while (ptr < end) {
135,570✔
194
        using unsigned_char_t = unsigned char;
120,258✔
195
        char c = *ptr;
120,258✔
196
        size_t len = sequence_length(c);
120,258✔
197
        if (len == 1) {
120,258✔
198
            // std::isalnum takes an int, but is undefined for negative values so we must pass an unsigned char
199
            if (!std::isalnum(unsigned_char_t(c)) && whitelist.find(c) == std::string::npos) {
119,460✔
200
                return true;
1,398✔
201
            }
1,398✔
202
        }
119,460✔
203
        else {
798✔
204
            // multibyte utf8, check if subsequent bytes have the upper bit set
205
            for (size_t i = 1; i < len; i++) {
888✔
206
                ++ptr;
834✔
207
                if (ptr == end || (*ptr & 0x80) == 0) {
834✔
208
                    return true;
744✔
209
                }
744✔
210
            }
834✔
211
        }
798✔
212
        ++ptr;
118,116✔
213
    }
118,116✔
214
    return false;
15,312✔
215
}
17,454✔
216

217
template <>
218
std::string print_value<>(StringData data)
219
{
17,598✔
220
    if (data.is_null()) {
17,598✔
221
        return "NULL";
144✔
222
    }
144✔
223
    std::string out;
17,454✔
224
    const char* start = data.data();
17,454✔
225
    const size_t len = data.size();
17,454✔
226

227
    if (contains_invalids(data)) {
17,454✔
228
        std::string encode_buffer;
2,142✔
229
        encode_buffer.resize(util::base64_encoded_size(len));
2,142✔
230
        util::base64_encode(data, encode_buffer);
2,142✔
231
        out = "B64\"" + encode_buffer + "\"";
2,142✔
232
    }
2,142✔
233
    else {
15,312✔
234
        out.reserve(len + 2);
15,312✔
235
        out += '"';
15,312✔
236
        for (const char* i = start; i != start + len; ++i) {
133,218✔
237
            out += *i;
117,906✔
238
        }
117,906✔
239
        out += '"';
15,312✔
240
    }
15,312✔
241
    return out;
17,454✔
242
}
17,598✔
243

244
template <>
245
std::string print_value<>(realm::Timestamp t)
246
{
2,304✔
247
    if (t.is_null()) {
2,304✔
248
        return "NULL";
6✔
249
    }
6✔
250
    std::stringstream ss;
2,298✔
251
    if (t.is_null()) {
2,298✔
252
        ss << "null";
×
253
    }
×
254
    else {
2,298✔
255
        ss << "T" << t.get_seconds() << ":" << t.get_nanoseconds();
2,298✔
256
    }
2,298✔
257
    return ss.str();
2,298✔
258
}
2,304✔
259

260
template <>
261
std::string print_value<>(realm::ObjectId oid)
262
{
809,061✔
263
    return "oid(" + oid.to_string() + ")";
809,061✔
264
}
809,061✔
265

266
template <>
267
std::string print_value<>(realm::ObjKey k)
268
{
90✔
269
    std::stringstream ss;
90✔
270
    if (!k) {
90✔
271
        ss << "NULL";
6✔
272
    }
6✔
273
    else {
84✔
274
        ss << "O" << k.value;
84✔
275
    }
84✔
276
    return ss.str();
90✔
277
}
90✔
278

279
std::string print_value(realm::ObjLink link, Group* g)
280
{
348✔
281
    if (!link) {
348✔
282
        return "NULL";
138✔
283
    }
138✔
284
    else {
210✔
285
        TableRef target_table = g ? g->get_table(link.get_table_key()) : TableRef();
210✔
286
        if (ColKey pk_col = target_table ? target_table->get_primary_key_column() : ColKey{}) {
210✔
287
            if (auto obj = target_table->try_get_object(link.get_obj_key())) {
132✔
288
                auto pk_val = obj.get_any(pk_col);
132✔
289
                std::ostringstream ostr;
132✔
290
                ostr << "obj(" << util::serializer::print_value(target_table->get_name()) << "," << pk_val << ')';
132✔
291
                return ostr.str();
132✔
292
            }
132✔
293
        }
132✔
294
    }
210✔
295
    std::stringstream ss;
78✔
296
    ss << "L" << link.get_table_key().value << ":" << link.get_obj_key().value;
78✔
297
    return ss.str();
78✔
298
}
348✔
299

300
template <>
301
std::string print_value<>(realm::UUID uuid)
302
{
2,412✔
303
    return "uuid(" + uuid.to_string() + ")";
2,412✔
304
}
2,412✔
305

306
template <>
307
std::string print_value<>(realm::TypeOfValue type)
308
{
606✔
309
    return '"' + type.to_string() + '"';
606✔
310
}
606✔
311

312
#if REALM_ENABLE_GEOSPATIAL
313
template <>
314
std::string print_value<>(const realm::Geospatial& geo)
315
{
×
316
    return geo.to_string();
×
317
}
×
318
#endif
319

320
// The variable name must be unique with respect to the already chosen variables at
321
// this level of subquery nesting and with respect to the names of the columns in the table.
322
// This assumes that columns can start with '$' and that we might one day want to support
323
// referencing the parent table columns in the subquery. This is currently disabled by an assertion in the
324
// core SubQuery constructor.
325
std::string SerialisationState::get_variable_name(ConstTableRef table)
326
{
228✔
327
    std::string guess_prefix = "$";
228✔
328
    const char start_char = 'x';
228✔
329
    char add_char = start_char;
228✔
330

331
    auto next_guess = [&]() {
228✔
332
        add_char = (((add_char + 1) - 'a') % ('z' - 'a' + 1)) + 'a';
186✔
333
        if (add_char == start_char) {
186✔
334
            guess_prefix += add_char;
6✔
335
        }
6✔
336
    };
186✔
337

338
    while (true) {
414✔
339
        std::string guess = guess_prefix + add_char;
414✔
340
        bool found_duplicate = false;
414✔
341
        for (size_t i = 0; i < subquery_prefix_list.size(); ++i) {
1,104✔
342
            if (guess == subquery_prefix_list[i]) {
720✔
343
                found_duplicate = true;
30✔
344
                break;
30✔
345
            }
30✔
346
        }
720✔
347
        if (found_duplicate) {
414✔
348
            next_guess();
30✔
349
            continue;
30✔
350
        }
30✔
351
        if (table->get_column_key(guess) != ColKey()) {
384✔
352
            next_guess();
156✔
353
            continue;
156✔
354
        }
156✔
355
        return guess;
228✔
356
    }
384✔
357
}
228✔
358

359
std::string SerialisationState::get_column_name(ConstTableRef table, ColKey col_key)
360
{
58,389✔
361
    ColumnType col_type = table->get_real_column_type(col_key);
58,389✔
362
    if (col_type == col_type_BackLink) {
58,389✔
363
        const Table::BacklinkOrigin origin = table->find_backlink_origin(col_key);
354✔
364
        REALM_ASSERT(origin);
354✔
365
        StringData source_table_name = origin->first->get_class_name();
354✔
366
        std::string source_col_name = get_column_name(origin->first, origin->second);
354✔
367

368
        return format("@links.%1.%2", source_table_name, source_col_name);
354✔
369
    }
354✔
370
    else if (col_key != ColKey()) {
58,035✔
371
        std::string col_name = table->get_column_name(col_key);
58,035✔
372
        size_t pos = col_name.find_first_of(" \t\r\n");
58,035✔
373
        while (pos != std::string::npos) {
58,359✔
374
            switch (col_name[pos]) {
324✔
375
                case ' ':
114✔
376
                    // space is unchanged
377
                    break;
114✔
378
                case '\t':
210✔
379
                    col_name[pos] = 't';
210✔
380
                    break;
210✔
381
                case '\r':
✔
382
                    col_name[pos] = 'r';
×
383
                    break;
×
384
                case '\n':
✔
385
                    col_name[pos] = 'n';
×
386
                    break;
×
387
            }
324✔
388
            col_name = col_name.substr(0, pos) + "\\" + col_name.substr(pos);
324✔
389
            pos += 2;
324✔
390
            pos = col_name.find_first_of(" \t\r\n", pos);
324✔
391
        }
324✔
392
        return col_name;
58,035✔
393
    }
58,035✔
UNCOV
394
    return "";
×
395
}
58,389✔
396

397
std::string SerialisationState::describe_column(ConstTableRef table, ColKey col_key)
398
{
25,005✔
399
    if (table && col_key) {
25,005✔
400
        std::string desc;
25,005✔
401
        if (!subquery_prefix_list.empty()) {
25,005✔
402
            desc += subquery_prefix_list.back() + value_separator;
54✔
403
        }
54✔
404
        desc += get_column_name(table, col_key);
25,005✔
405
        return desc;
25,005✔
406
    }
25,005✔
407
    return "";
×
408
}
25,005✔
409

410
std::string SerialisationState::get_backlink_column_name(ConstTableRef from, ColKey col_key)
411
{
×
412
    ColumnType col_type = col_key.get_type();
×
413
    REALM_ASSERT_EX(col_type == col_type_Link, col_type);
×
414

415
    auto target_table = from->get_opposite_table(col_key);
×
416
    auto backlink_col = from->get_opposite_column(col_key);
×
417
    return get_column_name(target_table, backlink_col);
×
418
}
×
419

420
std::string SerialisationState::describe_columns(const LinkMap& link_map, ColKey target_col_key)
421
{
23,358✔
422
    std::string desc;
23,358✔
423
    if (!subquery_prefix_list.empty()) {
23,358✔
424
        desc += subquery_prefix_list.back();
180✔
425
    }
180✔
426
    if (link_map.links_exist()) {
23,358✔
427
        if (!desc.empty()) {
8,472✔
428
            desc += util::serializer::value_separator;
120✔
429
        }
120✔
430
        desc += link_map.description(*this);
8,472✔
431
    }
8,472✔
432
    ConstTableRef target = link_map.get_target_table();
23,358✔
433
    if (target && target_col_key) {
23,358✔
434
        if (!desc.empty()) {
21,990✔
435
            desc += util::serializer::value_separator;
7,164✔
436
        }
7,164✔
437
        desc += get_column_name(target, target_col_key);
21,990✔
438
    }
21,990✔
439
    return desc;
23,358✔
440
}
23,358✔
441

442
std::string SerialisationState::describe_expression_type(util::Optional<ExpressionComparisonType> type)
443
{
23,856✔
444
    if (type) {
23,856✔
445
        switch (*type) {
7,842✔
446
            case ExpressionComparisonType::Any:
3,270✔
447
                return "ANY ";
3,270✔
448
            case ExpressionComparisonType::All:
2,334✔
449
                return "ALL ";
2,334✔
450
            case ExpressionComparisonType::None:
2,238✔
451
                return "NONE ";
2,238✔
452
        }
7,842✔
453
    }
7,842✔
454
    return "";
16,014✔
455
}
23,856✔
456

457
} // namespace serializer
458
} // namespace util
459
} // 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