GCC Code Coverage Report


Directory: libs/json/include/boost/json/
File: detail/impl/string_impl.ipp
Date: 2025-12-23 17:20:53
Exec Total Coverage
Lines: 227 229 99.1%
Functions: 13 13 100.0%
Branches: 88 99 88.9%

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