diff --git a/include/boost/json/detail/impl/string_impl.ipp b/include/boost/json/detail/impl/string_impl.ipp index ad75f8693..1bc541ff0 100644 --- a/include/boost/json/detail/impl/string_impl.ipp +++ b/include/boost/json/detail/impl/string_impl.ipp @@ -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( - sbo_chars_ - t->size); + set_storage_kind(short_flag); + std::memcpy(s_.buf, data(), t->size); + s_.buf[sbo_chars_] = static_cast(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) diff --git a/include/boost/json/detail/string_impl.hpp b/include/boost/json/detail/string_impl.hpp index bffcf3c09..12a9e086e 100644 --- a/include/boost/json/detail/string_impl.hpp +++ b/include/boost/json/detail/string_impl.hpp @@ -52,13 +52,33 @@ 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(s_.k); + current = current & 0x2F; + s_.k = static_cast(current | storage_kind); + } + static constexpr kind short_string_ = static_cast( ((unsigned char) - kind::string) | 0x80); + kind::string) | short_flag); static constexpr @@ -66,7 +86,7 @@ class string_impl key_string_ = static_cast( ((unsigned char) - kind::string) | 0x40); + kind::string) | key_flag); struct sbo { @@ -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 @@ -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 @@ -323,17 +338,14 @@ class string_impl void term(std::size_t n) noexcept { - if(s_.k == short_string_) + if( is_short() ) { - s_.buf[sbo_chars_] = - static_cast( - sbo_chars_ - n); + s_.buf[sbo_chars_] = static_cast(sbo_chars_ - n); s_.buf[n] = 0; } else { - p_.t->size = static_cast< - std::uint32_t>(n); + p_.t->size = static_cast(n); data()[n] = 0; } } @@ -341,19 +353,17 @@ class string_impl char* data() noexcept { - if(s_.k == short_string_) + if( is_short() ) return s_.buf; - return reinterpret_cast< - char*>(p_.t + 1); + return reinterpret_cast(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(p_.t + 1); } char* @@ -367,6 +377,39 @@ class string_impl { return data() + size(); } + + inline + constexpr + bool + is_short() const noexcept + { + return static_cast(s_.k) & short_flag; + } + + inline + constexpr + bool + is_key() const noexcept + { + return static_cast(s_.k) & key_flag; + } + + inline + constexpr + bool + is_seamless() const noexcept + { + return static_cast(s_.k) & seamless_flag; + } + + void + set_seamless(bool on) noexcept + { + unsigned char const seamless = on ? seamless_flag : 0; + unsigned char current = static_cast(s_.k); + current = current & 0xCF; + s_.k = static_cast(current | seamless); + } }; template diff --git a/include/boost/json/impl/serializer.hpp b/include/boost/json/impl/serializer.hpp index 0a7255d06..1c3b6886c 100644 --- a/include/boost/json/impl/serializer.hpp +++ b/include/boost/json/impl/serializer.hpp @@ -195,6 +195,7 @@ write_impl(string_like_conversion_tag, writer& w, stream& ss0) { string_view const sv = *reinterpret_cast(w.p_); w.cs0_ = { sv.data(), sv.size() }; + w.buf_[2] = false; return write_string(w, ss0); } @@ -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 ) { @@ -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 ) { @@ -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 @@ -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 ) diff --git a/include/boost/json/impl/serializer.ipp b/include/boost/json/impl/serializer.ipp index 15e46c909..8bac8d678 100644 --- a/include/boost/json/impl/serializer.ipp +++ b/include/boost/json/impl/serializer.ipp @@ -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 @@ -242,7 +245,8 @@ do_str2: } else { - ss.append('\x22'); // '"' + if(w.buf_[2] == false) + ss.append('\x22'); // '"' return true; } } @@ -304,7 +308,10 @@ do_str3: } else { - ss.append('\x22'); // '"' + if(w.buf_[2] == false) + { + ss.append('\x22'); // '"' + } return true; } } @@ -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(w, ss); } @@ -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; fn1_ = &detail::do_write_string; st_.clear(); @@ -518,6 +527,7 @@ serializer:: reset(string_view sv) noexcept { cs0_ = { sv.data(), sv.size() }; + buf_[2] = false; fn0_ = &detail::do_write_string; fn1_ = &detail::do_write_string; st_.clear(); diff --git a/include/boost/json/string.hpp b/include/boost/json/string.hpp index 87b0db397..1625a4806 100644 --- a/include/boost/json/string.hpp +++ b/include/boost/json/string.hpp @@ -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 diff --git a/test/serializer.cpp b/test/serializer.cpp index d740d8aa2..c170f34c7 100644 --- a/test/serializer.cpp +++ b/test/serializer.cpp @@ -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 @@ -907,6 +930,7 @@ class serializer_test testNumber(); testArray(); testObject(); + testSeamless(); testMembers(); testVectors(); diff --git a/test/string.cpp b/test/string.cpp index 282cad7f4..89a334b19 100644 --- a/test/string.cpp +++ b/test/string.cpp @@ -2642,6 +2642,22 @@ class string_test }); } + void + testSeamless() + { + string s; + BOOST_TEST( !s.is_seamless() ); + + s.set_seamless(false); + BOOST_TEST( !s.is_seamless() ); + + s.set_seamless(true); + BOOST_TEST( s.is_seamless() ); + + s.set_seamless(false); + BOOST_TEST( !s.is_seamless() ); + } + void testFind() { @@ -2889,6 +2905,7 @@ class string_test testCopy(); testResize(); testSwap(); + testSeamless(); testFind(); testRfind();