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