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

realm / realm-core / jorgen.edelbo_402

21 Aug 2024 11:10AM CUT coverage: 91.054% (-0.03%) from 91.085%
jorgen.edelbo_402

Pull #7803

Evergreen

jedelbo
Small fix to Table::typed_write

When writing the realm to a new file from a write transaction,
the Table may be COW so that the top ref is changed. So don't
use the ref that is present in the group when the operation starts.
Pull Request #7803: Feature/string compression

103494 of 181580 branches covered (57.0%)

1929 of 1999 new or added lines in 46 files covered. (96.5%)

695 existing lines in 51 files now uncovered.

220142 of 241772 relevant lines covered (91.05%)

7344461.76 hits per line

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

86.63
/src/realm/array_string.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/array_string.hpp>
20
#include <realm/impl/array_writer.hpp>
21
#include <realm/table.hpp>
22
#include <realm/string_interner.hpp>
23
#include <realm/string_compressor.hpp>
24
#include <realm/mixed.hpp>
25

26
using namespace realm;
27

28
ArrayString::ArrayString(Allocator& a)
29
    : m_alloc(a)
69,252,618✔
30
{
138,549,834✔
31
    m_arr = new (&m_storage) ArrayStringShort(a, true);
138,549,834✔
32
}
138,549,834✔
33

34
void ArrayString::create()
35
{
553,683✔
36
    static_cast<ArrayStringShort*>(m_arr)->create();
553,683✔
37
}
553,683✔
38

39
void ArrayString::init_from_mem(MemRef mem) noexcept
40
{
131,473,941✔
41
    char* header = mem.get_addr();
131,473,941✔
42

43
    ArrayParent* parent = m_arr->get_parent();
131,473,941✔
44
    size_t ndx_in_parent = m_arr->get_ndx_in_parent();
131,473,941✔
45

46
    bool long_strings = Array::get_hasrefs_from_header(header);
131,473,941✔
47
    if (!long_strings) {
131,473,941✔
48
        // Small strings
49
        bool is_small = Array::get_wtype_from_header(header) == Array::wtype_Multiply;
127,653,831✔
50
        if (is_small) {
127,653,831✔
51
            auto arr = new (&m_storage) ArrayStringShort(m_alloc, m_nullable);
125,600,733✔
52
            arr->init_from_mem(mem);
125,600,733✔
53
            m_type = Type::small_strings;
125,600,733✔
54
        }
125,600,733✔
55
        else {
2,053,098✔
56
            auto arr = new (&m_storage) Array(m_alloc);
2,053,098✔
57
            arr->init_from_mem(mem);
2,053,098✔
58
            // init for new interned strings
59
            m_type = Type::interned_strings;
2,053,098✔
60
            // consider if we want this invariant: REALM_ASSERT_DEBUG(m_string_interner);
61
        }
2,053,098✔
62
    }
127,653,831✔
63
    else {
3,820,110✔
64
        bool is_big = Array::get_context_flag_from_header(header);
3,820,110✔
65
        if (!is_big) {
3,820,110✔
66
            auto arr = new (&m_storage) ArraySmallBlobs(m_alloc);
763,854✔
67
            arr->init_from_mem(mem);
763,854✔
68
            m_type = Type::medium_strings;
763,854✔
69
        }
763,854✔
70
        else {
3,056,256✔
71
            auto arr = new (&m_storage) ArrayBigBlobs(m_alloc, m_nullable);
3,056,256✔
72
            arr->init_from_mem(mem);
3,056,256✔
73
            m_type = Type::big_strings;
3,056,256✔
74
        }
3,056,256✔
75
    }
3,820,110✔
76
    m_arr->set_parent(parent, ndx_in_parent);
131,473,941✔
77
}
131,473,941✔
78

79
void ArrayString::init_from_parent()
80
{
4,650,648✔
81
    ref_type ref = m_arr->get_ref_from_parent();
4,650,648✔
82
    init_from_ref(ref);
4,650,648✔
83
}
4,650,648✔
84

85
void ArrayString::destroy() noexcept
86
{
1,044✔
87
    if (m_arr->is_attached()) {
1,044✔
88
        Array::destroy_deep(m_arr->get_ref(), m_alloc);
468✔
89
        detach();
468✔
90
    }
468✔
91
}
1,044✔
92

93
void ArrayString::detach() noexcept
94
{
4,180,653✔
95
    m_arr->detach();
4,180,653✔
96
    // Make sure the object is in a state like right after construction
97
    // Next call must be to create()
98
    m_arr = new (&m_storage) ArrayStringShort(m_alloc, true);
4,180,653✔
99
    m_type = Type::small_strings;
4,180,653✔
100
}
4,180,653✔
101

102
size_t ArrayString::size() const
103
{
13,308,882✔
104
    switch (m_type) {
13,308,882✔
105
        case Type::small_strings:
3,741,744✔
106
            return static_cast<ArrayStringShort*>(m_arr)->size();
3,741,744✔
107
        case Type::medium_strings:
350,361✔
108
            return static_cast<ArraySmallBlobs*>(m_arr)->size();
350,361✔
109
        case Type::big_strings:
8,833,224✔
110
            return static_cast<ArrayBigBlobs*>(m_arr)->size();
8,833,224✔
111
        case Type::interned_strings:
384,720✔
112
            return static_cast<Array*>(m_arr)->size();
384,720✔
113
    }
13,308,882✔
114
    return {};
×
115
}
13,308,882✔
116

117
void ArrayString::add(StringData value)
118
{
437,829✔
119
    switch (upgrade_leaf(value.size())) {
437,829✔
120
        case Type::small_strings:
384,489✔
121
            static_cast<ArrayStringShort*>(m_arr)->add(value);
384,489✔
122
            break;
384,489✔
123
        case Type::medium_strings:
25,947✔
124
            static_cast<ArraySmallBlobs*>(m_arr)->add_string(value);
25,947✔
125
            break;
25,947✔
126
        case Type::big_strings:
23,526✔
127
            static_cast<ArrayBigBlobs*>(m_arr)->add_string(value);
23,526✔
128
            break;
23,526✔
129
        case Type::interned_strings: {
3,867✔
130
            auto a = static_cast<Array*>(m_arr);
3,867✔
131
            size_t ndx = a->size();
3,867✔
132
            a->add(0);
3,867✔
133
            set(ndx, value);
3,867✔
134
            break;
3,867✔
135
        }
×
136
    }
437,829✔
137
}
437,829✔
138

139
void ArrayString::set(size_t ndx, StringData value)
140
{
2,212,467✔
141
    switch (upgrade_leaf(value.size())) {
2,212,467✔
142
        case Type::small_strings:
452,199✔
143
            static_cast<ArrayStringShort*>(m_arr)->set(ndx, value);
452,199✔
144
            break;
452,199✔
145
        case Type::medium_strings:
220,884✔
146
            static_cast<ArraySmallBlobs*>(m_arr)->set_string(ndx, value);
220,884✔
147
            break;
220,884✔
148
        case Type::big_strings:
768,666✔
149
            static_cast<ArrayBigBlobs*>(m_arr)->set_string(ndx, value);
768,666✔
150
            break;
768,666✔
151
        case Type::interned_strings: {
771,459✔
152
            auto id = m_string_interner->intern(value);
771,459✔
153
            static_cast<Array*>(m_arr)->set(ndx, id);
771,459✔
154
            break;
771,459✔
155
        }
×
156
    }
2,212,467✔
157
}
2,212,467✔
158

159
void ArrayString::insert(size_t ndx, StringData value)
160
{
3,709,617✔
161
    switch (upgrade_leaf(value.size())) {
3,709,617✔
162
        case Type::small_strings:
1,725,309✔
163
            static_cast<ArrayStringShort*>(m_arr)->insert(ndx, value);
1,725,309✔
164
            break;
1,725,309✔
165
        case Type::medium_strings:
270,333✔
166
            static_cast<ArraySmallBlobs*>(m_arr)->insert_string(ndx, value);
270,333✔
167
            break;
270,333✔
168
        case Type::big_strings:
1,309,125✔
169
            static_cast<ArrayBigBlobs*>(m_arr)->insert_string(ndx, value);
1,309,125✔
170
            break;
1,309,125✔
171
        case Type::interned_strings: {
405,543✔
172
            static_cast<Array*>(m_arr)->insert(ndx, 0);
405,543✔
173
            set(ndx, value);
405,543✔
174
            break;
405,543✔
UNCOV
175
        }
×
176
    }
3,709,617✔
177
}
3,709,617✔
178

179
StringData ArrayString::get(size_t ndx) const
180
{
143,856,279✔
181
    switch (m_type) {
143,856,279✔
182
        case Type::small_strings:
125,114,742✔
183
            return static_cast<ArrayStringShort*>(m_arr)->get(ndx);
125,114,742✔
184
        case Type::medium_strings:
1,053,507✔
185
            return static_cast<ArraySmallBlobs*>(m_arr)->get_string(ndx);
1,053,507✔
186
        case Type::big_strings:
9,299,577✔
187
            return static_cast<ArrayBigBlobs*>(m_arr)->get_string(ndx);
9,299,577✔
188
        case Type::interned_strings: {
8,537,646✔
189
            size_t id = size_t(static_cast<Array*>(m_arr)->get(ndx));
8,537,646✔
190
            return m_string_interner->get(id);
8,537,646✔
UNCOV
191
        }
×
192
    }
143,856,279✔
193
    return {};
×
194
}
143,856,279✔
195

196
std::optional<StringID> ArrayString::get_string_id(size_t ndx) const
197
{
60,596,004✔
198
    if (m_type == Type::interned_strings) {
60,596,004✔
199
        return StringID(static_cast<Array*>(m_arr)->get(ndx));
594,840✔
200
    }
594,840✔
201
    return m_string_interner->lookup(get(ndx));
60,001,164✔
202
}
60,596,004✔
203

204
Mixed ArrayString::get_any(size_t ndx) const
205
{
128,637✔
206
    return Mixed(get(ndx));
128,637✔
207
}
128,637✔
208

209
bool ArrayString::is_null(size_t ndx) const
210
{
4,134,699✔
211
    switch (m_type) {
4,134,699✔
212
        case Type::small_strings:
395,742✔
213
            return static_cast<ArrayStringShort*>(m_arr)->is_null(ndx);
395,742✔
214
        case Type::medium_strings:
21,816✔
215
            return static_cast<ArraySmallBlobs*>(m_arr)->is_null(ndx);
21,816✔
216
        case Type::big_strings:
3,479,058✔
217
            return static_cast<ArrayBigBlobs*>(m_arr)->is_null(ndx);
3,479,058✔
218
        case Type::interned_strings: {
239,379✔
219
            size_t id = size_t(static_cast<Array*>(m_arr)->get(ndx));
239,379✔
220
            return id == 0;
239,379✔
UNCOV
221
        }
×
222
    }
4,134,699✔
223
    return {};
×
224
}
4,134,699✔
225

226
void ArrayString::erase(size_t ndx)
227
{
474,633✔
228
    switch (m_type) {
474,633✔
229
        case Type::small_strings:
353,688✔
230
            static_cast<ArrayStringShort*>(m_arr)->erase(ndx);
353,688✔
231
            break;
353,688✔
232
        case Type::medium_strings:
55,107✔
233
            static_cast<ArraySmallBlobs*>(m_arr)->erase(ndx);
55,107✔
234
            break;
55,107✔
235
        case Type::big_strings:
23,940✔
236
            static_cast<ArrayBigBlobs*>(m_arr)->erase(ndx);
23,940✔
237
            break;
23,940✔
238
        case Type::interned_strings:
41,934✔
239
            static_cast<Array*>(m_arr)->erase(ndx);
41,934✔
240
            break;
41,934✔
241
    }
474,633✔
242
}
474,633✔
243

244
void ArrayString::move(ArrayString& dst, size_t ndx)
245
{
1,074✔
246
    size_t sz = size();
1,074✔
247
    for (size_t i = ndx; i < sz; i++) {
115,404✔
248
        dst.add(get(i));
114,330✔
249
    }
114,330✔
250

251
    switch (m_type) {
1,074✔
252
        case Type::small_strings:
1,008✔
253
            static_cast<ArrayStringShort*>(m_arr)->truncate(ndx);
1,008✔
254
            break;
1,008✔
255
        case Type::medium_strings:
60✔
256
            static_cast<ArraySmallBlobs*>(m_arr)->truncate(ndx);
60✔
257
            break;
60✔
258
        case Type::big_strings:
6✔
259
            static_cast<ArrayBigBlobs*>(m_arr)->truncate(ndx);
6✔
260
            break;
6✔
NEW
261
        case Type::interned_strings:
✔
NEW
262
            m_arr->truncate(ndx);
×
UNCOV
263
            break;
×
264
    }
1,074✔
265
}
1,074✔
266

267
void ArrayString::clear()
268
{
354✔
269
    switch (m_type) {
354✔
270
        case Type::small_strings:
174✔
271
            static_cast<ArrayStringShort*>(m_arr)->clear();
174✔
272
            break;
174✔
273
        case Type::medium_strings:
6✔
274
            static_cast<ArraySmallBlobs*>(m_arr)->clear();
6✔
275
            break;
6✔
276
        case Type::big_strings:
48✔
277
            static_cast<ArrayBigBlobs*>(m_arr)->clear();
48✔
278
            break;
48✔
279
        case Type::interned_strings:
126✔
280
            static_cast<Array*>(m_arr)->clear();
126✔
281
            break;
126✔
282
    }
354✔
283
}
354✔
284

285
size_t ArrayString::find_first(StringData value, size_t begin, size_t end) const noexcept
286
{
19,374✔
287
    // This should only be called if we don't have a string id for this particular array (aka no string interner)
288
    std::optional<StringID> id;
19,374✔
289
    if (m_type == Type::interned_strings)
19,374✔
290
        id = m_string_interner->lookup(value);
624✔
291

292
    return find_first(value, begin, end, id);
19,374✔
293
}
19,374✔
294

295
size_t ArrayString::find_first(StringData value, size_t begin, size_t end, std::optional<StringID> id) const noexcept
296
{
383,496✔
297
    switch (m_type) {
383,496✔
298
        case Type::small_strings:
210,822✔
299
            return static_cast<ArrayStringShort*>(m_arr)->find_first(value, begin, end);
210,822✔
300
        case Type::medium_strings: {
58,251✔
301
            BinaryData as_binary(value.data(), value.size());
58,251✔
302
            return static_cast<ArraySmallBlobs*>(m_arr)->find_first(as_binary, true, begin, end);
58,251✔
303
            break;
×
304
        }
×
305
        case Type::big_strings: {
17,769✔
306
            BinaryData as_binary(value.data(), value.size());
17,769✔
307
            return static_cast<ArrayBigBlobs*>(m_arr)->find_first(as_binary, true, begin, end);
17,769✔
308
            break;
×
309
        }
×
310
        case Type::interned_strings: {
96,654✔
311
            if (id) {
96,654✔
312
                return static_cast<Array*>(m_arr)->find_first(*id, begin, end);
93,234✔
313
            }
93,234✔
314
            break;
3,420✔
315
        }
96,654✔
316
        default:
3,420✔
NEW
317
            break;
×
318
    }
383,496✔
319
    return not_found;
3,420✔
320
}
383,496✔
321

322
namespace {
323

324
template <class T>
325
inline StringData get_string(const T* arr, size_t ndx)
UNCOV
326
{
×
UNCOV
327
    return arr->get_string(ndx);
×
UNCOV
328
}
×
329

330
template <>
331
inline StringData get_string(const ArrayStringShort* arr, size_t ndx)
UNCOV
332
{
×
UNCOV
333
    return arr->get(ndx);
×
UNCOV
334
}
×
335

336
template <class T, class U>
337
size_t lower_bound_string(const T* arr, U value)
UNCOV
338
{
×
UNCOV
339
    size_t i = 0;
×
UNCOV
340
    size_t sz = arr->size();
×
UNCOV
341
    while (0 < sz) {
×
UNCOV
342
        size_t half = sz / 2;
×
UNCOV
343
        size_t mid = i + half;
×
UNCOV
344
        auto probe = get_string(arr, mid);
×
UNCOV
345
        if (probe < value) {
×
UNCOV
346
            i = mid + 1;
×
UNCOV
347
            sz -= half + 1;
×
UNCOV
348
        }
×
UNCOV
349
        else {
×
UNCOV
350
            sz = half;
×
UNCOV
351
        }
×
UNCOV
352
    }
×
UNCOV
353
    return i;
×
UNCOV
354
}
×
355
} // namespace
356

357
size_t ArrayString::lower_bound(StringData value)
UNCOV
358
{
×
UNCOV
359
    switch (m_type) {
×
UNCOV
360
        case Type::small_strings:
×
UNCOV
361
            return lower_bound_string(static_cast<ArrayStringShort*>(m_arr), value);
×
UNCOV
362
        case Type::medium_strings:
×
UNCOV
363
            return lower_bound_string(static_cast<ArraySmallBlobs*>(m_arr), value);
×
UNCOV
364
        case Type::big_strings:
×
UNCOV
365
            return lower_bound_string(static_cast<ArrayBigBlobs*>(m_arr), value);
×
NEW
366
        case Type::interned_strings:
×
367
            REALM_UNREACHABLE();
UNCOV
368
            break;
×
UNCOV
369
    }
×
370
    return realm::npos;
×
UNCOV
371
}
×
372

373
ArrayString::Type ArrayString::upgrade_leaf(size_t value_size)
374
{
6,357,684✔
375
    if (m_type == Type::big_strings)
6,357,684✔
376
        return Type::big_strings;
2,095,794✔
377

378
    if (m_type == Type::interned_strings)
4,261,890✔
379
        return Type::interned_strings;
1,180,869✔
380

381
    if (m_type == Type::medium_strings) {
3,081,021✔
382
        if (value_size <= medium_string_max_size)
500,907✔
383
            return Type::medium_strings;
500,487✔
384

385
        // Upgrade root leaf from medium to big strings
386
        auto string_medium = static_cast<ArraySmallBlobs*>(m_arr);
420✔
387
        ArrayBigBlobs big_blobs(m_alloc, true);
420✔
388
        big_blobs.create(); // Throws
420✔
389

390
        size_t n = string_medium->size();
420✔
391
        for (size_t i = 0; i < n; i++) {
6,804✔
392
            big_blobs.add_string(string_medium->get_string(i)); // Throws
6,384✔
393
        }
6,384✔
394
        auto parent = string_medium->get_parent();
420✔
395
        auto ndx_in_parent = string_medium->get_ndx_in_parent();
420✔
396
        string_medium->destroy();
420✔
397

398
        auto arr = new (&m_storage) ArrayBigBlobs(m_alloc, true);
420✔
399
        arr->init_from_mem(big_blobs.get_mem());
420✔
400
        arr->set_parent(parent, ndx_in_parent);
420✔
401
        arr->update_parent();
420✔
402

403
        m_type = Type::big_strings;
420✔
404
        return Type::big_strings;
420✔
405
    }
500,907✔
406

407
    // m_type == Type::small
408
    if (value_size <= small_string_max_size)
2,580,114✔
409
        return Type::small_strings;
2,561,328✔
410

411
    if (value_size <= medium_string_max_size) {
18,900✔
412
        // Upgrade root leaf from small to medium strings
413
        auto string_short = static_cast<ArrayStringShort*>(m_arr);
16,644✔
414
        ArraySmallBlobs string_long(m_alloc);
16,644✔
415
        string_long.create(); // Throws
16,644✔
416

417
        size_t n = string_short->size();
16,644✔
418
        for (size_t i = 0; i < n; i++) {
37,110✔
419
            string_long.add_string(string_short->get(i)); // Throws
20,466✔
420
        }
20,466✔
421
        auto parent = string_short->get_parent();
16,644✔
422
        auto ndx_in_parent = string_short->get_ndx_in_parent();
16,644✔
423
        string_short->destroy();
16,644✔
424

425
        auto arr = new (&m_storage) ArraySmallBlobs(m_alloc);
16,644✔
426
        arr->init_from_mem(string_long.get_mem());
16,644✔
427
        arr->set_parent(parent, ndx_in_parent);
16,644✔
428
        arr->update_parent();
16,644✔
429

430
        m_type = Type::medium_strings;
16,644✔
431
    }
16,644✔
432
    else {
2,147,485,903✔
433
        // Upgrade root leaf from small to big strings
434
        auto string_short = static_cast<ArrayStringShort*>(m_arr);
2,147,485,903✔
435
        ArrayBigBlobs big_blobs(m_alloc, true);
2,147,485,903✔
436
        big_blobs.create(); // Throws
2,147,485,903✔
437

438
        size_t n = string_short->size();
2,147,485,903✔
439
        for (size_t i = 0; i < n; i++) {
2,147,488,375✔
440
            big_blobs.add_string(string_short->get(i)); // Throws
4,839✔
441
        }
4,839✔
442
        auto parent = string_short->get_parent();
2,147,485,903✔
443
        auto ndx_in_parent = string_short->get_ndx_in_parent();
2,147,485,903✔
444
        string_short->destroy();
2,147,485,903✔
445

446
        auto arr = new (&m_storage) ArrayBigBlobs(m_alloc, true);
2,147,485,903✔
447
        arr->init_from_mem(big_blobs.get_mem());
2,147,485,903✔
448
        arr->set_parent(parent, ndx_in_parent);
2,147,485,903✔
449
        arr->update_parent();
2,147,485,903✔
450

451
        m_type = Type::big_strings;
2,147,485,903✔
452
    }
2,147,485,903✔
453

454
    return m_type;
18,786✔
455
}
2,580,114✔
456

457
void ArrayString::verify() const
458
{
179,628✔
459
#ifdef REALM_DEBUG
179,628✔
460
    switch (m_type) {
179,628✔
461
        case Type::small_strings:
18,177✔
462
            static_cast<ArrayStringShort*>(m_arr)->verify();
18,177✔
463
            break;
18,177✔
464
        case Type::medium_strings:
1,032✔
465
            static_cast<ArraySmallBlobs*>(m_arr)->verify();
1,032✔
466
            break;
1,032✔
467
        case Type::big_strings:
5,163✔
468
            static_cast<ArrayBigBlobs*>(m_arr)->verify();
5,163✔
469
            break;
5,163✔
470
        case Type::interned_strings:
155,256✔
471
            static_cast<Array*>(m_arr)->verify();
155,256✔
472
            break;
155,256✔
473
    }
179,628✔
474
#endif
179,628✔
475
}
179,628✔
476

477
template <>
478
ref_type ArrayString::typed_write(ref_type ref, _impl::ArrayWriterBase& out, Allocator& alloc)
479
{
318,087✔
480
    Array leaf(alloc);
318,087✔
481
    leaf.init_from_ref(ref);
318,087✔
482
    ref_type ret_val;
318,087✔
483
    auto header = leaf.get_header();
318,087✔
484
    if (NodeHeader::get_hasrefs_from_header(header) ||
318,087✔
485
        NodeHeader::get_wtype_from_header(header) == NodeHeader::wtype_Multiply) {
318,087✔
486
        // We're interning these strings
487
        ArrayString as(alloc);
177,399✔
488
        as.init_from_ref(ref);
177,399✔
489
        StringInterner* interner = out.table->get_string_interner(out.col_key);
177,399✔
490
        auto sz = as.size();
177,399✔
491
        Array interned(Allocator::get_default());
177,399✔
492
        interned.create(NodeHeader::type_Normal, true, sz);
177,399✔
493
        for (size_t i = 0; i < sz; ++i) {
1,699,593✔
494
            interned.set(i, interner->intern(as.get(i)));
1,522,194✔
495
        }
1,522,194✔
496
        ret_val = interned.write(out, false, false, out.compress);
177,399✔
497
        interned.destroy();
177,399✔
498
        // in a transactional setting:
499
        // Destroy all sub-arrays if present, in order to release memory in file
500
        // This is contrary to the rest of the handling in this function, but needed
501
        // here since sub-arrays may not have been COW'ed and therefore not freed in file.
502
        // We rely on 'only_modified' to indicate that we're in a transactional setting.
503
        if (out.only_modified)
177,399✔
504
            leaf.destroy_deep(true);
177,291✔
505
    }
177,399✔
506
    else {
140,688✔
507
        // just write out the array using integer leaf compression
508
        ret_val = leaf.write(out, false, out.only_modified, out.compress);
140,688✔
509
    }
140,688✔
510
    return ret_val;
318,087✔
511
}
318,087✔
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