This commit is contained in:
Vinnie Falco 2019-11-11 16:06:03 -08:00
parent 40a16c9380
commit 68f3df0403
26 changed files with 758 additions and 602 deletions

View File

@ -446,8 +446,8 @@ main(
{
std::vector<std::unique_ptr<any_impl const>> 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);

View File

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

View File

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

View File

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

View File

@ -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<T*>(
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 <boost/json/detail/static_stack.ipp>
#endif
#endif

View File

@ -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 <boost/json/detail/static_stack.hpp>
namespace boost {
namespace json {
namespace detail {
template<class T, std::size_t N>
static_stack<T, N>::
~static_stack()
{
if(begin_ != t_)
sp_->deallocate(begin_,
capacity() * sizeof(T),
alignof(T));
}
template<class T, std::size_t N>
void
static_stack<T, N>::
grow()
{
auto const n = 2 * capacity();
auto const begin =
reinterpret_cast<T*>(
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 <boost/json/detail/static_stack.ipp>
#endif
#endif

View File

@ -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<size_type>(n));
reserve(static_cast<std::size_t>(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;

View File

@ -11,8 +11,9 @@
#define BOOST_JSON_IMPL_ARRAY_IPP
#include <boost/json/array.hpp>
#include <boost/json/detail/exchange.hpp>
#include <boost/json/detail/assert.hpp>
#include <boost/json/detail/except.hpp>
#include <boost/json/detail/exchange.hpp>
#include <boost/pilfer.hpp>
#include <cstdlib>
#include <limits>
@ -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<value*>(
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<size_type>(n))
, n_(static_cast<std::size_t>(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<size_type>(
static_cast<std::size_t>(
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;

View File

@ -441,8 +441,7 @@ loop_str1:
goto yield;
}
++p;
st_.pop();
st_.push(state::str2);
*st_ = state::str2;
BOOST_FALLTHROUGH;
// string, no-copy loop

View File

@ -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>(args)...);
string_view(
rs_.begin() + offset,
key_size),
std::forward<Args>(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>(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<std::uint32_t>(count_),
sp_ };
count_, sp_ };
}
unchecked_array
@ -303,10 +325,7 @@ pop_array() noexcept
auto const n =
count_ * sizeof(value);
return { reinterpret_cast<value*>(
rs_.pop(n)),
// VFALCO bad narrowing
static_cast<std::uint32_t>(count_),
sp_ };
rs_.pop(n)), count_, sp_ };
}
string_view

View File

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

View File

@ -44,7 +44,7 @@
</Type>
<Type Name="boost::json::string">
<DisplayString>{{ {impl_.capacity>sizeof(impl_.buf)?impl_.p:impl_.buf,[impl_.size]s} }}</DisplayString>
<DisplayString>{impl_.capacity>sizeof(impl_.buf)?impl_.p:impl_.buf,[impl_.size]s}</DisplayString>
<Expand>
<Item Name="[capacity]">impl_.capacity</Item>
<Item Name="[storage]">sp_</Item>

View File

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

View File

@ -66,7 +66,7 @@ class serializer
};
#endif
detail::static_stack<node, 50> stack_;
detail::static_stack<node, 16> 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

View File

@ -36,7 +36,6 @@ the program, with the macro BOOST_BEAST_SPLIT_COMPILATION defined.
#include <boost/json/detail/format.ipp>
#include <boost/json/detail/number.ipp>
#include <boost/json/detail/raw_stack.ipp>
#include <boost/json/detail/static_stack.ipp>
#include <boost/json/detail/ryu/impl/d2s.ipp>
#endif

View File

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

View File

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

View File

@ -23,7 +23,7 @@ public:
using init_list = std::initializer_list<value>;
string_view const str_;
unsigned long min_capacity_;
std::size_t min_capacity_;
array_test()
: str_(

View File

@ -513,10 +513,6 @@ public:
void
run() override
{
log <<
"sizeof(basic_parser) == " <<
sizeof(basic_parser) << "\n";
testObject();
testArray();
testString();

View File

@ -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 <boost/json.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
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

View File

@ -331,10 +331,6 @@ public:
void
run() override
{
log <<
"sizeof(number_parser) == " <<
sizeof(number_parser) <<
std::endl;
testParse();
testMembers();
}

View File

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

View File

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

View File

@ -14,200 +14,16 @@
#include <boost/beast/_experimental/unit_test/suite.hpp>
#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<std::size_t N>
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();
}
};

View File

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

View File

@ -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<std::vector<int>>::value);
BOOST_JSON_STATIC_ASSERT(
detail::is_range<std::initializer_list<int>>::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<std::vector<int>>::value);
BOOST_JSON_STATIC_ASSERT(
detail::is_range<std::initializer_list<int>>::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();
}
};