detail/impl/string_impl.ipp

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