/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 |