diff --git a/doc/qbk/02_7_parsing.qbk b/doc/qbk/02_7_parsing.qbk index 59428bbb..08cb3afe 100644 --- a/doc/qbk/02_7_parsing.qbk +++ b/doc/qbk/02_7_parsing.qbk @@ -165,9 +165,7 @@ extensions: [Skip UTF-8 Validation] [ UTF-8 byte sequences appearing within strings - are not checked for validity - [br] - (as defined by + are not checked for validity (as defined by [@https://www.unicode.org/versions/Unicode13.0.0/UnicodeStandard-13.0.pdf The Unicode Standard Version 13.0 Section 3.9]). ] [ diff --git a/doc/qbk/quickref.xml b/doc/qbk/quickref.xml index 4712ee6f..e5a752d9 100644 --- a/doc/qbk/quickref.xml +++ b/doc/qbk/quickref.xml @@ -104,6 +104,7 @@ error_code error_condition generic_category + memory_resource string_view system_error diff --git a/doc/source.dox b/doc/source.dox index db2a4be0..67151367 100644 --- a/doc/source.dox +++ b/doc/source.dox @@ -7,6 +7,11 @@ INPUT = \ $(LIB_DIR)/include/boost/json/detail/basic_parser.hpp \ $(LIB_DIR)/include/boost/pilfer.hpp +# $(LIB_DIR)/include/boost/json/error.hpp \ +# $(LIB_DIR)/include/boost/json/string_view.hpp \ +# $(LIB_DIR)/include/boost/json/value_builder.hpp + + ALIASES += esafe="@par Exception Safety" INPUT_ENCODING = UTF-8 diff --git a/example/validate.cpp b/example/validate.cpp index 1f2b57ef..00fc2524 100644 --- a/example/validate.cpp +++ b/example/validate.cpp @@ -9,7 +9,6 @@ /* This example verifies that a file contains valid JSON. - It is implementing by subclassing basic_parser */ #include diff --git a/include/boost/json/array.hpp b/include/boost/json/array.hpp index 539bac04..3a8165af 100644 --- a/include/boost/json/array.hpp +++ b/include/boost/json/array.hpp @@ -28,17 +28,13 @@ class value_ref; /** A dynamically sized array of JSON values This is the type used to represent JSON array values. It - is modeled for equivalence to `std::vector`. - -
+ is modeled for equivalence to `std::vector`.\n The elements are stored contiguously, which means that elements can be accessed not only through iterators, but also using offsets to regular pointers to elements. A pointer to an element of an @ref array may be passed to - any function that expects a pointer to @ref value. - -
+ any function that expects a pointer to @ref value.\n The storage of the array is handled automatically, being expanded and contracted as needed. Arrays usually occupy @@ -48,16 +44,12 @@ class value_ref; is inserted, but only when the additional memory is used up. The total amount of allocated memory can be queried using the @ref capacity function. Extra memory can be - relinquished by calling @ref shrink_to_fit. - -
+ relinquished by calling @ref shrink_to_fit.\n Reallocations are usually costly operations in terms of performance. The @ref reserve function can be used to eliminate reallocations if the number of elements is - known beforehand. - -
+ known beforehand.\n The complexity (efficiency) of common operations on arrays is as follows: @@ -99,6 +91,12 @@ class array class undo_construct; class undo_insert; + friend class value; + + BOOST_JSON_DECL + explicit + array(detail::unchecked_array&& ua); + public: /// The type used to represent unsigned integers using size_type = std::size_t; @@ -155,13 +153,6 @@ public: destroy(); } -#ifndef BOOST_JSON_DOCS - // private - explicit - BOOST_JSON_DECL - array(detail::unchecked_array&& ua); -#endif - //------------------------------------------------------ /** Default constructor. diff --git a/include/boost/json/detail/basic_parser.hpp b/include/boost/json/detail/basic_parser.hpp index 540aafe2..e4247e89 100644 --- a/include/boost/json/detail/basic_parser.hpp +++ b/include/boost/json/detail/basic_parser.hpp @@ -39,9 +39,7 @@ namespace json { JSON is presented to the parser by one or more calls to @ref write_some. The parsing events are realized through member function calls to a handler passed as - an argument to the write function. - -
+ an argument to the write function.\n The parser may dynamically allocate intermediate storage as needed to accommodate the nesting level @@ -59,9 +57,7 @@ namespace json { function @ref parse. This class is designed for users who wish to perform custom actions instead of building a @ref value. For example, to produce a - DOM from an external library. - -
+ DOM from an external library.\n To use this class it is necessary to create a derived class which calls @ref reset at the beginning of @@ -378,9 +374,7 @@ public: parsing event. The parse proceeds from the current state, which is at the beginning of a new JSON or in the middle of the current JSON - if any characters were already parsed. - -
+ if any characters were already parsed.\n The characters in the buffer are processed starting from the beginning, until one of the @@ -410,10 +404,8 @@ public: member function returns `false`, it must set the error code to a suitable value. This error code will be returned by the write function to - the caller. - -
- + the caller.\n + The following declaration meets the parser's handler requirements: diff --git a/include/boost/json/detail/object_impl.hpp b/include/boost/json/detail/object_impl.hpp index 3c594968..e0f74ce2 100644 --- a/include/boost/json/detail/object_impl.hpp +++ b/include/boost/json/detail/object_impl.hpp @@ -20,7 +20,7 @@ namespace boost { namespace json { -struct key_value_pair; +class key_value_pair; namespace detail { diff --git a/include/boost/json/error.hpp b/include/boost/json/error.hpp index e8c92e2f..4f6ca825 100644 --- a/include/boost/json/error.hpp +++ b/include/boost/json/error.hpp @@ -21,27 +21,119 @@ namespace boost { namespace json { -#ifndef BOOST_JSON_STANDALONE - -/// The type of error code used by the library. -using error_code = boost::system::error_code; - -/// The type of error category used by the library. -using error_category = boost::system::error_category; - -/// The type of error condition used by the library. -using error_condition = boost::system::error_condition; - -/// The type of system error thrown by the library. -using system_error = boost::system::system_error; - #ifdef BOOST_JSON_DOCS + +/** The type of error code used by the library. + + This type alias is set depending + on how the library is configured: + + @par Use with Boost + + If the macro `BOOST_JSON_STANDALONE` is + not defined, this type will be an alias + for `boost::system::error_code`. + Compiling a program using the library will + require Boost, and a compiler conforming + to C++11 or later. + + @par Use without Boost + + If the macro `BOOST_JSON_STANDALONE` is + defined, this type will be an alias + for `std::error_code`. + Compiling a program using the library will + require only a compiler conforming to C++17 + or later. +*/ +using error_code = __see_below__; + +/** The type of error category used by the library. + + This type alias is set depending + on how the library is configured: + + @par Use with Boost + + If the macro `BOOST_JSON_STANDALONE` is + not defined, this type will be an alias + for `boost::system::error_category`. + Compiling a program using the library will + require Boost, and a compiler conforming + to C++11 or later. + + @par Use without Boost + + If the macro `BOOST_JSON_STANDALONE` is + defined, this type will be an alias + for `std::error_category`. + Compiling a program using the library will + require only a compiler conforming to C++17 + or later. +*/ +using error_category = __see_below__; + +/** The type of error condition used by the library. + + This type alias is set depending + on how the library is configured: + + @par Use with Boost + + If the macro `BOOST_JSON_STANDALONE` is + not defined, this type will be an alias + for `boost::system::error_condition`. + Compiling a program using the library will + require Boost, and a compiler conforming + to C++11 or later. + + @par Use without Boost + + If the macro `BOOST_JSON_STANDALONE` is + defined, this type will be an alias + for `std::error_condition`. + Compiling a program using the library will + require only a compiler conforming to C++17 + or later. +*/ +using error_condition = __see_below__; + +/** The type of system error thrown by the library. + + This type alias is set depending + on how the library is configured: + + @par Use with Boost + + If the macro `BOOST_JSON_STANDALONE` is + not defined, this type will be an alias + for `boost::system::system_error`. + Compiling a program using the library will + require Boost, and a compiler conforming + to C++11 or later. + + @par Use without Boost + + If the macro `BOOST_JSON_STANDALONE` is + defined, this type will be an alias + for `std::system_error`. + Compiling a program using the library will + require only a compiler conforming to C++17 + or later. +*/ +using system_error = __see_below__; + /// Returns the generic error category used by the library. error_category const& generic_category(); -#else + +#elif ! defined(BOOST_JSON_STANDALONE) + +using error_code = boost::system::error_code; +using error_category = boost::system::error_category; +using error_condition = boost::system::error_condition; +using system_error = boost::system::system_error; using boost::system::generic_category; -#endif #else diff --git a/include/boost/json/impl/object.ipp b/include/boost/json/impl/object.ipp index 05645fff..3673cb05 100644 --- a/include/boost/json/impl/object.ipp +++ b/include/boost/json/impl/object.ipp @@ -73,13 +73,6 @@ public: // //---------------------------------------------------------- -object:: -object(object_test const*) -{ - object_impl impl(3, 1, 3, 0, sp_); - impl_.swap(impl); -} - object:: object(detail::unchecked_object&& uo) : sp_(uo.storage()) @@ -90,6 +83,13 @@ object(detail::unchecked_object&& uo) impl_.build(); } +object:: +object(object_test const*) +{ + object_impl impl(3, 1, 3, 0, sp_); + impl_.swap(impl); +} + object:: object(storage_ptr sp) noexcept : sp_(std::move(sp)) diff --git a/include/boost/json/impl/parser.ipp b/include/boost/json/impl/parser.ipp index d532465b..ad7996ce 100644 --- a/include/boost/json/impl/parser.ipp +++ b/include/boost/json/impl/parser.ipp @@ -20,6 +20,194 @@ namespace boost { namespace json { +bool +parser:: +handler:: +on_document_begin( + error_code&) +{ + return true; +} + +bool +parser:: +handler:: +on_document_end( + error_code&) +{ + return true; +} + +bool +parser:: +handler:: +on_object_begin( + error_code&) +{ + vb.begin_object(); + return true; +} + +bool +parser:: +handler:: +on_object_end( + error_code&) +{ + vb.end_object(); + return true; +} + +bool +parser:: +handler:: +on_array_begin( + error_code&) +{ + vb.begin_array(); + return true; +} + +bool +parser:: +handler:: +on_array_end( + error_code&) +{ + vb.end_array(); + return true; +} + +bool +parser:: +handler:: +on_key_part( + string_view s, + error_code&) +{ + vb.insert_key_part(s); + return true; +} + +bool +parser:: +handler:: +on_key( + string_view s, + error_code&) +{ + vb.insert_key(s); + return true; +} + +bool +parser:: +handler:: +on_string_part( + string_view s, + error_code&) +{ + vb.insert_string_part(s); + return true; +} + +bool +parser:: +handler:: +on_string( + string_view s, + error_code&) +{ + vb.insert_string(s); + return true; +} + +bool +parser:: +handler:: +on_number_part( + string_view, + error_code&) +{ + return true; +} + +bool +parser:: +handler:: +on_int64( + std::int64_t i, + string_view, + error_code&) +{ + vb.insert_int64(i); + return true; +} + +bool +parser:: +handler:: +on_uint64( + std::uint64_t u, + string_view, + error_code&) +{ + vb.insert_uint64(u); + return true; +} + +bool +parser:: +handler:: +on_double( + double d, + string_view, + error_code&) +{ + vb.insert_double(d); + return true; +} + +bool +parser:: +handler:: +on_bool( + bool b, + error_code&) +{ + vb.insert_bool(b); + return true; +} + +bool +parser:: +handler:: +on_null(error_code&) +{ + vb.insert_null(); + return true; +} + +bool +parser:: +handler:: +on_comment_part( + string_view, error_code&) +{ + return true; +} + +bool +parser:: +handler:: +on_comment( + string_view, error_code&) +{ + return true; +} + +//---------------------------------------------------------- + parser:: parser() noexcept : parser( @@ -122,12 +310,12 @@ value parser:: release() { - /* + // VFALCO Do we need to put the throw + // in a separate raise() function? if(! p_.is_complete()) BOOST_THROW_EXCEPTION( std::logic_error( "no value")); - */ return p_.handler().vb.release(); } diff --git a/include/boost/json/impl/value_builder.ipp b/include/boost/json/impl/value_builder.ipp index e4bf32b3..05a95c64 100644 --- a/include/boost/json/impl/value_builder.ipp +++ b/include/boost/json/impl/value_builder.ipp @@ -98,56 +98,7 @@ reset(storage_ptr sp) noexcept clear(); sp_ = std::move(sp); lev_.st = state::begin; -} -value -value_builder:: -release() -{ - // An exception here means that the value - // was not properly constructed. For example, - // an array or object was not closed, or - // there was no top level value. - if( lev_.st != state::top || - lev_.count != 1) - BOOST_THROW_EXCEPTION( - std::logic_error( - "no value")); - auto ua = pop_array(); - BOOST_ASSERT(rs_.empty()); - union U - { - value v; - U(){} - ~U(){} - }; - U u; - ua.relocate(&u.v); - lev_.st = state::need_reset; - sp_ = {}; - return pilfer(u.v); -} - -void -value_builder:: -clear() noexcept -{ - destroy(); - rs_.clear(); - lev_.count = 0; - key_size_ = 0; - str_size_ = 0; - lev_.st = state::need_reset; - sp_ = {}; -} - -//---------------------------------------------------------- - -bool -value_builder:: -on_document_begin( - error_code&) -{ // reset() must be called before // building every new top level value. BOOST_ASSERT(lev_.st == state::begin); @@ -161,24 +112,86 @@ on_document_begin( // inside a notional 1-element array. rs_.add(sizeof(value)); lev_.st = state::top; - - return true; } -bool +value value_builder:: -on_document_end(error_code&) +release() { // If this goes off, then an - // array or object was never finished. + // array or object was never closed. BOOST_ASSERT(lev_.st == state::top); BOOST_ASSERT(lev_.count == 1); - return true; + + auto ua = pop_array(); + BOOST_ASSERT(rs_.empty()); + union U + { + value v; + U(){} + ~U(){} + }; + U u; + ua.relocate(&u.v); + lev_.st = state::need_reset; + + // give up the resource in case + // it uses shared ownership. + sp_ = {}; + + return pilfer(u.v); } -bool +void value_builder:: -on_object_begin(error_code&) +clear() noexcept +{ + destroy(); + rs_.clear(); + lev_.count = 0; + key_size_ = 0; + str_size_ = 0; + lev_.st = state::need_reset; + + // give up the resource in case + // it uses shared ownership. + sp_ = {}; +} + +//---------------------------------------------------------- + +void +value_builder:: +begin_array() +{ + // prevent splits from exceptions + rs_.prepare( + sizeof(level) + + sizeof(value) + + alignof(value) - 1); + push(lev_); + lev_.align = + detail::align_to(rs_); + rs_.add(sizeof(value)); + lev_.count = 0; + lev_.st = state::arr; +} + +void +value_builder:: +end_array() +{ + BOOST_ASSERT( + lev_.st == state::arr); + auto ua = pop_array(); + rs_.subtract(lev_.align); + pop(lev_); + emplace(std::move(ua)); +} + +void +value_builder:: +begin_object() { // prevent splits from exceptions rs_.prepare( @@ -192,123 +205,65 @@ on_object_begin(error_code&) object::value_type)); lev_.count = 0; lev_.st = state::obj; - return true; } -bool +void value_builder:: -on_object_end( - error_code& ec) +end_object() { BOOST_ASSERT( lev_.st == state::obj); auto uo = pop_object(); rs_.subtract(lev_.align); pop(lev_); - return emplace( - ec, std::move(uo)); + emplace(std::move(uo)); } -bool +void value_builder:: -on_array_begin(error_code&) +insert_key_part( + string_view s) { - // prevent splits from exceptions - rs_.prepare( - sizeof(level) + - sizeof(value) + - alignof(value) - 1); - push(lev_); - lev_.align = - detail::align_to(rs_); - rs_.add(sizeof(value)); - lev_.count = 0; - lev_.st = state::arr; - return true; -} - -bool -value_builder:: -on_array_end( - error_code& ec) -{ - BOOST_ASSERT( - lev_.st == state::arr); - auto ua = pop_array(); - rs_.subtract(lev_.align); - pop(lev_); - return emplace( - ec, std::move(ua)); -} - -bool -value_builder:: -on_key_part( - string_view s, - error_code& ec) -{ - if( s.size() > - string::max_size() - key_size_) - { - ec = error::key_too_large; - return false; - } push_chars(s); key_size_ += static_cast< std::uint32_t>(s.size()); - return true; } -bool +void value_builder:: -on_key( - string_view s, - error_code& ec) +insert_key( + string_view s) { BOOST_ASSERT( lev_.st == state::obj); - if(! on_key_part(s, ec)) - return false; + push_chars(s); + key_size_ += static_cast< + std::uint32_t>(s.size()); push(key_size_); key_size_ = 0; lev_.st = state::key; - return true; } -bool +void value_builder:: -on_string_part( - string_view s, - error_code& ec) +insert_string_part( + string_view s) { - if( s.size() > - string::max_size() - str_size_) - { - ec = error::string_too_large; - return false; - } push_chars(s); str_size_ += static_cast< std::uint32_t>(s.size()); - return true; } -bool +void value_builder:: -on_string( - string_view s, - error_code& ec) +insert_string( + string_view s) { - if( s.size() > - string::max_size() - str_size_) - { - ec = error::string_too_large; - return false; - } if(str_size_ == 0) { // fast path - return emplace(ec, s, sp_); + emplace(s, sp_); + return; } string str(sp_); @@ -324,54 +279,46 @@ on_string( str.data() + sv.size(), s.data(), s.size()); str.grow(sv.size() + s.size()); - return emplace( - ec, std::move(str), sp_); + emplace(std::move(str), sp_); } -bool +void value_builder:: -on_int64( - int64_t i, - string_view, - error_code& ec) +insert_int64( + int64_t i) { - return emplace(ec, i, sp_); + emplace(i, sp_); } -bool +void value_builder:: -on_uint64( - uint64_t u, - string_view, - error_code& ec) +insert_uint64( + uint64_t u) { - return emplace(ec, u, sp_); + emplace(u, sp_); } -bool +void value_builder:: -on_double( - double d, - string_view, - error_code& ec) +insert_double( + double d) { - return emplace(ec, d, sp_); + emplace(d, sp_); } -bool +void value_builder:: -on_bool( - bool b, - error_code& ec) +insert_bool( + bool b) { - return emplace(ec, b, sp_); + emplace(b, sp_); } -bool +void value_builder:: -on_null(error_code& ec) +insert_null() { - return emplace(ec, nullptr, sp_); + emplace(nullptr, sp_); } //---------------------------------------------------------- @@ -527,33 +474,19 @@ emplace_array(Args&&... args) } template -bool +void value_builder:: emplace( - error_code& ec, Args&&... args) { if(lev_.st == state::key) { - if(lev_.count < - object::max_size()) - { - emplace_object(std::forward< - Args>(args)...); - return true; - } - ec = error::object_too_large; - return false; + emplace_object( + std::forward(args)...); + return; } - if(lev_.count < - array::max_size()) - { - emplace_array(std::forward< - Args>(args)...); - return true; - } - ec = error::array_too_large; - return false; + emplace_array( + std::forward(args)...); } template diff --git a/include/boost/json/memory_resource.hpp b/include/boost/json/memory_resource.hpp index 362b293e..92a64d28 100644 --- a/include/boost/json/memory_resource.hpp +++ b/include/boost/json/memory_resource.hpp @@ -20,9 +20,35 @@ namespace boost { namespace json { -#ifndef BOOST_JSON_STANDALONE +#ifdef BOOST_JSON_DOCS + +/** The type of memory resource used by the library. + + This type alias is set depending + on how the library is configured: + + @par Use with Boost + + If the macro `BOOST_JSON_STANDALONE` is + not defined, this type will be an alias + for `boost::container::pmr::memory_resource`. + Compiling a program using the library will + require Boost, and a compiler conforming + to C++11 or later. + + @par Use without Boost + + If the macro `BOOST_JSON_STANDALONE` is + defined, this type will be an alias + for `std::pmr::memory_resource`. + Compiling a program using the library will + require only a compiler conforming to C++17 + or later. +*/ +using memory_resource = __see_below__; + +#elif ! defined(BOOST_JSON_STANDALONE) -/// The type of memory_resource used by the library. using memory_resource = boost::container::pmr::memory_resource; #else diff --git a/include/boost/json/object.hpp b/include/boost/json/object.hpp index 3dba63cc..f6edd321 100644 --- a/include/boost/json/object.hpp +++ b/include/boost/json/object.hpp @@ -35,24 +35,18 @@ class object_test; /** A dynamically sized associative container of JSON key/value pairs. This is an associative container whose elements - are key/value pairs with unique keys. - -
+ are key/value pairs with unique keys.\n The elements are stored contiguously, which means that elements can be accessed not only through iterators, but also using offsets to regular pointers to elements. A pointer to an element of an @ref object may be passed to any function that expects a pointer to - @ref key_value_pair. - -
+ @ref key_value_pair.\n The container also maintains an internal index to speed up find operations, reducing the average complexity - for most lookups and insertions - -
+ for most lookups and insertions.\n Reallocations are usually costly operations in terms of performance, as elements are copied and the internal @@ -109,7 +103,14 @@ class object return 1.0; } + friend class value; + + BOOST_JSON_DECL + explicit + object(detail::unchecked_object&& uo); + friend class object_test; + BOOST_JSON_DECL object(object_test const*); @@ -177,13 +178,6 @@ public: impl_.destroy(sp_); } -#ifndef BOOST_JSON_DOCS - // private - BOOST_JSON_DECL - explicit - object(detail::unchecked_object&& uo); -#endif - //------------------------------------------------------ /** Default constructor. @@ -1210,14 +1204,13 @@ public: Returns a reference to the value that is mapped to a key equivalent to key, performing an insertion - of a null value if such key does not already exist. -
+ of a null value if such key does not already exist.\n + If an insertion occurs and results in a rehashing of the container, all iterators are invalidated. Otherwise iterators are not affected. References are not invalidated. Rehashing occurs only if the new - number of elements is greater than - @ref capacity(). + number of elements is greater than @ref capacity(). @par Complexity diff --git a/include/boost/json/parser.hpp b/include/boost/json/parser.hpp index f63b524a..1c6f3435 100644 --- a/include/boost/json/parser.hpp +++ b/include/boost/json/parser.hpp @@ -82,95 +82,24 @@ class parser { } - bool on_document_begin(error_code& ec) - { - return vb.on_document_begin(ec); - } - - bool on_document_end(error_code& ec) - { - return vb.on_document_end(ec); - } - - bool on_object_begin(error_code& ec) - { - return vb.on_object_begin(ec); - } - - bool on_object_end(error_code& ec) - { - return vb.on_object_end(ec); - } - - bool on_array_begin(error_code& ec) - { - return vb.on_array_begin(ec); - } - - bool on_array_end(error_code& ec) - { - return vb.on_array_end(ec); - } - - bool on_key_part(string_view s, error_code& ec) - { - return vb.on_key_part(s, ec); - } - - bool on_key(string_view s, error_code& ec) - { - return vb.on_key(s, ec); - } - - bool on_string_part(string_view s, error_code& ec) - { - return vb.on_string_part(s, ec); - } - - bool on_string(string_view s, error_code& ec) - { - return vb.on_string(s, ec); - } - - bool on_number_part(string_view s, error_code& ec) - { - return vb.on_number_part(s, ec); - } - - bool on_int64(std::int64_t i, string_view, error_code& ec) - { - return vb.on_int64(i, {}, ec); - } - - bool on_uint64(std::uint64_t u, string_view, error_code& ec) - { - return vb.on_uint64(u, {}, ec); - } - - bool on_double(double d, string_view, error_code& ec) - { - return vb.on_double(d, {}, ec); - } - - bool on_bool(bool b, error_code& ec) - { - return vb.on_bool(b, ec); - } - - bool on_null(error_code& ec) - { - return vb.on_null(ec); - } - - bool on_comment_part(string_view, error_code&) - { - return true; - } - - bool on_comment(string_view, error_code&) - { - return true; - } + inline bool on_document_begin(error_code& ec); + inline bool on_document_end(error_code& ec); + inline bool on_object_begin(error_code& ec); + inline bool on_object_end(error_code& ec); + inline bool on_array_begin(error_code& ec); + inline bool on_array_end(error_code& ec); + inline bool on_key_part(string_view s, error_code& ec); + inline bool on_key(string_view s, error_code& ec); + inline bool on_string_part(string_view s, error_code& ec); + inline bool on_string(string_view s, error_code& ec); + inline bool on_number_part(string_view, error_code&); + inline bool on_int64(std::int64_t i, string_view, error_code& ec); + inline bool on_uint64(std::uint64_t u, string_view, error_code& ec); + inline bool on_double(double d, string_view, error_code& ec); + inline bool on_bool(bool b, error_code& ec); + inline bool on_null(error_code& ec); + inline bool on_comment_part(string_view, error_code&); + inline bool on_comment(string_view, error_code&); }; basic_parser p_; @@ -204,9 +133,7 @@ public: @note Before any JSON can be parsed, the function - @ref reset must be called. - -
+ @ref reset must be called.\n The `sp` parameter is only used to allocate intermediate storage; it will not be used @@ -241,9 +168,7 @@ public: @note Before any JSON can be parsed, the function - @ref reset must be called. - -
+ @ref reset must be called.\n The `sp` parameter is only used to allocate intermediate storage; it will not be used @@ -333,9 +258,7 @@ public: buffer. The parse proceeds from the current state, which is at the beginning of a new JSON or in the middle of the current JSON if any - characters were already parsed. - -
+ characters were already parsed.\n The characters in the buffer are processed starting from the beginning, until one of the @@ -384,9 +307,7 @@ public: buffer. The parse proceeds from the current state, which is at the beginning of a new JSON or in the middle of the current JSON if any - characters were already parsed. - -
+ characters were already parsed.\n The characters in the buffer are processed starting from the beginning, until one of the diff --git a/include/boost/json/string_view.hpp b/include/boost/json/string_view.hpp index 40c1869a..50b825bb 100644 --- a/include/boost/json/string_view.hpp +++ b/include/boost/json/string_view.hpp @@ -21,9 +21,35 @@ namespace boost { namespace json { -#ifndef BOOST_JSON_STANDALONE +#ifdef BOOST_JSON_DOCS + +/** The type of string view used by the library. + + This type alias is set depending + on how the library is configured: + + @par Use with Boost + + If the macro `BOOST_JSON_STANDALONE` is + not defined, this type will be an alias + for `boost::string_view`. + Compiling a program using the library will + require Boost, and a compiler conforming + to C++11 or later. + + @par Use without Boost + + If the macro `BOOST_JSON_STANDALONE` is + defined, this type will be an alias + for `std::string_view`. + Compiling a program using the library will + require only a compiler conforming to C++17 + or later. +*/ +using string_view = __see_below__; + +#elif ! defined(BOOST_JSON_STANDALONE) -/// The type of string view used by the library. using string_view = boost::string_view; #else diff --git a/include/boost/json/value.hpp b/include/boost/json/value.hpp index 3ee53dd4..c808abcf 100644 --- a/include/boost/json/value.hpp +++ b/include/boost/json/value.hpp @@ -76,6 +76,11 @@ class value struct undo; struct init_iter; + friend class value_builder; + friend class key_value_pair; + inline value(detail::unchecked_object&& uo); + inline value(detail::unchecked_array&& ua); + public: /** Destructor. @@ -90,12 +95,6 @@ public: BOOST_JSON_DECL ~value(); -#ifndef BOOST_JSON_DOCS - // private - inline value(detail::unchecked_object&& uo); - inline value(detail::unchecked_array&& ua); -#endif - /** Default constructor. The constructed value is null, @@ -2670,8 +2669,9 @@ swap(value& lhs, value& rhs) This is the type of element used by the @ref object container. */ -struct key_value_pair +class key_value_pair { +public: /** Copy assignment (deleted). */ key_value_pair& diff --git a/include/boost/json/value_builder.hpp b/include/boost/json/value_builder.hpp index c62df10f..6e15d80a 100644 --- a/include/boost/json/value_builder.hpp +++ b/include/boost/json/value_builder.hpp @@ -22,34 +22,53 @@ namespace json { //---------------------------------------------------------- -/** A factory for building a value DOM. +/** A factory for building a value. A value builder implements an algorithm for efficiently constructing a @ref value from an external source (provided by the caller). - - The builder uses a dynamically allocated internal - storage to hold portions of the document, allowing - complete objects and arrays to be constructed using - a single allocation when their contents are - eventually known. This internal storage is reused - when creating multiple values with the same builder. + It uses a dynamically allocated internal storage + to hold portions of the document, allowing complete + objects and arrays to be constructed using a single + allocation when their contents are eventually known. + This internal storage is reused when creating multiple + values with the same builder. \n To use the builder construct it with an optionally specified memory resource to use for the internal storage. Then call @ref reset once before building each complete DOM, optionally specifying the memory resource to use for the resulting @ref value. + Once the reset function is called, the value may + be built iteratively by calling the appropriate + insertion functions as desired. After construction + is finished, the caller can take ownership of the + resulting value by calling @ref release. - The functions @ref on_document_begin and - @ref on_document_end must be called exactly once - at the beginning and at the end of construction. - The remaining event handling functions are called - according to their descriptions to build the document. + @par Example + + The following code constructs a @ref value which + when serialized produces a JSON object with three + elements. + + @code + value_builder vb; + vb.reset(); + vb.begin_object(); + vb.insert_key("a"); + vb.insert_int64(1); + vb.insert_key("b"); + vb.insert_null(); + vb.insert_key("c"); + vb.insert_string("hello"); + vb.end_object(); + assert( to_string(vb.release()) == "{\"a\":1,\"b\":null,\"c\":\"hello\"}" ); + @endcode */ class value_builder { enum class state : char; + struct level { std::uint32_t count; @@ -66,30 +85,26 @@ class value_builder public: /** Destructor. - All dynamically allocated memory, including - any partially built results, is freed. + All dynamically allocated memory and + partial or complete elements is freed. */ BOOST_JSON_DECL ~value_builder(); /** Constructor. - Constructs a empty builder using the default - memory resource, or the optionally specified - @ref storage_ptr, to allocate intermediate storage. + Constructs a empty builder. Before any + @ref value can be built, the function + @ref reset must be called. - @note - Before any @ref value can be built, - the function @ref start must be called. - -
- - The `sp` parameter is only used to - allocate intermediate storage; it will not be used + The `sp` parameter is only used to allocate + intermediate storage; it will not be used for the @ref value returned by @ref release. - @param sp The @ref storage_ptr to use for - intermediate storage allocations. + @param sp A pointer to the @ref memory_resource + to use for intermediate storage allocations. If + this argument is omitted, the default memory + resource is used. */ BOOST_JSON_DECL explicit @@ -98,31 +113,32 @@ public: /** Reserve internal storage space. This function reserves space for `n` bytes - in the parser's internal temporary storage. + in the builders's internal temporary storage. The request is only a hint to the - implementation. + implementation. @par Exception Safety Strong guarantee. - @param n The number of bytes to reserve. A - good choices is `C * sizeof(value)` where - `C` is the total number of @ref value elements - in a typical parsed JSON. + @param n The number of bytes to reserve. */ BOOST_JSON_DECL void reserve(std::size_t n); - /** Prepare the builder for a new value. + /** Prepare to build a new value. This function must be called before building - a new @ref value. Any previously existing full - or partial values are destroyed, but internal + a new @ref value. Any previously existing partial + or complete elements are destroyed, but internal dynamically allocated memory is preserved which may be reused to build new values. + @par Exception Safety + + No-throw guarantee. + @param sp A pointer to the @ref memory_resource to use for the resulting value. The builder will acquire shared ownership of the memory resource. @@ -131,13 +147,16 @@ public: void reset(storage_ptr sp = {}) noexcept; - /** Return the parsed JSON as a @ref value. + /** Return the completed value. If @ref is_complete() returns `true`, then the parsed value is returned. Otherwise an exception is thrown. - @throw std::logic_error `! is_complete()` + @par Exception Safety + + Basic guarantee. + Calls to `memory_resource::allocate` may throw. @return The parsed value. Ownership of this value is transferred to the caller. @@ -153,10 +172,12 @@ public: internal memory which may be reused on a subsequent parse. - @note + After calling this function, it is necessary + to call @ref start before building a new value. - After this call, it is necessary to call - @ref start to parse a new JSON incrementally. + @par Exception Safety + + No-throw guarantee. */ BOOST_JSON_DECL void @@ -164,339 +185,245 @@ public: //-------------------------------------------- - /** Begin building a new value. + /** Insert an array. - This function must be called exactly once - after calling @ref reset, before any other - event functions are invoked. + This function opens a new, empty array + which will be inserted into the result as + the next element of the currently open array + or object, or as the top-level element if + no other elements exist.\n - @return `true` on success. + After calling this function, elements + are inserted into the array by calling + the other insertion functions (including + @ref begin_array and @ref begin_object).\n - @param ec Set to the error, if any occurred. + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. */ BOOST_JSON_DECL - bool - on_document_begin( - error_code& ec); + void + begin_array(); - /** Finish building a new value. + /** Insert an array. - This function must be called exactly once - before calling @ref release, and after all - event functions have been called. + This function closes the current array, + which must have been opened by a previously + balanced call to @ref begin_array. + The array is then inserted into the currently + open array or object, or the top level if no + enclosing array or object is open. - @return `true` on success. - - @param ec Set to the error, if any occurred. + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. */ BOOST_JSON_DECL - bool - on_document_end( - error_code& ec); + void + end_array(); - /** Begin building an object. + /** Insert an object. - This instructs the builder to begin building - a new JSON object, either as the top-level - element of the resulting value, or as the - next element of the current object or array - being built. + This function opens a new, empty object + which will be inserted into the result as + the next element of the currently open array + or object, or as the top-level element if + no other elements exist.\n - @return `true` on success. + After calling this function, elements are + inserted into the object by first inserting + the key using @ref insert_key and + @ref insert_key_part, and then calling + the other insertion functions (including + @ref begin_object and @ref begin_array) to + add the value corresponding to the key.\n - @param ec Set to the error, if any occurred. + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. */ BOOST_JSON_DECL - bool - on_object_begin( - error_code& ec); + void + begin_object(); - /** Finish building an object. + /** Insert an object. - This event function instructs the builder that - the object currently being built, which was created - by the last call to @ref on_object_begin, is finished. + This function closes the current object, + which must have been opened by a previously + balanced call to @ref begin_object. + The object is then inserted into the currently + open array or object, or the top level if no + enclosing array or object is open. - @return `true` on success. - - @param ec Set to the error, if any occurred. + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. */ BOOST_JSON_DECL - bool - on_object_end( - error_code& ec); + void + end_object(); - /** Begin building an array. - - This instructs the builder to begin building - a new JSON array, either as the top-level - element of the resulting value, or as the - next element of the current object or array - being built. - - @return `true` on success. - - @param ec Set to the error, if any occurred. - */ - BOOST_JSON_DECL - bool - on_array_begin( - error_code& ec); - - /** Finish building an array. - - This function instructs the builder that the - array currently being built, which was created - by the last call to @ref on_array_begin, is finished. - - @return `true` on success. - - @param ec Set to the error, if any occurred. - */ - BOOST_JSON_DECL - bool - on_array_end( - error_code& ec); - - /** Continue creating a key. + /** Set the key for the next value. This function appends the specified characters - to the key being built as the next element of - a currently open object. If a key is not currently - being built, the behavior is undefined. + to the current key, which must be part of an + open object. If a key is not currently being + built or an object is not open, the behavior + is undefined. - @return `true` on success. + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. @param s The characters to append. This may be empty. - - @param ec Set to the error, if any occurred. */ BOOST_JSON_DECL - bool - on_key_part( - string_view s, - error_code& ec); + void + insert_key_part( + string_view s); - /** Finish creating a key. + /** Set the key for the next value. This function appends the specified characters - to the key being built as the next element of - a currently open object, and finishes construction - of the key. If a key is not currently being built, - the behavior is undefined. + to the current key, which must be part of an + open object. If a key is not currently being + built or an object is not open, the behavior + is undefined. After the characters are inserted, + the key is finished and a value must be inserted + next. - @return `true` on success. + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. @param s The characters to append. This may be empty. - - @param ec Set to the error, if any occurred. */ BOOST_JSON_DECL - bool - on_key( - string_view s, - error_code& ec); + void + insert_key( + string_view s); - /** Begin or continue creating a string. + /** Insert a string. This function appends the specified characters - to the string being built. If a string is not - currently being built, then a new empty string - is started. + to the current string, which will be created if + it did not already exist from an immediately + prior call to @ref insert_string_part. - @return `true` on success. + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. @param s The characters to append. This may be empty. - - @param ec Set to the error, if any occurred. */ BOOST_JSON_DECL - bool - on_string_part( - string_view s, - error_code& ec); + void + insert_string_part( + string_view s); - /** Create a string or finish creating a string. + /** Insert a string. This function appends the specified characters - to the string being built. If a string is not - currently being built, then a new string is created - with the specified characters. + to the current string, which will be created if + it did not already exist from an immediately prior + call to @ref insert_string_part. + The string is then inserted into the currently + open array or object, or the top level if no + array or object is open. - @return `true` on success. + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. @param s The characters to append. This may be empty. - - @param ec Set to the error, if any occurred. */ BOOST_JSON_DECL - bool - on_string( - string_view s, - error_code& ec); + void + insert_string( + string_view s); - /** Begin building a number from a string. + /** Insert a number. - This instructs the builder to begin building - a new JSON number, either as the top-level - element of the resulting value, or as the - next element of the current object or array - being built. + This function inserts a number into the currently + open array or object, or the top level if no + array or object is open. - @note This function has no effect and always - returns `true`. + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. - @return `true` on success. - - @param s The characters to append. This may be empty. - - @param ec Set to the error, if any occurred. - */ - bool - on_number_part( - string_view s, - error_code& ec) - { - (void)s; - (void)ec; - return true; - } - - /** Build a number. - - This function builds a number from the specified - value and adds it as the top-level element of the - resulting value, or as the next element of the - current object or array being built. - - @return `true` on success. - - @param i The integer to build. - - @param s The characters to append. This value is ignored. - - @param ec Set to the error, if any occurred. + @param i The number to insert. */ BOOST_JSON_DECL - bool - on_int64( - int64_t i, - string_view s, - error_code& ec); + void + insert_int64( + int64_t i); - /** Build a number. + /** Insert a number. - This function builds a number from the specified - value and adds it as the top-level element of the - resulting value, or as the next element of the - current object or array being built. + This function inserts a number into the currently + open array or object, or the top level if no + array or object is open. - @return `true` on success. + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. - @param i The unsigned integer to build. - - @param s The characters to append. This value is ignored. - - @param ec Set to the error, if any occurred. + @param u The number to insert. */ BOOST_JSON_DECL - bool - on_uint64( - uint64_t u, - string_view s, - error_code& ec); + void + insert_uint64( + uint64_t u); - /** Build a number. + /** Insert a number. - This function builds a number from the specified - value and adds it as the top-level element of the - resulting value, or as the next element of the - current object or array being built. + This function inserts a number into the currently + open array or object, or the top level if no + array or object is open. - @return `true` on success. + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. - @param i The floating point number to build. - - @param s The characters to append. This value is ignored. - - @param ec Set to the error, if any occurred. + @param d The number to insert. */ BOOST_JSON_DECL - bool - on_double( - double d, - string_view s, - error_code& ec); + void + insert_double( + double d); - /** Build a boolean. + /** Insert a boolean. - This function builds a boolean from the specified - value and adds it as the top-level element of the - resulting value, or as the next element of the - current object or array being built. + This function inserts a boolean into the currently + open array or object, or the top level if no + array or object is open. - @return `true` on success. + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. - @param b The boolean to build. - - @param ec Set to the error, if any occurred. + @param b The boolean to insert. */ BOOST_JSON_DECL - bool - on_bool( - bool b, - error_code& ec); + void + insert_bool( + bool b); /** Build a null. - This function builds a null from the specified - value and adds it as the top-level element of the - resulting value, or as the next element of the - current object or array being built. + This function inserts a null into the currently + open array or object, or the top level if no + array or object is open. - @return `true` on success. - - @param ec Set to the error, if any occurred. + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. */ BOOST_JSON_DECL - bool - on_null(error_code& ec); - - /** Specify part of comment. - - This function has no effect and always returns `true`. - - @param s The characters to append. This value is ignored. - - @param ec Set to the error, if any occurred. - */ - bool - on_comment_part( - string_view s, - error_code& ec) - { - (void)s; - (void)ec; - return true; - } - - /** Specify a comment. - - This function has no effect and always returns `true`. - - @param s The characters to append. This value is ignored. - - @param ec Set to the error, if any occurred. - */ - bool - on_comment( - string_view s, - error_code& ec) - { - (void)s; - (void)ec; - return true; - } + void + insert_null(); private: inline @@ -522,9 +449,8 @@ private: Args&&... args); template - bool + void emplace( - error_code& ec, Args&&... args); template diff --git a/test/value_builder.cpp b/test/value_builder.cpp index d2157d5a..28fcce02 100644 --- a/test/value_builder.cpp +++ b/test/value_builder.cpp @@ -10,6 +10,8 @@ // Test that header file is self-contained. #include +#include + #include "test_suite.hpp" namespace boost { @@ -21,13 +23,20 @@ public: void testBuilder() { - error_code ec; - value_builder vb; - vb.reset(); - vb.on_document_begin(ec); - vb.on_null(ec); - vb.on_document_end(ec); - vb.release(); + // This is from the javadoc + + value_builder vb; + vb.reset(); + vb.begin_object(); + vb.insert_key("a"); + vb.insert_int64(1); + vb.insert_key("b"); + vb.insert_null(); + vb.insert_key("c"); + vb.insert_string("hello"); + vb.end_object(); + assert( to_string(vb.release()) == "{\"a\":1,\"b\":null,\"c\":\"hello\"}" ); + } void