Refactor value_builder

This commit is contained in:
Vinnie Falco 2020-08-16 15:06:03 -07:00
parent a342c936e4
commit a90527ca7b
18 changed files with 778 additions and 678 deletions

View File

@ -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]).
]
[

View File

@ -104,6 +104,7 @@
<member><link linkend="json.ref.boost__json__error_code">error_code</link></member>
<member><link linkend="json.ref.boost__json__error_condition">error_condition</link></member>
<member><link linkend="json.ref.boost__json__generic_category">generic_category</link></member>
<member><link linkend="json.ref.boost__json__memory_resource">memory_resource</link></member>
<member><link linkend="json.ref.boost__json__string_view">string_view</link></member>
<member><link linkend="json.ref.boost__json__system_error">system_error</link></member>
</simplelist>

View File

@ -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

View File

@ -9,7 +9,6 @@
/*
This example verifies that a file contains valid JSON.
It is implementing by subclassing basic_parser
*/
#include <boost/json.hpp>

View File

@ -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<value>`.
<br>
is modeled for equivalence to `std::vector<value>`.\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.
<br>
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.
<br>
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.
<br>
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.

View File

@ -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.
<br>
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.
<br>
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.
<br>
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.
<br>
the caller.\n
The following declaration meets the parser's
handler requirements:

View File

@ -20,7 +20,7 @@
namespace boost {
namespace json {
struct key_value_pair;
class key_value_pair;
namespace detail {

View File

@ -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

View File

@ -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))

View File

@ -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();
}

View File

@ -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<value>(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<value>(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<class... Args>
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>(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>(args)...);
}
template<class T>

View File

@ -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

View File

@ -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.
<br>
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.
<br>
@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
<br>
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.
<br>
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

View File

@ -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<handler> p_;
@ -204,9 +133,7 @@ public:
@note
Before any JSON can be parsed, the function
@ref reset must be called.
<br>
@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.
<br>
@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.
<br>
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.
<br>
characters were already parsed.\n
The characters in the buffer are processed
starting from the beginning, until one of the

View File

@ -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

View File

@ -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&

View File

@ -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.
<br>
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<class... Args>
bool
void
emplace(
error_code& ec,
Args&&... args);
template<class T>

View File

@ -10,6 +10,8 @@
// Test that header file is self-contained.
#include <boost/json/value_builder.hpp>
#include <boost/json/serializer.hpp>
#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