diff --git a/include/boost/json/detail/impl/handler.ipp b/include/boost/json/detail/impl/handler.ipp index dba0841bb..e7426524d 100644 --- a/include/boost/json/detail/impl/handler.ipp +++ b/include/boost/json/detail/impl/handler.ipp @@ -53,13 +53,8 @@ on_object_end( std::size_t n, error_code& ec) { - if( !ignore_duplicate_keys ) - ec = st.check_duplicates(n); - if( ec.failed() ) - return false; - - st.push_object(n); - return true; + ec = st.push_object(n, ignore_duplicate_keys); + return !ec.failed(); } bool @@ -90,7 +85,7 @@ on_key_part( st.push_chars(s); return true; } - + bool handler:: on_key( @@ -101,12 +96,12 @@ on_key( st.push_key(s); return true; } - + bool handler:: on_string_part( string_view s, - std::size_t, + std::size_t, error_code&) { st.push_chars(s); @@ -117,7 +112,7 @@ bool handler:: on_string( string_view s, - std::size_t, + std::size_t, error_code&) { st.push_string(s); @@ -143,7 +138,7 @@ on_int64( st.push_int64(i); return true; } - + bool handler:: on_uint64( @@ -165,7 +160,7 @@ on_double( st.push_double(d); return true; } - + bool handler:: on_bool( @@ -192,7 +187,7 @@ on_comment_part( { return true; } - + bool handler:: on_comment( diff --git a/include/boost/json/detail/object.hpp b/include/boost/json/detail/object.hpp index 73c1956a5..ccf96f65b 100644 --- a/include/boost/json/detail/object.hpp +++ b/include/boost/json/detail/object.hpp @@ -28,30 +28,29 @@ class unchecked_object // first one is a string key, // second one is the value. value* data_; - std::size_t size_; + value* end_; storage_ptr const& sp_; + bool ignore_duplicates_; public: inline ~unchecked_object(); + inline unchecked_object( value* data, std::size_t size, // # of kv-pairs - storage_ptr const& sp) noexcept - : data_(data) - , size_(size) - , sp_(sp) - { - } + storage_ptr const& sp, + bool ignore_duplicates) noexcept; unchecked_object( unchecked_object&& other) noexcept : data_(other.data_) - , size_(other.size_) + , end_(other.end_) , sp_(other.sp_) + , ignore_duplicates_(other.ignore_duplicates_) { - other.data_ = nullptr; + other.data_ = other.end_ = nullptr; } storage_ptr const& @@ -60,19 +59,25 @@ class unchecked_object return sp_; } + inline std::size_t - size() const noexcept + size() const noexcept; + + bool + ignore_duplicate_keys() const noexcept { - return size_; + return ignore_duplicates_; } value* - release() noexcept + front() noexcept { - auto const data = data_; - data_ = nullptr; - return data; + return data_; } + + inline + void + pop_front() noexcept; }; template diff --git a/include/boost/json/impl/object.hpp b/include/boost/json/impl/object.hpp index 628f4479b..82d67f430 100644 --- a/include/boost/json/impl/object.hpp +++ b/include/boost/json/impl/object.hpp @@ -518,19 +518,43 @@ namespace detail { unchecked_object:: ~unchecked_object() { - if(! data_) - return; if(sp_.is_not_shared_and_deallocate_is_trivial()) return; - value* p = data_; - while(size_--) + + for( value* p = data_; p != end_; p += 2 ) { p[0].~value(); p[1].~value(); - p += 2; } } +unchecked_object:: +unchecked_object( + value* data, + std::size_t size, + storage_ptr const& sp, + bool ignore_duplicates) noexcept + : data_(data) + , end_(data + 2 * size) + , sp_(sp) + , ignore_duplicates_(ignore_duplicates) +{ +} + +std::size_t +unchecked_object:: +size() const noexcept +{ + return std::size_t(end_ - data_) / 2; +} + +void +unchecked_object:: +pop_front() noexcept +{ + data_ += 2; +} + } // detail BOOST_JSON_NS_END diff --git a/include/boost/json/impl/object.ipp b/include/boost/json/impl/object.ipp index 28f732db1..e683dc973 100644 --- a/include/boost/json/impl/object.ipp +++ b/include/boost/json/impl/object.ipp @@ -197,7 +197,7 @@ destroy() noexcept //---------------------------------------------------------- object:: -object(detail::unchecked_object&& uo) +object(detail::unchecked_object& uo) : sp_(uo.storage()) { if(uo.size() == 0) @@ -210,20 +210,18 @@ object(detail::unchecked_object&& uo) uo.size() <= max_size()); t_ = table::allocate( uo.size(), 0, sp_); + t_->size = 0; // insert all elements, keeping - // the last of any duplicate keys. + // the last of any duplicate keys, unless uo.ignore_duplicates is false. auto dest = begin(); - auto src = uo.release(); - auto const end = src + 2 * uo.size(); if(t_->is_small()) { - t_->size = 0; - while(src != end) + for( ; uo.size(); uo.pop_front() ) { + auto src = uo.front(); access::construct_key_value_pair( dest, pilfer(src[0]), pilfer(src[1])); - src += 2; auto result = detail::find_in_object(*this, dest->key()); if(! result.first) { @@ -232,6 +230,11 @@ object(detail::unchecked_object&& uo) continue; } // handle duplicate + if( !uo.ignore_duplicate_keys() ) + { + dest->~key_value_pair(); + return; + } auto& v = *result.first; // don't bother to check if // storage deallocate is trivial @@ -243,11 +246,11 @@ object(detail::unchecked_object&& uo) } return; } - while(src != end) + for( ; uo.size() ; uo.pop_front() ) { + auto src = uo.front(); access::construct_key_value_pair( dest, pilfer(src[0]), pilfer(src[1])); - src += 2; auto& head = t_->bucket(dest->key()); auto i = head; for(;;) @@ -260,6 +263,7 @@ object(detail::unchecked_object&& uo) head = static_cast( dest - begin()); ++dest; + ++t_->size; break; } auto& v = (*t_)[i]; @@ -270,6 +274,11 @@ object(detail::unchecked_object&& uo) } // handle duplicate + if( !uo.ignore_duplicate_keys() ) + { + dest->~key_value_pair(); + return; + } access::next(*dest) = access::next(v); // don't bother to check if @@ -282,8 +291,6 @@ object(detail::unchecked_object&& uo) break; } } - t_->size = static_cast< - index_t>(dest - begin()); } object:: diff --git a/include/boost/json/impl/value_stack.ipp b/include/boost/json/impl/value_stack.ipp index 71c6e6f3d..11ad65cf3 100644 --- a/include/boost/json/impl/value_stack.ipp +++ b/include/boost/json/impl/value_stack.ipp @@ -82,37 +82,6 @@ has_chars() return chars_ != 0; } -error_code -value_stack:: -stack:: -check_duplicates(std::size_t n) -{ - error_code ec; - - for( value* first = top_ - 2 * n; first != top_; first += 2 ) - { - BOOST_ASSERT( first->is_string() ); - value* other = first + 2; - while( true ) - { - BOOST_ASSERT( other->is_string() ); - if( first->get_string() == other->get_string() ) - { - BOOST_JSON_FAIL( ec, error::duplicate_key ); - goto before_return; - } - - if( other == top_ ) - break; - - other += 2; - } - } - -before_return: - return ec; -} - //-------------------------------------- // destroy the values but @@ -320,7 +289,7 @@ exchange(Unchecked&& u) // which belongs to `u`. detail::access:: construct_value( - &jv.v, std::move(u)); + &jv.v, static_cast(u)); std::memcpy( reinterpret_cast< char*>(top_), @@ -395,16 +364,25 @@ push_array(std::size_t n) st_.exchange(std::move(ua)); } -void +error_code value_stack:: -push_object(std::size_t n) +push_object(std::size_t n, bool ignore_duplicates) { // we already have room if n > 0 if(BOOST_JSON_UNLIKELY(n == 0)) st_.maybe_grow(); + detail::unchecked_object uo( - st_.release(n * 2), n, sp_); - st_.exchange(std::move(uo)); + st_.release(n * 2), n, sp_, ignore_duplicates); + st_.exchange(uo); + + error_code ec; + // constructed object should have consumed all of uo's data + if( uo.size() ) + { + BOOST_JSON_FAIL( ec, error::duplicate_key ); + } + return ec; } void @@ -498,13 +476,6 @@ push_null() st_.push(nullptr, sp_); } -error_code -value_stack:: -check_duplicates(std::size_t n) -{ - return st_.check_duplicates(n); -} - BOOST_JSON_NS_END #endif diff --git a/include/boost/json/object.hpp b/include/boost/json/object.hpp index a4ef9351f..1c59f48e9 100644 --- a/include/boost/json/object.hpp +++ b/include/boost/json/object.hpp @@ -90,7 +90,7 @@ class object BOOST_JSON_DECL explicit - object(detail::unchecked_object&& uo); + object(detail::unchecked_object& uo); public: /** The type of _Allocator_ returned by @ref get_allocator diff --git a/include/boost/json/value.hpp b/include/boost/json/value.hpp index c5527d4d6..f4efb7b73 100644 --- a/include/boost/json/value.hpp +++ b/include/boost/json/value.hpp @@ -81,8 +81,8 @@ class value explicit value( - detail::unchecked_object&& uo) - : obj_(std::move(uo)) + detail::unchecked_object& uo) + : obj_(uo) { } diff --git a/include/boost/json/value_stack.hpp b/include/boost/json/value_stack.hpp index bd3b447e5..67061aa51 100644 --- a/include/boost/json/value_stack.hpp +++ b/include/boost/json/value_stack.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -140,7 +141,6 @@ class value_stack inline void run_dtors(bool b) noexcept; inline std::size_t size() const noexcept; inline bool has_chars(); - inline error_code check_duplicates(std::size_t n); inline void clear() noexcept; inline void maybe_grow(); @@ -356,8 +356,8 @@ class value_stack top of the stack to form the array. */ BOOST_JSON_DECL - void - push_object(std::size_t n); + error_code + push_object(std::size_t n, bool ignore_duplicates = true); /** Push part of a key or string onto the stack. @@ -502,10 +502,6 @@ class value_stack BOOST_JSON_DECL void push_null(); - - BOOST_JSON_DECL - error_code - check_duplicates(std::size_t n); }; BOOST_JSON_NS_END diff --git a/test/parse.cpp b/test/parse.cpp index 1a7ef19b2..48cd3ce6a 100644 --- a/test/parse.cpp +++ b/test/parse.cpp @@ -210,11 +210,23 @@ class parse_test BOOST_TEST( jv.as_object().size() == 1 ); parse_options opt; + opt.ignore_duplicate_keys = false; error_code ec; - opt.ignore_duplicate_keys = false; jv = parse( R"( {"a": 1, "a": 2} )", ec, {}, opt ); BOOST_TEST( ec == error::duplicate_key ); + + ec.clear(); + std::string s; + s = "{\"999\":null"; + for(int i = 1; i < 1000; ++i) + s += + ",\"" + + std::to_string(i) + + "\":null"; + s.append("}"); + jv = parse( s, ec, {}, opt ); + BOOST_TEST( ec == error::duplicate_key ); } void