string_impl.ipp 12 KB

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