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