Coverage Report

Created: 2026-06-07 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boost/boost/json/detail/impl/string_impl.ipp
Line
Count
Source
1
//
2
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
4
//
5
// Distributed under the Boost Software License, Version 1.0. (See accompanying
6
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7
//
8
// Official repository: https://github.com/boostorg/json
9
//
10
11
#ifndef BOOST_JSON_DETAIL_IMPL_STRING_IMPL_IPP
12
#define BOOST_JSON_DETAIL_IMPL_STRING_IMPL_IPP
13
14
#include <boost/json/detail/string_impl.hpp>
15
#include <boost/json/detail/except.hpp>
16
#include <cstring>
17
#include <functional>
18
19
namespace boost {
20
namespace json {
21
namespace detail {
22
23
inline
24
bool
25
ptr_in_range(
26
    const char* first,
27
    const char* last,
28
    const char* ptr) noexcept
29
0
{
30
0
    return std::less<const char*>()(ptr, last) &&
31
0
        std::greater_equal<const char*>()(ptr, first);
32
0
}
33
34
string_impl::
35
string_impl() noexcept
36
51.9k
{
37
51.9k
    s_.k = short_string_;
38
51.9k
    s_.buf[sbo_chars_] =
39
51.9k
        static_cast<char>(
40
51.9k
            sbo_chars_);
41
51.9k
    s_.buf[0] = 0;
42
51.9k
}
43
44
string_impl::
45
string_impl(
46
    std::size_t size,
47
    storage_ptr const& sp)
48
10.1k
{
49
10.1k
    if(size <= sbo_chars_)
50
0
    {
51
0
        s_.k = short_string_;
52
0
        s_.buf[sbo_chars_] =
53
0
            static_cast<char>(
54
0
                sbo_chars_ - size);
55
0
        s_.buf[size] = 0;
56
0
    }
57
10.1k
    else
58
10.1k
    {
59
10.1k
        s_.k = kind::string;
60
10.1k
        auto const n = growth(
61
10.1k
            size, sbo_chars_ + 1);
62
10.1k
        p_.t = ::new(sp->allocate(
63
10.1k
            sizeof(table) +
64
10.1k
                n + 1,
65
10.1k
            alignof(table))) table{
66
10.1k
                static_cast<
67
10.1k
                    std::uint32_t>(size),
68
10.1k
                static_cast<
69
10.1k
                    std::uint32_t>(n)};
70
10.1k
        data()[n] = 0;
71
10.1k
    }
72
10.1k
}
73
74
// construct a key, unchecked
75
string_impl::
76
string_impl(
77
    key_t,
78
    string_view s,
79
    storage_ptr const& sp)
80
1.82M
{
81
1.82M
    BOOST_ASSERT(
82
1.82M
        s.size() <= max_size());
83
1.82M
    k_.k = key_string_;
84
1.82M
    k_.n = static_cast<
85
1.82M
        std::uint32_t>(s.size());
86
1.82M
    k_.s = reinterpret_cast<char*>(
87
1.82M
        sp->allocate(s.size() + 1,
88
1.82M
            alignof(char)));
89
1.82M
    k_.s[s.size()] = 0; // null term
90
1.82M
    std::memcpy(&k_.s[0],
91
1.82M
        s.data(), s.size());
92
1.82M
}
93
94
// construct a key, unchecked
95
string_impl::
96
string_impl(
97
    key_t,
98
    string_view s1,
99
    string_view s2,
100
    storage_ptr const& sp)
101
5.81k
{
102
5.81k
    auto len = s1.size() + s2.size();
103
5.81k
    BOOST_ASSERT(len <= max_size());
104
5.81k
    k_.k = key_string_;
105
5.81k
    k_.n = static_cast<
106
5.81k
        std::uint32_t>(len);
107
5.81k
    k_.s = reinterpret_cast<char*>(
108
5.81k
        sp->allocate(len + 1,
109
5.81k
            alignof(char)));
110
5.81k
    k_.s[len] = 0; // null term
111
5.81k
    std::memcpy(&k_.s[0],
112
5.81k
        s1.data(), s1.size());
113
5.81k
    std::memcpy(&k_.s[s1.size()],
114
5.81k
        s2.data(), s2.size());
115
5.81k
}
116
117
std::uint32_t
118
string_impl::
119
growth(
120
    std::size_t new_size,
121
    std::size_t capacity)
122
20.2k
{
123
20.2k
    if(new_size > max_size())
124
0
    {
125
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
126
0
        detail::throw_system_error( error::string_too_large, &loc );
127
0
    }
128
    // growth factor 2
129
20.2k
    if( capacity >
130
20.2k
        max_size() - capacity)
131
0
        return static_cast<
132
0
            std::uint32_t>(max_size()); // overflow
133
20.2k
    return static_cast<std::uint32_t>(
134
20.2k
        (std::max)(capacity * 2, new_size));
135
20.2k
}
136
137
char*
138
string_impl::
139
assign(
140
    std::size_t new_size,
141
    storage_ptr const& sp)
142
43.5k
{
143
43.5k
    if(new_size > capacity())
144
6.87k
    {
145
6.87k
        string_impl tmp(growth(
146
6.87k
            new_size,
147
6.87k
            capacity()), sp);
148
6.87k
        destroy(sp);
149
6.87k
        *this = tmp;
150
6.87k
    }
151
43.5k
    term(new_size);
152
43.5k
    return data();
153
43.5k
}
154
155
char*
156
string_impl::
157
append(
158
    std::size_t n,
159
    storage_ptr const& sp)
160
0
{
161
0
    if(n > max_size() - size())
162
0
    {
163
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
164
0
        detail::throw_system_error( error::string_too_large, &loc );
165
0
    }
166
0
    if(n <= capacity() - size())
167
0
    {
168
0
        term(size() + n);
169
0
        return end() - n;
170
0
    }
171
0
    string_impl tmp(growth(
172
0
        size() + n, capacity()), sp);
173
0
    std::memcpy(
174
0
        tmp.data(), data(), size());
175
0
    tmp.term(size() + n);
176
0
    destroy(sp);
177
0
    *this = tmp;
178
0
    return end() - n;
179
0
}
180
181
void
182
string_impl::
183
insert(
184
    std::size_t pos,
185
    const char* s,
186
    std::size_t n,
187
    storage_ptr const& sp)
188
0
{
189
0
    const auto curr_size = size();
190
0
    if(pos > curr_size)
191
0
    {
192
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
193
0
        detail::throw_system_error( error::out_of_range, &loc );
194
0
    }
195
0
    const auto curr_data = data();
196
0
    if(n <= capacity() - curr_size)
197
0
    {
198
0
        const bool inside = detail::ptr_in_range(curr_data, curr_data + curr_size, s);
199
0
        if (!inside || (inside && ((s - curr_data) + n <= pos)))
200
0
        {
201
0
            std::memmove(&curr_data[pos + n], &curr_data[pos], curr_size - pos + 1);
202
0
            std::memcpy(&curr_data[pos], s, n);
203
0
        }
204
0
        else
205
0
        {
206
0
            const std::size_t offset = s - curr_data;
207
0
            std::memmove(&curr_data[pos + n], &curr_data[pos], curr_size - pos + 1);
208
0
            if (offset < pos)
209
0
            {
210
0
                const std::size_t diff = pos - offset;
211
0
                std::memcpy(&curr_data[pos], &curr_data[offset], diff);
212
0
                std::memcpy(&curr_data[pos + diff], &curr_data[pos + n], n - diff);
213
0
            }
214
0
            else
215
0
            {
216
0
                std::memcpy(&curr_data[pos], &curr_data[offset + n], n);
217
0
            }
218
0
        }
219
0
        size(curr_size + n);
220
0
    }
221
0
    else
222
0
    {
223
0
        if(n > max_size() - curr_size)
224
0
        {
225
0
            BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
226
0
            detail::throw_system_error( error::string_too_large, &loc );
227
0
        }
228
0
        string_impl tmp(growth(
229
0
            curr_size + n, capacity()), sp);
230
0
        tmp.size(curr_size + n);
231
0
        std::memcpy(
232
0
            tmp.data(),
233
0
            curr_data,
234
0
            pos);
235
0
        std::memcpy(
236
0
            tmp.data() + pos + n,
237
0
            curr_data + pos,
238
0
            curr_size + 1 - pos);
239
0
        std::memcpy(
240
0
            tmp.data() + pos,
241
0
            s,
242
0
            n);
243
0
        destroy(sp);
244
0
        *this = tmp;
245
0
    }
246
0
}
247
248
char*
249
string_impl::
250
insert_unchecked(
251
    std::size_t pos,
252
    std::size_t n,
253
    storage_ptr const& sp)
254
0
{
255
0
    const auto curr_size = size();
256
0
    if(pos > curr_size)
257
0
    {
258
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
259
0
        detail::throw_system_error( error::out_of_range, &loc );
260
0
    }
261
0
    const auto curr_data = data();
262
0
    if(n <= capacity() - size())
263
0
    {
264
0
        auto const dest =
265
0
            curr_data + pos;
266
0
        std::memmove(
267
0
            dest + n,
268
0
            dest,
269
0
            curr_size + 1 - pos);
270
0
        size(curr_size + n);
271
0
        return dest;
272
0
    }
273
0
    if(n > max_size() - curr_size)
274
0
    {
275
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
276
0
        detail::throw_system_error( error::string_too_large, &loc );
277
0
    }
278
0
    string_impl tmp(growth(
279
0
        curr_size + n, capacity()), sp);
280
0
    tmp.size(curr_size + n);
281
0
    std::memcpy(
282
0
        tmp.data(),
283
0
        curr_data,
284
0
        pos);
285
0
    std::memcpy(
286
0
        tmp.data() + pos + n,
287
0
        curr_data + pos,
288
0
        curr_size + 1 - pos);
289
0
    destroy(sp);
290
0
    *this = tmp;
291
0
    return data() + pos;
292
0
}
293
294
void
295
string_impl::
296
replace(
297
    std::size_t pos,
298
    std::size_t n1,
299
    const char* s,
300
    std::size_t n2,
301
    storage_ptr const& sp)
302
0
{
303
0
    const auto curr_size = size();
304
0
    if (pos > curr_size)
305
0
    {
306
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
307
0
        detail::throw_system_error( error::out_of_range, &loc );
308
0
    }
309
0
    const auto curr_data = data();
310
0
    n1 = (std::min)(n1, curr_size - pos);
311
0
    const auto delta = (std::max)(n1, n2) -
312
0
        (std::min)(n1, n2);
313
    // if we are shrinking in size or we have enough
314
    // capacity, dont reallocate
315
0
    if (n1 > n2 || delta <= capacity() - curr_size)
316
0
    {
317
0
        const bool inside = detail::ptr_in_range(curr_data, curr_data + curr_size, s);
318
        // there is nothing to replace; return
319
0
        if (inside && s == curr_data + pos && n1 == n2)
320
0
            return;
321
0
        if (!inside || (inside && ((s - curr_data) + n2 <= pos)))
322
0
        {
323
            // source outside
324
0
            std::memmove(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
325
0
            std::memcpy(&curr_data[pos], s, n2);
326
0
        }
327
0
        else
328
0
        {
329
            // source inside
330
0
            const std::size_t offset = s - curr_data;
331
0
            if (n2 >= n1)
332
0
            {
333
                // grow/unchanged
334
0
                const std::size_t diff = offset <= pos + n1 ? (std::min)((pos + n1) - offset, n2) : 0;
335
                // shift all right of splice point by n2 - n1 to the right
336
0
                std::memmove(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
337
                // copy all before splice point
338
0
                std::memmove(&curr_data[pos], &curr_data[offset], diff);
339
                // copy all after splice point
340
0
                std::memmove(&curr_data[pos + diff], &curr_data[(offset - n1) + n2 + diff], n2 - diff);
341
0
            }
342
0
            else
343
0
            {
344
                // shrink
345
                // copy all elements into place
346
0
                std::memmove(&curr_data[pos], &curr_data[offset], n2);
347
                // shift all elements after splice point left
348
0
                std::memmove(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
349
0
            }
350
0
        }
351
0
        size((curr_size - n1) + n2);
352
0
    }
353
0
    else
354
0
    {
355
0
        if (delta > max_size() - curr_size)
356
0
        {
357
0
            BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
358
0
            detail::throw_system_error( error::string_too_large, &loc );
359
0
        }
360
        // would exceed capacity, reallocate
361
0
        string_impl tmp(growth(
362
0
            curr_size + delta, capacity()), sp);
363
0
        tmp.size(curr_size + delta);
364
0
        std::memcpy(
365
0
            tmp.data(),
366
0
            curr_data,
367
0
            pos);
368
0
        std::memcpy(
369
0
            tmp.data() + pos + n2,
370
0
            curr_data + pos + n1,
371
0
            curr_size - pos - n1 + 1);
372
0
        std::memcpy(
373
0
            tmp.data() + pos,
374
0
            s,
375
0
            n2);
376
0
        destroy(sp);
377
0
        *this = tmp;
378
0
    }
379
0
}
380
381
// unlike the replace overload, this function does
382
// not move any characters
383
char*
384
string_impl::
385
replace_unchecked(
386
    std::size_t pos,
387
    std::size_t n1,
388
    std::size_t n2,
389
    storage_ptr const& sp)
390
0
{
391
0
    const auto curr_size = size();
392
0
    if(pos > curr_size)
393
0
    {
394
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
395
0
        detail::throw_system_error( error::out_of_range, &loc );
396
0
    }
397
0
    const auto curr_data = data();
398
0
    const auto delta = (std::max)(n1, n2) -
399
0
        (std::min)(n1, n2);
400
    // if the size doesn't change, we don't need to
401
    // do anything
402
0
    if (!delta)
403
0
      return curr_data + pos;
404
    // if we are shrinking in size or we have enough
405
    // capacity, dont reallocate
406
0
    if(n1 > n2 || delta <= capacity() - curr_size)
407
0
    {
408
0
        auto const replace_pos = curr_data + pos;
409
0
        std::memmove(
410
0
            replace_pos + n2,
411
0
            replace_pos + n1,
412
0
            curr_size - pos - n1 + 1);
413
0
        size((curr_size - n1) + n2);
414
0
        return replace_pos;
415
0
    }
416
0
    if(delta > max_size() - curr_size)
417
0
    {
418
0
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
419
0
        detail::throw_system_error( error::string_too_large, &loc );
420
0
    }
421
    // would exceed capacity, reallocate
422
0
    string_impl tmp(growth(
423
0
        curr_size + delta, capacity()), sp);
424
0
    tmp.size(curr_size + delta);
425
0
    std::memcpy(
426
0
        tmp.data(),
427
0
        curr_data,
428
0
        pos);
429
0
    std::memcpy(
430
0
        tmp.data() + pos + n2,
431
0
        curr_data + pos + n1,
432
0
        curr_size - pos - n1 + 1);
433
0
    destroy(sp);
434
0
    *this = tmp;
435
0
    return data() + pos;
436
0
}
437
438
void
439
string_impl::
440
shrink_to_fit(
441
    storage_ptr const& sp) noexcept
442
0
{
443
0
    if(s_.k == short_string_)
444
0
        return;
445
0
    auto const t = p_.t;
446
0
    if(t->size <= sbo_chars_)
447
0
    {
448
0
        s_.k = short_string_;
449
0
        std::memcpy(
450
0
            s_.buf, data(), t->size);
451
0
        s_.buf[sbo_chars_] =
452
0
            static_cast<char>(
453
0
                sbo_chars_ - t->size);
454
0
        s_.buf[t->size] = 0;
455
0
        sp->deallocate(t,
456
0
            sizeof(table) +
457
0
                t->capacity + 1,
458
0
            alignof(table));
459
0
        return;
460
0
    }
461
0
    if(t->size >= t->capacity)
462
0
        return;
463
0
#ifndef BOOST_NO_EXCEPTIONS
464
0
    try
465
0
    {
466
0
#endif
467
0
        string_impl tmp(t->size, sp);
468
0
        std::memcpy(
469
0
            tmp.data(),
470
0
            data(),
471
0
            size());
472
0
        destroy(sp);
473
0
        *this = tmp;
474
0
#ifndef BOOST_NO_EXCEPTIONS
475
0
    }
476
0
    catch(std::exception const&)
477
0
    {
478
        // eat the exception
479
0
    }
480
0
#endif
481
0
}
482
483
} // detail
484
} // namespace json
485
} // namespace boost
486
487
#endif