boost/json/detail/string_impl.hpp
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
//
// 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_STRING_IMPL_HPP
#define BOOST_JSON_DETAIL_STRING_IMPL_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/kind.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/json/detail/value.hpp>
#include <algorithm>
#include <iterator>
namespace boost {
namespace json {
class value;
class string;
namespace detail {
class string_impl
{
struct table
{
std::uint32_t size;
std::uint32_t capacity;
};
#if BOOST_JSON_ARCH == 64
static constexpr std::size_t sbo_chars_ = 14;
#elif BOOST_JSON_ARCH == 32
static constexpr std::size_t sbo_chars_ = 10;
#else
# error Unknown architecture
#endif
static
constexpr
kind
short_string_ =
static_cast<kind>(
((unsigned char)
kind::string) | 0x80);
static
constexpr
kind
key_string_ =
static_cast<kind>(
((unsigned char)
kind::string) | 0x40);
struct sbo
{
kind k; // must come first
char buf[sbo_chars_ + 1];
};
struct pointer
{
kind k; // must come first
table* t;
};
struct key
{
kind k; // must come first
std::uint32_t n;
char* s;
};
union
{
sbo s_;
pointer p_;
key k_;
};
#if BOOST_JSON_ARCH == 64
BOOST_STATIC_ASSERT(sizeof(sbo) <= 16);
BOOST_STATIC_ASSERT(sizeof(pointer) <= 16);
BOOST_STATIC_ASSERT(sizeof(key) <= 16);
#elif BOOST_JSON_ARCH == 32
BOOST_STATIC_ASSERT(sizeof(sbo) <= 24);
BOOST_STATIC_ASSERT(sizeof(pointer) <= 24);
BOOST_STATIC_ASSERT(sizeof(key) <= 24);
#endif
public:
static
constexpr
std::size_t
max_size() noexcept
{
// max_size depends on the address model
using min = std::integral_constant<std::size_t,
std::size_t(-1) - sizeof(table)>;
return min::value < BOOST_JSON_MAX_STRING_SIZE ?
min::value : BOOST_JSON_MAX_STRING_SIZE;
}
BOOST_JSON_DECL
string_impl() noexcept;
BOOST_JSON_DECL
string_impl(
std::size_t new_size,
storage_ptr const& sp);
BOOST_JSON_DECL
string_impl(
key_t,
string_view s,
storage_ptr const& sp);
BOOST_JSON_DECL
string_impl(
key_t,
string_view s1,
string_view s2,
storage_ptr const& sp);
BOOST_JSON_DECL
string_impl(
char** dest,
std::size_t len,
storage_ptr const& sp);
template<class InputIt>
string_impl(
InputIt first,
InputIt last,
storage_ptr const& sp,
std::random_access_iterator_tag)
: string_impl(last - first, sp)
{
char* out = data();
#if defined(_MSC_VER) && _MSC_VER <= 1900
while( first != last )
*out++ = *first++;
#else
std::copy(first, last, out);
#endif
}
template<class InputIt>
string_impl(
InputIt first,
InputIt last,
storage_ptr const& sp,
std::input_iterator_tag)
: string_impl(0, sp)
{
struct undo
{
string_impl* s;
storage_ptr const& sp;
~undo()
{
if(s)
s->destroy(sp);
}
};
undo u{this, sp};
auto dest = data();
while(first != last)
{
if(size() < capacity())
size(size() + 1);
else
dest = append(1, sp);
*dest++ = *first++;
}
term(size());
u.s = nullptr;
}
std::size_t
size() const noexcept
{
return s_.k == kind::string ?
p_.t->size :
sbo_chars_ -
s_.buf[sbo_chars_];
}
std::size_t
capacity() const noexcept
{
return s_.k == kind::string ?
p_.t->capacity :
sbo_chars_;
}
void
size(std::size_t n)
{
if(s_.k == kind::string)
p_.t->size = static_cast<
std::uint32_t>(n);
else
s_.buf[sbo_chars_] =
static_cast<char>(
sbo_chars_ - n);
}
BOOST_JSON_DECL
static
std::uint32_t
growth(
std::size_t new_size,
std::size_t capacity);
char const*
release_key(
std::size_t& n) noexcept
{
BOOST_ASSERT(
k_.k == key_string_);
n = k_.n;
auto const s = k_.s;
// prevent deallocate
k_.k = short_string_;
return s;
}
void
destroy(
storage_ptr const& sp) noexcept
{
if(s_.k == kind::string)
{
sp->deallocate(p_.t,
sizeof(table) +
p_.t->capacity + 1,
alignof(table));
}
else if(s_.k != key_string_)
{
// do nothing
}
else
{
BOOST_ASSERT(
s_.k == key_string_);
// VFALCO unfortunately the key string
// kind increases the cost of the destructor.
// This function should be skipped when using
// monotonic_resource.
sp->deallocate(k_.s, k_.n + 1);
}
}
BOOST_JSON_DECL
char*
assign(
std::size_t new_size,
storage_ptr const& sp);
BOOST_JSON_DECL
char*
append(
std::size_t n,
storage_ptr const& sp);
BOOST_JSON_DECL
void
insert(
std::size_t pos,
const char* s,
std::size_t n,
storage_ptr const& sp);
BOOST_JSON_DECL
char*
insert_unchecked(
std::size_t pos,
std::size_t n,
storage_ptr const& sp);
BOOST_JSON_DECL
void
replace(
std::size_t pos,
std::size_t n1,
const char* s,
std::size_t n2,
storage_ptr const& sp);
BOOST_JSON_DECL
char*
replace_unchecked(
std::size_t pos,
std::size_t n1,
std::size_t n2,
storage_ptr const& sp);
BOOST_JSON_DECL
void
shrink_to_fit(
storage_ptr const& sp) noexcept;
void
term(std::size_t n) noexcept
{
if(s_.k == short_string_)
{
s_.buf[sbo_chars_] =
static_cast<char>(
sbo_chars_ - n);
s_.buf[n] = 0;
}
else
{
p_.t->size = static_cast<
std::uint32_t>(n);
data()[n] = 0;
}
}
char*
data() noexcept
{
if(s_.k == short_string_)
return s_.buf;
return reinterpret_cast<
char*>(p_.t + 1);
}
char const*
data() const noexcept
{
if(s_.k == short_string_)
return s_.buf;
return reinterpret_cast<
char const*>(p_.t + 1);
}
char*
end() noexcept
{
return data() + size();
}
char const*
end() const noexcept
{
return data() + size();
}
};
template<class T>
string_view
to_string_view(T const& t) noexcept
{
return string_view(t);
}
template<class T, class U>
using string_and_stringlike = std::integral_constant<bool,
std::is_same<T, string>::value &&
std::is_convertible<U const&, string_view>::value>;
template<class T, class U>
using string_comp_op_requirement
= typename std::enable_if<
string_and_stringlike<T, U>::value ||
string_and_stringlike<U, T>::value,
bool>::type;
} // detail
} // namespace json
} // namespace boost
#endif