Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 7 additions & 14 deletions include/boost/json/detail/impl/string_impl.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -426,26 +426,19 @@ replace_unchecked(
}

void
string_impl::
shrink_to_fit(
storage_ptr const& sp) noexcept
string_impl::shrink_to_fit(storage_ptr const& sp) noexcept
{
if(s_.k == short_string_)
if( is_short() )
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);
set_storage_kind(short_flag);
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));
sp->deallocate(t, sizeof(table) + t->capacity + 1, alignof(table));
return;
}
if(t->size >= t->capacity)
Expand Down
101 changes: 72 additions & 29 deletions include/boost/json/detail/string_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,41 @@ class string_impl
# error Unknown architecture
#endif

static constexpr
unsigned char
short_flag = 0x80;

static constexpr
unsigned char
key_flag = 0x40;

static constexpr
unsigned char
seamless_flag = 0x20;

void
set_storage_kind(unsigned char storage_kind)
{
unsigned char current = static_cast<unsigned char>(s_.k);
current = current & 0x2F;
s_.k = static_cast<kind>(current | storage_kind);
}

static
constexpr
kind
short_string_ =
static_cast<kind>(
((unsigned char)
kind::string) | 0x80);
kind::string) | short_flag);

static
constexpr
kind
key_string_ =
static_cast<kind>(
((unsigned char)
kind::string) | 0x40);
kind::string) | key_flag);

struct sbo
{
Expand Down Expand Up @@ -235,8 +255,8 @@ class string_impl
release_key(
std::size_t& n) noexcept
{
BOOST_ASSERT(
k_.k == key_string_);
BOOST_ASSERT( is_key() );
BOOST_ASSERT( ! is_seamless() );
n = k_.n;
auto const s = k_.s;
// prevent deallocate
Expand All @@ -245,30 +265,25 @@ class string_impl
}

void
destroy(
storage_ptr const& sp) noexcept
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_)
if( is_short() )
{
// do nothing
}
else
else if( is_key() )
{
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);
}
else
{
sp->deallocate(
p_.t, sizeof(table) + p_.t->capacity + 1, alignof(table));
}
}

BOOST_JSON_DECL
Expand Down Expand Up @@ -323,37 +338,32 @@ class string_impl
void
term(std::size_t n) noexcept
{
if(s_.k == short_string_)
if( is_short() )
{
s_.buf[sbo_chars_] =
static_cast<char>(
sbo_chars_ - n);
s_.buf[sbo_chars_] = static_cast<char>(sbo_chars_ - n);
s_.buf[n] = 0;
}
else
{
p_.t->size = static_cast<
std::uint32_t>(n);
p_.t->size = static_cast<std::uint32_t>(n);
data()[n] = 0;
}
}

char*
data() noexcept
{
if(s_.k == short_string_)
if( is_short() )
return s_.buf;
return reinterpret_cast<
char*>(p_.t + 1);
return reinterpret_cast<char*>(p_.t + 1);
}

char const*
data() const noexcept
{
if(s_.k == short_string_)
if( is_short() )
return s_.buf;
return reinterpret_cast<
char const*>(p_.t + 1);
return reinterpret_cast<char const*>(p_.t + 1);
}

char*
Expand All @@ -367,6 +377,39 @@ class string_impl
{
return data() + size();
}

inline
constexpr
bool
is_short() const noexcept
{
return static_cast<unsigned char>(s_.k) & short_flag;
}

inline
constexpr
bool
is_key() const noexcept
{
return static_cast<unsigned char>(s_.k) & key_flag;
}

inline
constexpr
bool
is_seamless() const noexcept
{
return static_cast<unsigned char>(s_.k) & seamless_flag;
}

void
set_seamless(bool on) noexcept
{
unsigned char const seamless = on ? seamless_flag : 0;
unsigned char current = static_cast<unsigned char>(s_.k);
current = current & 0xCF;
s_.k = static_cast<kind>(current | seamless);
}
};

template<class T>
Expand Down
5 changes: 5 additions & 0 deletions include/boost/json/impl/serializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ write_impl(string_like_conversion_tag, writer& w, stream& ss0)
{
string_view const sv = *reinterpret_cast<T const*>(w.p_);
w.cs0_ = { sv.data(), sv.size() };
w.buf_[2] = false;
return write_string(w, ss0);
}

Expand Down Expand Up @@ -331,6 +332,7 @@ write_impl(map_like_conversion_tag, writer& w, stream& ss0)
using std::get;
string_view const sv = get<0>(*it);
w.cs0_ = { sv.data(), sv.size() };
w.buf_[2] = false;
}
if( true )
{
Expand Down Expand Up @@ -492,6 +494,7 @@ struct serialize_struct_elem_helper
{
string_view const sv = D::name;
w.cs0_ = { sv.data(), sv.size() };
w.buf_[2] = false;
}
if( true )
{
Expand Down Expand Up @@ -619,6 +622,7 @@ write_impl(described_enum_conversion_tag, writer& w, stream& ss)
{
string_view const sv = name;
w.cs0_ = { sv.data(), sv.size() };
w.buf_[2] = false;
return write_string(w, ss);
}
else
Expand Down Expand Up @@ -777,6 +781,7 @@ BOOST_FORCEINLINE
bool
write_impl(path_conversion_tag, writer& w, stream& ss)
{
w.buf_[2] = false;
#if defined(_MSC_VER)
# pragma warning( push )
# pragma warning( disable : 4127 )
Expand Down
22 changes: 16 additions & 6 deletions include/boost/json/impl/serializer.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,14 @@ do_write_string(writer& w, stream& ss0)
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

// opening quote
if(w.buf_[2] == false)
{
do_str1:
if(BOOST_JSON_LIKELY(ss))
ss.append('\x22'); // '"'
else
return w.suspend(writer::state::str1);
if(BOOST_JSON_LIKELY(ss))
ss.append('\x22'); // '"'
else
return w.suspend(writer::state::str1);
}

// fast loop,
// copy unescaped
Expand All @@ -242,7 +245,8 @@ do_str2:
}
else
{
ss.append('\x22'); // '"'
if(w.buf_[2] == false)
ss.append('\x22'); // '"'
return true;
}
}
Expand Down Expand Up @@ -304,7 +308,10 @@ do_str3:
}
else
{
ss.append('\x22'); // '"'
if(w.buf_[2] == false)
{
ss.append('\x22'); // '"'
}
return true;
}
}
Expand Down Expand Up @@ -404,6 +411,7 @@ write_value(writer& w, stream& ss)
{
auto const& js = pv->get_string();
w.cs0_ = { js.data(), js.size() };
w.buf_[2] = js.is_seamless();
return do_write_string<true>(w, ss);
}

Expand Down Expand Up @@ -507,6 +515,7 @@ serializer::
reset(string const* p) noexcept
{
cs0_ = { p->data(), p->size() };
buf_[2] = p->is_seamless();
fn0_ = &detail::do_write_string<true>;
fn1_ = &detail::do_write_string<false>;
st_.clear();
Expand All @@ -518,6 +527,7 @@ serializer::
reset(string_view sv) noexcept
{
cs0_ = { sv.data(), sv.size() };
buf_[2] = false;
fn0_ = &detail::do_write_string<true>;
fn1_ = &detail::do_write_string<false>;
st_.clear();
Expand Down
13 changes: 13 additions & 0 deletions include/boost/json/string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,19 @@ class string
return sp_.get();
}

constexpr
bool
is_seamless() const noexcept
{
return impl_.is_seamless();
}

void
set_seamless(bool on) noexcept
{
impl_.set_seamless(on);
}

//------------------------------------------------------
//
// Element Access
Expand Down
24 changes: 24 additions & 0 deletions test/serializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,29 @@ class serializer_test
check("{\"x\":1,\"y\":null}");
}

void
testSeamless()
{
try
{
string s = "1234";
s.set_seamless(true);

value jv = s;
grind("1234", jv, "");

jv = {"1234", s};
grind("[\"1234\",1234]", jv, "");

jv = { {"a", "1234"}, {"b", s} };
grind("{\"a\":\"1234\",\"b\":1234]", jv, "");
}
catch(std::exception const&)
{
BOOST_TEST_FAIL();
}
}

//------------------------------------------------------

void
Expand Down Expand Up @@ -907,6 +930,7 @@ class serializer_test
testNumber();
testArray();
testObject();
testSeamless();

testMembers();
testVectors();
Expand Down
Loading
Loading