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

realm / realm-core / jorgen.edelbo_337

03 Jul 2024 01:04PM UTC coverage: 90.864% (-0.1%) from 90.984%
jorgen.edelbo_337

Pull #7826

Evergreen

nicola-cab
Merge branch 'master' of github.com:realm/realm-core into next-major
Pull Request #7826: Merge Next major

102968 of 181176 branches covered (56.83%)

3131 of 3738 new or added lines in 54 files covered. (83.76%)

106 existing lines in 23 files now uncovered.

217725 of 239616 relevant lines covered (90.86%)

6844960.2 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,679✔
46
    int L = jd + 68569;
329,679✔
47
    int n = (4 * L) / 146097;
329,679✔
48
    int i, j;
329,679✔
49

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

60
// Confirmed to work for all val < 16389
61
static void out_dec(char** buffer, unsigned val, int width)
62
{
1,978,074✔
63
    int w = width;
1,978,074✔
64
    char* p = *buffer;
1,978,074✔
65
    while (width > 0) {
6,593,580✔
66
        width--;
4,615,506✔
67
        unsigned div10 = (val * 6554) >> 16;
4,615,506✔
68
        p[width] = char(val - div10 * 10) + '0';
4,615,506✔
69
        val = div10;
4,615,506✔
70
    }
4,615,506✔
71
    *buffer += w;
1,978,074✔
72
}
1,978,074✔
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,679✔
79
    if (is_null()) {
329,679✔
80
        return "null";
×
81
    }
×
82
    int64_t seconds = get_seconds();
329,679✔
83
    int32_t nano = get_nanoseconds();
329,679✔
84
    if (nano < 0) {
329,679✔
85
        nano += Timestamp::nanoseconds_per_second;
6✔
86
        seconds--;
6✔
87
    }
6✔
88

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

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

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

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

108
    char* p = buffer.data();
329,679✔
109
    if (year < 0) {
329,679✔
110
        *p++ = '-';
6✔
111
        year = -year;
6✔
112
    }
6✔
113
    out_dec(&p, year, 4);
329,679✔
114
    *p++ = '-';
329,679✔
115
    out_dec(&p, month, 2);
329,679✔
116
    *p++ = '-';
329,679✔
117
    out_dec(&p, day, 2);
329,679✔
118
    *p++ = ' ';
329,679✔
119
    out_dec(&p, hours, 2);
329,679✔
120
    *p++ = ':';
329,679✔
121
    out_dec(&p, minutes, 2);
329,679✔
122
    *p++ = ':';
329,679✔
123
    out_dec(&p, secs, 2);
329,679✔
124
    *p = '\0';
329,679✔
125
    if (nano) {
329,679✔
126
        snprintf(p, 32 - (p - buffer.data()), ".%09d", nano);
837✔
127
    }
837✔
128
    return buffer.data();
329,679✔
129
}
329,679✔
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
{
28,356✔
189
    // the custom whitelist is different from std::isprint because it doesn't include quotations
190
    const static std::string whitelist = " {|}~:;<=>?@!#$%&()*+,-./[]^_`";
28,356✔
191
    const char* ptr = data.data();
28,356✔
192
    const char* end = ptr + data.size();
28,356✔
193
    while (ptr < end) {
354,252✔
194
        using unsigned_char_t = unsigned char;
328,038✔
195
        char c = *ptr;
328,038✔
196
        size_t len = sequence_length(c);
328,038✔
197
        if (len == 1) {
328,038✔
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) {
327,240✔
200
                return true;
1,398✔
201
            }
1,398✔
202
        }
327,240✔
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;
325,896✔
213
    }
325,896✔
214
    return false;
26,214✔
215
}
28,356✔
216

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

227
    if (contains_invalids(data)) {
28,356✔
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 {
26,214✔
234
        out.reserve(len + 2);
26,214✔
235
        out += '"';
26,214✔
236
        for (const char* i = start; i != start + len; ++i) {
351,900✔
237
            out += *i;
325,686✔
238
        }
325,686✔
239
        out += '"';
26,214✔
240
    }
26,214✔
241
    return out;
28,356✔
242
}
28,500✔
243

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

260
template <>
261
std::string print_value<>(realm::ObjectId oid)
262
{
750,027✔
263
    return "oid(" + oid.to_string() + ")";
750,027✔
264
}
750,027✔
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,893✔
361
    ColumnType col_type = table->get_real_column_type(col_key);
58,893✔
362
    if (col_type == col_type_BackLink) {
58,893✔
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,539✔
371
        std::string col_name = table->get_column_name(col_key);
58,539✔
372
        size_t pos = col_name.find_first_of(" \t\r\n");
58,539✔
373
        while (pos != std::string::npos) {
58,863✔
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,539✔
393
    }
58,539✔
UNCOV
394
    return "";
×
395
}
58,893✔
396

397
std::string SerialisationState::describe_column(ConstTableRef table, ColKey col_key)
398
{
25,653✔
399
    if (table && col_key) {
25,653✔
400
        std::string desc;
25,653✔
401
        if (!subquery_prefix_list.empty()) {
25,653✔
402
            desc += subquery_prefix_list.back() + value_separator;
54✔
403
        }
54✔
404
        desc += get_column_name(table, col_key);
25,653✔
405
        return desc;
25,653✔
406
    }
25,653✔
407
    return "";
×
408
}
25,653✔
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,286✔
422
    std::string desc;
23,286✔
423
    if (!subquery_prefix_list.empty()) {
23,286✔
424
        desc += subquery_prefix_list.back();
180✔
425
    }
180✔
426
    if (link_map.links_exist()) {
23,286✔
427
        if (!desc.empty()) {
8,400✔
428
            desc += util::serializer::value_separator;
120✔
429
        }
120✔
430
        desc += link_map.description(*this);
8,400✔
431
    }
8,400✔
432
    ConstTableRef target = link_map.get_target_table();
23,286✔
433
    if (target && target_col_key) {
23,286✔
434
        if (!desc.empty()) {
21,918✔
435
            desc += util::serializer::value_separator;
7,092✔
436
        }
7,092✔
437
        desc += get_column_name(target, target_col_key);
21,918✔
438
    }
21,918✔
439
    return desc;
23,286✔
440
}
23,286✔
441

442
std::string SerialisationState::describe_expression_type(util::Optional<ExpressionComparisonType> type)
443
{
23,784✔
444
    if (type) {
23,784✔
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 "";
15,942✔
455
}
23,784✔
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