diff --git a/doc/qbk/main.qbk b/doc/qbk/main.qbk
index 9ae4ce6e..ea9e1520 100644
--- a/doc/qbk/main.qbk
+++ b/doc/qbk/main.qbk
@@ -30,9 +30,11 @@
[template include_file[path][^<''''''[path]''''''>]]
[template issue[n] '''#'''[n]'''''']
-[/ C++ Named Requirements ]
+[/ Named Requirements ]
[def __Allocator__ [@https://en.cppreference.com/w/cpp/named_req/Allocator ['Allocator]]]
+[def __InputIterator__ [@https://en.cppreference.com/w/cpp/named_req/InputIterator ['InputIterator]]]
+
[def __ConstBufferSequence__ [@boost:/doc/html/boost_asio/reference/ConstBufferSequence.html ['ConstBufferSequence]]]
[def __MutableBufferSequence__ [@boost:/doc/html/boost_asio/reference/MutableBufferSequence.html ['MutableBufferSequence]]]
diff --git a/doc/xsl/class_detail.xsl b/doc/xsl/class_detail.xsl
index a90d5493..a1c626bf 100644
--- a/doc/xsl/class_detail.xsl
+++ b/doc/xsl/class_detail.xsl
@@ -1,5 +1,6 @@
- __Allocator__
- __ConstBufferSequence__
- __MutableBufferSequence__
+__Allocator__
+__InputIterator__
+__ConstBufferSequence__
+__MutableBufferSequence__
diff --git a/include/boost/json/array.hpp b/include/boost/json/array.hpp
index 2dd2c81b..9553b6ba 100644
--- a/include/boost/json/array.hpp
+++ b/include/boost/json/array.hpp
@@ -24,11 +24,57 @@ namespace json {
class value;
-/** The native type of array values
+/** A dynamically sized array of JSON values
+
+ This is the type used to represent JSON values of kind array.
+ It strives for semantic equivalence to `std::vector`.
+
+ 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. This means that a pointer to an
+ element of an @ref array may be passed to any function that expects
+ a pointer to an element of an array.
+
+ The storage of the array is handled automatically, being expanded
+ and contracted as needed. Arrays usually occupy more space than
+ array language constructs, because more memory is allocated to
+ handle future growth. This way an array does not need to reallocate
+ each time an element is inserted, but only when the additional
+ memory is exhausted. 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.
+
+ 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.
+
+ The complexity (efficiency) of common operations on arrays is as follows:
+
+ @li Random access - constant *O(1)*
+ @li Insertion or removal of elements at the end - amortized constant *O(1)*
+ @li Insertion or removal of elements - linear in the distance to the end of
+ the array *O(n)*
+
+ @par Storage
+
+ All elements stored in the container will use the same storage that
+ was used to construct the container, including recursive children
+ of those elements.
+
+ @par Thread Safety
+
+ Non-const member functions may not be called concurrently.
+
+ @ref array meets the requirements of
+ Container,
+ SequenceContainer,
+ ContiguousContainer, and
+ ReversibleContainer.
*/
class array
{
- class table;
+ struct table;
+ struct undo;
friend class value;
table* tab_ = nullptr;
@@ -38,57 +84,222 @@ class array
struct cleanup_insert;
public:
+ /// The type of each element
using value_type = value;
+
+ /// The type used to represent unsigned integers
using size_type = std::size_t;
+
+ /// The type used to represent signed integers
using difference_type = std::ptrdiff_t;
+
+ /// A reference to an element
using reference = value&;
+
+ /// A const reference to an element
using const_reference = value const&;
+
+ /// A pointer to an element
using pointer = value*;
+
+ /// A const pointer to an element
using const_pointer = value const*;
+
+ /// A random access iterator to an element
using iterator = value*;
+
+ /// A const random access iterator to an element
using const_iterator = value const*;
+
+ /// A reverse random access iterator to an element
using reverse_iterator =
std::reverse_iterator;
+
+ /// A const reverse random access iterator to an element
using const_reverse_iterator =
std::reverse_iterator;
//--------------------------------------------------------------------------
- //
- // Special Members
- //
- //--------------------------------------------------------------------------
+ /** Destroy the container
+
+ The destructor for each element is called, any used
+ memory is deallocated, and shared ownership of the
+ underlying storage is released.
+
+ @par Complexity
+
+ Linear in @ref size()
+ */
BOOST_JSON_DECL
~array();
- BOOST_JSON_DECL
- array();
+ //--------------------------------------------------------------------------
+ /** Construct an empty container
+
+ The container and all inserted elements will use the
+ default storage.
+
+ @par Complexity
+
+ Constant.
+
+ @par Exception Safety
+
+ No-throw guarantee.
+ */
+ BOOST_JSON_DECL
+ array() noexcept;
+
+ /** Construct an empty container
+
+ The container and all inserted elements will use the
+ storage pointed to by `sp`.
+
+ @par Complexity
+
+ Constant.
+
+ @param sp A pointer to the @ref storage to use.
+ The array will acquire shared ownership of the pointer.
+
+ @par Exception Safety
+
+ No-throw guarantee.
+ */
BOOST_JSON_DECL
explicit
- array(storage_ptr store);
+ array(storage_ptr sp) noexcept;
- BOOST_JSON_DECL
- explicit
- array(
- size_type count);
+ /** Construct a container with `count` copies of `v`
- BOOST_JSON_DECL
- array(
- size_type count,
- storage_ptr store);
+ The container and all inserted elements will use the
+ default storage.
+ @par Complexity
+
+ Linear in `count`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param count The number of copies to insert.
+
+ @param v The value to be inserted.
+ */
BOOST_JSON_DECL
array(
size_type count,
value_type const& v);
+ /** Construct a container with `count` copies of `v`
+
+ The container and all inserted elements will use the
+ storage pointed to by `sp`.
+
+ @par Complexity
+
+ Linear in `count`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param count The number of copies to insert.
+
+ @param v The value to be inserted.
+
+ @param sp A pointer to the @ref storage to use.
+ The array will acquire shared ownership of the pointer.
+ */
BOOST_JSON_DECL
array(
size_type count,
value_type const& v,
- storage_ptr store);
+ storage_ptr sp);
+ /** Construct a container with `count` null values
+
+ The container and all inserted elements will use the
+ default storage.
+
+ @par Complexity
+
+ Linear in `count`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param count The number of null values to insert.
+ */
+ BOOST_JSON_DECL
+ explicit
+ array(
+ size_type count);
+
+ /** Construct a container with `count` null values
+
+ The container and all inserted elements will use the
+ storage pointed to by `sp`.
+
+ @par Complexity
+
+ Linear in `count`
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param count The number of copies to insert.
+
+ @param sp A pointer to the @ref storage to use.
+ The array will acquire shared ownership of the pointer.
+ */
+ BOOST_JSON_DECL
+ array(
+ size_type count,
+ storage_ptr sp);
+
+ /** Construct a container with the contents of a range
+
+ The elements in the range `[first, last)` are
+ inserted in order.
+ The container and all inserted elements will use the
+ default storage.
+
+ @par Constraints
+
+ `not std::is_convertible_v`
+
+ @par Requires
+
+ `std::is_constructible_v::value_type>`
+
+ @par Complexity
+
+ Linear in `std::distance(first, last)`
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param first An input iterator pointing to the first
+ element to insert, or pointing to the end of the range.
+
+ @param last An input iterator pointing to the end
+ of the range.
+
+ @tparam InputIt a type meeting the requirements of
+ __InputIterator__.
+ */
template<
class InputIt
#ifndef GENERATING_DOCUMENTATION
@@ -100,6 +311,42 @@ public:
array(
InputIt first, InputIt last);
+ /** Construct a container with the contents of a range
+
+ The elements in the range `[first, last)` are
+ inserted in order.
+ The container and all inserted elements will use the
+ @ref storage pointed to by `sp`.
+
+ @par Constraints
+
+ `not std::is_convertible_v`
+
+ @par Requires
+
+ `std::is_constructible_v::value_type>`
+
+ @par Complexity
+
+ Linear in `std::distance(first, last)`
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param first An input iterator pointing to the first
+ element to insert, or pointing to the end of the range.
+
+ @param last An input iterator pointing to the end
+ of the range.
+
+ @param sp A pointer to the @ref storage to use.
+ The array will acquire shared ownership of the pointer.
+
+ @tparam InputIt a type meeting the requirements of
+ __InputIterator__.
+ */
template<
class InputIt
#ifndef GENERATING_DOCUMENTATION
@@ -110,103 +357,466 @@ public:
>
array(
InputIt first, InputIt last,
- storage_ptr store);
+ storage_ptr sp);
+ /** Copy constructor
+
+ Constructs the container with a copy of the contents
+ of `other.
+ The container and all inserted elements will use the
+ default storage.
+
+ @par Complexity
+
+ Linear in `other.size()`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param other The array to copy
+ */
BOOST_JSON_DECL
array(array const& other);
+ /** Copy constructor
+
+ Constructs the container with a copy of the contents
+ of `other.
+ The container and all inserted elements will use the
+ default storage.
+
+ @par Complexity
+
+ Linear in `other.size()`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param other The array to copy
+
+ @param sp A pointer to the @ref storage to use.
+ The array will acquire shared ownership of the pointer.
+ */
BOOST_JSON_DECL
array(
array const& other,
- storage_ptr store);
+ storage_ptr sp);
- BOOST_JSON_DECL
- array(array&& other) noexcept;
+ /** Pilfer constructor
+ Constructs the container with the contents of `other`
+ using pilfer semantics.
+ Ownership of the @ref storage is transferred.
+
+ @note
+
+ After construction, the moved-from object may only be
+ destroyed.
+
+ @par Complexity
+
+ Constant.
+
+ @par Exception Safety
+
+ No-throw guarantee.
+
+ @param other The array to pilfer
+
+ @see
+
+ Pilfering constructors are described in
+ Valueless Variants Considered Harmful, by Peter Dimov.
+ */
BOOST_JSON_DECL
array(pilfered other) noexcept;
+ /** Move constructor
+
+ Constructs the container with the contents of `other`
+ using move semantics. Ownership of the underlying
+ memory is transferred.
+ The container acquires shared ownership of the
+ @ref storage used by `other`.
+
+ @note
+
+ After construction, the moved-from object behaves as
+ if newly constructed with its current storage pointer.
+
+ @par Complexity
+
+ Constant.
+
+ @par Exception Safety
+
+ No-throw guarantee.
+
+ @param other The array to move
+ */
+ BOOST_JSON_DECL
+ array(array&& other) noexcept;
+
+ /** Storage-extended move constructor
+
+ Using `*sp` as the @ref storage for the new container,
+ moves all the elements from `other`.
+
+ @li If `*other.get_storage() == *sp`, ownership of the
+ underlying memory is transferred in constant time, with
+ no possibility of exceptions.
+ After construction, the moved-from object behaves as if
+ newly constructed with its current @ref storage pointer.
+
+ @li If `*other.get_storage() != *sp`, an element-wise
+ copy is performed. In this case, the moved-from container
+ is not changed.
+
+ The container and all inserted elements will use the
+ specified storage.
+
+ @par Complexity
+
+ At most, linear in `other.size()`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param other The array to move
+
+ @param sp A pointer to the @ref storage to use.
+ The array will acquire shared ownership of the pointer.
+ */
BOOST_JSON_DECL
array(
array&& other,
- storage_ptr store);
+ storage_ptr sp);
+ /** Constructs the container with the contents of an initializer list
+
+ The container and all inserted elements will use the
+ default storage.
+
+ @par Complexity
+
+ Linear in `init.size()`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param init The initializer list to insert
+ */
BOOST_JSON_DECL
array(
- std::initializer_list list);
+ std::initializer_list init);
+ /** Constructs the container with the contents of an initializer list
+
+ The container and all inserted elements will use the
+ @ref storage pointed to by `sp`.
+
+ @par Complexity
+
+ Linear in `init.size()`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param init The initializer list to insert
+
+ @param sp A pointer to the @ref storage to use.
+ The array will acquire shared ownership of the pointer.
+ */
BOOST_JSON_DECL
array(
- std::initializer_list list,
- storage_ptr store);
+ std::initializer_list init,
+ storage_ptr sp);
- BOOST_JSON_DECL
- array&
- operator=(array&& other);
+ //--------------------------------------------------------------------------
+ /** Copy assignment operator
+
+ Replaces the contents with an element-wise copy of other.
+
+ @par Complexity
+
+ Linear in @ref size() and `other.size()`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param other The array to copy
+ */
BOOST_JSON_DECL
array&
operator=(array const& other);
+ /** Move assignment operator
+
+ Replaces the contents with those of `other` using move
+ semantics (the data in `other` is moved into this container).
+
+ @li If `*other.get_storage() == get_storage()`,
+ ownership of the underlying memory is transferred in
+ constant time, with no possibility of exceptions.
+ After construction, the moved-from object behaves as if
+ newly constructed with its current @ref storage pointer.
+
+ @li If `*other.get_storage() != *sp`, an element-wise
+ copy is performed. In this case the moved-from container
+ is not modified, and exceptions may be thrown.
+
+ @par Complexity
+
+ At most, linear in `other.size()`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param other The array to assign from
+ */
+ BOOST_JSON_DECL
+ array&
+ operator=(array&& other);
+
+ /** Assign the contents of an initializer list
+
+ Replaces the contents with the contents of an
+ initializer list.
+
+ @par Complexity
+
+ Linear in `init.size()`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param init The initializer list to assign
+ */
BOOST_JSON_DECL
array&
operator=(
- std::initializer_list list);
+ std::initializer_list init);
+ /** Return a pointer to the storage associated with the container
+
+ Shared ownership of the @ref storage is propagated by
+ the container to all of its children recursively.
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
storage_ptr const&
get_storage() const noexcept;
//--------------------------------------------------------------------------
//
- // Elements
+ // Element access
//
//--------------------------------------------------------------------------
+ /** Access an element, with bounds checking
+
+ Returns a reference to the element specified at
+ location `pos`, with bounds checking. If pos is not
+ within the range of the container, an exception of
+ type `std::out_of_range` is thrown.
+
+ @par Complexity
+
+ Constant.
+
+ @param pos A zero-based index
+
+ @throws std::out_of_range `pos >= size()`
+ */
BOOST_JSON_DECL
reference
at(size_type pos);
+ /** Access an element, with bounds checking
+
+ Returns a reference to the element specified at
+ location `pos`, with bounds checking. If pos is not
+ within the range of the container, an exception of
+ type `std::out_of_range` is thrown.
+
+ @par Complexity
+
+ Constant.
+
+ @param pos A zero-based index
+
+ @throws std::out_of_range `pos >= size()`
+ */
BOOST_JSON_DECL
const_reference
at(size_type pos) const;
+ /** Access an element
+
+ Returns a reference to the element specified at
+ location `pos`. No bounds checking is performed.
+
+ @par Precondition
+
+ `pos >= size`
+
+ @par Complexity
+
+ Constant.
+
+ @param pos A zero-based index
+ */
BOOST_JSON_DECL
reference
operator[](size_type pos);
+ /** Access an element
+
+ Returns a reference to the element specified at
+ location `pos`. No bounds checking is performed.
+
+ @par Precondition
+
+ `pos >= size`
+
+ @par Complexity
+
+ Constant.
+
+ @param pos A zero-based index
+ */
BOOST_JSON_DECL
const_reference
operator[](size_type pos) const;
+ /** Access the first element
+
+ Returns a reference to the first element.
+
+ @par Precondition
+
+ `not empty()`
+
+ @par Complexity
+
+ Constant.
+ */
reference
front()
{
return (*this)[0];
}
+ /** Access the first element
+
+ Returns a reference to the first element.
+
+ @par Precondition
+
+ `not empty()`
+
+ @par Complexity
+
+ Constant.
+ */
const_reference
front() const
{
return (*this)[0];
}
+ /** Access the last element
+
+ Returns a reference to the last element.
+
+ @par Precondition
+
+ `not empty()`
+
+ @par Complexity
+
+ Constant.
+ */
reference
back()
{
return (*this)[size() - 1];
}
+ /** Access the last element
+
+ Returns a reference to the last element.
+
+ @par Precondition
+
+ `not empty()`
+
+ @par Complexity
+
+ Constant.
+ */
const_reference
back() const
{
return (*this)[size() - 1];
}
+ /** Access the underlying array directly
+
+ Returns a pointer to the underlying array serving
+ as element storage. The value returned is such that
+ the range `[data(), data()+size())` is always a
+ valid range, even if the container is empty.
+
+ @par Complexity
+
+ Constant.
+
+ @note
+
+ If `size() == 0`, the function may or may not return
+ a null pointer.
+ */
BOOST_JSON_DECL
value_type*
data() noexcept;
+ /** Access the underlying array directly
+
+ Returns a pointer to the underlying array serving
+ as element storage. The value returned is such that
+ the range `[data(), data()+size())` is always a
+ valid range, even if the container is empty.
+
+ @par Complexity
+
+ Constant.
+
+ @note
+
+ If `size() == 0`, the function may or may not return
+ a null pointer.
+ */
BOOST_JSON_DECL
value_type const*
data() const noexcept;
@@ -217,14 +827,41 @@ public:
//
//--------------------------------------------------------------------------
+ /** Return an iterator to the first element
+
+ If the container is empty, the returned iterator
+ will be equal to @ref end().
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
iterator
begin() noexcept;
+ /** Return an iterator to the first element
+
+ If the container is empty, the returned iterator
+ will be equal to @ref end().
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
const_iterator
begin() const noexcept;
+ /** Return an iterator to the first element
+
+ If the container is empty, the returned iterator
+ will be equal to @ref end().
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
const_iterator
cbegin() noexcept
@@ -232,14 +869,41 @@ public:
return begin();
}
+ /** Return an iterator to the element following the last element
+
+ The element acts as a placeholder; attempting to
+ access it results in undefined behavior.
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
iterator
end() noexcept;
+ /** Return an iterator to the element following the last element
+
+ The element acts as a placeholder; attempting to
+ access it results in undefined behavior.
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
const_iterator
end() const noexcept;
+ /** Return an iterator to the element following the last element
+
+ The element acts as a placeholder; attempting to
+ access it results in undefined behavior.
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
const_iterator
cend() noexcept
@@ -247,14 +911,44 @@ public:
return end();
}
+ /** Return a reverse iterator to the first element of the reversed container
+
+ The pointed-to element corresponds to the last element
+ of the non-reversed container. If the container is empty,
+ the returned iterator is equal to @ref rend()
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
reverse_iterator
rbegin() noexcept;
+ /** Return a reverse iterator to the first element of the reversed container
+
+ The pointed-to element corresponds to the last element
+ of the non-reversed container. If the container is empty,
+ the returned iterator is equal to @ref rend()
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
const_reverse_iterator
rbegin() const noexcept;
+ /** Return a reverse iterator to the first element of the reversed container
+
+ The pointed-to element corresponds to the last element
+ of the non-reversed container. If the container is empty,
+ the returned iterator is equal to @ref rend()
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
const_reverse_iterator
crbegin() noexcept
@@ -262,14 +956,47 @@ public:
return rbegin();
}
+ /** Return a reverse iterator to the element following the last element of the reversed container
+
+ The pointed-to element corresponds to the element
+ preceding the first element of the non-reversed container.
+ This element acts as a placeholder, attempting to access
+ it results in undefined behavior.
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
reverse_iterator
rend() noexcept;
+ /** Return a reverse iterator to the element following the last element of the reversed container
+
+ The pointed-to element corresponds to the element
+ preceding the first element of the non-reversed container.
+ This element acts as a placeholder, attempting to access
+ it results in undefined behavior.
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
const_reverse_iterator
rend() const noexcept;
+ /** Return a reverse iterator to the element following the last element of the reversed container
+
+ The pointed-to element corresponds to the element
+ preceding the first element of the non-reversed container.
+ This element acts as a placeholder, attempting to access
+ it results in undefined behavior.
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
const_reverse_iterator
crend() noexcept
@@ -283,26 +1010,110 @@ public:
//
//--------------------------------------------------------------------------
+ /** Check if the container has no elements
+
+ Returns `true` if there are no elements in the container,
+ i.e. @ref size() returns 0.
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
bool
empty() const noexcept;
+ /** Return the number of elements in the container
+
+ This returns the number of elements in the container.
+ The value returned may be different from the value
+ returned from @ref capacity.
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
size_type
size() const noexcept;
+ /** Return the maximum number of elements the container can hold
+
+ The maximum is an implementation-defined number dependent
+ on system or library implementation. This value is a
+ theoretical limit; at runtime, the actual maximum size
+ may be less due to resource limits.
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
size_type
max_size() const noexcept;
+ /** Increase the capacity to at least a certain amount
+
+ This increases the capacity of the array to a value
+ that is greater than or equal to `new_capacity`. If
+ `new_capacity > capacity()`, new memory is allocated.
+ Otherwise, the call has no effect. The number of
+ elements and therefore the @ref size() of the container
+ is not changed.
+
+ @note
+
+ If new memory is allocated, all iterators including any
+ past-the-end iterators, and all references to the
+ elements are invalidated. Otherwise, no iterators or
+ references are invalidated.
+
+ @par Complexity
+
+ At most, linear in @ref size().
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @throw std::length_error `new_capacity > max_size()`
+
+ @param new_capacity The new capacity of the array.
+ */
BOOST_JSON_DECL
void
reserve(size_type new_capacity);
+ /** Return the number of elements that can be held in currently allocated memory
+
+ This number may be larger than the value returned
+ by @ref size().
+
+ @par Complexity
+
+ Constant.
+ */
BOOST_JSON_DECL
size_type
capacity() const noexcept;
+ /** Request the removal of unused capacity
+
+ This performs a non-binding request to reduce @ref capacity()
+ to @ref size(). The request may or may not be fulfilled. If
+ reallocation occurs, all iterators including any past-the-end
+ iterators, and all references to the elements are invalidated.
+ Otherwise, no iterators or references are invalidated.
+
+ @par Complexity
+
+ At most, linear in @ref size().
+
+ @par Exception Safety
+
+ No-throw guarantee.
+ */
BOOST_JSON_DECL
void
shrink_to_fit() noexcept;
@@ -313,83 +1124,483 @@ public:
//
//--------------------------------------------------------------------------
+ /** Clear the contents
+
+ Erases all elements from the container. After this
+ call, @ref size() and @ref capacity() both return
+ zero. All references, pointers, or iterators referring
+ to contained elements are invalidated. Any past-the-end
+ iterators are also invalidated.
+
+ @par Complexity
+
+ Linear in @ref size().
+ */
BOOST_JSON_DECL
void
clear() noexcept;
+ /** Insert elements before the specified location
+
+ This inserts a copy of `v` before `pos`.
+ If `capacity() < size() + 1`, a reallocation
+ occurs first, and all iterators and references
+ are invalidated.
+ Otherwise, only the iterators and references from
+ the insertion point forward are invalidated. All
+ past-the-end iterators are also invalidated.
+
+ @par Complexity
+
+ Constant plus linear in `std::distance(pos, end())`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param pos Iterator before which the content will
+ be inserted. This may be the @ref end() iterator.
+
+ @param v The value to insert. A copy will be made
+ using container's associated @ref storage.
+
+ @return An iterator to the inserted value
+ */
BOOST_JSON_DECL
iterator
insert(
- const_iterator before,
+ const_iterator pos,
value_type const& v);
+ /** Insert elements before the specified location
+
+ This inserts `v` before `pos` via move-construction.
+ If `capacity() < size() + 1`, a reallocation occurs
+ first, and all iterators and references are
+ invalidated.
+ Otherwise, only the iterators and references from
+ the insertion point forward are invalidated. All
+ past-the-end iterators are also invalidated.
+
+ @par Complexity
+
+ Constant plus linear in `std::distance(pos, end())`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param pos Iterator before which the content will
+ be inserted. This may be the @ref end() iterator.
+
+ @param v The value to insert. Ownership of the
+ value will be transferred via move construction,
+ using the container's associated @ref storage.
+
+ @return An iterator to the inserted value
+ */
BOOST_JSON_DECL
iterator
insert(
- const_iterator before,
+ const_iterator pos,
value_type&& v);
+ /** Insert elements before the specified location
+
+ This inserts `count` copies of `v` before `pos`.
+ If `capacity() < size() + count`, a reallocation
+ occurs first, and all iterators and references are
+ invalidated.
+ Otherwise, only the iterators and references from
+ the insertion point forward are invalidated. All
+ past-the-end iterators are also invalidated.
+
+ @par Complexity
+
+ Linear in `count + std::distance(pos, end())`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param pos Iterator before which the content will
+ be inserted. This may be the @ref end() iterator.
+
+ @param count The number of copies to insert.
+
+ @param v The value to insert. Copies will be made
+ using container's associated @ref storage.
+
+ @return An iterator to the first inserted value,
+ or `pos` if `count == 0`.
+ */
BOOST_JSON_DECL
iterator
- insert (
- const_iterator before,
+ insert(
+ const_iterator pos,
size_type count,
value_type const& v);
- template
+ /** Insert elements before the specified location
+
+ The elements in the range `[first, last)` are
+ inserted in order.
+ If `capacity() < size() + std::distance(first, last)`,
+ a reallocation occurs first, and all iterators and
+ references are invalidated.
+ Otherwise, only the iterators and references from
+ the insertion point forward are invalidated. All
+ past-the-end iterators are also invalidated.
+
+ @par Precondition
+
+ `first` and `last` are not iterators into `*this`.
+
+ @par Constraints
+
+ `not std::is_convertible_v`
+
+ @par Requires
+
+ `std::is_constructible_v::value_type>`
+
+ @par Complexity
+
+ Linear in `std::distance(first, last) + std::distance(pos, end())`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param pos Iterator before which the content will
+ be inserted. This may be the @ref end() iterator.
+
+ @param first An input iterator pointing to the first
+ element to insert, or pointing to the end of the range.
+
+ @param last An input iterator pointing to the end
+ of the range.
+
+ @tparam InputIt a type meeting the requirements of
+ __InputIterator__.
+
+ @return An iterator to the first inserted value, or
+ `pos` if `first == last`.
+ */
+ template<
+ class InputIt
+#if 0
+ #ifndef GENERATING_DOCUMENTATION
+ ,class = typename std::enable_if<
+ ! std::is_convertible<
+ InputIt, value>::value>::type
+ #endif
+#endif
+ >
iterator
insert(
- const_iterator before,
+ const_iterator pos,
InputIt first, InputIt last);
+ /** Insert elements before the specified location
+
+ The elements in the initializer list `init` are
+ inserted in order.
+ If `capacity() < size() + init.size()`,
+ a reallocation occurs first, and all iterators and
+ references are invalidated.
+ Otherwise, only the iterators and references from
+ the insertion point forward are invalidated. All
+ past-the-end iterators are also invalidated.
+
+ @par Complexity
+
+ Linear in `init.size() + std::distance(pos, end())`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param pos Iterator before which the content will
+ be inserted. This may be the @ref end() iterator.
+
+ @param init The initializer list to insert
+
+ @return An iterator to the first inserted value, or
+ `pos` if `init.size() == 0`.
+ */
BOOST_JSON_DECL
iterator
insert(
- const_iterator before,
- std::initializer_list list);
+ const_iterator pos,
+ std::initializer_list init);
+ /** Insert a constructed element in-place
+
+ Inserts a new element into the container directly before
+ `pos`. The element is constructed using placement-new
+ with the parameter `std::forward(arg)`.
+ If `capacity() < size() + 1`,
+ a reallocation occurs first, and all iterators and
+ references are invalidated.
+ Otherwise, only the iterators and references from
+ the insertion point forward are invalidated. All
+ past-the-end iterators are also invalidated.
+
+ @par Complexity
+
+ Constant plus linear in `std::distance(pos, end())`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param pos Iterator before which the element will
+ be inserted. This may be the @ref end() iterator.
+
+ @param arg The argument to forward to the @ref value
+ constructor.
+
+ @return An iterator to the inserted element
+ */
template
iterator
emplace(
- const_iterator before,
+ const_iterator pos,
Arg&& arg);
+ /** Erases elements from the container
+
+ The element at `pos` is removed.
+
+ @par Complexity
+
+ Constant plus linear in `std::distance(pos, end())`
+
+ @par Exception Safety
+
+ No-throw guarantee.
+
+ @param pos Iterator to the element to remove
+
+ @return Iterator following the last removed element.
+ If the iterator `pos` refers to the last element,
+ the @ref end() iterator is returned.
+ */
BOOST_JSON_DECL
iterator
- erase(const_iterator pos);
+ erase(const_iterator pos) noexcept;
+ /** Erases elements from the container
+
+ The elements in the range `[first, last)` are removed.
+
+ @par Complexity
+
+ Linear in `std::distance(first, last) + std::distance(pos, end())`
+
+ @par Exception Safety
+
+ No-throw guarantee.
+
+ @param first An iterator pointing to the first
+ element to erase, or pointing to the end of the range.
+
+ @param last An iterator pointing to one past the
+ last element to erase, or pointing to the end of the
+ range.
+
+ @return Iterator following the last removed element.
+ If the iterator `pos` refers to the last element,
+ the @ref end() iterator is returned.
+ */
BOOST_JSON_DECL
iterator
erase(
const_iterator first,
- const_iterator last);
+ const_iterator last) noexcept;
+ /** Add an element to the end.
+
+ This appends a copy of `v` to the container's
+ elements.
+ If `capacity() < size() + 1`, a reallocation
+ occurs first, and all iterators and references
+ are invalidated. Any past-the-end iterators are
+ always invalidated.
+
+ @par Complexity
+
+ Amortized constant.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param v The value to insert. A copy will be made
+ using container's associated @ref storage.
+ */
BOOST_JSON_DECL
void
push_back(value_type const& v);
+ /** Add an element to the end.
+
+ This appends `v` to the container's elements via
+ move-construction.
+ If `capacity() < size() + 1`, a reallocation
+ occurs first, and all iterators and references
+ are invalidated. Any past-the-end iterators are
+ always invalidated.
+
+ @par Complexity
+
+ Amortized constant.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param v The value to insert. Ownership of the
+ value will be transferred via move construction,
+ using the container's associated @ref storage.
+ */
BOOST_JSON_DECL
void
push_back(value_type&& v);
+ /** Append a constructed element in-place
+
+ Appends a new element to the end of the container's
+ list of elements.
+ The element is constructed using placement-new
+ with the parameter `std::forward(arg)`.
+ If `capacity() < size() + 1`,
+ a reallocation occurs first, and all iterators and
+ references are invalidated.
+ Otherwise, only the iterators and references from
+ the insertion point forward are invalidated. All
+ past-the-end iterators are also invalidated.
+
+ @par Complexity
+
+ Amortized constant.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param arg The argument to forward to the @ref value
+ constructor.
+
+ @return A reference to the inserted element
+ */
template
reference
emplace_back(Arg&& arg);
+ /** Remove the last element
+
+ The last element of the container is erased.
+
+ @par Precondition
+
+ `not empty()`
+
+ @par Exception Safety
+
+ No-throw guarantee.
+ */
BOOST_JSON_DECL
void
- pop_back();
+ pop_back() noexcept;
+ /** Change the number of elements stored
+
+ Resizes the container to contain `count` elements.
+ If `capacity() < size() + count`, a reallocation
+ occurs first, and all iterators and references
+ are invalidated. Any past-the-end iterators are
+ always invalidated.
+
+ @li If `size() > count`, the container is reduced
+ to its first `count` elements.
+
+ @li If `size() < count`, additional null values
+ are appended.
+
+ @par Complexity
+
+ Linear in `abs(size() - count)`, plus the cost of
+ reallocation if @ref capacity() is less than `count`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param count The new size of the container.
+ */
BOOST_JSON_DECL
void
resize(size_type count);
+ /** Change the number of elements stored
+
+ Resizes the container to contain `count` elements.
+ If `capacity() < size() + count`, a reallocation
+ occurs first, and all iterators and references
+ are invalidated. Any past-the-end iterators are
+ always invalidated.
+
+ @li If `size() > count`, the container is reduced
+ to its first `count` elements.
+
+ @li If `size() < count`, additional copies of `v`
+ are appended.
+
+ @par Complexity
+
+ Linear in `abs(size() - count)`, plus the cost of
+ reallocation if @ref capacity() is less than `count`.
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param count The new size of the container.
+ */
BOOST_JSON_DECL
void
resize(
size_type count,
value_type const& v);
+ /** Swap the contents
+
+ Exchanges the contents of this array with another array.
+ All iterators and references remain valid.
+
+ @par Precondition
+
+ `*get_storage() == *other.get_storage()`
+
+ @par Exception Safety
+
+ No-throw guarantee.
+
+ @param other The array to swap with
+ */
BOOST_JSON_DECL
void
swap(array& other) noexcept;
@@ -402,33 +1613,33 @@ private:
template
array(
InputIt first, InputIt last,
- storage_ptr store,
+ storage_ptr sp,
std::input_iterator_tag);
template
array(
InputIt first, InputIt last,
- storage_ptr store,
+ storage_ptr sp,
std::forward_iterator_tag);
template
iterator
insert(
- const_iterator before,
+ const_iterator pos,
InputIt first, InputIt last,
std::input_iterator_tag);
template
iterator
insert(
- const_iterator before,
+ const_iterator pos,
InputIt first, InputIt last,
std::forward_iterator_tag);
template
iterator
emplace_impl(
- const_iterator before,
+ const_iterator pos,
Arg&& arg);
BOOST_JSON_DECL
@@ -437,9 +1648,7 @@ private:
BOOST_JSON_DECL
void
- destroy(
- value* first,
- value* last);
+ copy(array const& other);
BOOST_JSON_DECL
void
@@ -447,6 +1656,10 @@ private:
value* to,
value* from,
size_type n) noexcept;
+
+ BOOST_JSON_DECL
+ void
+ assign(std::initializer_list init);
};
} // json
diff --git a/include/boost/json/detail/storage_adaptor.hpp b/include/boost/json/detail/storage_adaptor.hpp
index c8ae007b..83ebfb57 100644
--- a/include/boost/json/detail/storage_adaptor.hpp
+++ b/include/boost/json/detail/storage_adaptor.hpp
@@ -51,14 +51,11 @@ struct storage_adaptor
{
// VFALCO This is all public because msvc friend bugs
- std::atomic count_;
-
explicit
storage_adaptor(Allocator const& alloc)
: boost::empty_value<
allocator_of_char>(
boost::empty_init_t{}, alloc)
- , count_(1)
{
}
diff --git a/include/boost/json/impl/array.hpp b/include/boost/json/impl/array.hpp
index b6b8334a..920f5da0 100644
--- a/include/boost/json/impl/array.hpp
+++ b/include/boost/json/impl/array.hpp
@@ -12,6 +12,7 @@
#include
#include
+#include
#include
#include
@@ -20,46 +21,38 @@ namespace json {
//------------------------------------------------------------------------------
-class array::table
+struct array::table
{
- union
+ struct data
{
- std::size_t capacity_;
- value unused_; // for alignment
+ size_type size;
+ size_type capacity;
};
- static_assert(
- sizeof(value) >= sizeof(std::size_t), "");
+ union
+ {
+ data d;
+ value unused; // for alignment
+ };
- BOOST_JSON_DECL
- ~table();
+ ~table() = delete;
-public:
- size_type size = 0;
-
- explicit
- table(size_type capacity)
- : capacity_(capacity)
+ table()
{
}
- size_type
- capacity() const noexcept
- {
- return capacity_;
- }
-
- value_type*
+ value*
begin() noexcept
{
return reinterpret_cast<
- value_type*>(this + 1);
+ value*>(this + 1);
}
- value_type*
+ value*
end() noexcept
{
- return begin() + size;
+ return reinterpret_cast<
+ value*>(this + 1) + d.size;
}
BOOST_JSON_DECL
@@ -74,7 +67,22 @@ public:
void
destroy(
table* tab,
- storage_ptr const& sp);
+ storage_ptr const& sp) noexcept;
+
+ BOOST_JSON_DECL
+ static
+ void
+ destroy(
+ value* first,
+ value* last) noexcept;
+
+ BOOST_JSON_DECL
+ static
+ void
+ relocate(
+ value* dest,
+ value* first,
+ value* last) noexcept;
};
//------------------------------------------------------------------------------
@@ -124,6 +132,10 @@ array(
first, last,
default_storage())
{
+ static_assert(
+ std::is_constructible::value,
+ "json::value is not constructible from the iterator's value type");
}
template
@@ -136,17 +148,25 @@ array(
std::move(store),
iter_cat{})
{
+ static_assert(
+ std::is_constructible::value,
+ "json::value is not constructible from the iterator's value type");
}
template
auto
array::
insert(
- const_iterator before,
+ const_iterator pos,
InputIt first, InputIt last) ->
iterator
{
- return insert(before, first, last,
+ static_assert(
+ std::is_constructible::value,
+ "json::value is not constructible from the iterator's value type");
+ return insert(pos, first, last,
iter_cat{});
}
@@ -154,12 +174,12 @@ template
auto
array::
emplace(
- const_iterator before,
+ const_iterator pos,
Arg&& arg) ->
iterator
{
return emplace_impl(
- before, std::forward(arg));
+ pos, std::forward(arg));
}
template
@@ -202,33 +222,33 @@ template
auto
array::
insert(
- const_iterator before,
+ const_iterator pos,
InputIt first, InputIt last,
std::input_iterator_tag) ->
iterator
{
- auto pos = before - begin();
+ auto d = pos - begin();
while(first != last)
- before = insert(before, *first++) + 1;
- return begin() + pos;
+ pos = insert(pos, *first++) + 1;
+ return begin() + d;
}
template
auto
array::
insert(
- const_iterator before,
+ const_iterator pos,
InputIt first, InputIt last,
std::forward_iterator_tag) ->
iterator
{
auto count = std::distance(first, last);
- auto pos = before - begin();
+ auto d = pos - begin();
reserve(size() + count);
- cleanup_insert c(pos, count, *this);
+ cleanup_insert c(d, count, *this);
while(count--)
{
- ::new(&begin()[pos++]) value_type(
+ ::new(&begin()[d++]) value(
*first++, sp_);
++c.valid;
}
@@ -240,17 +260,17 @@ template
auto
array::
emplace_impl(
- const_iterator before,
+ const_iterator pos,
Arg&& arg) ->
iterator
{
- auto const pos = before - begin();
+ auto const d = pos - begin();
reserve(size() + 1);
- cleanup_insert c(pos, 1, *this);
- ::new(&tab_->begin()[pos]) value_type(
+ cleanup_insert c(d, 1, *this);
+ ::new(&tab_->begin()[d]) value(
std::forward(arg), sp_);
c.ok = true;
- return begin() + pos;
+ return begin() + d;
}
} // json
diff --git a/include/boost/json/impl/array.ipp b/include/boost/json/impl/array.ipp
index 76f775b3..413a2bfe 100644
--- a/include/boost/json/impl/array.ipp
+++ b/include/boost/json/impl/array.ipp
@@ -27,14 +27,6 @@ namespace json {
//------------------------------------------------------------------------------
-array::
-table::
-~table()
-{
- while(size > 0)
- begin()[--size].~value();
-}
-
auto
array::
table::
@@ -43,10 +35,19 @@ create(
storage_ptr const& sp) ->
table*
{
- return ::new(sp->allocate(
+ // a reasonable minimum
+ if( capacity < 3)
+ capacity = 3;
+
+ BOOST_STATIC_ASSERT(
+ sizeof(table) == sizeof(value));
+ auto const p = ::new(sp->allocate(
sizeof(table) +
capacity * sizeof(value),
- sizeof(value))) table(capacity);
+ alignof(value))) table;
+ p->d.size = 0;
+ p->d.capacity = capacity;
+ return p;
}
void
@@ -54,16 +55,52 @@ array::
table::
destroy(
table* tab,
- storage_ptr const& sp)
+ storage_ptr const& sp) noexcept
{
- auto const capacity = tab->capacity();
- tab->~table();
+ destroy(tab->begin(), tab->end());
sp->deallocate(tab,
sizeof(table) +
- capacity * sizeof(value),
- sizeof(value));
+ tab->d.capacity * sizeof(value),
+ alignof(value));
}
+void
+array::
+table::
+destroy(
+ value* first,
+ value* last) noexcept
+{
+ while(last != first)
+ (*--last).~value();
+}
+
+void
+array::
+table::
+relocate(
+ value* dest,
+ value* first,
+ value* last) noexcept
+{
+ while(first != last)
+ boost::relocate(dest++, *first++);
+}
+
+//------------------------------------------------------------------------------
+
+struct array::undo
+{
+ table* tab;
+ storage_ptr const& sp;
+
+ ~undo()
+ {
+ if(tab)
+ table::destroy(tab, sp);
+ }
+};
+
//------------------------------------------------------------------------------
array::
@@ -118,7 +155,7 @@ cleanup_insert::
{
if(ok)
{
- self.tab_->size += n;
+ self.tab_->d.size += n;
}
else
{
@@ -147,35 +184,14 @@ array::
}
array::
-array()
+array() noexcept
: sp_(default_storage())
{
}
array::
-array(storage_ptr store)
- : sp_(std::move(store))
-{
-}
-
-array::
-array(
- size_type count)
- : array(
- count,
- value(kind::null),
- default_storage())
-{
-}
-
-array::
-array(
- size_type count,
- storage_ptr store)
- : array(
- count,
- value(kind::null),
- std::move(store))
+array(storage_ptr sp) noexcept
+ : sp_(std::move(sp))
{
}
@@ -194,34 +210,56 @@ array::
array(
size_type count,
value const& v,
- storage_ptr store)
- : sp_(std::move(store))
+ storage_ptr sp)
+ : sp_(std::move(sp))
+{
+ if(count > 0)
+ {
+ undo u{table::create(
+ count, sp_), sp_};
+ while(count--)
+ {
+ ::new(u.tab->end()) value(v, sp_);
+ ++u.tab->d.size;
+ }
+ std::swap(tab_, u.tab);
+ }
+}
+
+array::
+array(
+ size_type count)
+ : array(
+ count,
+ value(kind::null),
+ default_storage())
+{
+}
+
+array::
+array(
+ size_type count,
+ storage_ptr sp)
+ : array(
+ count,
+ value(kind::null),
+ std::move(sp))
{
- resize(count, v);
}
array::
array(array const& other)
- : sp_(other.get_storage())
+ : array(other, other.sp_)
{
- *this = other;
}
array::
array(
array const& other,
- storage_ptr store)
- : sp_(std::move(store))
-{
- *this = other;
-}
-
-array::
-array(array&& other) noexcept
- : tab_(boost::exchange(
- other.tab_, nullptr))
- , sp_(other.sp_)
+ storage_ptr sp)
+ : sp_(std::move(sp))
{
+ copy(other);
}
array::
@@ -233,73 +271,78 @@ array(pilfered other) noexcept
{
}
+array::
+array(array&& other) noexcept
+ : tab_(boost::exchange(
+ other.tab_, nullptr))
+ , sp_(other.sp_)
+{
+}
+
array::
array(
array&& other,
- storage_ptr store)
- : sp_(std::move(store))
+ storage_ptr sp)
+ : sp_(std::move(sp))
{
- *this = std::move(other);
+ if(*sp_ != *other.sp_)
+ copy(other);
+ else
+ std::swap(tab_, other.tab_);
}
array::
array(
- std::initializer_list list)
- : sp_(default_storage())
+ std::initializer_list init)
+ : array(
+ init,
+ default_storage())
{
- *this = list;
}
array::
array(
- std::initializer_list list,
- storage_ptr store)
- : sp_(std::move(store))
+ std::initializer_list init,
+ storage_ptr sp)
+ : sp_(std::move(sp))
{
- *this = list;
+ assign(init);
+}
+
+//------------------------------------------------------------------------------
+
+array&
+array::
+operator=(array const& other)
+{
+ copy(other);
+ return *this;
}
array&
array::
operator=(array&& other)
{
- if(*sp_ == *other.sp_)
+ if(*sp_ != *other.sp_)
+ {
+ copy(other);
+ }
+ else
{
if(tab_)
table::destroy(tab_, sp_);
tab_ = boost::exchange(
other.tab_, nullptr);
}
- else
- {
- *this = other;
- }
- return *this;
-}
-
-array&
-array::
-operator=(array const& other)
-{
- cleanup_assign c(*this);
- reserve(other.size());
- for(auto const& v : other)
- emplace_impl(end(), v);
- c.ok = true;
return *this;
}
array&
array::
operator=(
- std::initializer_list list)
+ std::initializer_list init)
{
- cleanup_assign c(*this);
- reserve(list.size());
- for(auto it = list.begin();
- it != list.end(); ++it)
- emplace_impl(end(), std::move(*it));
- c.ok = true;
+ assign(init);
return *this;
}
@@ -312,7 +355,7 @@ get_storage() const noexcept
//------------------------------------------------------------------------------
//
-// Elements
+// Element access
//
//------------------------------------------------------------------------------
@@ -322,8 +365,8 @@ at(size_type pos) ->
reference
{
if(pos >= size())
- throw std::out_of_range(
- "json::array index out of bounds");
+ BOOST_THROW_EXCEPTION(std::out_of_range(
+ "json::array index out of bounds"));
return tab_->begin()[pos];
}
@@ -333,8 +376,8 @@ at(size_type pos) const ->
const_reference
{
if(pos >= size())
- throw std::out_of_range(
- "json::array index out of bounds");
+ BOOST_THROW_EXCEPTION(std::out_of_range(
+ "json::array index out of bounds"));
return tab_->begin()[pos];
}
@@ -470,7 +513,7 @@ bool
array::
empty() const noexcept
{
- return ! tab_ || tab_->size == 0;
+ return ! tab_ || tab_->d.size == 0;
}
auto
@@ -480,7 +523,7 @@ size() const noexcept ->
{
if(! tab_)
return 0;
- return tab_->size;
+ return tab_->d.size;
}
auto
@@ -500,39 +543,33 @@ reserve(size_type new_capacity)
if(new_capacity == 0)
return;
- // a reasonable minimum
- if( new_capacity < 3)
- new_capacity = 3;
-
if(tab_)
{
- // can only grow
- if(new_capacity <= tab_->capacity())
+ // never shrink
+ if(new_capacity <= tab_->d.capacity)
return;
// grow at least 50%
new_capacity = (std::max)(
- (tab_->capacity() * 3 + 1) / 2,
+ (tab_->d.capacity * 3 + 1) / 2,
new_capacity);
}
- auto tab = table::create(new_capacity, sp_);
+ auto tab =
+ table::create(new_capacity, sp_);
if(! tab_)
{
tab_ = tab;
return;
}
- for(size_type i = 0; i < tab_->size; ++i)
- relocate(
- &tab->begin()[i],
- tab_->begin()[i]);
- tab->size = tab_->size;
+ table::relocate(
+ tab->begin(),
+ tab_->begin(), tab_->end());
+ tab->d.size = tab_->d.size;
+ tab_->d.size = 0;
std::swap(tab, tab_);
- {
- tab->size = 0; // VFALCO hack to make it work
- table::destroy(tab, sp_);
- }
+ table::destroy(tab, sp_);
}
auto
@@ -542,21 +579,44 @@ capacity() const noexcept ->
{
if(! tab_)
return 0;
- return tab_->capacity();
+ return tab_->d.capacity;
}
void
array::
shrink_to_fit() noexcept
{
- if(! tab_ ||
- tab_->capacity() <= tab_->size)
+ if(capacity() <= size())
return;
- auto tab = table::create(tab_->size, sp_);
- for(size_type i = 0; i < tab_->size; ++i)
- ::new(&tab->begin()[i]) value(
- std::move(tab_->begin()[i]));
- tab->size = tab_->size;
+ table* tab;
+ if(tab_->d.size == 0)
+ {
+ table::destroy(tab_, sp_);
+ tab_ = nullptr;
+ return;
+ }
+ if( size() < 3 &&
+ capacity() <= 3)
+ return;
+
+#ifndef BOOST_NO_EXCEPTIONS
+ try
+#endif
+ {
+ tab = table::create(tab_->d.size, sp_);
+ }
+#ifndef BOOST_NO_EXCEPTIONS
+ catch(...)
+ {
+ return;
+ }
+#endif
+
+ table::relocate(
+ tab->begin(),
+ tab_->begin(), tab_->end());
+ tab->d.size = tab_->d.size;
+ tab_->d.size = 0;
std::swap(tab, tab_);
table::destroy(tab, sp_);
}
@@ -573,45 +633,47 @@ clear() noexcept
{
if(! tab_)
return;
- destroy(begin(), end());
- tab_->size = 0;
+ table::destroy(
+ tab_->begin(),
+ tab_->end());
+ tab_->d.size = 0;
}
auto
array::
insert(
- const_iterator before,
+ const_iterator pos,
value const& v) ->
iterator
{
- return emplace_impl(before, v);
+ return emplace_impl(pos, v);
}
auto
array::
insert(
- const_iterator before,
+ const_iterator pos,
value&& v) ->
iterator
{
return emplace_impl(
- before, std::move(v));
+ pos, std::move(v));
}
auto
array::
insert(
- const_iterator before,
+ const_iterator pos,
size_type count,
value const& v) ->
iterator
{
- auto pos = before - begin();
+ auto p = pos - begin();
reserve(size() + count);
- cleanup_insert c(pos, count, *this);
+ cleanup_insert c(p, count, *this);
while(count--)
{
- ::new(&begin()[pos++])
+ ::new(&begin()[p++])
value(v, sp_);
++c.valid;
}
@@ -622,18 +684,18 @@ insert(
auto
array::
insert(
- const_iterator before,
- std::initializer_list list) ->
+ const_iterator pos,
+ std::initializer_list init) ->
iterator
{
- auto pos = before - begin();
- reserve(size() + list.size());
+ auto p = pos - begin();
+ reserve(size() + init.size());
cleanup_insert c(
- pos, list.size(), *this);
- for(auto it = list.begin();
- it != list.end(); ++it)
+ p, init.size(), *this);
+ for(auto it = init.begin();
+ it != init.end(); ++it)
{
- ::new(&begin()[pos++]) value(
+ ::new(&begin()[p++]) value(
std::move(*it), sp_);
++c.valid;
}
@@ -643,29 +705,29 @@ insert(
auto
array::
-erase(const_iterator pos) ->
+erase(const_iterator pos) noexcept ->
iterator
{
- auto it = data() + (pos - begin());
- destroy(it, it + 1);
- move(it, it + 1, 1);
- --tab_->size;
- return it;
+ auto p = data() + (pos - begin());
+ table::destroy(p, p + 1);
+ move(p, p + 1, 1);
+ --tab_->d.size;
+ return p;
}
auto
array::
erase(
const_iterator first,
- const_iterator last) ->
+ const_iterator last) noexcept ->
iterator
{
auto const n = last - first;
- auto it = data() + (first - begin());
- destroy(it, it + n);
- move(it, it + n, n);
- tab_->size -= n;
- return it;
+ auto p = data() + (first - begin());
+ table::destroy(p, p + n);
+ move(p, p + n, n);
+ tab_->d.size -= n;
+ return p;
}
void
@@ -684,19 +746,33 @@ push_back(value&& v)
void
array::
-pop_back()
+pop_back() noexcept
{
back().~value();
- --tab_->size;
+ --tab_->d.size;
}
void
array::
resize(size_type count)
{
- resize(
- count,
- value(kind::null));
+ if(count <= size())
+ {
+ table::destroy(
+ tab_->begin() + count,
+ tab_->end());
+ tab_->d.size = count;
+ return;
+ }
+
+ reserve(count);
+ auto first = tab_->end();
+ auto const last =
+ tab_->begin() + count;
+ while(first != last)
+ ::new(first++) value(
+ json::kind::null, sp_);
+ tab_->d.size = count;
}
void
@@ -705,20 +781,40 @@ resize(
size_type count,
value const& v)
{
- if(count > size())
+ if(count <= size())
{
- reserve(count);
- while(count--)
- emplace_impl(end(), v);
+ table::destroy(
+ tab_->begin() + count,
+ tab_->end());
+ tab_->d.size = count;
+ return;
}
- else if(count < size())
+
+ reserve(count);
+
+ struct revert
{
- tab_->size = count;
- count = size() - count;
- for(size_type i = size() - 1;
- count-- > 0; --i)
- tab_->begin()[i].~value();
+ value* it;
+ table* tab;
+
+ ~revert()
+ {
+ if(it != tab->end())
+ table::destroy(
+ tab->end(), it);
+ }
+ };
+
+ revert r{tab_->end(), tab_};
+ auto const last =
+ tab_->begin() + count;
+ do
+ {
+ ::new(r.it) value(v, sp_);
+ ++r.it;
}
+ while(r.it != last);
+ tab_->d.size = count;
}
void
@@ -745,12 +841,26 @@ release_storage() noexcept
void
array::
-destroy(
- value* first,
- value* last)
+copy(array const& other)
{
- while(first != last)
- (*first++).~value();
+ if(other.empty())
+ {
+ if(tab_)
+ {
+ table::destroy(tab_, sp_);
+ tab_ = nullptr;
+ }
+ return;
+ }
+
+ undo u{table::create(
+ other.size(), sp_), sp_};
+ for(auto const& v : other)
+ {
+ ::new(u.tab->end()) value(v, sp_);
+ ++u.tab->d.size;
+ }
+ std::swap(tab_, u.tab);
}
void
@@ -766,23 +876,41 @@ move(
to += n;
from += n;
while(n--)
- {
- ::new(&*--to) value(
- std::move(*--from));
- from->~value();
- }
+ boost::relocate(
+ --to, *--from);
}
else
{
while(n--)
- {
- ::new(&*to++) value(
- std::move(*from));
- (*from++).~value();
- }
+ boost::relocate(
+ to++, *from++);
}
}
+void
+array::
+assign(std::initializer_list init)
+{
+ if(init.size() == 0)
+ {
+ if(tab_)
+ {
+ table::destroy(tab_, sp_);
+ tab_ = nullptr;
+ }
+ return;
+ }
+
+ undo u{table::create(
+ init.size(), sp_), sp_};
+ for(auto const& v : init)
+ {
+ ::new(u.tab->end()) value(v, sp_);
+ ++u.tab->d.size;
+ }
+ std::swap(tab_, u.tab);
+}
+
} // json
} // boost
diff --git a/include/boost/json/impl/object.ipp b/include/boost/json/impl/object.ipp
index d0aa73a0..dc65fe1c 100644
--- a/include/boost/json/impl/object.ipp
+++ b/include/boost/json/impl/object.ipp
@@ -189,7 +189,6 @@ public:
//------------------------------------------------------------------------------
-constexpr
std::pair<
std::uint64_t,
std::uint64_t>
@@ -203,7 +202,6 @@ init(std::true_type) noexcept
};
}
-constexpr
std::pair<
std::uint32_t,
std::uint32_t>
diff --git a/include/boost/json/impl/value.hpp b/include/boost/json/impl/value.hpp
index 12ea91f8..1bd1571f 100644
--- a/include/boost/json/impl/value.hpp
+++ b/include/boost/json/impl/value.hpp
@@ -362,6 +362,33 @@ public:
//------------------------------------------------------------------------------
+struct value::undo
+{
+ union
+ {
+ value old;
+ };
+ value* cur;
+ bool commit = false;
+
+ explicit
+ undo(value* cur_)
+ : cur(cur_)
+ {
+ relocate(&old, *cur);
+ }
+
+ ~undo()
+ {
+ if(commit)
+ old.~value();
+ else
+ relocate(cur, old);
+ }
+};
+
+//------------------------------------------------------------------------------
+
template
auto
value::
diff --git a/include/boost/json/impl/value.ipp b/include/boost/json/impl/value.ipp
index ab6a8ca2..c11115cd 100644
--- a/include/boost/json/impl/value.ipp
+++ b/include/boost/json/impl/value.ipp
@@ -36,52 +36,90 @@ value(value&& other) noexcept
switch(other.kind_)
{
case json::kind::object:
- ::new(&obj_) object(
- std::move(other.obj_));
- other.obj_.~object();
- ::new(&other.nat_.sp_)
- storage_ptr(obj_.get_storage());
+ relocate(&obj_, other.obj_);
+ ::new(&other.nat_.sp_) storage_ptr(
+ obj_.get_storage());
break;
case json::kind::array:
- ::new(&arr_) array(
- std::move(other.arr_));
- other.arr_.~array();
- ::new(&other.nat_.sp_)
- storage_ptr(arr_.get_storage());
+ relocate(&arr_, other.arr_);
+ ::new(&other.nat_.sp_) storage_ptr(
+ arr_.get_storage());
break;
case json::kind::string:
- ::new(&str_) string(
- std::move(other.str_));
- other.str_.~string();
+ relocate(&str_, other.str_);
::new(&other.nat_.sp_) storage_ptr(
str_.get_allocator().get_storage());
break;
case json::kind::number:
- ::new(&nat_.sp_)
- storage_ptr(other.nat_.sp_);
- ::new(&nat_.num_) number(
- other.nat_.num_);
- other.nat_.num_.~number();
+ relocate(&nat_.num_, other.nat_.num_);
+ ::new(&nat_.sp_) storage_ptr(
+ other.nat_.sp_);
break;
case json::kind::boolean:
- ::new(&nat_.sp_)
- storage_ptr(other.nat_.sp_);
nat_.bool_ = other.nat_.bool_;
+ ::new(&nat_.sp_) storage_ptr(
+ other.nat_.sp_);
break;
case json::kind::null:
- ::new(&nat_.sp_)
- storage_ptr(other.nat_.sp_);
+ ::new(&nat_.sp_) storage_ptr(
+ other.nat_.sp_);
break;
}
kind_ = other.kind_;
other.kind_ = json::kind::null;
}
+value::
+value(
+ value&& other,
+ storage_ptr sp)
+{
+ switch(other.kind_)
+ {
+ case json::kind::object:
+ ::new(&obj_) object(
+ std::move(other.obj_),
+ std::move(sp));
+ break;
+
+ case json::kind::array:
+ ::new(&arr_) array(
+ std::move(other.arr_),
+ std::move(sp));
+ break;
+
+ case json::kind::string:
+ ::new(&str_) string(
+ std::move(other.str_),
+ string::allocator_type(
+ std::move(sp)));
+ break;
+
+ case json::kind::number:
+ relocate(&nat_.num_, other.nat_.num_);
+ ::new(&nat_.sp_) storage_ptr(
+ std::move(sp));
+ break;
+
+ case json::kind::boolean:
+ nat_.bool_ = other.nat_.bool_;
+ ::new(&nat_.sp_) storage_ptr(
+ std::move(sp));
+ break;
+
+ case json::kind::null:
+ ::new(&nat_.sp_) storage_ptr(
+ std::move(sp));
+ break;
+ }
+ kind_ = other.kind_;
+}
+
value::
value(pilfered p) noexcept
{
@@ -89,31 +127,30 @@ value(pilfered p) noexcept
switch(other.kind_)
{
case json::kind::object:
- ::new(&obj_) object(
- pilfer(other.obj_));
+ relocate(&obj_, other.obj_);
+ ::new(&other.nat_.sp_) storage_ptr;
break;
case json::kind::array:
- ::new(&arr_) array(
- pilfer(other.arr_));
+ relocate(&arr_, other.arr_);
+ ::new(&other.nat_.sp_) storage_ptr;
break;
case json::kind::string:
- ::new(&str_) string(
- std::move(other.str_));
+ relocate(&str_, other.str_);
+ ::new(&other.nat_.sp_) storage_ptr;
break;
case json::kind::number:
+ relocate(&nat_.num_, other.nat_.num_);
::new(&nat_.sp_) storage_ptr(
std::move(other.nat_.sp_));
- ::new(&nat_.num_) number(
- std::move(other.nat_.num_));
break;
case json::kind::boolean:
+ nat_.bool_ = other.nat_.bool_;
::new(&nat_.sp_) storage_ptr(
std::move(other.nat_.sp_));
- nat_.bool_ = other.nat_.bool_;
break;
case json::kind::null:
@@ -122,35 +159,71 @@ value(pilfered p) noexcept
break;
}
kind_ = other.kind_;
-}
-
-value::
-value(
- value&& other,
- storage_ptr store)
-{
- move(std::move(store), std::move(other));
+ other.kind_ = json::kind::null;
}
value::
value(value const& other)
- : value(other, other.get_storage())
+ : value(
+ other,
+ other.get_storage())
{
}
value::
value(
value const& other,
- storage_ptr store)
+ storage_ptr sp)
{
- copy(std::move(store), other);
+ switch(other.kind_)
+ {
+ case json::kind::object:
+ ::new(&obj_) object(
+ other.obj_, std::move(sp));
+ break;
+
+ case json::kind::array:
+ ::new(&arr_) array(
+ other.arr_, std::move(sp));
+ break;
+
+ case json::kind::string:
+ ::new(&str_) string(
+ other.str_,
+ string::allocator_type(
+ std::move(sp)));
+ break;
+
+ case json::kind::number:
+ ::new(&nat_.num_) number(
+ other.nat_.num_);
+ ::new(&nat_.sp_) storage_ptr(
+ std::move(sp));
+ break;
+
+ case json::kind::boolean:
+ nat_.bool_ = other.nat_.bool_;
+ ::new(&nat_.sp_) storage_ptr(
+ std::move(sp));
+ break;
+
+ case json::kind::null:
+ ::new(&nat_.sp_) storage_ptr(
+ std::move(sp));
+ break;
+ }
+ kind_ = other.kind_;
}
value&
value::
operator=(value&& other)
{
- move(destroy(), std::move(other));
+ undo u(this);
+ move(
+ std::move(other),
+ u.old.get_storage());
+ u.commit = true;
return *this;
}
@@ -158,8 +231,13 @@ value&
value::
operator=(value const& other)
{
- if(this != &other)
- copy(destroy(), other);
+ if(this == &other)
+ return *this;
+
+ undo u(this);
+ ::new(this) value(
+ other, u.old.get_storage());
+ u.commit = true;
return *this;
}
@@ -178,10 +256,10 @@ value() noexcept
}
value::
-value(storage_ptr store) noexcept
+value(storage_ptr sp) noexcept
: value(
json::kind::null,
- std::move(store))
+ std::move(sp))
{
}
@@ -196,9 +274,9 @@ value(json::kind k) noexcept
value::
value(
json::kind k,
- storage_ptr store) noexcept
+ storage_ptr sp) noexcept
{
- construct(k, std::move(store));
+ construct(k, std::move(sp));
}
value::
@@ -211,10 +289,10 @@ value(object obj) noexcept
value::
value(
object obj,
- storage_ptr store)
+ storage_ptr sp)
: obj_(
std::move(obj),
- std::move(store))
+ std::move(sp))
, kind_(json::kind::object)
{
}
@@ -229,8 +307,8 @@ value(array arr) noexcept
value::
value(
array arr,
- storage_ptr store)
- : arr_(std::move(arr), std::move(store))
+ storage_ptr sp)
+ : arr_(std::move(arr), std::move(sp))
, kind_(json::kind::array)
{
}
@@ -245,8 +323,8 @@ value(string str) noexcept
value::
value(
string str,
- storage_ptr store)
- : str_(move_string(str, store))
+ storage_ptr sp)
+ : str_(move_string(str, sp))
, kind_(json::kind::string)
{
}
@@ -261,40 +339,14 @@ value(number num)
value::
value(
number num,
- storage_ptr store)
+ storage_ptr sp)
: kind_(json::kind::number)
{
::new(&nat_.num_) number(num);
::new(&nat_.sp_) storage_ptr(
- std::move(store));
+ std::move(sp));
}
-#if 0
-value::
-value(
- std::initializer_list> init)
- : value(
- init,
- default_storage())
-{
-}
-
-value::
-value(
- std::initializer_list> init,
- storage_ptr store)
- : value(
- json::kind::object,
- std::move(store))
-{
- for(auto& e : init)
- obj_.emplace(e.first,
- std::move(e.second));
-}
-#endif
-
value::
value(std::initializer_list init)
: value(
@@ -306,19 +358,19 @@ value(std::initializer_list init)
value::
value(
std::initializer_list init,
- storage_ptr store)
+ storage_ptr sp)
{
if(maybe_object(init))
{
kind_ = json::kind::object;
::new(&obj_) object(
- init, std::move(store));
+ init, std::move(sp));
}
else
{
kind_ = json::kind::array;
::new(&arr_) array(
- init, std::move(store));
+ init, std::move(sp));
}
}
@@ -894,13 +946,13 @@ typename std::enable_if<
typename string::allocator_type
>::value, string>::type
value::
-move_string(S& str, storage_ptr& store)
+move_string(S& str, storage_ptr& sp)
{
// workaround for missing std::string
// ctors in some stdlib versions
auto s = string(typename
string::allocator_type(
- std::move(store)));
+ std::move(sp)));
s = std::move(str);
return s;
}
@@ -911,11 +963,11 @@ typename std::enable_if<
typename string::allocator_type
>::value, string>::type
value::
-move_string(S& str, storage_ptr& store)
+move_string(S& str, storage_ptr& sp)
{
return {std::move(str), typename
string::allocator_type(
- std::move(store))};
+ std::move(sp))};
}
storage_ptr
@@ -1030,8 +1082,7 @@ struct value::op_move_string
void
value::
move(
- storage_ptr sp,
- value&& other)
+ value&& other, storage_ptr sp)
{
switch(other.kind_)
{
@@ -1115,7 +1166,7 @@ move(
::new(&nat_.num_) number(
std::move(other.nat_.num_));
kind_ = other.kind_;
- nat_.num_.~number();
+ other.nat_.num_.~number();
other.kind_ = json::kind::null;
break;
@@ -1168,93 +1219,6 @@ struct value::op_copy_string
}
};
-// unchecked
-void
-value::
-copy(
- storage_ptr sp,
- value const& other)
-{
- switch(other.kind_)
- {
- case json::kind::object:
- #ifndef BOOST_NO_EXCEPTIONS
- try
- {
- #endif
- ::new(&obj_) object(
- other.obj_, sp);
- #ifndef BOOST_NO_EXCEPTIONS
- }
- catch(...)
- {
- kind_ = json::kind::null;
- ::new(&nat_.sp_)
- storage_ptr(std::move(sp));
- throw;
- }
- #endif
- break;
-
- case json::kind::array:
- #ifndef BOOST_NO_EXCEPTIONS
- try
- {
- #endif
- ::new(&arr_) array(
- other.arr_, sp);
-
- #ifndef BOOST_NO_EXCEPTIONS
- }
- catch(...)
- {
- kind_ = json::kind::null;
- ::new(&nat_.sp_)
- storage_ptr(std::move(sp));
- throw;
- }
- #endif
- break;
-
- case json::kind::string:
- #ifndef BOOST_NO_EXCEPTIONS
- try
- {
- #endif
- op_copy_string{*this, sp}(other.str_);
- #ifndef BOOST_NO_EXCEPTIONS
- }
- catch(...)
- {
- kind_ = json::kind::null;
- ::new(&nat_.sp_)
- storage_ptr(std::move(sp));
- throw;
- }
- #endif
- break;
-
- case json::kind::number:
- ::new(&nat_.sp_)
- storage_ptr(std::move(sp));
- ::new(&nat_.num_) number(
- other.nat_.num_);
- break;
-
- case json::kind::boolean:
- ::new(&nat_.sp_)
- storage_ptr(std::move(sp));
- nat_.bool_ = other.nat_.bool_;
- break;
-
- case json::kind::null:
- ::new(&nat_.sp_)
- storage_ptr(std::move(sp));
- break;
- }
- kind_ = other.kind_;
-}
-
//------------------------------------------------------------------------------
// friends
diff --git a/include/boost/json/object.hpp b/include/boost/json/object.hpp
index 5ce626ef..4147cf06 100644
--- a/include/boost/json/object.hpp
+++ b/include/boost/json/object.hpp
@@ -57,14 +57,12 @@ public:
{
BOOST_JSON_DECL
static
- constexpr
std::pair<
std::uint64_t, std::uint64_t>
init(std::true_type) noexcept;
BOOST_JSON_DECL
static
- constexpr
std::pair<
std::uint32_t, std::uint32_t>
init(std::false_type) noexcept;
@@ -312,7 +310,7 @@ public:
BOOST_JSON_DECL
void
insert(
- std::initializer_list list);
+ std::initializer_list init);
BOOST_JSON_DECL
insert_return_type
diff --git a/include/boost/json/value.hpp b/include/boost/json/value.hpp
index a3f304b9..d18c01cb 100644
--- a/include/boost/json/value.hpp
+++ b/include/boost/json/value.hpp
@@ -94,6 +94,7 @@ using has_to_json =
*/
class value
{
+ struct undo;
friend class value_test;
#ifndef GENERATING_DOCUMENTATION
@@ -134,15 +135,15 @@ public:
BOOST_JSON_DECL
value(value&& other) noexcept;
- /// Pilfer constructor
- BOOST_JSON_DECL
- value(pilfered other) noexcept;
-
- /// Move construct a value, using the specified storage
+ /// Storage-extended move constructor
BOOST_JSON_DECL
value(
value&& other,
- storage_ptr store);
+ storage_ptr sp);
+
+ /// Pilfer constructor
+ BOOST_JSON_DECL
+ value(pilfered other) noexcept;
/// Construct a copy of a value
BOOST_JSON_DECL
@@ -152,7 +153,7 @@ public:
BOOST_JSON_DECL
value(
value const& other,
- storage_ptr store);
+ storage_ptr sp);
/// Move-assign a value
BOOST_JSON_DECL
@@ -180,7 +181,7 @@ public:
*/
BOOST_JSON_DECL
explicit
- value(storage_ptr store) noexcept;
+ value(storage_ptr sp) noexcept;
/** Construct a value using the default storage
@@ -203,7 +204,7 @@ public:
BOOST_JSON_DECL
value(
json::kind k,
- storage_ptr store) noexcept;
+ storage_ptr sp) noexcept;
/** Construct a value from an object.
*/
@@ -213,7 +214,7 @@ public:
/** Construct a value from an object using the specified storage
*/
BOOST_JSON_DECL
- value(object obj, storage_ptr store);
+ value(object obj, storage_ptr sp);
/** Construct a value from an array.
*/
@@ -223,7 +224,7 @@ public:
/** Construct a value from an array using the specified storage
*/
BOOST_JSON_DECL
- value(array arr, storage_ptr store);
+ value(array arr, storage_ptr sp);
/** Construct a value from a string.
*/
@@ -233,7 +234,7 @@ public:
/** Construct a value from a string using the specified storage
*/
BOOST_JSON_DECL
- value(string str, storage_ptr store);
+ value(string str, storage_ptr sp);
/** Construct a value from a number
*/
@@ -243,24 +244,7 @@ public:
/** Construct a value from a number using the specified storage
*/
BOOST_JSON_DECL
- value(number num, storage_ptr store);
-
-#if 0
- /** Construct an object from an initializer list.
- */
- BOOST_JSON_DECL
- value(
- std::initializer_list> init);
-
- /** Construct an object from an initializer list using the specified storage
- */
- BOOST_JSON_DECL
- value(
- std::initializer_list> init,
- storage_ptr store);
-#endif
+ value(number num, storage_ptr sp);
/** Construct an array from an initializer list.
*/
@@ -271,7 +255,7 @@ public:
*/
BOOST_JSON_DECL
value(std::initializer_list init,
- storage_ptr store);
+ storage_ptr sp);
/** Assign a value from an object
*/
@@ -383,6 +367,14 @@ public:
reset(json::kind::null);
}
+ /** Swap this value with another value.
+
+
+ */
+ BOOST_JSON_DECL
+ void
+ swap(value& other) noexcept;
+
//--------------------------------------------------------------------------
//
// Exchange
@@ -410,8 +402,8 @@ public:
has_to_json::value>::type
#endif
>
- value(T const& t, storage_ptr store)
- : value(std::move(store))
+ value(T const& t, storage_ptr sp)
+ : value(std::move(sp))
{
value_exchange<
detail::remove_cr
@@ -932,11 +924,7 @@ private:
BOOST_JSON_DECL
void
- move(storage_ptr, value&&);
-
- BOOST_JSON_DECL
- void
- copy(storage_ptr, value const&);
+ move(value&&, storage_ptr);
BOOST_JSON_DECL
friend
diff --git a/test/Jamfile b/test/Jamfile
index fcb1bc2c..7a2f32e7 100644
--- a/test/Jamfile
+++ b/test/Jamfile
@@ -8,24 +8,24 @@
#
local SOURCES =
- _detail_stack.cpp
- allocator.cpp
+ #_detail_stack.cpp
+ #allocator.cpp
array.cpp
- assign_string.cpp
- assign_vector.cpp
- basic_parser.cpp
- error.cpp
- iterator.cpp
- json.cpp
- kind.cpp
- number.cpp
- object.cpp
- parse_file.cpp
- parser.cpp
- serializer.cpp
- storage.cpp
- string.cpp
- value.cpp
+ #assign_string.cpp
+ #assign_vector.cpp
+ #basic_parser.cpp
+ #error.cpp
+ #iterator.cpp
+ #json.cpp
+ #kind.cpp
+ #number.cpp
+ #object.cpp
+ #parse_file.cpp
+ #parser.cpp
+ #serializer.cpp
+ #storage.cpp
+ #string.cpp
+ #value.cpp
;
local RUN_TESTS ;
diff --git a/test/_detail_stack.cpp b/test/_detail_stack.cpp
index 3ac3775c..b328efc2 100644
--- a/test/_detail_stack.cpp
+++ b/test/_detail_stack.cpp
@@ -46,7 +46,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,stack);
+BEAST_DEFINE_TESTSUITE(boost,json,stack);
} // detail
} // json
diff --git a/test/allocator.cpp b/test/allocator.cpp
index e4d75fde..45c4a3f3 100644
--- a/test/allocator.cpp
+++ b/test/allocator.cpp
@@ -43,7 +43,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,allocator);
+BEAST_DEFINE_TESTSUITE(boost,json,allocator);
} // json
} // boost
diff --git a/test/array.cpp b/test/array.cpp
index b6194fc8..aeae3234 100644
--- a/test/array.cpp
+++ b/test/array.cpp
@@ -21,247 +21,258 @@ class array_test : public beast::unit_test::suite
{
public:
void
- check(array const& arr)
+ check(array const& a)
{
- BEAST_EXPECT(arr.size() == 3);
- BEAST_EXPECT(arr[0].is_number());
- BEAST_EXPECT(arr[1].is_bool());
- BEAST_EXPECT(arr[2].is_string());
+ BEAST_EXPECT(a.size() == 3);
+ BEAST_EXPECT(a[0].is_number());
+ BEAST_EXPECT(a[1].is_bool());
+ BEAST_EXPECT(a[2].is_string());
}
void
check(
- array const& arr,
+ array const& a,
storage_ptr const& sp)
{
- check(arr);
- BEAST_EXPECT(arr.get_storage() == sp);
- BEAST_EXPECT(arr[0].get_storage() == sp);
- BEAST_EXPECT(arr[1].get_storage() == sp);
- BEAST_EXPECT(arr[2].get_storage() == sp);
+ check(a);
+ check_storage(a, sp);
}
void
testSpecial()
{
- auto sp = make_storage();
- storage_ptr sp0 =
- default_storage();
- BEAST_EXPECT(*sp != *sp0);
+ // ~array()
+ {
+ // implied
+ }
// array()
{
- array arr;
- BEAST_EXPECT(arr.empty());
- BEAST_EXPECT(arr.size() == 0);
+ scoped_fail_storage fs;
+ array a;
+ BEAST_EXPECT(a.empty());
+ BEAST_EXPECT(a.size() == 0);
}
- // array(storage)
+ // array(storage_ptr)
{
- array arr(sp);
- BEAST_EXPECT(arr.get_storage() == sp);
- BEAST_EXPECT(arr.get_storage() != sp0);
- }
-
- // array(size_type)
- {
- array arr(3);
- BEAST_EXPECT(arr.size() == 3);
- for(auto const& v : arr)
- BEAST_EXPECT(v.is_null());
- }
-
- // array(size_type, storage)
- {
- array arr(3, sp);
- BEAST_EXPECT(arr.size() == 3);
- for(auto const& v : arr)
- BEAST_EXPECT(v.get_storage() == sp);
+ scoped_fail_storage fs;
+ array a(default_storage());
+ check_storage(a, default_storage());
}
// array(size_type, value)
{
- array arr(3, value(kind::boolean));
- BEAST_EXPECT(arr.size() == 3);
- for(auto const& v : arr)
+ array a(3, true);
+ BEAST_EXPECT(a.size() == 3);
+ for(auto const& v : a)
BEAST_EXPECT(v.is_bool());
+ check_storage(a, default_storage());
}
// array(size_type, value, storage)
+ fail_loop([](storage_ptr const& sp)
{
- array arr(3, value(kind::boolean), sp);
- BEAST_EXPECT(arr.size() == 3);
- for(auto const& v : arr)
- BEAST_EXPECT(v.get_storage() == sp);
+ array a(3, true, sp);
+ BEAST_EXPECT(a.size() == 3);
+ check_storage(a, sp);
+ });
+
+ // array(size_type)
+ {
+ array a(3);
+ BEAST_EXPECT(a.size() == 3);
+ for(auto const& v : a)
+ BEAST_EXPECT(v.is_null());
+ check_storage(a, default_storage());
}
+ // array(size_type, storage)
+ fail_loop([](storage_ptr const& sp)
+ {
+ array a(3, sp);
+ BEAST_EXPECT(a.size() == 3);
+ check_storage(a, sp);
+ });
+
// array(InputIt, InputIt)
{
- std::initializer_list list =
+ std::initializer_list init =
{ 1, true, "hello" };
- array arr(list.begin(), list.end());
- check(arr, sp0);
+ array a(init.begin(), init.end());
+ check(a);
+ check_storage(a, default_storage());
}
// array(InputIt, InputIt, storage)
+ fail_loop([this](storage_ptr const& sp)
{
- std::initializer_list list =
+ std::initializer_list init =
{ 1, true, "hello" };
- array arr(list.begin(), list.end(), sp);
- check(arr, sp);
- }
+ array a(init.begin(), init.end(), sp);
+ check(a);
+ check_storage(a, sp);
+ });
// array(array const&)
{
- std::initializer_list list =
+ std::initializer_list init =
{ 1, true, "hello" };
- array arr1(list.begin(), list.end(), sp);
- array arr2 = arr1;
- check(arr2, sp);
+ array a1(init.begin(), init.end());
+ array a2(a1);
+ check(a2);
+ check_storage(a2, default_storage());
}
// array(array const&, storage)
+ fail_loop([](storage_ptr const& sp)
{
- std::initializer_list list =
+ std::initializer_list init =
{ 1, true, "hello" };
- array arr1(list.begin(), list.end());
- array arr2(arr1, sp);
- BEAST_EXPECT(arr2.size() == 3);
- BEAST_EXPECT(
- arr1[0].get_storage() !=
- arr2[0].get_storage());
- BEAST_EXPECT(
- arr1[1].get_storage() !=
- arr2[1].get_storage());
- BEAST_EXPECT(
- arr1[2].get_storage() !=
- arr2[2].get_storage());
+ array a1(init.begin(), init.end());
+ array a2(a1, sp);
+ BEAST_EXPECT(a2.size() == 3);
+ check_storage(a2, sp);
+ });
+
+ // array(pilfered)
+ {
+ std::initializer_list init =
+ { 1, true, "hello" };
+ array a1(init.begin(), init.end());
+ array a2(pilfer(a1));
+ BEAST_EXPECT(a1.empty());
+ BEAST_EXPECT(! a1.get_storage());
+ check(a2);
+ check_storage(a2, default_storage());
}
// array(array&&)
{
- std::initializer_list list =
+ std::initializer_list init =
{ 1, true, "hello" };
- array arr1(list.begin(), list.end(), sp);
- array arr2 = std::move(arr1);
- BEAST_EXPECT(arr1.empty());
- BEAST_EXPECT(
- arr1.get_storage() ==
- arr2.get_storage());
- check(arr2, sp);
+ array a1(init.begin(), init.end());
+ array a2 = std::move(a1);
+ BEAST_EXPECT(a1.empty());
+ check(a2);
+ check_storage(a2, default_storage());
}
// array(array&&, storage)
+ fail_loop([this](storage_ptr const& sp)
{
- std::initializer_list list =
+ std::initializer_list init =
{ 1, true, "hello" };
- array arr1(list.begin(), list.end());
- array arr2(std::move(arr1), sp);
- BEAST_EXPECT(! arr1.empty());
- BEAST_EXPECT(
- arr1.get_storage() !=
- arr2.get_storage());
- check(arr2, sp);
- }
+ array a1(init.begin(), init.end());
+ array a2(std::move(a1), sp);
+ BEAST_EXPECT(! a1.empty());
+ check(a2);
+ check_storage(a1, default_storage());
+ check_storage(a2, sp);
+ });
// array(init_list)
{
- array arr({1, true, "hello"});
- check(arr, sp0);
+ array a({1, true, "hello"});
+ check(a);
+ check_storage(a, default_storage());
}
// array(init_list, storage)
+ fail_loop([this](storage_ptr const& sp)
{
- array arr({1, true, "hello"}, sp);
- check(arr, sp);
+ array a({1, true, "hello"}, sp);
+ check(a, sp);
+ check_storage(a, sp);
+ });
+
+ // operator=(array const&)
+ {
+ {
+ array a1({1, true, "hello"});
+ array a2({nullptr, value(kind::object), 1.f});
+ a2 = a1;
+ check(a1);
+ check(a2);
+ check_storage(a1, default_storage());
+ check_storage(a2, default_storage());
+ }
+
+ fail_loop([this](storage_ptr const& sp)
+ {
+ array a1({1, true, "hello"});
+ array a2({nullptr, value(kind::object), 1.f}, sp);
+ a2 = a1;
+ check(a1);
+ check(a2);
+ check_storage(a1, default_storage());
+ check_storage(a2, sp);
+ });
}
// operator=(array&&)
{
{
- array arr1({1, true, "hello"});
- array arr2({nullptr, value(kind::object), 1.f});
- arr2 = std::move(arr1);
- BEAST_EXPECT(arr1.empty());
- BEAST_EXPECT(
- arr1.get_storage() ==
- arr2.get_storage());
- check(arr2, sp0);
+ array a1({1, true, "hello"});
+ array a2({nullptr, object{}, 1.f});
+ a2 = std::move(a1);
+ BEAST_EXPECT(a1.empty());
+ check(a2);
}
- {
- array arr0({nullptr, value(kind::object), 1.f, 1.f}, sp);
- array arr1({nullptr, value(kind::object), 1.f, 1.f}, sp);
- array arr2({nullptr, value(kind::object), 1.f, 1.f, 1.f}, sp);
- array arr3({nullptr, value(kind::object), 1.f, 1.f, 1.f, 1.f}, sp);
- array arr4({value(kind::object), nullptr, 1.f}, sp);
- array arr5({nullptr, 1.f, value(kind::object)}, sp);
- array arr6({nullptr, 1.f, value(kind::object), 1.f}, sp);
- array arr7({nullptr, 1.f, value(kind::object), 1.f, 1.f}, sp);
- }
- {
- array arr1({1, true, "hello"});
- array arr2({nullptr, value(kind::object), 1.f}, sp);
- arr2 = std::move(arr1);
- BEAST_EXPECT(! arr1.empty());
- BEAST_EXPECT(
- arr1.get_storage() !=
- arr2.get_storage());
- check(arr2, sp);
- }
- }
- // operator=(array const&)
- {
+ fail_loop([this](storage_ptr const& sp)
{
- array arr1({1, true, "hello"});
- array arr2({nullptr, value(kind::object), 1.f});
- arr2 = arr1;
- BEAST_EXPECT(! arr1.empty());
- check(arr2, sp0);
- }
- {
- array arr1({1, true, "hello"});
- array arr2({nullptr, value(kind::object), 1.f}, sp);
- arr2 = arr1;
- BEAST_EXPECT(! arr1.empty());
- BEAST_EXPECT(
- arr1.get_storage() !=
- arr2.get_storage());
- check(arr2, sp);
- }
+ array a1({1, true, "hello"});
+ array a2({nullptr, value(kind::object), 1.f}, sp);
+ a2 = std::move(a1);
+ check(a1);
+ check(a2);
+ check_storage(a1, default_storage());
+ check_storage(a2, sp);
+ });
}
// operator=(init_list)
{
{
- std::initializer_list list =
+ std::initializer_list init =
{ 1, true, "hello" };
- array arr({nullptr, value(kind::object), 1.f});
- arr = list;
- check(arr, sp0);
+ array a({nullptr, value(kind::object), 1.f});
+ a = init;
+ check(a);
+ check_storage(a, default_storage());
}
+
+ fail_loop([this](storage_ptr const& sp)
{
- std::initializer_list list =
+ std::initializer_list init =
{ 1, true, "hello" };
- array arr({nullptr, value(kind::object), 1.f}, sp);
- arr = list;
- check(arr, sp);
- }
+ array a({nullptr, value(kind::object), 1.f}, sp);
+ a = init;
+ check(a);
+ check_storage(a, sp);
+ });
+ }
+
+ // get_storage()
+ {
+ // implied
}
}
void
- testElements()
+ testElementAccess()
{
// at(pos)
{
- array arr({1, true, "hello"});
- BEAST_EXPECT(arr.at(0).is_number());
- BEAST_EXPECT(arr.at(1).is_bool());
- BEAST_EXPECT(arr.at(2).is_string());
+ array a({1, true, "hello"});
+ scoped_fail_storage fs;
+ BEAST_EXPECT(a.at(0).is_number());
+ BEAST_EXPECT(a.at(1).is_bool());
+ BEAST_EXPECT(a.at(2).is_string());
try
{
- arr.at(3);
+ a.at(3);
BEAST_FAIL();
}
catch(std::out_of_range const&)
@@ -272,13 +283,14 @@ public:
// at(pos) const
{
- array const arr({1, true, "hello"});
- BEAST_EXPECT(arr.at(0).is_number());
- BEAST_EXPECT(arr.at(1).is_bool());
- BEAST_EXPECT(arr.at(2).is_string());
+ array const a({1, true, "hello"});
+ scoped_fail_storage fs;
+ BEAST_EXPECT(a.at(0).is_number());
+ BEAST_EXPECT(a.at(1).is_bool());
+ BEAST_EXPECT(a.at(2).is_string());
try
{
- arr.at(3);
+ a.at(3);
BEAST_FAIL();
}
catch(std::out_of_range const&)
@@ -289,75 +301,95 @@ public:
// operator[](size_type)
{
- array arr({1, true, "hello"});
- BEAST_EXPECT(arr[0].is_number());
- BEAST_EXPECT(arr[1].is_bool());
- BEAST_EXPECT(arr[2].is_string());
+ array a({1, true, "hello"});
+ scoped_fail_storage fs;
+ BEAST_EXPECT(a[0].is_number());
+ BEAST_EXPECT(a[1].is_bool());
+ BEAST_EXPECT(a[2].is_string());
}
// operator[](size_type) const
{
- array const arr({1, true, "hello"});
- BEAST_EXPECT(arr[0].is_number());
- BEAST_EXPECT(arr[1].is_bool());
- BEAST_EXPECT(arr[2].is_string());
+ array const a({1, true, "hello"});
+ scoped_fail_storage fs;
+ BEAST_EXPECT(a[0].is_number());
+ BEAST_EXPECT(a[1].is_bool());
+ BEAST_EXPECT(a[2].is_string());
}
// front()
{
- array arr({1, true, "hello"});
- BEAST_EXPECT(arr.front().is_number());
+ array a({1, true, "hello"});
+ scoped_fail_storage fs;
+ BEAST_EXPECT(a.front().is_number());
}
// front() const
{
- array const arr({1, true, "hello"});
- BEAST_EXPECT(arr.front().is_number());
+ array const a({1, true, "hello"});
+ scoped_fail_storage fs;
+ BEAST_EXPECT(a.front().is_number());
}
// back()
{
- array arr({1, true, "hello"});
- BEAST_EXPECT(arr.back().is_string());
+ array a({1, true, "hello"});
+ scoped_fail_storage fs;
+ BEAST_EXPECT(a.back().is_string());
}
// back() const
{
- array const arr({1, true, "hello"});
- BEAST_EXPECT(arr.back().is_string());
+ array const a({1, true, "hello"});
+ scoped_fail_storage fs;
+ BEAST_EXPECT(a.back().is_string());
}
// data()
{
- array arr({1, true, "hello"});
- BEAST_EXPECT(arr.data() == &arr[0]);
+ {
+ array a({1, true, "hello"});
+ scoped_fail_storage fs;
+ BEAST_EXPECT(a.data() == &a[0]);
+ }
+ {
+ BEAST_EXPECT(array{}.data() == nullptr);
+ }
}
// data() const
{
- array const arr({1, true, "hello"});
- BEAST_EXPECT(arr.data() == &arr[0]);
+ {
+ array const a({1, true, "hello"});
+ scoped_fail_storage fs;
+ BEAST_EXPECT(a.data() == &a[0]);
+ }
+ {
+ array const a;
+ BEAST_EXPECT(a.data() == nullptr);
+ }
}
}
void
testIterators()
{
- array arr({1, true, "hello"});
- auto const& ac(arr);
+ array a({1, true, "hello"});
+ auto const& ac(a);
+
{
- auto it = arr.begin();
+ auto it = a.begin();
BEAST_EXPECT(it->is_number()); ++it;
BEAST_EXPECT(it->is_bool()); it++;
BEAST_EXPECT(it->is_string()); ++it;
- BEAST_EXPECT(it == arr.end());
+ BEAST_EXPECT(it == a.end());
}
{
- auto it = arr.cbegin();
+ auto it = a.cbegin();
BEAST_EXPECT(it->is_number()); ++it;
BEAST_EXPECT(it->is_bool()); it++;
BEAST_EXPECT(it->is_string()); ++it;
- BEAST_EXPECT(it == arr.cend());
+ BEAST_EXPECT(it == a.cend());
}
{
auto it = ac.begin();
@@ -367,18 +399,18 @@ public:
BEAST_EXPECT(it == ac.end());
}
{
- auto it = arr.end();
+ auto it = a.end();
--it; BEAST_EXPECT(it->is_string());
it--; BEAST_EXPECT(it->is_bool());
--it; BEAST_EXPECT(it->is_number());
- BEAST_EXPECT(it == arr.begin());
+ BEAST_EXPECT(it == a.begin());
}
{
- auto it = arr.cend();
+ auto it = a.cend();
--it; BEAST_EXPECT(it->is_string());
it--; BEAST_EXPECT(it->is_bool());
--it; BEAST_EXPECT(it->is_number());
- BEAST_EXPECT(it == arr.cbegin());
+ BEAST_EXPECT(it == a.cbegin());
}
{
auto it = ac.end();
@@ -387,6 +419,62 @@ public:
--it; BEAST_EXPECT(it->is_number());
BEAST_EXPECT(it == ac.begin());
}
+
+ {
+ auto it = a.rbegin();
+ BEAST_EXPECT(it->is_string()); ++it;
+ BEAST_EXPECT(it->is_bool()); it++;
+ BEAST_EXPECT(it->is_number()); ++it;
+ BEAST_EXPECT(it == a.rend());
+ }
+ {
+ auto it = a.crbegin();
+ BEAST_EXPECT(it->is_string()); ++it;
+ BEAST_EXPECT(it->is_bool()); it++;
+ BEAST_EXPECT(it->is_number()); ++it;
+ BEAST_EXPECT(it == a.crend());
+ }
+ {
+ auto it = ac.rbegin();
+ BEAST_EXPECT(it->is_string()); ++it;
+ BEAST_EXPECT(it->is_bool()); it++;
+ BEAST_EXPECT(it->is_number()); ++it;
+ BEAST_EXPECT(it == ac.rend());
+ }
+ {
+ auto it = a.rend();
+ --it; BEAST_EXPECT(it->is_number());
+ it--; BEAST_EXPECT(it->is_bool());
+ --it; BEAST_EXPECT(it->is_string());
+ BEAST_EXPECT(it == a.rbegin());
+ }
+ {
+ auto it = a.crend();
+ --it; BEAST_EXPECT(it->is_number());
+ it--; BEAST_EXPECT(it->is_bool());
+ --it; BEAST_EXPECT(it->is_string());
+ BEAST_EXPECT(it == a.crbegin());
+ }
+ {
+ auto it = ac.rend();
+ --it; BEAST_EXPECT(it->is_number());
+ it--; BEAST_EXPECT(it->is_bool());
+ --it; BEAST_EXPECT(it->is_string());
+ BEAST_EXPECT(it == ac.rbegin());
+ }
+
+ {
+ array a2;
+ array const& ca2(a2);
+ BEAST_EXPECT(std::distance(
+ a2.begin(), a2.end()) == 0);
+ BEAST_EXPECT(std::distance(
+ ca2.begin(), ca2.end()) == 0);
+ BEAST_EXPECT(std::distance(
+ a2.rbegin(), a2.rend()) == 0);
+ BEAST_EXPECT(std::distance(
+ ca2.rbegin(), ca2.rend()) == 0);
+ }
}
void
@@ -394,46 +482,67 @@ public:
{
// empty()
{
- array arr;
- BEAST_EXPECT(arr.empty());
- arr.emplace_back(1);
- BEAST_EXPECT(! arr.empty());
+ array a;
+ BEAST_EXPECT(a.empty());
+ a.emplace_back(1);
+ BEAST_EXPECT(! a.empty());
}
// size()
{
- array arr;
- BEAST_EXPECT(arr.size() == 0);
- arr.emplace_back(1);
- BEAST_EXPECT(arr.size() == 1);
+ array a;
+ BEAST_EXPECT(a.size() == 0);
+ a.emplace_back(1);
+ BEAST_EXPECT(a.size() == 1);
}
// max_size()
{
- array arr;
- BEAST_EXPECT(arr.max_size() > 0);
+ array a;
+ BEAST_EXPECT(a.max_size() > 0);
}
// reserve()
{
- array arr;
- arr.reserve(50);
- BEAST_EXPECT(arr.capacity() >= 50);
+ array a;
+ a.reserve(50);
+ BEAST_EXPECT(a.capacity() >= 50);
}
// capacity()
{
- array arr;
- BEAST_EXPECT(arr.capacity() == 0);
+ array a;
+ BEAST_EXPECT(a.capacity() == 0);
}
// shrink_to_fit()
{
- array arr;
- arr.reserve(50);
- BEAST_EXPECT(arr.capacity() >= 50);
- arr.shrink_to_fit();
- BEAST_EXPECT(arr.capacity() == 0);
+ fail_loop([](storage_ptr const& sp)
+ {
+ array a(1, sp);
+ a.resize(a.capacity());
+ a.shrink_to_fit();
+ BEAST_EXPECT(a.size() == a.capacity());
+ });
+
+ fail_loop([](storage_ptr const& sp)
+ {
+ array a(sp);
+ a.reserve(10);
+ BEAST_EXPECT(a.capacity() >= 10);
+ a.shrink_to_fit();
+ BEAST_EXPECT(a.capacity() == 0);
+ });
+
+ fail_loop([](storage_ptr const& sp)
+ {
+ array a(3, sp);
+ a.reserve(10);
+ BEAST_EXPECT(a.capacity() >= 10);
+ a.shrink_to_fit();
+ if(a.capacity() > 3)
+ throw test_failure{};
+ });
}
}
@@ -442,130 +551,186 @@ public:
{
// clear
{
- array arr({1, true, "hello"});
- arr.clear();
- BEAST_EXPECT(arr.capacity() > 0);
+ {
+ array a;
+ BEAST_EXPECT(a.size() == 0);
+ BEAST_EXPECT(a.capacity() == 0);
+ a.clear();
+ BEAST_EXPECT(a.size() == 0);
+ BEAST_EXPECT(a.capacity() == 0);
+ }
+ {
+ array a({1, true, "hello"});
+ a.clear();
+ BEAST_EXPECT(a.size() == 0);
+ BEAST_EXPECT(a.capacity() > 0);
+ }
}
// insert(before, value_type const&)
+ fail_loop([this](storage_ptr const& sp)
{
- array arr({1, "hello"});
+ array a({1, "hello"}, sp);
value v(true);
- arr.insert(arr.begin() + 1, v);
- check(arr);
- }
+ a.insert(a.begin() + 1, v);
+ check(a);
+ check_storage(a, sp);
+ });
// insert(before, value_type const&)
+ fail_loop([this](storage_ptr const& sp)
{
- array arr({1, "hello"});
- arr.insert(arr.begin() + 1, true);
- check(arr);
- }
+ array a({1, "hello"}, sp);
+ a.insert(a.begin() + 1, true);
+ check(a);
+ check_storage(a, sp);
+ });
+
// insert(before, size_type, value_type const&)
+ fail_loop([this](storage_ptr const& sp)
{
- array arr({1, "hello"});
- arr.insert(arr.begin() + 1, 3, true);
- BEAST_EXPECT(arr[0].is_number());
- BEAST_EXPECT(arr[1].is_bool());
- BEAST_EXPECT(arr[2].is_bool());
- BEAST_EXPECT(arr[3].is_bool());
- BEAST_EXPECT(arr[4].is_string());
- }
+ array a({1, "hello"}, sp);
+ a.insert(a.begin() + 1, 3, true);
+ BEAST_EXPECT(a[0].is_number());
+ BEAST_EXPECT(a[1].is_bool());
+ BEAST_EXPECT(a[2].is_bool());
+ BEAST_EXPECT(a[3].is_bool());
+ BEAST_EXPECT(a[4].is_string());
+ });
// insert(before, InputIt, InputIt)
+ fail_loop([this](storage_ptr const& sp)
{
std::initializer_list<
- value> list = {1, true};
- array arr({"hello"});
- arr.insert(arr.begin(),
- list.begin(), list.end());
- check(arr);
- }
+ value> init = {1, true};
+ array a({"hello"}, sp);
+ a.insert(a.begin(),
+ init.begin(), init.end());
+ check(a);
+ });
// insert(before, init_list)
+ fail_loop([this](storage_ptr const& sp)
{
- array arr({"hello"});
- arr.insert(arr.begin(), {1, true});
- check(arr);
- }
+ array a({"hello"}, sp);
+ a.insert(a.begin(), {1, true});
+ check(a);
+ });
// emplace(before, arg)
+ fail_loop([this](storage_ptr const& sp)
{
- array arr({1, "hello"});
- auto it = arr.emplace(
- arr.begin() + 1, true);
- BEAST_EXPECT(it == arr.begin() + 1);
- check(arr);
- }
+ array a({1, "hello"}, sp);
+ auto it = a.emplace(
+ a.begin() + 1, true);
+ BEAST_EXPECT(it == a.begin() + 1);
+ check(a);
+ });
// erase(pos)
{
- array arr({1, true, nullptr, "hello"});
- arr.erase(arr.begin() + 2);
- check(arr);
+ array a({1, true, nullptr, "hello"});
+ a.erase(a.begin() + 2);
+ check(a);
}
// push_back(value const&)
+ fail_loop([this](storage_ptr const& sp)
{
- array arr({1, true});
+ array a({1, true}, sp);
value v("hello");
- arr.push_back(v);
+ a.push_back(v);
BEAST_EXPECT(
v.as_string() == "hello");
- check(arr);
- }
+ check(a);
+ check_storage(a, sp);
+ });
// push_back(value&&)
{
- array arr({1, true});
- value v("hello");
- arr.push_back(std::move(v));
- BEAST_EXPECT(v.is_null());
- check(arr);
+ fail_loop([this](storage_ptr const& sp)
+ {
+ array a({1, true}, sp);
+ value v("hello");
+ a.push_back(std::move(v));
+ check(a);
+ check_storage(a, sp);
+ });
}
// emplace_back(arg)
+ fail_loop([this](storage_ptr const& sp)
{
- array arr({1, true});
- arr.emplace_back("hello");
- check(arr);
- }
+ array a({1, true}, sp);
+ a.emplace_back("hello");
+ check(a);
+ check_storage(a, sp);
+ });
// pop_back()
+ fail_loop([this](storage_ptr const& sp)
{
- array arr({1, true, "hello", nullptr});
- arr.pop_back();
- check(arr);
- }
+ array a({1, true, "hello", nullptr}, sp);
+ a.pop_back();
+ check(a);
+ check_storage(a, sp);
+ });
// resize(size_type)
{
- array arr;
- arr.resize(3);
- BEAST_EXPECT(arr.size() == 3);
- BEAST_EXPECT(arr[0].is_null());
- BEAST_EXPECT(arr[1].is_null());
- BEAST_EXPECT(arr[2].is_null());
+ value v(array{});
+ v.emplace_back(1);
+ v.emplace_back(true);
+ v.emplace_back("hello");
+
+ fail_loop([&](storage_ptr const& sp)
+ {
+ array a(5, sp);
+ a.resize(3);
+ BEAST_EXPECT(a.size() == 3);
+ check_storage(a, sp);
+ });
+
+ fail_loop([&](storage_ptr const& sp)
+ {
+ array a(sp);
+ a.resize(3);
+ BEAST_EXPECT(a.size() == 3);
+ check_storage(a, sp);
+ });
}
// resize(size_type, value_type const&)
{
- array arr;
- value v(kind::boolean);
- arr.resize(3, v);
- BEAST_EXPECT(arr.size() == 3);
- BEAST_EXPECT(arr[0].is_bool());
- BEAST_EXPECT(arr[1].is_bool());
- BEAST_EXPECT(arr[2].is_bool());
+ value v(array{});
+ v.emplace_back(1);
+ v.emplace_back(true);
+ v.emplace_back("hello");
+
+ fail_loop([&](storage_ptr const& sp)
+ {
+ array a(5, v, sp);
+ a.resize(3, v);
+ BEAST_EXPECT(a.size() == 3);
+ check_storage(a, sp);
+ });
+
+ fail_loop([&](storage_ptr const& sp)
+ {
+ array a(3, v, sp);
+ a.resize(5, v);
+ BEAST_EXPECT(a.size() == 5);
+ check_storage(a, sp);
+ });
}
// swap
{
- array arr1({1, true, "hello"});
- array arr2;
- arr1.swap(arr2);
- check(arr2);
- BEAST_EXPECT(arr1.empty());
+ array a1({1, true, "hello"});
+ array a2;
+ a1.swap(a2);
+ check(a2);
+ BEAST_EXPECT(a1.empty());
}
}
@@ -578,146 +743,146 @@ public:
{
auto sp = make_storage();
{
- array arr1;
+ array a1;
while(sp->fail < 200)
{
try
{
- array arr(sp);
- arr.emplace_back(nullptr);
- arr = arr0;
- arr1 = arr;
+ array a(sp);
+ a.emplace_back(nullptr);
+ a = arr0;
+ a1 = a;
break;
}
- catch(std::bad_alloc const&)
+ catch(test_failure const&)
{
}
}
- check(arr1);
+ check(a1);
}
}
}
// operator=(init_list)
{
- std::initializer_list list(
+ std::initializer_list init(
{1, true, "hello"});
auto sp = make_storage();
- array arr1;
+ array a1;
while(sp->fail < 200)
{
try
{
- array arr(sp);
- arr.emplace_back(nullptr);
- arr = list;
- arr1 = arr;
+ array a(sp);
+ a.emplace_back(nullptr);
+ a = init;
+ a1 = a;
break;
}
- catch(std::bad_alloc const&)
+ catch(test_failure const&)
{
}
}
- check(arr1);
+ check(a1);
}
// insert(before, count, value_type const&)
{
auto sp = make_storage();
- array arr1;
+ array a1;
while(sp->fail < 200)
{
try
{
- array arr({1, true}, sp);
- arr.insert(arr.begin() + 1,
+ array a({1, true}, sp);
+ a.insert(a.begin() + 1,
3, value(kind::null));
- arr1 = arr;
+ a1 = a;
break;
}
- catch(std::bad_alloc const&)
+ catch(test_failure const&)
{
}
}
- BEAST_EXPECT(arr1.size() == 5);
- BEAST_EXPECT(arr1[0].is_number());
- BEAST_EXPECT(arr1[1].is_null());
- BEAST_EXPECT(arr1[2].is_null());
- BEAST_EXPECT(arr1[3].is_null());
- BEAST_EXPECT(arr1[4].is_bool());
+ BEAST_EXPECT(a1.size() == 5);
+ BEAST_EXPECT(a1[0].is_number());
+ BEAST_EXPECT(a1[1].is_null());
+ BEAST_EXPECT(a1[2].is_null());
+ BEAST_EXPECT(a1[3].is_null());
+ BEAST_EXPECT(a1[4].is_bool());
}
#if _ITERATOR_DEBUG_LEVEL == 0
// insert(before, InputIt, InputIt)
{
- std::initializer_list list(
+ std::initializer_list init(
{1, true, "hello"});
auto sp = make_storage();
- array arr1;
+ array a1;
while(sp->fail < 200)
{
try
{
- array arr(sp);
- arr.insert(arr.end(),
- list.begin(), list.end());
- arr1 = arr;
+ array a(sp);
+ a.insert(a.end(),
+ init.begin(), init.end());
+ a1 = a;
break;
}
- catch(std::bad_alloc const&)
+ catch(test_failure const&)
{
}
}
- check(arr1);
+ check(a1);
}
#endif
// emplace(before, arg)
{
auto sp = make_storage();
- array arr1;
+ array a1;
while(sp->fail < 200)
{
try
{
- array arr({1, nullptr}, sp);
- arr.emplace(arr.begin() + 1, true);
- arr1 = arr;
+ array a({1, nullptr}, sp);
+ a.emplace(a.begin() + 1, true);
+ a1 = a;
break;
}
- catch(std::bad_alloc const&)
+ catch(test_failure const&)
{
}
}
- BEAST_EXPECT(arr1.size() == 3);
- BEAST_EXPECT(arr1[0].is_number());
- BEAST_EXPECT(arr1[1].is_bool());
- BEAST_EXPECT(arr1[2].is_null());
+ BEAST_EXPECT(a1.size() == 3);
+ BEAST_EXPECT(a1[0].is_number());
+ BEAST_EXPECT(a1[1].is_bool());
+ BEAST_EXPECT(a1[2].is_null());
}
#if _ITERATOR_DEBUG_LEVEL == 0
// emplace(before, arg)
{
auto sp = make_storage();
- array arr1;
+ array a1;
while(sp->fail < 200)
{
try
{
- array arr({1, "hello"}, sp);
- arr.emplace(arr.begin() + 1, true);
- arr1 = arr;
+ array a({1, "hello"}, sp);
+ a.emplace(a.begin() + 1, true);
+ a1 = a;
break;
}
- catch(std::bad_alloc const&)
+ catch(test_failure const&)
{
}
}
- check(arr1);
- BEAST_EXPECT(arr1.size() == 3);
- BEAST_EXPECT(arr1[0].is_number());
- BEAST_EXPECT(arr1[1].is_bool());
- BEAST_EXPECT(arr1[2].is_string());
+ check(a1);
+ BEAST_EXPECT(a1.size() == 3);
+ BEAST_EXPECT(a1[0].is_number());
+ BEAST_EXPECT(a1[1].is_bool());
+ BEAST_EXPECT(a1[2].is_string());
}
#endif
}
@@ -725,6 +890,7 @@ public:
void
testInitList()
{
+#if 0
auto const ci =
[]( int n,
std::initializer_list init)
@@ -744,13 +910,26 @@ public:
});array({ {"x", nullptr}
});array({ {"x", nullptr}, {"y", nullptr}
});
+#endif
+
+ {
+ auto sp = default_storage();
+ array({nullptr, value(kind::object), 1.f, 1.f}, sp);
+ array({nullptr, value(kind::object), 1.f, 1.f}, sp);
+ array({nullptr, value(kind::object), 1.f, 1.f, 1.f}, sp);
+ array({nullptr, value(kind::object), 1.f, 1.f, 1.f, 1.f}, sp);
+ array({value(kind::object), nullptr, 1.f}, sp);
+ array({nullptr, 1.f, value(kind::object)}, sp);
+ array({nullptr, 1.f, value(kind::object), 1.f}, sp);
+ array({nullptr, 1.f, value(kind::object), 1.f, 1.f}, sp);
+ }
}
void
run() override
{
testSpecial();
- testElements();
+ testElementAccess();
testIterators();
testCapacity();
testModifiers();
@@ -759,7 +938,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,array);
+BEAST_DEFINE_TESTSUITE(boost,json,array);
} // json
} // boost
diff --git a/test/assign_string.cpp b/test/assign_string.cpp
index b1fe1e56..921dec43 100644
--- a/test/assign_string.cpp
+++ b/test/assign_string.cpp
@@ -41,7 +41,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,assign_string);
+BEAST_DEFINE_TESTSUITE(boost,json,assign_string);
} // json
} // boost
diff --git a/test/assign_vector.cpp b/test/assign_vector.cpp
index 6eaa1998..22c48672 100644
--- a/test/assign_vector.cpp
+++ b/test/assign_vector.cpp
@@ -50,7 +50,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,assign_vector);
+BEAST_DEFINE_TESTSUITE(boost,json,assign_vector);
} // json
} // boost
diff --git a/test/basic_parser.cpp b/test/basic_parser.cpp
index 700dd124..7256378f 100644
--- a/test/basic_parser.cpp
+++ b/test/basic_parser.cpp
@@ -312,7 +312,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,basic_parser);
+BEAST_DEFINE_TESTSUITE(boost,json,basic_parser);
} // json
} // boost
diff --git a/test/error.cpp b/test/error.cpp
index 36b766e1..ce7b4f39 100644
--- a/test/error.cpp
+++ b/test/error.cpp
@@ -62,7 +62,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,error);
+BEAST_DEFINE_TESTSUITE(boost,json,error);
} // json
} // boost
diff --git a/test/iterator.cpp b/test/iterator.cpp
index 180e8544..94a641ac 100644
--- a/test/iterator.cpp
+++ b/test/iterator.cpp
@@ -111,7 +111,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,iterator);
+BEAST_DEFINE_TESTSUITE(boost,json,iterator);
} // json
} // boost
diff --git a/test/kind.cpp b/test/kind.cpp
index a32f0174..c3e047cd 100644
--- a/test/kind.cpp
+++ b/test/kind.cpp
@@ -35,7 +35,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,kind);
+BEAST_DEFINE_TESTSUITE(boost,json,kind);
} // json
} // boost
diff --git a/test/number.cpp b/test/number.cpp
index d4e7f9fa..0a6dd889 100644
--- a/test/number.cpp
+++ b/test/number.cpp
@@ -316,7 +316,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,number);
+BEAST_DEFINE_TESTSUITE(boost,json,number);
} // json
} // boost
diff --git a/test/object.cpp b/test/object.cpp
index 1ae6f50a..bb1210af 100644
--- a/test/object.cpp
+++ b/test/object.cpp
@@ -886,7 +886,7 @@ public:
obj1 = obj;
break;
}
- catch(std::bad_alloc const&)
+ catch(test_failure const&)
{
}
}
@@ -911,7 +911,7 @@ public:
obj1 = obj;
break;
}
- catch(std::bad_alloc const&)
+ catch(test_failure const&)
{
}
}
@@ -935,7 +935,7 @@ public:
obj1 = obj;
break;
}
- catch(std::bad_alloc const&)
+ catch(test_failure const&)
{
}
}
@@ -958,7 +958,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,object);
+BEAST_DEFINE_TESTSUITE(boost,json,object);
} // json
} // boost
diff --git a/test/parser.cpp b/test/parser.cpp
index b3560898..d8b5ae1c 100644
--- a/test/parser.cpp
+++ b/test/parser.cpp
@@ -76,7 +76,7 @@ R"xx({
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,parser);
+BEAST_DEFINE_TESTSUITE(boost,json,parser);
} // json
} // boost
diff --git a/test/serializer.cpp b/test/serializer.cpp
index dbb23a3c..728e9504 100644
--- a/test/serializer.cpp
+++ b/test/serializer.cpp
@@ -60,7 +60,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,serializer);
+BEAST_DEFINE_TESTSUITE(boost,json,serializer);
} // json
} // boost
diff --git a/test/storage.cpp b/test/storage.cpp
index b1a8e642..eddf8348 100644
--- a/test/storage.cpp
+++ b/test/storage.cpp
@@ -32,7 +32,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,storage);
+BEAST_DEFINE_TESTSUITE(boost,json,storage);
} // json
} // boost
diff --git a/test/string.cpp b/test/string.cpp
index abb6226a..06c7c617 100644
--- a/test/string.cpp
+++ b/test/string.cpp
@@ -38,7 +38,7 @@ public:
//obj1 = obj;
break;
}
- catch(std::bad_alloc const&)
+ catch(test_failure const&)
{
}
}
@@ -54,7 +54,7 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,string);
+BEAST_DEFINE_TESTSUITE(boost,json,string);
} // json
} // boost
diff --git a/test/test_storage.hpp b/test/test_storage.hpp
index 050ab8b1..9388032c 100644
--- a/test/test_storage.hpp
+++ b/test/test_storage.hpp
@@ -10,57 +10,16 @@
#ifndef BOOST_JSON_TEST_STORAGE_HPP
#define BOOST_JSON_TEST_STORAGE_HPP
+#include
#include
+#include
+#include
#include
#include
namespace boost {
namespace json {
-struct fail_storage : storage
-{
- std::size_t fail_max = 1;
- std::size_t fail = 0;
-
- void*
- allocate(
- std::size_t n,
- std::size_t) override
- {
- if(++fail == fail_max)
- {
- ++fail_max;
- fail = 0;
- throw std::bad_alloc{};
- }
- return std::allocator<
- char>{}.allocate(n);
- }
-
- void
- deallocate(
- void* p,
- std::size_t n,
- std::size_t) noexcept override
- {
- auto cp =
- reinterpret_cast(p);
- return std::allocator<
- char>{}.deallocate(cp, n);
- }
- bool
- is_equal(
- storage const& other
- ) const noexcept override
- {
- auto p = dynamic_cast<
- fail_storage const*>(&other);
- if(! p)
- return false;
- return this == p;
- }
-};
-
struct unique_storage : storage
{
void*
@@ -85,17 +44,173 @@ struct unique_storage : storage
}
bool
is_equal(
- storage const& other
- ) const noexcept override
+ storage const&) const noexcept override
{
- auto p = dynamic_cast<
- unique_storage const*>(&other);
- if(! p)
- return false;
- return this == p;
+ return false;
}
};
+struct test_failure : std::exception
+{
+ virtual
+ char const*
+ what() const noexcept override
+ {
+ return "test failure";
+ }
+};
+
+struct fail_storage : storage
+{
+ std::size_t fail_max = 1;
+ std::size_t fail = 0;
+
+ ~fail_storage()
+ {
+ }
+
+ void*
+ allocate(
+ std::size_t n,
+ std::size_t) override
+ {
+ if(++fail == fail_max)
+ {
+ ++fail_max;
+ fail = 0;
+ throw test_failure{};
+ }
+ return std::allocator<
+ char>{}.allocate(n);
+ }
+
+ void
+ deallocate(
+ void* p,
+ std::size_t n,
+ std::size_t) noexcept override
+ {
+ auto cp =
+ reinterpret_cast(p);
+ return std::allocator<
+ char>{}.deallocate(cp, n);
+ }
+ bool
+ is_equal(
+ storage const&) const noexcept override
+ {
+ return false;
+ }
+};
+
+class scoped_fail_storage
+{
+ storage_ptr sp_;
+
+public:
+ scoped_fail_storage()
+ : sp_(default_storage())
+ {
+ default_storage(
+ make_storage());
+ }
+
+ ~scoped_fail_storage()
+ {
+ default_storage(sp_);
+ }
+};
+
+template
+void
+fail_loop(F&& f)
+{
+ auto sp = make_storage();
+ while(sp->fail < 200)
+ {
+ try
+ {
+ f(sp);
+ }
+ catch(test_failure const&)
+ {
+ continue;
+ }
+ break;
+ }
+ BEAST_EXPECT(sp->fail < 200);
+}
+
+inline
+bool
+equal_storage(
+ value const& v,
+ storage_ptr const& sp);
+
+inline
+bool
+equal_storage(
+ array const& a,
+ storage_ptr const& sp)
+{
+ if(*a.get_storage() != *sp)
+ return false;
+ for(auto const& v : a)
+ if(! equal_storage(v, sp))
+ return false;
+ return true;
+}
+
+bool
+equal_storage(
+ value const& v,
+ storage_ptr const& sp)
+{
+ switch(v.kind())
+ {
+ case json::kind::object:
+ if(*v.as_object().get_storage() != *sp)
+ return false;
+ for(auto const& e : v)
+ if(! equal_storage(e.second, sp))
+ return false;
+ return true;
+
+ case json::kind::array:
+ if(*v.as_array().get_storage() != *sp)
+ return false;
+ return equal_storage(v.as_array(), sp);
+
+ case json::kind::string:
+ return *v.as_string().get_allocator().get_storage() == *sp;
+
+ case json::kind::number:
+ case json::kind::boolean:
+ case json::kind::null:
+ break;
+ }
+
+ return *v.get_storage() == *sp;
+}
+
+inline
+void
+check_storage(
+ array const& a,
+ storage_ptr const& sp)
+{
+ BEAST_EXPECT(equal_storage(a, sp));
+}
+
+inline
+void
+check_storage(
+ value const& v,
+ storage_ptr const& sp)
+{
+ BEAST_EXPECT(equal_storage(v, sp));
+}
+
} // json
} // boost
diff --git a/test/value.cpp b/test/value.cpp
index ebc8f7bb..8a811007 100644
--- a/test/value.cpp
+++ b/test/value.cpp
@@ -656,7 +656,37 @@ public:
}
};
-BEAST_DEFINE_TESTSUITE(beast,json,value);
+BEAST_DEFINE_TESTSUITE(boost,json,value);
} // json
} // boost
+
+#if 0
+
+ /** {brief}
+
+ {description}
+
+ @par Constraints
+
+ {constraints}
+
+ @par Requires
+
+ {requires}
+
+ @par Complexity
+
+ {complexity}
+
+ @par Exception Safety
+
+ Strong guarantee.
+ Calls to @ref storage::allocate may throw.
+
+ @param {name} {desc}
+
+ @tparam {type} {desc}
+ */
+
+#endif