123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 |
- //
- // Copyright (c) 2019 Vinnie Falco ([email protected])
- // Copyright (c) 2020 Krystian Stasiowski ([email protected])
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- //
- // Official repository: https://github.com/boostorg/json
- //
- #ifndef BOOST_JSON_DETAIL_IMPL_STRING_IMPL_IPP
- #define BOOST_JSON_DETAIL_IMPL_STRING_IMPL_IPP
- #include <boost/json/detail/string_impl.hpp>
- #include <boost/json/detail/except.hpp>
- #include <cstring>
- #include <functional>
- namespace boost {
- namespace json {
- namespace detail {
- inline
- bool
- ptr_in_range(
- const char* first,
- const char* last,
- const char* ptr) noexcept
- {
- return std::less<const char*>()(ptr, last) &&
- std::greater_equal<const char*>()(ptr, first);
- }
- string_impl::
- string_impl() noexcept
- {
- s_.k = short_string_;
- s_.buf[sbo_chars_] =
- static_cast<char>(
- sbo_chars_);
- s_.buf[0] = 0;
- }
- string_impl::
- string_impl(
- std::size_t size,
- storage_ptr const& sp)
- {
- if(size <= sbo_chars_)
- {
- s_.k = short_string_;
- s_.buf[sbo_chars_] =
- static_cast<char>(
- sbo_chars_ - size);
- s_.buf[size] = 0;
- }
- else
- {
- s_.k = kind::string;
- auto const n = growth(
- size, sbo_chars_ + 1);
- p_.t = ::new(sp->allocate(
- sizeof(table) +
- n + 1,
- alignof(table))) table{
- static_cast<
- std::uint32_t>(size),
- static_cast<
- std::uint32_t>(n)};
- data()[n] = 0;
- }
- }
- // construct a key, unchecked
- string_impl::
- string_impl(
- key_t,
- string_view s,
- storage_ptr const& sp)
- {
- BOOST_ASSERT(
- s.size() <= max_size());
- k_.k = key_string_;
- k_.n = static_cast<
- std::uint32_t>(s.size());
- k_.s = reinterpret_cast<char*>(
- sp->allocate(s.size() + 1,
- alignof(char)));
- k_.s[s.size()] = 0; // null term
- std::memcpy(&k_.s[0],
- s.data(), s.size());
- }
- // construct a key, unchecked
- string_impl::
- string_impl(
- key_t,
- string_view s1,
- string_view s2,
- storage_ptr const& sp)
- {
- auto len = s1.size() + s2.size();
- BOOST_ASSERT(len <= max_size());
- k_.k = key_string_;
- k_.n = static_cast<
- std::uint32_t>(len);
- k_.s = reinterpret_cast<char*>(
- sp->allocate(len + 1,
- alignof(char)));
- k_.s[len] = 0; // null term
- std::memcpy(&k_.s[0],
- s1.data(), s1.size());
- std::memcpy(&k_.s[s1.size()],
- s2.data(), s2.size());
- }
- std::uint32_t
- string_impl::
- growth(
- std::size_t new_size,
- std::size_t capacity)
- {
- if(new_size > max_size())
- {
- BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
- detail::throw_system_error( error::string_too_large, &loc );
- }
- // growth factor 2
- if( capacity >
- max_size() - capacity)
- return static_cast<
- std::uint32_t>(max_size()); // overflow
- return static_cast<std::uint32_t>(
- (std::max)(capacity * 2, new_size));
- }
- char*
- string_impl::
- assign(
- std::size_t new_size,
- storage_ptr const& sp)
- {
- if(new_size > capacity())
- {
- string_impl tmp(growth(
- new_size,
- capacity()), sp);
- destroy(sp);
- *this = tmp;
- }
- term(new_size);
- return data();
- }
- char*
- string_impl::
- append(
- std::size_t n,
- storage_ptr const& sp)
- {
- if(n > max_size() - size())
- {
- BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
- detail::throw_system_error( error::string_too_large, &loc );
- }
- if(n <= capacity() - size())
- {
- term(size() + n);
- return end() - n;
- }
- string_impl tmp(growth(
- size() + n, capacity()), sp);
- std::memcpy(
- tmp.data(), data(), size());
- tmp.term(size() + n);
- destroy(sp);
- *this = tmp;
- return end() - n;
- }
- void
- string_impl::
- insert(
- std::size_t pos,
- const char* s,
- std::size_t n,
- storage_ptr const& sp)
- {
- const auto curr_size = size();
- if(pos > curr_size)
- {
- BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
- detail::throw_system_error( error::out_of_range, &loc );
- }
- const auto curr_data = data();
- if(n <= capacity() - curr_size)
- {
- const bool inside = detail::ptr_in_range(curr_data, curr_data + curr_size, s);
- if (!inside || (inside && ((s - curr_data) + n <= pos)))
- {
- std::memmove(&curr_data[pos + n], &curr_data[pos], curr_size - pos + 1);
- std::memcpy(&curr_data[pos], s, n);
- }
- else
- {
- const std::size_t offset = s - curr_data;
- std::memmove(&curr_data[pos + n], &curr_data[pos], curr_size - pos + 1);
- if (offset < pos)
- {
- const std::size_t diff = pos - offset;
- std::memcpy(&curr_data[pos], &curr_data[offset], diff);
- std::memcpy(&curr_data[pos + diff], &curr_data[pos + n], n - diff);
- }
- else
- {
- std::memcpy(&curr_data[pos], &curr_data[offset + n], n);
- }
- }
- size(curr_size + n);
- }
- else
- {
- if(n > max_size() - curr_size)
- {
- BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
- detail::throw_system_error( error::string_too_large, &loc );
- }
- string_impl tmp(growth(
- curr_size + n, capacity()), sp);
- tmp.size(curr_size + n);
- std::memcpy(
- tmp.data(),
- curr_data,
- pos);
- std::memcpy(
- tmp.data() + pos + n,
- curr_data + pos,
- curr_size + 1 - pos);
- std::memcpy(
- tmp.data() + pos,
- s,
- n);
- destroy(sp);
- *this = tmp;
- }
- }
- char*
- string_impl::
- insert_unchecked(
- std::size_t pos,
- std::size_t n,
- storage_ptr const& sp)
- {
- const auto curr_size = size();
- if(pos > curr_size)
- {
- BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
- detail::throw_system_error( error::out_of_range, &loc );
- }
- const auto curr_data = data();
- if(n <= capacity() - size())
- {
- auto const dest =
- curr_data + pos;
- std::memmove(
- dest + n,
- dest,
- curr_size + 1 - pos);
- size(curr_size + n);
- return dest;
- }
- if(n > max_size() - curr_size)
- {
- BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
- detail::throw_system_error( error::string_too_large, &loc );
- }
- string_impl tmp(growth(
- curr_size + n, capacity()), sp);
- tmp.size(curr_size + n);
- std::memcpy(
- tmp.data(),
- curr_data,
- pos);
- std::memcpy(
- tmp.data() + pos + n,
- curr_data + pos,
- curr_size + 1 - pos);
- destroy(sp);
- *this = tmp;
- return data() + pos;
- }
- void
- string_impl::
- replace(
- std::size_t pos,
- std::size_t n1,
- const char* s,
- std::size_t n2,
- storage_ptr const& sp)
- {
- const auto curr_size = size();
- if (pos > curr_size)
- {
- BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
- detail::throw_system_error( error::out_of_range, &loc );
- }
- const auto curr_data = data();
- n1 = (std::min)(n1, curr_size - pos);
- const auto delta = (std::max)(n1, n2) -
- (std::min)(n1, n2);
- // if we are shrinking in size or we have enough
- // capacity, dont reallocate
- if (n1 > n2 || delta <= capacity() - curr_size)
- {
- const bool inside = detail::ptr_in_range(curr_data, curr_data + curr_size, s);
- // there is nothing to replace; return
- if (inside && s == curr_data + pos && n1 == n2)
- return;
- if (!inside || (inside && ((s - curr_data) + n2 <= pos)))
- {
- // source outside
- std::memmove(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
- std::memcpy(&curr_data[pos], s, n2);
- }
- else
- {
- // source inside
- const std::size_t offset = s - curr_data;
- if (n2 >= n1)
- {
- // grow/unchanged
- const std::size_t diff = offset <= pos + n1 ? (std::min)((pos + n1) - offset, n2) : 0;
- // shift all right of splice point by n2 - n1 to the right
- std::memmove(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
- // copy all before splice point
- std::memmove(&curr_data[pos], &curr_data[offset], diff);
- // copy all after splice point
- std::memmove(&curr_data[pos + diff], &curr_data[(offset - n1) + n2 + diff], n2 - diff);
- }
- else
- {
- // shrink
- // copy all elements into place
- std::memmove(&curr_data[pos], &curr_data[offset], n2);
- // shift all elements after splice point left
- std::memmove(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
- }
- }
- size((curr_size - n1) + n2);
- }
- else
- {
- if (delta > max_size() - curr_size)
- {
- BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
- detail::throw_system_error( error::string_too_large, &loc );
- }
- // would exceed capacity, reallocate
- string_impl tmp(growth(
- curr_size + delta, capacity()), sp);
- tmp.size(curr_size + delta);
- std::memcpy(
- tmp.data(),
- curr_data,
- pos);
- std::memcpy(
- tmp.data() + pos + n2,
- curr_data + pos + n1,
- curr_size - pos - n1 + 1);
- std::memcpy(
- tmp.data() + pos,
- s,
- n2);
- destroy(sp);
- *this = tmp;
- }
- }
- // unlike the replace overload, this function does
- // not move any characters
- char*
- string_impl::
- replace_unchecked(
- std::size_t pos,
- std::size_t n1,
- std::size_t n2,
- storage_ptr const& sp)
- {
- const auto curr_size = size();
- if(pos > curr_size)
- {
- BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
- detail::throw_system_error( error::out_of_range, &loc );
- }
- const auto curr_data = data();
- const auto delta = (std::max)(n1, n2) -
- (std::min)(n1, n2);
- // if the size doesn't change, we don't need to
- // do anything
- if (!delta)
- return curr_data + pos;
- // if we are shrinking in size or we have enough
- // capacity, dont reallocate
- if(n1 > n2 || delta <= capacity() - curr_size)
- {
- auto const replace_pos = curr_data + pos;
- std::memmove(
- replace_pos + n2,
- replace_pos + n1,
- curr_size - pos - n1 + 1);
- size((curr_size - n1) + n2);
- return replace_pos;
- }
- if(delta > max_size() - curr_size)
- {
- BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
- detail::throw_system_error( error::string_too_large, &loc );
- }
- // would exceed capacity, reallocate
- string_impl tmp(growth(
- curr_size + delta, capacity()), sp);
- tmp.size(curr_size + delta);
- std::memcpy(
- tmp.data(),
- curr_data,
- pos);
- std::memcpy(
- tmp.data() + pos + n2,
- curr_data + pos + n1,
- curr_size - pos - n1 + 1);
- destroy(sp);
- *this = tmp;
- return data() + pos;
- }
- void
- string_impl::
- shrink_to_fit(
- storage_ptr const& sp) noexcept
- {
- if(s_.k == short_string_)
- return;
- auto const t = p_.t;
- if(t->size <= sbo_chars_)
- {
- s_.k = short_string_;
- std::memcpy(
- s_.buf, data(), t->size);
- s_.buf[sbo_chars_] =
- static_cast<char>(
- sbo_chars_ - t->size);
- s_.buf[t->size] = 0;
- sp->deallocate(t,
- sizeof(table) +
- t->capacity + 1,
- alignof(table));
- return;
- }
- if(t->size >= t->capacity)
- return;
- #ifndef BOOST_NO_EXCEPTIONS
- try
- {
- #endif
- string_impl tmp(t->size, sp);
- std::memcpy(
- tmp.data(),
- data(),
- size());
- destroy(sp);
- *this = tmp;
- #ifndef BOOST_NO_EXCEPTIONS
- }
- catch(std::exception const&)
- {
- // eat the exception
- }
- #endif
- }
- } // detail
- } // namespace json
- } // namespace boost
- #endif
|