diff --git a/bench/bench.cpp b/bench/bench.cpp index 5d26a775..735f6641 100644 --- a/bench/bench.cpp +++ b/bench/bench.cpp @@ -446,8 +446,8 @@ main( { std::vector> vi; vi.reserve(10); - //vi.emplace_back(new boost_default_impl); //vi.emplace_back(new boost_vec_impl); + vi.emplace_back(new boost_default_impl); vi.emplace_back(new boost_impl); vi.emplace_back(new rapidjson_impl); //vi.emplace_back(new nlohmann_impl); diff --git a/include/boost/json/array.hpp b/include/boost/json/array.hpp index dd0bf956..dd7079a8 100644 --- a/include/boost/json/array.hpp +++ b/include/boost/json/array.hpp @@ -32,7 +32,7 @@ to_string_test( class unchecked_array { value* data_; - unsigned long size_; + std::size_t size_; storage_ptr const& sp_; public: @@ -41,7 +41,7 @@ public: unchecked_array( value* data, - unsigned long size, + std::size_t size, storage_ptr const& sp) noexcept : data_(data) , size_(size) @@ -64,7 +64,7 @@ public: return sp_; } - unsigned long + std::size_t size() const noexcept { return size_; @@ -128,13 +128,13 @@ class array { public: /// The type used to represent unsigned integers - using size_type = unsigned long; + using size_type = std::size_t; /// The type of each element using value_type = value; /// The type used to represent signed integers - using difference_type = long; + using difference_type = std::ptrdiff_t; /// A reference to an element using reference = value&; @@ -245,7 +245,7 @@ public: */ BOOST_JSON_DECL array( - size_type count, + std::size_t count, value const& v, storage_ptr sp = {}); @@ -273,7 +273,7 @@ public: */ BOOST_JSON_DECL array( - size_type count, + std::size_t count, storage_ptr sp = {}); /** Construct a container with the contents of a range @@ -609,7 +609,7 @@ public: */ inline reference - at(size_type pos); + at(std::size_t pos); /** Access an element, with bounds checking @@ -628,7 +628,7 @@ public: */ inline const_reference - at(size_type pos) const; + at(std::size_t pos) const; /** Access an element @@ -647,7 +647,7 @@ public: */ inline reference - operator[](size_type pos) noexcept; + operator[](std::size_t pos) noexcept; /** Access an element @@ -666,7 +666,7 @@ public: */ inline const_reference - operator[](size_type pos) const noexcept; + operator[](std::size_t pos) const noexcept; /** Access the first element @@ -974,7 +974,7 @@ public: Constant. */ - size_type + std::size_t size() const noexcept { return impl_.size; @@ -993,7 +993,7 @@ public: */ static constexpr - size_type + std::size_t max_size() noexcept { return BOOST_JSON_MAX_ARRAY_SIZE; @@ -1008,7 +1008,7 @@ public: Constant. */ - size_type + std::size_t capacity() const noexcept { return impl_.capacity; @@ -1046,7 +1046,7 @@ public: */ BOOST_JSON_FORCEINLINE void - reserve(size_type new_capacity) + reserve(std::size_t new_capacity) { // never shrink if(new_capacity <= impl_.capacity) @@ -1201,7 +1201,7 @@ public: iterator insert( const_iterator pos, - size_type count, + std::size_t count, value const& v); /** Insert elements before the specified location @@ -1519,7 +1519,7 @@ public: */ BOOST_JSON_DECL void - resize(size_type count); + resize(std::size_t count); /** Change the number of elements stored @@ -1552,7 +1552,7 @@ public: BOOST_JSON_DECL void resize( - size_type count, + std::size_t count, value const& v); /** Swap the contents @@ -1593,8 +1593,8 @@ private: struct impl_type { value* vec = nullptr; - size_type size = 0; - size_type capacity = 0; + std::uint32_t size = 0; + std::uint32_t capacity = 0; impl_type() = default; impl_type(impl_type const&) = default; @@ -1603,7 +1603,7 @@ private: inline impl_type( - size_type capacity, + std::size_t capacity, storage_ptr const& sp); inline @@ -1612,7 +1612,7 @@ private: impl_type&& other) noexcept; inline - size_type + std::size_t index_of(value const*) const noexcept; inline @@ -1678,7 +1678,7 @@ private: BOOST_JSON_DECL void - reserve_impl(size_type capacity); + reserve_impl(std::size_t capacity); BOOST_JSON_DECL static @@ -1686,7 +1686,7 @@ private: relocate( value* dest, value* src, - size_type n) noexcept; + std::size_t n) noexcept; class undo_create; class undo_assign; diff --git a/include/boost/json/detail/raw_stack.hpp b/include/boost/json/detail/raw_stack.hpp index f380339d..5aa0ded8 100644 --- a/include/boost/json/detail/raw_stack.hpp +++ b/include/boost/json/detail/raw_stack.hpp @@ -48,13 +48,19 @@ public: return size_ == 0; } + char* + begin() noexcept + { + return base_; + } + void clear() noexcept { size_ = 0; } - void* + char* push(std::size_t n) { prepare(n); @@ -63,7 +69,7 @@ public: return p; } - void* + char* pop(std::size_t n) noexcept { BOOST_JSON_ASSERT( diff --git a/include/boost/json/detail/raw_stack.ipp b/include/boost/json/detail/raw_stack.ipp index c8ad2f7b..a2ed5b95 100644 --- a/include/boost/json/detail/raw_stack.ipp +++ b/include/boost/json/detail/raw_stack.ipp @@ -25,7 +25,7 @@ grow(std::size_t n) if(n > max_size() - size_) BOOST_JSON_THROW( stack_overflow_exception()); - auto new_capacity = size_ + n; + auto new_capacity = capacity_ + n; if( new_capacity < min_capacity_) new_capacity = min_capacity_; // 2x growth diff --git a/include/boost/json/detail/static_stack.hpp b/include/boost/json/detail/static_stack.hpp index 85deaff5..87416701 100644 --- a/include/boost/json/detail/static_stack.hpp +++ b/include/boost/json/detail/static_stack.hpp @@ -37,8 +37,13 @@ class static_stack public: using value_type = T; - BOOST_JSON_DECL - ~static_stack(); + ~static_stack() + { + if(begin_ != t_) + sp_->deallocate(begin_, + capacity() * sizeof(T), + alignof(T)); + } explicit static_stack( @@ -122,17 +127,36 @@ private: return end_ - begin_; } - BOOST_JSON_DECL void - grow(); + grow() + { + auto const n = 2 * capacity(); + auto const begin = + reinterpret_cast( + sp_->allocate( + n * sizeof(T), + alignof(T))); + if(! empty()) + { + std::memcpy(begin, begin_, + size() * sizeof(T)); + top_ = begin + (top_ - begin_); + if(begin_ != t_) + sp_->deallocate(begin_, + capacity() * sizeof(T), + alignof(T)); + } + else + { + top_ = begin; + } + begin_ = begin; + end_ = begin_ + n; + } }; } // detail } // json } // boost -#ifdef BOOST_JSON_HEADER_ONLY -#include -#endif - #endif diff --git a/include/boost/json/detail/static_stack.ipp b/include/boost/json/detail/static_stack.ipp deleted file mode 100644 index f8173993..00000000 --- a/include/boost/json/detail/static_stack.ipp +++ /dev/null @@ -1,66 +0,0 @@ -// -// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/vinniefalco/json -// - -#ifndef BOOST_JSON_DETAIL_STATIC_STACK_IPP -#define BOOST_JSON_DETAIL_STATIC_STACK_IPP - -#include - -namespace boost { -namespace json { -namespace detail { - -template -static_stack:: -~static_stack() -{ - if(begin_ != t_) - sp_->deallocate(begin_, - capacity() * sizeof(T), - alignof(T)); -} - -template -void -static_stack:: -grow() -{ - auto const n = 2 * capacity(); - auto const begin = - reinterpret_cast( - sp_->allocate( - n * sizeof(T), - alignof(T))); - if(! empty()) - { - std::memcpy(begin, begin_, - size() * sizeof(T)); - top_ = begin + (top_ - begin_); - if(begin_ != t_) - sp_->deallocate(begin_, - capacity() * sizeof(T), - alignof(T)); - } - else - { - top_ = begin; - } - begin_ = begin; - end_ = begin_ + n; -} - -} // detail -} // json -} // boost - -#ifdef BOOST_JSON_HEADER_ONLY -#include -#endif - -#endif diff --git a/include/boost/json/impl/array.hpp b/include/boost/json/impl/array.hpp index b37ff311..827cccc7 100644 --- a/include/boost/json/impl/array.hpp +++ b/include/boost/json/impl/array.hpp @@ -46,10 +46,10 @@ auto array:: impl_type:: index_of(value const* pos) const noexcept -> - size_type + std::size_t { return static_cast< - size_type>(pos - vec); + std::size_t>(pos - vec); } //---------------------------------------------------------- @@ -95,17 +95,17 @@ public: class array::undo_insert { array& self_; - size_type const n_; + std::size_t const n_; public: value* it; - size_type const pos; + std::size_t const pos; bool commit = false; BOOST_JSON_DECL undo_insert( value const* pos_, - unsigned long long n, + std::size_t n, array& self); BOOST_JSON_DECL @@ -130,7 +130,7 @@ public: auto array:: -at(size_type pos) -> +at(std::size_t pos) -> reference { if(pos >= impl_.size) @@ -142,7 +142,7 @@ at(size_type pos) -> auto array:: -at(size_type pos) const -> +at(std::size_t pos) const -> const_reference { if(pos >= impl_.size) @@ -154,7 +154,7 @@ at(size_type pos) const -> auto array:: -operator[](size_type pos) noexcept -> +operator[](std::size_t pos) noexcept -> reference { return impl_.vec[pos]; @@ -162,7 +162,7 @@ operator[](size_type pos) noexcept -> auto array:: -operator[](size_type pos) const noexcept -> +operator[](std::size_t pos) const noexcept -> const_reference { return impl_.vec[pos]; @@ -432,7 +432,7 @@ array( BOOST_JSON_THROW( std::length_error( "n > max_size")); - reserve(static_cast(n)); + reserve(static_cast(n)); while(impl_.size < n) { ::new( @@ -484,7 +484,7 @@ insert( std::length_error( "n > max_size")); undo_insert u(pos, static_cast< - size_type>(n), *this); + std::size_t>(n), *this); while(first != last) u.emplace(*first++); u.commit = true; diff --git a/include/boost/json/impl/array.ipp b/include/boost/json/impl/array.ipp index a22032cb..6330ec5c 100644 --- a/include/boost/json/impl/array.ipp +++ b/include/boost/json/impl/array.ipp @@ -11,8 +11,9 @@ #define BOOST_JSON_IMPL_ARRAY_IPP #include -#include #include +#include +#include #include #include #include @@ -27,13 +28,12 @@ namespace json { array:: impl_type:: impl_type( - size_type capacity_, + std::size_t capacity_, storage_ptr const& sp) { - // The choice of minimum capacity - // affects the speed of parsing. -// if( capacity_ < min_capacity_) -// capacity_ = min_capacity_; + if(capacity_ > max_size()) + BOOST_JSON_THROW( + detail::object_too_large_exception()); if(capacity_ > 0) vec = reinterpret_cast( sp->allocate( @@ -41,7 +41,8 @@ impl_type( alignof(value))); else vec = nullptr; - capacity = capacity_; + capacity = static_cast< + std::uint32_t>(capacity_); size = 0; } @@ -133,10 +134,10 @@ array:: undo_insert:: undo_insert( value const* pos_, - unsigned long long n, + std::size_t n, array& self) : self_(self) - , n_(static_cast(n)) + , n_(static_cast(n)) , pos(self.impl_.index_of(pos_)) { if(n > max_size()) @@ -149,7 +150,9 @@ undo_insert( it = self_.impl_.vec + pos; relocate(it + n_, it, self_.impl_.size - pos); - self_.impl_.size += n_; + self_.impl_.size = static_cast< + std::uint32_t>( + self_.impl_.size + n_); } array:: @@ -161,7 +164,9 @@ undo_insert:: auto const first = self_.impl_.vec + pos; self_.destroy(first, it); - self_.impl_.size -= n_; + self_.impl_.size = static_cast< + std::uint32_t>( + self_.impl_.size - n_); relocate( first, first + n_, self_.impl_.size - pos); @@ -182,7 +187,7 @@ array(storage_ptr sp) noexcept array:: array( - size_type count, + std::size_t count, value const& v, storage_ptr sp) : sp_(std::move(sp)) @@ -201,7 +206,7 @@ array( array:: array( - size_type count, + std::size_t count, storage_ptr sp) : sp_(std::move(sp)) { @@ -286,7 +291,8 @@ array(unchecked_array&& ua) : sp_(ua.get_storage()) , impl_(ua.size(), sp_) // exact { - impl_.size = ua.size(); + impl_.size = static_cast< + std::uint32_t>(ua.size()); ua.relocate(impl_.vec); } @@ -390,7 +396,7 @@ auto array:: insert( const_iterator pos, - size_type count, + std::size_t count, value const& v) -> iterator { @@ -409,7 +415,7 @@ insert( iterator { undo_insert u(pos, - static_cast( + static_cast( init.size()), *this); for(auto const& v : init) u.emplace(v); @@ -439,14 +445,15 @@ erase( iterator { auto const n = static_cast< - size_type>(last - first); + std::size_t>(last - first); auto const p = impl_.vec + impl_.index_of(first); destroy(p, p + n); relocate(p, p + n, impl_.size - impl_.index_of(last)); - impl_.size -= n; + impl_.size = static_cast< + std::uint32_t>(impl_.size - n); return p; } @@ -461,14 +468,15 @@ pop_back() noexcept void array:: -resize(size_type count) +resize(std::size_t count) { if(count <= impl_.size) { destroy( impl_.vec + count, impl_.vec + impl_.size); - impl_.size = count; + impl_.size = static_cast< + std::uint32_t>(count); return; } @@ -479,13 +487,14 @@ resize(size_type count) impl_.vec + count; while(it != end) ::new(it++) value(sp_); - impl_.size = count; + impl_.size = static_cast< + std::uint32_t>(count); } void array:: resize( - size_type count, + std::size_t count, value const& v) { if(count <= size()) @@ -493,7 +502,8 @@ resize( destroy( impl_.vec + count, impl_.vec + impl_.size); - impl_.size = count; + impl_.size = static_cast< + std::uint32_t>(count); return; } @@ -523,7 +533,8 @@ resize( ++u.it; } while(u.it != end); - impl_.size = count; + impl_.size = static_cast< + std::uint32_t>(count); u.it = nullptr; } @@ -597,7 +608,7 @@ copy( std::length_error( "size > max_size()")); reserve(static_cast< - size_type>(init.size())); + std::size_t>(init.size())); for(auto const& v : init) { ::new( @@ -609,7 +620,7 @@ copy( void array:: -reserve_impl(size_type capacity) +reserve_impl(std::size_t capacity) { if(impl_.vec) { @@ -638,7 +649,7 @@ array:: relocate( value* dest, value* src, - size_type n) noexcept + std::size_t n) noexcept { if(n == 0) return; diff --git a/include/boost/json/impl/basic_parser.ipp b/include/boost/json/impl/basic_parser.ipp index b2fb9651..67ea75e1 100644 --- a/include/boost/json/impl/basic_parser.ipp +++ b/include/boost/json/impl/basic_parser.ipp @@ -441,8 +441,7 @@ loop_str1: goto yield; } ++p; - st_.pop(); - st_.push(state::str2); + *st_ = state::str2; BOOST_FALLTHROUGH; // string, no-copy loop diff --git a/include/boost/json/impl/parser.ipp b/include/boost/json/impl/parser.ipp index 878a29a2..3b173798 100644 --- a/include/boost/json/impl/parser.ipp +++ b/include/boost/json/impl/parser.ipp @@ -50,25 +50,23 @@ key enum class parser::state : char { - need_start, - begin, + need_start, // start() not called yet + begin, // we have a storage_ptr // These states indicate what is // currently at top of the stack. - top, // empty value - arr, // empty array value - obj, // empty object value - key, // complete key - end // final value + top, // empty top value + arr, // empty array value + obj, // empty object value + key, // complete key + end // compelte top value }; void parser:: destroy() noexcept { - if(st_ == state::need_start) - return; if(key_size_ > 0) { // remove partial key @@ -86,23 +84,26 @@ destroy() noexcept str_size_ = 0; } // unwind the rest - while(! rs_.empty()) + do { switch(st_) { + case state::need_start: + BOOST_JSON_ASSERT( + rs_.empty()); + break; + + case state::begin: + BOOST_JSON_ASSERT( + rs_.empty()); + break; + case state::top: rs_.subtract( sizeof(value)); - break; - - case state::end: - { - auto ua = - pop_array(); BOOST_JSON_ASSERT( - ua.size() == 1); + rs_.empty()); break; - } case state::arr: { @@ -133,13 +134,20 @@ destroy() noexcept break; } - default: - // should never get here + case state::end: + { + auto ua = + pop_array(); + BOOST_JSON_ASSERT( + ua.size() == 1); BOOST_JSON_ASSERT( rs_.empty()); break; } + + } } + while(! rs_.empty()); } parser:: @@ -170,6 +178,9 @@ clear() noexcept destroy(); rs_.clear(); basic_parser::reset(); + count_ = 0; + key_size_ = 0; + str_size_ = 0; st_ = state::need_start; sp_ = {}; } @@ -178,22 +189,28 @@ value parser:: release() noexcept { - BOOST_JSON_ASSERT(is_done()); - BOOST_JSON_ASSERT(st_ == state::end); - auto ua = pop_array(); - BOOST_JSON_ASSERT(rs_.empty()); - union U + if(is_done()) { - value v; - U(){} - ~U(){} - }; - U u; - ua.relocate(&u.v); - basic_parser::reset(); - st_ = state::need_start; - sp_ = {}; - return std::move(u.v); + BOOST_JSON_ASSERT(st_ == state::end); + auto ua = pop_array(); + BOOST_JSON_ASSERT(rs_.empty()); + union U + { + value v; + U(){} + ~U(){} + }; + U u; + ua.relocate(&u.v); + basic_parser::reset(); + st_ = state::need_start; + sp_ = {}; + return std::move(u.v); + } + // return null + value jv(std::move(sp_)); + clear(); + return jv; } //---------------------------------------------------------- @@ -233,13 +250,19 @@ emplace(Args&&... args) U u; std::size_t key_size; pop(key_size); - auto const key = - pop_chars(key_size); + // remember the offset in case + // the stack is reallocated. + auto const offset = + rs_.pop(key_size) - + rs_.begin(); st_ = state::obj; // prevent splits from exceptions rs_.prepare(2 * sizeof(u.v)); ::new(&u.v) object::value_type( - key, std::forward(args)...); + string_view( + rs_.begin() + offset, + key_size), + std::forward(args)...); rs_.subtract(sizeof(u.v)); push(u.v); rs_.add(sizeof(u.v)); @@ -256,6 +279,7 @@ emplace(Args&&... args) ~U(){} }; U u; + // prevent splits from exceptions rs_.prepare(2 * sizeof(value)); ::new(&u.v) value( std::forward(args)...); @@ -288,9 +312,7 @@ pop_object() noexcept object::value_type); return { reinterpret_cast< object::value_type*>(rs_.pop(n)), - // VFALCO bad narrowing - static_cast(count_), - sp_ }; + count_, sp_ }; } unchecked_array @@ -303,10 +325,7 @@ pop_array() noexcept auto const n = count_ * sizeof(value); return { reinterpret_cast( - rs_.pop(n)), - // VFALCO bad narrowing - static_cast(count_), - sp_ }; + rs_.pop(n)), count_, sp_ }; } string_view diff --git a/include/boost/json/impl/serializer.ipp b/include/boost/json/impl/serializer.ipp index c541c6b6..6c4db0ed 100644 --- a/include/boost/json/impl/serializer.ipp +++ b/include/boost/json/impl/serializer.ipp @@ -93,7 +93,7 @@ is_done() const noexcept std::size_t serializer:: -next(char* dest, std::size_t size) +read(char* dest, std::size_t size) { static constexpr char hex[] = "0123456789abcdef"; static constexpr char esc[] = @@ -370,7 +370,7 @@ loop_str: std::memcpy(p, ss, n); ss = s; p += n; - *p++ = ch; + //*p++ = ch; *p++ = '\\'; *p++ = c; if(c != 'u') @@ -505,7 +505,7 @@ to_string( s.reserve(s.capacity() + 1); s.grow(static_cast< string::size_type>( - sr.next(s.data() + s.size(), + sr.read(s.data() + s.size(), s.capacity() - s.size()))); } return s; @@ -519,7 +519,7 @@ operator<<(std::ostream& os, value const& jv) { char buf[4096]; auto const n = - sr.next(buf, sizeof(buf)); + sr.read(buf, sizeof(buf)); os.write(buf, n); } return os; diff --git a/include/boost/json/json.natvis b/include/boost/json/json.natvis index 4d1cb0b7..5c8af52d 100644 --- a/include/boost/json/json.natvis +++ b/include/boost/json/json.natvis @@ -44,7 +44,7 @@ - {{ {impl_.capacity>sizeof(impl_.buf)?impl_.p:impl_.buf,[impl_.size]s} }} + {impl_.capacity>sizeof(impl_.buf)?impl_.p:impl_.buf,[impl_.size]s} impl_.capacity sp_ diff --git a/include/boost/json/parser.hpp b/include/boost/json/parser.hpp index 9b8ca84f..196f86a1 100644 --- a/include/boost/json/parser.hpp +++ b/include/boost/json/parser.hpp @@ -33,9 +33,9 @@ class parser final storage_ptr sp_; detail::raw_stack rs_; - std::size_t count_; - std::size_t key_size_; - std::size_t str_size_; + std::size_t count_ = 0; + std::size_t key_size_ = 0; + std::size_t str_size_ = 0; state st_; inline diff --git a/include/boost/json/serializer.hpp b/include/boost/json/serializer.hpp index 4863ca84..edce044d 100644 --- a/include/boost/json/serializer.hpp +++ b/include/boost/json/serializer.hpp @@ -66,7 +66,7 @@ class serializer }; #endif - detail::static_stack stack_; + detail::static_stack stack_; string_view str_; unsigned char nbuf_; @@ -91,7 +91,7 @@ public: BOOST_JSON_DECL std::size_t - next(char* dest, std::size_t size); + read(char* dest, std::size_t size); }; BOOST_JSON_DECL diff --git a/include/boost/json/src.hpp b/include/boost/json/src.hpp index a888b821..662b3dce 100644 --- a/include/boost/json/src.hpp +++ b/include/boost/json/src.hpp @@ -36,7 +36,6 @@ the program, with the macro BOOST_BEAST_SPLIT_COMPILATION defined. #include #include #include -#include #include #endif diff --git a/include/boost/json/value.hpp b/include/boost/json/value.hpp index 69796370..240d6816 100644 --- a/include/boost/json/value.hpp +++ b/include/boost/json/value.hpp @@ -1848,7 +1848,7 @@ private: class unchecked_object { object::value_type* data_; - unsigned long size_; + std::size_t size_; storage_ptr const& sp_; public: @@ -1857,7 +1857,7 @@ public: unchecked_object( object::value_type* data, - unsigned long size, + std::size_t size, storage_ptr const& sp) noexcept : data_(data) , size_(size) @@ -1880,7 +1880,7 @@ public: return sp_; } - unsigned long + std::size_t size() const noexcept { return size_; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2b0d5c7c..6db147dd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -49,6 +49,7 @@ add_executable (json-tests ryu/d2s_intrinsics_test.cpp ryu/d2s_table_test.cpp ryu/d2s_test.cpp + ryu/gtest.hpp ) add_test(json-tests json-tests) diff --git a/test/array.cpp b/test/array.cpp index 1ed830b8..c9c164c5 100644 --- a/test/array.cpp +++ b/test/array.cpp @@ -23,7 +23,7 @@ public: using init_list = std::initializer_list; string_view const str_; - unsigned long min_capacity_; + std::size_t min_capacity_; array_test() : str_( diff --git a/test/basic_parser.cpp b/test/basic_parser.cpp index 7a102d02..e7e0a3c7 100644 --- a/test/basic_parser.cpp +++ b/test/basic_parser.cpp @@ -513,10 +513,6 @@ public: void run() override { - log << - "sizeof(basic_parser) == " << - sizeof(basic_parser) << "\n"; - testObject(); testArray(); testString(); diff --git a/test/json.cpp b/test/json.cpp index 1ab46915..d3fdf417 100644 --- a/test/json.cpp +++ b/test/json.cpp @@ -4,8 +4,40 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -// Official repository: https://github.com/vinniefalco/BeastLounge +// Official repository: https://github.com/vinniefalco/json // // Test that header file is self-contained. #include + +#include + +namespace boost { +namespace json { + +struct zjson_test : public beast::unit_test::suite +{ + void + run() override + { + log << + "sizeof()\n" + " object == " << sizeof(object) << "\n" + " value_type == " << sizeof(object::value_type) << "\n" + " array == " << sizeof(array) << "\n" + " string == " << sizeof(string) << "\n" + " value == " << sizeof(value) << "\n" + " serializer == " << sizeof(serializer) << "\n" + " number_parser == " << sizeof(detail::number_parser) << "\n" + " basic_parser == " << sizeof(basic_parser) << "\n" + " parser == " << sizeof(parser) << "\n" + << std::endl; + ; + pass(); + } +}; + +BEAST_DEFINE_TESTSUITE(boost,json,zjson); + +} // json +} // boost diff --git a/test/number.cpp b/test/number.cpp index 9c085d0d..675b095e 100644 --- a/test/number.cpp +++ b/test/number.cpp @@ -331,10 +331,6 @@ public: void run() override { - log << - "sizeof(number_parser) == " << - sizeof(number_parser) << - std::endl; testParse(); testMembers(); } diff --git a/test/parser.cpp b/test/parser.cpp index c1441363..dc35f5a7 100644 --- a/test/parser.cpp +++ b/test/parser.cpp @@ -36,9 +36,8 @@ public: s.data(), s.size(), ec); - if(! BEAST_EXPECTS(! ec, - ec.message())) - return nullptr; + BEAST_EXPECTS(! ec, + ec.message()); //log << " " << to_string_test(p.get()) << std::endl; return p.release(); } @@ -49,7 +48,8 @@ public: string_view s1) { auto const s2 = - to_string_test(jv1); + //to_string_test(jv1); // use this if serializer is broken + to_string(jv1); auto jv2 = from_string_test(s2); if(! BEAST_EXPECT(equal(jv1, jv2))) @@ -59,43 +59,43 @@ public: } void - testParse() + grind_one(string_view s) { - string_view const js = - "{\"1\":{},\"2\":[],\"3\":\"x\",\"4\":1," - "\"5\":-1,\"6\":1.0,\"7\":false,\"8\":null}"; - - // parse(value) - { - check_round_trip( - parse(js), - js); - } - - // parse(value, storage_ptr) - { - check_round_trip( - parse(js, storage_ptr{}), - js); - } - - // parse(value, error_code) - { - error_code ec; - auto jv = parse(js, ec); - BEAST_EXPECTS(! ec, ec.message()); - check_round_trip(jv, js); - } - - // parse(value, storage_ptr, error_code) - { - error_code ec; - auto jv = parse(js, storage_ptr{}, ec); - BEAST_EXPECTS(! ec, ec.message()); - check_round_trip(jv, js); - } + auto const jv = + from_string_test(s); + check_round_trip(jv, s); } + void + grind(string_view s) + { + grind_one(s); + + fail_loop([&](storage_ptr const& sp) + { + auto const jv = + from_string_test(s, sp); + check_round_trip(jv, s); + }); + + if(s.size() > 1) + { + // Destroy the parser at every + // split point to check leaks. + for(std::size_t i = 1; + i < s.size(); ++i) + { + scoped_storage< + fail_storage> ss; + parser p; + error_code ec; + p.start(ss); + p.write(s.data(), i, ec); + } + } + + } + void legacyTests() { @@ -125,10 +125,11 @@ R"xx({ ; parser p; error_code ec; + p.start(); p.write(in.data(), in.size(), ec); if(BEAST_EXPECTS(! ec, ec.message())) { - BEAST_EXPECT(to_string_test(p.get()) == + BEAST_EXPECT(to_string_test(p.release()) == "{\"glossary\":{\"title\":\"example glossary\",\"GlossDiv\":" "{\"title\":\"S\",\"GlossList\":{\"GlossEntry\":{\"ID\":\"SGML\"," "\"SortAs\":\"SGML\",\"GlossTerm\":\"Standard Generalized Markup " @@ -140,24 +141,6 @@ R"xx({ } } - void - grind(string_view s) - { - { - auto const jv = - from_string_test(s); - check_round_trip(jv, s); - } -#if 1 - fail_loop([&](storage_ptr const& sp) - { - auto const jv = - from_string_test(s, sp); - check_round_trip(jv, s); - }); -#endif - } - void testObjects() { @@ -170,6 +153,28 @@ R"xx({ grind( "{\"1\":{},\"2\":[],\"3\":\"x\",\"4\":1," "\"5\":-1,\"6\":1.0,\"7\":false,\"8\":null}"); + + // big strings + { + std::string const big(4000, '*'); + { + std::string js; + js = "{\"" + big + "\":null}"; + grind(js); + } + + { + std::string js; + js = "{\"x\":\"" + big + "\"}"; + grind(js); + } + + { + std::string js; + js = "{\"" + big + "\":\"" + big + "\"}"; + grind(js); + } + } } void @@ -209,6 +214,30 @@ R"xx({ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "\""); + + // big string + { + std::string const big(4000, '*'); + { + std::string js; + js = "\"" + big + "\""; + auto const N = js.size() / 2; + error_code ec; + parser p; + p.start(); + p.write_some(js.data(), N, ec); + if(BEAST_EXPECTS(! ec, + ec.message())) + { + p.write(js.data() + N, + js.size() - N, ec); + if(BEAST_EXPECTS(! ec, + ec.message())) + check_round_trip( + p.release(), js); + } + } + } } void @@ -243,21 +272,82 @@ R"xx({ grind("null"); } + void + testMembers() + { + // need start error + { + parser p; + error_code ec; + p.write("", 0, ec); + BEAST_EXPECTS( + ec == error::need_start, + ec.message()); + } + } + + //------------------------------------------------------ + + void + testFreeFunctions() + { + string_view const js = + "{\"1\":{},\"2\":[],\"3\":\"x\",\"4\":1," + "\"5\":-1,\"6\":1.0,\"7\":false,\"8\":null}"; + + // parse(value) + { + { + check_round_trip( + parse(js), + js); + } + + { + BEAST_THROWS(parse( + "{,"), + system_error); + } + } + + // parse(value, storage_ptr) + { + check_round_trip( + parse(js, storage_ptr{}), + js); + } + + // parse(value, error_code) + { + error_code ec; + auto jv = parse(js, ec); + BEAST_EXPECTS(! ec, ec.message()); + check_round_trip(jv, js); + } + + // parse(value, storage_ptr, error_code) + { + error_code ec; + auto jv = parse(js, storage_ptr{}, ec); + BEAST_EXPECTS(! ec, ec.message()); + check_round_trip(jv, js); + } + } + void run() { - log << - "sizeof(parser) == " << - sizeof(parser) << "\n"; - - grind("[{\"x\": [{\"x\": [{\"x\":null}] }] }]"); - testParse(); testObjects(); testArrays(); - //testStrings(); + testStrings(); testNumbers(); testBool(); testNull(); + testMembers(); + testFreeFunctions(); + + // This still doesn't work.. + //legacyTests(); } }; diff --git a/test/ryu/gtest.hpp b/test/ryu/gtest.hpp index 1ab38385..b14a9700 100644 --- a/test/ryu/gtest.hpp +++ b/test/ryu/gtest.hpp @@ -15,7 +15,7 @@ #define TEST(s1,s2) \ struct s2 ## s1 ## _test; \ -BEAST_DEFINE_TESTSUITE(boost,ryu,s2##s1); \ +BEAST_DEFINE_TESTSUITE(boost,Ryu,s2##s1); \ struct s2 ## s1 ## _test : ::boost::beast::unit_test::suite { \ void run() override; }; void s2##s1##_test::run() diff --git a/test/serializer.cpp b/test/serializer.cpp index d94b49b2..118538d9 100644 --- a/test/serializer.cpp +++ b/test/serializer.cpp @@ -14,200 +14,16 @@ #include #include "parse-vectors.hpp" -#define SOFT_FAIL +#include "test.hpp" namespace boost { namespace json { -static -void -print( - std::ostream& os, - json::value const& jv); - -static -void -print( - std::ostream& os, - object const& obj) -{ - os << "{"; - for(auto it = obj.begin(); - it != obj.end(); ++it) - { - if(it != obj.begin()) - os << ","; - os << "\"" << it->key() << "\":"; - print(os, it->value()); - } - os << "}"; -} - -static -void -print( - std::ostream& os, - array const& arr) -{ - os << "["; - for(auto it = arr.begin(); - it != arr.end(); ++it) - { - if(it != arr.begin()) - os << ","; - print(os, *it); - } - os << "]"; -} - -static -void -print( - std::ostream& os, - json::value const& jv) -{ - switch(jv.kind()) - { - case kind::object: - print(os, jv.get_object()); - break; - case kind::array: - print(os, jv.get_array()); - break; - case kind::string: - os << "\"" << jv.get_string() << "\""; - break; - case kind::int64: - os << jv.as_int64(); - break; - case kind::uint64: - os << jv.as_uint64(); - break; - case kind::double_: - os << jv.as_double(); - break; - case kind::boolean: - if(jv.as_bool()) - os << "true"; - else - os << "false"; - break; - case kind::null: - os << "null"; - break; - } -} - class serializer_test : public beast::unit_test::suite { public: - static - unsigned - common( - string_view s1, - string_view s2) - { - unsigned n = 0; - auto p1 = s1.data(); - auto p2 = s2.data(); - auto end = s1.size() > s2.size() ? - s2.end() : s1.end(); - while(p1 < end) - { - ++n; - if(*p1++ != *p2++) - break; - } - return n; - } - void - round_trip( - string_view name, - string_view s0) - { - error_code ec; - auto jv0 = parse(s0, ec); - #ifdef SOFT_FAIL - if(ec) - return; - #else - if( ! BEAST_EXPECTS( - ! ec, ec.message())) - return; - #endif - auto s1 = to_string(jv0); - parser p; - p.start(); - auto n = p.write( - s1.data(), s1.size(), ec); - #ifdef SOFT_FAIL - if(ec) - #else - if( ! BEAST_EXPECTS( - ! ec, ec.message())) - #endif - { - auto c1 = s1.data() + n; - if( n > 60) - n = 60; - auto c0 = c1 - n; - log << - "context\n" - " " << string_view(c0, c1-c0) << std::endl; - log << - name << "\n" - " " << s0 << "\n" - " " << s1 << std::endl << std::endl; - return; - } - auto jv1 = p.release(); - auto s2 = to_string(jv1); - #ifdef SOFT_FAIL - if(s1 != s2) - #else - if(! BEAST_EXPECT(s1 == s2)) - #endif - { - auto c = common(s1, s2); - log << - name << "\n" - " " << s0 << "\n" - " " << s1.substr(0, c) << "\n" - " " << s2.substr(0, c) << std::endl << std::endl; - } - } - - void - print_grind( - string_view name, - json::value const& jv) - { - auto const s0 = to_string(jv); - log << s0 << std::endl; - round_trip(name, s0); - for(std::size_t i = 1; - i < s0.size() - 1; ++i) - { - std::string s; - s.resize(s0.size()); - serializer sr(jv); - auto const n1 = - sr.next(&s[0], i); - if(BEAST_EXPECT(n1 == i)) - sr.next( - &s[n1], s.size() - n1); - if(! BEAST_EXPECT(s == s0)) - { - log << - " " << s0 << "\n" - " " << s << std::endl << std::endl; - } - } - } - - void - testSerializer() + testMembers() { value jv; @@ -222,11 +38,11 @@ public: BEAST_EXPECT(! sr.is_done()); } - // next() + // read() { serializer sr(jv); char buf[1024]; - auto n = sr.next( + auto n = sr.read( buf, sizeof(buf)); BEAST_EXPECT(sr.is_done()); BEAST_EXPECT(string_view( @@ -235,114 +51,356 @@ public: } void - testRoundTrips() + grind_one( + string_view s, + value const& jv, + string_view name = {}) { + error_code ec; + auto const s1 = to_string(jv); + auto const jv2 = parse(s1, ec); + if(! BEAST_EXPECT(equal(jv, jv2))) + { + if(name.empty()) + log << + " " << s << "\n" + " " << s1 << + std::endl; + else + log << name << ":\n" + " " << s << "\n" + " " << s1 << + std::endl; + } + } + + void + grind( + string_view s0, + value const& jv, + string_view name = {}) + { + grind_one(s0, jv, name); + + auto const s1 = + to_string(jv); + for(std::size_t i = 1; + i < s1.size(); ++i) + { + serializer sr(jv); + string s2; + s2.reserve(s1.size()); + s2.grow(sr.read( + s2.data(), i)); + auto const dump = + [&] + { + if(name.empty()) + log << + " " << s0 << "\n" + " " << s1 << "\n" + " " << s2 << std::endl; + else + log << name << ":\n" + " " << s0 << "\n" + " " << s1 << "\n" + " " << s2 << std::endl; + }; + if(! BEAST_EXPECT( + s2.size() == i)) + { + dump(); + break; + } + s2.grow(sr.read( + s2.data() + i, + s1.size() - i)); + if(! BEAST_EXPECT( + s2.size() == s1.size())) + { + dump(); + break; + } + if(! BEAST_EXPECT(s2 == s1)) + { + dump(); + break; + } + } + } + + void + check( + string_view s, + string_view name = {}) + { + auto const jv = parse(s); + grind(s, jv, name); + } + + void + testObject() + { + check("{}"); + check("{\"x\":1}"); + check("{\"x\":[]}"); + check("{\"x\":1,\"y\":null}"); + } + + void + testArray() + { + check("[]"); + check("[[]]"); + check("[[],[],[]]"); + check("[[[[[[[[[[]]]]]]]]]]"); + check("[{}]"); + check("[{},{}]"); + check("[1,2,3,4,5]"); + check("[true,false,null]"); + } + + void + testString() + { + check("\"\""); + check("\"x\""); + check("\"xyz\""); + check("\"x z\""); + + // escapes + check("\"\\\"\""); // double quote + check("\"\\\\\""); // backslash + check("\"\\b\""); // backspace + check("\"\\f\""); // formfeed + check("\"\\n\""); // newline + check("\"\\r\""); // carriage return + check("\"\\t\""); // horizontal tab + + // contro\l characters + check("\"\\u0000\""); + check("\"\\u0001\""); + check("\"\\u0002\""); + check("\"\\u0003\""); + check("\"\\u0004\""); + check("\"\\u0005\""); + check("\"\\u0006\""); + check("\"\\u0007\""); + check("\"\\u0008\""); + check("\"\\u0009\""); + check("\"\\u000a\""); + check("\"\\u000b\""); + check("\"\\u000c\""); + check("\"\\u000d\""); + check("\"\\u000e\""); + check("\"\\u000f\""); + check("\"\\u0010\""); + check("\"\\u0011\""); + check("\"\\u0012\""); + check("\"\\u0013\""); + check("\"\\u0014\""); + check("\"\\u0015\""); + check("\"\\u0016\""); + check("\"\\u0017\""); + check("\"\\u0018\""); + check("\"\\u0019\""); + check("\"\\u0020\""); + check("\"\\u0021\""); + } + + void + testNumber() + { + // VFALCO These don't perfectly round-trip, + // because the representations are not exact. + // The test needs to do a better job of comparison. + + check("-999999999999999999999"); + check("-100000000000000000009"); + check("-10000000000000000000"); + //check("-9223372036854775809"); + check("-9223372036854775808"); + check("-9223372036854775807"); + check("-999999999999999999"); + check("-99999999999999999"); + check("-9999999999999999"); + check("-999999999999999"); + check("-99999999999999"); + check("-9999999999999"); + check("-999999999999"); + check("-99999999999"); + check("-9999999999"); + check("-999999999"); + check("-99999999"); + check("-9999999"); + check("-999999"); + check("-99999"); + check("-9999"); + check("-999"); + check("-99"); + check("-9"); + check( "0"); + check( "9"); + check( "99"); + check( "999"); + check( "9999"); + check( "99999"); + check( "999999"); + check( "9999999"); + check( "99999999"); + check( "999999999"); + check( "9999999999"); + check( "99999999999"); + check( "999999999999"); + check( "9999999999999"); + check( "99999999999999"); + check( "999999999999999"); + check( "9999999999999999"); + check( "99999999999999999"); + check( "999999999999999999"); + check( "9223372036854775807"); + check( "9223372036854775808"); + check( "9999999999999999999"); + check( "18446744073709551615"); + //check( "18446744073709551616"); + check( "99999999999999999999"); + check( "999999999999999999999"); + check( "1000000000000000000000"); + check( "9999999999999999999999"); + check( "99999999999999999999999"); + + //check("-0.9999999999999999999999"); + check("-0.9999999999999999"); + //check("-0.9007199254740991"); + //check("-0.999999999999999"); + //check("-0.99999999999999"); + //check("-0.9999999999999"); + //check("-0.999999999999"); + //check("-0.99999999999"); + //check("-0.9999999999"); + //check("-0.999999999"); + //check("-0.99999999"); + //check("-0.9999999"); + //check("-0.999999"); + //check("-0.99999"); + //check("-0.9999"); + //check("-0.8125"); + //check("-0.999"); + //check("-0.99"); + check("-1.0"); + check("-0.9"); + check("-0.0"); + check( "0.0"); + check( "0.9"); + //check( "0.99"); + //check( "0.999"); + //check( "0.8125"); + //check( "0.9999"); + //check( "0.99999"); + //check( "0.999999"); + //check( "0.9999999"); + //check( "0.99999999"); + //check( "0.999999999"); + //check( "0.9999999999"); + //check( "0.99999999999"); + //check( "0.999999999999"); + //check( "0.9999999999999"); + //check( "0.99999999999999"); + //check( "0.999999999999999"); + //check( "0.9007199254740991"); + check( "0.9999999999999999"); + //check( "0.9999999999999999999999"); + //check( "0.999999999999999999999999999"); + + check("-1e308"); + check("-1e-308"); + //check("-9999e300"); + //check("-999e100"); + //check("-99e10"); + check("-9e1"); + check( "9e1"); + //check( "99e10"); + //check( "999e100"); + //check( "9999e300"); + check( "999999999999999999.0"); + check( "999999999999999999999.0"); + check( "999999999999999999999e5"); + check( "999999999999999999999.0e5"); + + check("-1e-1"); + check("-1e0"); + check("-1e1"); + check( "0e0"); + check( "1e0"); + check( "1e10"); + } + + void + testScalar() + { + check("true"); + check("false"); + check("null"); + } + + void + testVectors() + { +#if 0 + check( + R"xx({ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } + })xx"); +#endif + parse_vectors const pv; for(auto const e : pv) { if(e.result != 'y') continue; - - round_trip(e.name, e.text); - } - } - - bool - good(string_view s) - { - error_code ec; - auto jv = parse(s, ec); - return !ec; - } - - template - void - tv(char const (&s)[N]) - { - round_trip( - "", - string_view(s, N - 1)); - } - - void - doTestStrings() - { - tv(R"("")"); - tv(R"("x")"); - tv(R"("xyz")"); - tv(R"("x z")"); - tv(R"("\"")"); - tv(R"("\\")"); - tv(R"("\/")"); - tv(R"("\b")"); - tv(R"("\f")"); - tv(R"("\n")"); - tv(R"("\r")"); - tv(R"("\t")"); - tv(R"("\u0000")"); - tv(R"("\u0001")"); - tv(R"("\u0002")"); - tv(R"("\u0003")"); - tv(R"("\u0004")"); - tv(R"("\u0005")"); - tv(R"("\u0006")"); - tv(R"("\u0007")"); - tv(R"("\u0008")"); - tv(R"("\u0009")"); - tv(R"("\u000a")"); - tv(R"("\u000b")"); - tv(R"("\u000c")"); - tv(R"("\u000d")"); - tv(R"("\u000e")"); - tv(R"("\u000f")"); - tv(R"("\u0010")"); - tv(R"("\u0011")"); - tv(R"("\u0012")"); - tv(R"("\u0013")"); - tv(R"("\u0014")"); - tv(R"("\u0015")"); - tv(R"("\u0016")"); - tv(R"("\u0017")"); - tv(R"("\u0018")"); - tv(R"("\u0019")"); - tv(R"("\u0020")"); - tv(R"("\u0020")"); - - tv(R"(0)"); - tv(R"(-0)"); - tv(R"(1)"); - tv(R"(-1)"); - tv(R"(99999)"); - tv(R"(-99999)"); - - tv(R"(true)"); - tv(R"(false)"); - - tv(R"(null)"); - } - - void - doTestVectors() - { - parse_vectors const pv; - for(auto const e : pv) - { - if( e.result == 'y' || - good(e.text)) - { - //log << i++ << " " << e.text << std::endl; - round_trip(e.name, e.text); - } + // skip these failures for now + if( + e.name == "number" || + e.name == "number_real_exponent" || + e.name == "number_real_fraction_exponent" || + e.name == "number_simple_real" || + e.name == "object_extreme_numbers" || + e.name == "pass01" + ) + continue; + check(e.text, e.name); } } void run() { - //testSerializer(); - doTestStrings(); - //doTestVectors(); - pass(); + testMembers(); + testObject(); + testArray(); + testString(); + testNumber(); + testScalar(); + testVectors(); } }; diff --git a/test/test.hpp b/test/test.hpp index b9468621..125c1362 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -54,7 +54,8 @@ struct fail_storage return true; } - std::size_t fail_max = 1; + std::size_t fail_max = + std::size_t(-1); std::size_t fail = 0; std::size_t nalloc = 0; diff --git a/test/value.cpp b/test/value.cpp index 46515b8c..222a7f91 100644 --- a/test/value.cpp +++ b/test/value.cpp @@ -71,32 +71,6 @@ public: string().capacity()); } - void - testCustomTypes() - { - using namespace value_test_ns; - - // to_json - { - T1 t; - value jv(t); - } - { - T2 t; - value jv(t); - } - { - T3 t; - value jv(t); - } - } - - BOOST_JSON_STATIC_ASSERT( - detail::is_range>::value); - - BOOST_JSON_STATIC_ASSERT( - detail::is_range>::value); - //------------------------------------------------------ void @@ -1443,30 +1417,46 @@ public: } } + //------------------------------------------------------ + + void + testCustomTypes() + { + using namespace value_test_ns; + + // to_json + { + T1 t; + value jv(t); + } + { + T2 t; + value jv(t); + } + { + T3 t; + value jv(t); + } + } + + BOOST_JSON_STATIC_ASSERT( + detail::is_range>::value); + + BOOST_JSON_STATIC_ASSERT( + detail::is_range>::value); + + //------------------------------------------------------ + void run() override { - log << - "sizeof(value) == " << - sizeof(value) << "\n"; - log << - "sizeof(object) == " << - sizeof(object) << "\n"; - log << - "sizeof(array) == " << - sizeof(array) << "\n"; - log << - "sizeof(string) == " << - sizeof(string) << "\n"; - - testCustomTypes(); - testConstruction(); testConversion(); testModifiers(); testExchange(); testObservers(); testAccessors(); + testCustomTypes(); } };