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

realm / realm-core / 1715

29 Sep 2023 07:16PM UTC coverage: 91.205% (-0.01%) from 91.218%
1715

push

Evergreen

web-flow
Fix object-store-benchmarks build and always build this target (#6999)

* Fix object-store-benchmarks build and always build this target
* Needed to update the target name
* Don't build ObjectStore benchmarks on UWP or Emscripten - We don't have a default scheduler implementation for those platforms
* Update CMakeLists.txt
* Make Object Store benchmarks use the explicit libuv scheduler
* try fix build
* Fix warn_unused_result warning for Android builds
* Fixed some 'duplicate library' warnings
* Updated baas version to latest release
* Updated construction of std::string_view
* Reverted baas commit version due to failures

---------

Co-authored-by: Yavor Georgiev <fealebenpae@users.noreply.github.com>

95908 of 175738 branches covered (0.0%)

1 of 1 new or added line in 1 file covered. (100.0%)

93 existing lines in 11 files now uncovered.

232487 of 254906 relevant lines covered (91.2%)

6682457.84 hits per line

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

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

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

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

166,260✔
89
    int64_t days = seconds / seconds_in_a_day;
332,553✔
90
    int julian_days = int(days + epoc_julian_days);
332,553✔
91

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

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

166,260✔
106
    julian_to_date(julian_days, &year, &month, &day);
332,553✔
107

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

131
namespace util {
132
namespace serializer {
133

134
template <>
135
std::string print_value<>(BinaryData data)
136
{
3,288✔
137
    if (data.is_null()) {
3,288✔
138
        return "NULL";
102✔
139
    }
102✔
140
    return print_value<StringData>(StringData(data.data(), data.size()));
3,186✔
141
}
3,186✔
142

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

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

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

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

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

187
static bool contains_invalids(StringData data)
188
{
66,456✔
189
    // the custom whitelist is different from std::isprint because it doesn't include quotations
32,913✔
190
    const static std::string whitelist = " {|}~:;<=>?@!#$%&()*+,-./[]^_`";
66,456✔
191
    const char* ptr = data.data();
66,456✔
192
    const char* end = ptr + data.size();
66,456✔
193
    while (ptr < end) {
600,480✔
194
        using unsigned_char_t = unsigned char;
536,160✔
195
        char c = *ptr;
536,160✔
196
        size_t len = sequence_length(c);
536,160✔
197
        if (len == 1) {
536,160✔
198
            // std::isalnum takes an int, but is undefined for negative values so we must pass an unsigned char
263,601✔
199
            if (!std::isalnum(unsigned_char_t(c)) && whitelist.find(c) == std::string::npos) {
535,362✔
200
                return true;
1,392✔
201
            }
1,392✔
202
        }
798✔
203
        else {
798✔
204
            // multibyte utf8, check if subsequent bytes have the upper bit set
399✔
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;
535,092✔
213
    }
534,024✔
214
    return false;
65,388✔
215
}
66,456✔
216

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

32,913✔
227
    if (contains_invalids(data)) {
66,456✔
228
        std::string encode_buffer;
2,136✔
229
        encode_buffer.resize(util::base64_encoded_size(len));
2,136✔
230
        util::base64_encode(start, len, encode_buffer.data(), encode_buffer.size());
2,136✔
231
        out = "B64\"" + encode_buffer + "\"";
2,136✔
232
    }
2,136✔
233
    else {
64,320✔
234
        out.reserve(len + 2);
64,320✔
235
        out += '"';
64,320✔
236
        for (const char* i = start; i != start + len; ++i) {
598,134✔
237
            out += *i;
533,814✔
238
        }
533,814✔
239
        out += '"';
64,320✔
240
    }
64,320✔
241
    return out;
66,456✔
242
}
66,456✔
243

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

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

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

279
std::string print_value(realm::ObjLink link, Group* g)
280
{
330✔
281
    if (!link) {
330✔
282
        return "NULL";
126✔
283
    }
126✔
284
    else {
204✔
285
        TableRef target_table = g ? g->get_table(link.get_table_key()) : TableRef();
204✔
286
        if (ColKey pk_col = target_table ? target_table->get_primary_key_column() : ColKey{}) {
204✔
287
            if (auto obj = target_table->try_get_object(link.get_obj_key())) {
126✔
288
                auto pk_val = obj.get_any(pk_col);
126✔
289
                std::ostringstream ostr;
126✔
290
                ostr << "obj(" << util::serializer::print_value(target_table->get_name()) << "," << pk_val << ')';
126✔
291
                return ostr.str();
126✔
292
            }
126✔
293
        }
78✔
294
    }
204✔
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
}
78✔
299

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

306
template <>
307
std::string print_value<>(realm::TypeOfValue type)
308
{
378✔
309
    return '"' + type.to_string() + '"';
378✔
310
}
378✔
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

114✔
331
    auto next_guess = [&]() {
207✔
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

114✔
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
    }
228✔
357
}
228✔
358

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

180✔
368
        return format("@links.%1.%2", source_table_name, source_col_name);
360✔
369
    }
360✔
370
    else if (col_key != ColKey()) {
216,549✔
371
        std::string col_name = table->get_column_name(col_key);
216,549✔
372
        size_t pos = col_name.find_first_of(" \t\r\n");
216,549✔
373
        while (pos != std::string::npos) {
216,873✔
374
            switch (col_name[pos]) {
324✔
375
                case ' ':
114✔
376
                    // space is unchanged
57✔
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;
216,549✔
393
    }
×
394
    return "";
×
395
}
×
396

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

442
std::string SerialisationState::describe_expression_type(util::Optional<ExpressionComparisonType> type)
443
{
19,194✔
444
    if (type) {
19,194✔
445
        switch (*type) {
6,312✔
446
            case ExpressionComparisonType::Any:
2,730✔
447
                return "ANY ";
2,730✔
448
            case ExpressionComparisonType::All:
1,842✔
449
                return "ALL ";
1,842✔
450
            case ExpressionComparisonType::None:
1,740✔
451
                return "NONE ";
1,740✔
452
        }
12,882✔
453
    }
12,882✔
454
    return "";
12,882✔
455
}
12,882✔
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