Refactor storage

This commit is contained in:
Vinnie Falco 2019-10-30 13:24:19 -07:00
parent 9ff45bb8fa
commit 96c4b77d86
24 changed files with 678 additions and 1310 deletions

View File

@ -14,15 +14,17 @@ source_group (TREE ${PROJECT_SOURCE_DIR}/include/boost/json PREFIX json FILES ${
GroupSources(bench "/")
if (MSVC)
add_compile_options(
/GL # Whole program optimization
/Ob2 # inline any suitable
)
add_link_options(
/LTCG # Link Time Code Generation
)
if (CMAKE_BUILD_TYPE EQUAL "Debug")
else()
add_compile_options(
/GL # Whole program optimization
/Ob2 # inline any suitable
)
add_link_options(
/LTCG # Link Time Code Generation
)
endif()
endif()
add_executable (bench

View File

@ -90,421 +90,6 @@ public:
}
};
class boost_null_impl : public any_impl
{
struct null_parser : basic_parser
{
std::size_t n_ = std::size_t(-1);
char buf[256];
void
on_stack_info(
stack& s) noexcept override
{
s.base = buf;
s.capacity = sizeof(buf);
}
void
on_stack_grow(
stack&,
unsigned,
error_code& ec) override
{
ec = error::too_deep;
}
void
on_document_begin(
error_code&) override
{
}
void
on_object_begin(
error_code&) override
{
}
void
on_object_end(
error_code&) override
{
}
void
on_array_begin(
error_code&) override
{
}
void
on_array_end(
error_code&) override
{
}
void
on_key_data(
string_view,
error_code&) override
{
}
void
on_key_end(
string_view,
error_code&) override
{
}
void
on_string_data(
string_view,
error_code&) override
{
}
void
on_string_end(
string_view,
error_code&) override
{
}
void
on_number(
ieee_decimal,
error_code&) override
{
}
void
on_bool(
bool,
error_code&) override
{
}
void
on_null(error_code&) override
{
}
null_parser() = default;
};
public:
string_view
name() const noexcept override
{
return "null parser";
}
void
parse(
string_view s,
int repeat) const override
{
while(repeat--)
{
error_code ec;
null_parser p;
p.write(s.data(), s.size(), ec);
}
}
void
serialize(
string_view s,
int repeat) const override
{
auto jv = json::parse(s);
while(repeat--)
to_string(jv);
}
};
class boost_vec_impl : public any_impl
{
struct vec_parser : basic_parser
{
std::vector<value> vec_;
char buf[256];
void
on_stack_info(
stack& s) noexcept override
{
s.base = buf;
s.capacity = sizeof(buf);
}
void
on_stack_grow(
stack&,
unsigned,
error_code& ec) override
{
ec = error::too_deep;
}
void
on_document_begin(
error_code&) override
{
}
void
on_object_begin(
error_code&) override
{
}
void
on_object_end(
error_code&) override
{
}
void
on_array_begin(
error_code&) override
{
}
void
on_array_end(
error_code&) override
{
}
void
on_key_data(
string_view,
error_code&) override
{
}
void
on_key_end(
string_view,
error_code&) override
{
}
void
on_string_data(
string_view,
error_code&) override
{
}
void
on_string_end(
string_view,
error_code&) override
{
}
void
on_number(
ieee_decimal dec,
error_code&) override
{
vec_.emplace_back(number(dec));
}
void
on_bool(
bool,
error_code&) override
{
}
void
on_null(error_code&) override
{
}
vec_parser() = default;
};
public:
string_view
name() const noexcept override
{
return "vector<value>";
}
void
parse(
string_view s,
int repeat) const override
{
while(repeat--)
{
error_code ec;
vec_parser p;
p.write(s.data(), s.size(), ec);
}
}
void
serialize(
string_view s,
int repeat) const override
{
auto jv = json::parse(s);
while(repeat--)
to_string(jv);
}
};
class boost_arr_impl : public any_impl
{
struct arr_parser : basic_parser
{
storage_ptr sp_;
array arr_;
char buf[256];
void
on_stack_info(
stack& s) noexcept override
{
s.base = buf;
s.capacity = sizeof(buf);
}
void
on_stack_grow(
stack&,
unsigned,
error_code& ec) override
{
ec = error::too_deep;
}
void
on_document_begin(
error_code&) override
{
}
void
on_object_begin(
error_code&) override
{
}
void
on_object_end(
error_code&) override
{
}
void
on_array_begin(
error_code&) override
{
}
void
on_array_end(
error_code&) override
{
}
void
on_key_data(
string_view,
error_code&) override
{
}
void
on_key_end(
string_view,
error_code&) override
{
}
void
on_string_data(
string_view,
error_code&) override
{
}
void
on_string_end(
string_view,
error_code&) override
{
}
void
on_number(
ieee_decimal dec,
error_code&) override
{
arr_.emplace_back(number(dec));
}
void
on_bool(
bool,
error_code&) override
{
}
void
on_null(error_code&) override
{
}
arr_parser(
storage_ptr sp = default_storage())
: arr_(std::move(sp))
{
}
};
public:
string_view
name() const noexcept override
{
return "array";
}
void
parse(
string_view s,
int repeat) const override
{
while(repeat--)
{
error_code ec;
#if 1
scoped_storage<
block_storage> ss;
arr_parser p(ss);
#else
arr_parser p;
#endif
p.write(s.data(), s.size(), ec);
}
}
void
serialize(
string_view s,
int repeat) const override
{
auto jv = json::parse(s);
while(repeat--)
to_string(jv);
}
};
//----------------------------------------------------------
struct rapidjson_impl : public any_impl
@ -674,9 +259,6 @@ main(
vi.reserve(10);
vi.emplace_back(new boost_impl);
vi.emplace_back(new rapidjson_impl);
//vi.emplace_back(new boost_null_impl);
//vi.emplace_back(new boost_vec_impl);
//vi.emplace_back(new boost_arr_impl);
//vi.emplace_back(new nlohmann_impl);
//benchParse(vs, vi);

View File

@ -23,6 +23,7 @@
#include <boost/json/parser.hpp>
#include <boost/json/serializer.hpp>
#include <boost/json/storage.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/json/string.hpp>
#include <boost/json/value.hpp>

View File

@ -12,7 +12,7 @@
#include <boost/json/detail/config.hpp>
#include <boost/json/kind.hpp>
#include <boost/json/storage.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/pilfer.hpp>
#include <cstdlib>
#include <initializer_list>

View File

@ -80,7 +80,8 @@ public:
explicit
block_storage(
std::size_t block_size = 64 * 1024)
: block_size_(block_size)
: storage(false)
, block_size_(block_size)
{
}

View File

@ -19,8 +19,8 @@ inline
T
exchange(T& t, U u) noexcept
{
T v = t;
t = u;
T v = std::move(t);
t = std::move(u);
return v;
}

View File

@ -52,14 +52,15 @@ impl_type::
destroy(
storage_ptr const& sp) noexcept
{
if(! vec || sp->is_scoped())
return;
auto it = vec + size;
while(it != vec)
(*--it).~value();
sp->deallocate(vec,
capacity * sizeof(value),
alignof(value));
if(vec && sp->need_free())
{
auto it = vec + size;
while(it != vec)
(*--it).~value();
sp->deallocate(vec,
capacity * sizeof(value),
alignof(value));
}
vec = nullptr;
size = 0;
capacity = 0;
@ -163,8 +164,7 @@ undo_insert(
array::
~array()
{
if(sp_)
impl_.destroy(sp_);
impl_.destroy(sp_);
}
//----------------------------------------------------------
@ -558,7 +558,7 @@ array::
destroy(
value* first, value* last) noexcept
{
if(! sp_->is_scoped())
if(sp_->need_free())
while(last != first)
(*--last).~value();
}

View File

@ -260,7 +260,7 @@ const_iterator(
object::
~object()
{
if(sp_ && ! sp_->is_scoped())
if(sp_->need_free())
release_storage();
}

View File

@ -1,29 +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_IMPL_STORAGE_HPP
#define BOOST_JSON_IMPL_STORAGE_HPP
#include <utility>
namespace boost {
namespace json {
template<class Storage, class... Args>
basic_storage_ptr<Storage>
make_storage(Args&&... args)
{
return basic_storage_ptr<Storage>(
new Storage(std::forward<Args>(args)...));
}
} // json
} // boost
#endif

View File

@ -1,67 +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_IMPL_STORAGE_IPP
#define BOOST_JSON_IMPL_STORAGE_IPP
#include <boost/json/storage.hpp>
#include <boost/json/detail/assert.hpp>
#include <memory>
namespace boost {
namespace json {
storage::
storage(unsigned long long id) noexcept
: refs_(1)
, id_(id)
{
}
//----------------------------------------------------------
storage_ptr const&
default_storage() noexcept
{
struct builtin : storage
{
builtin()
: storage(0x3b88990852d58ae4)
{
}
void*
do_allocate(
std::size_t n,
std::size_t) override
{
return std::allocator<
char>().allocate(n);
}
void
do_deallocate(
void* p,
std::size_t n,
std::size_t) noexcept override
{
std::allocator<
char>().deallocate(
static_cast<char*>(p), n);
}
};
static storage_ptr const sp =
make_storage<builtin>();
return sp;
}
} // json
} // boost
#endif

View File

@ -0,0 +1,88 @@
//
// Copyright (c) 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_IMPL_STORAGE_PTR_HPP
#define BOOST_JSON_IMPL_STORAGE_PTR_HPP
#include <boost/json/storage_ptr.hpp>
#include <new>
#include <utility>
namespace boost {
namespace json {
storage_ptr&
storage_ptr::
operator=(
storage_ptr&& other) noexcept
{
release();
p_ = other.p_;
other.p_ = nullptr;
return *this;
}
storage_ptr&
storage_ptr::
operator=(
storage_ptr const& other) noexcept
{
if(other.p_)
++other.p_->refs_;
release();
p_ = other.p_;
return *this;
}
storage*
storage_ptr::
get() const noexcept
{
struct default_impl : storage
{
default_impl()
: storage(true,
0x3b88990852d58ae4)
{
}
void*
do_allocate(
std::size_t n,
std::size_t) override
{
return ::operator new(n);
}
void
do_deallocate(
void* p,
std::size_t,
std::size_t) noexcept override
{
::operator delete(p);
}
};
//[[clang::require_constant_initialization]]
static scoped_storage<default_impl> impl;
return p_ ? p_ : impl.get();
}
template<class Storage, class... Args>
storage_ptr
make_storage(Args&&... args)
{
return storage_ptr(new Storage(
std::forward<Args>(args)...));
}
} // json
} // boost
#endif

View File

@ -11,7 +11,7 @@
#define BOOST_JSON_OBJECT_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/storage.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/json/detail/string.hpp>
#include <boost/pilfer.hpp>
#include <cstdlib>

View File

@ -31,7 +31,6 @@ the program, with the macro BOOST_BEAST_SPLIT_COMPILATION defined.
#include <boost/json/impl/object.ipp>
#include <boost/json/impl/parser.ipp>
#include <boost/json/impl/serializer.ipp>
#include <boost/json/impl/storage.ipp>
#include <boost/json/impl/string.ipp>
#include <boost/json/impl/value.ipp>

View File

@ -12,10 +12,7 @@
#include <boost/json/detail/config.hpp>
#include <atomic>
#include <cstdlib>
#include <memory>
#include <type_traits>
#include <utility>
#include <cstddef>
namespace boost {
namespace json {
@ -24,26 +21,13 @@ namespace json {
*/
class storage
{
std::atomic<std::size_t> refs_;
unsigned long long id_ = 0;
bool scoped_ = false;
std::atomic<
unsigned long long> refs_{ 1 };
unsigned long long const id_ = 0;
bool const need_free_ = true;
bool counted_ = true;
void
addref() noexcept
{
if(! scoped_)
++refs_;
}
void
release() noexcept
{
if(! scoped_ && --refs_ == 0)
delete this;
}
template<class T>
friend class basic_storage_ptr;
friend class storage_ptr;
template<class T>
friend class scoped_storage;
@ -52,6 +36,21 @@ public:
virtual
~storage() = default;
inline
bool
need_free() const noexcept
{
return need_free_;
}
bool
is_equal(
storage const& other) const noexcept
{
return (this == &other) || (
this->id_ != 0 &&
this->id_ == other.id_);
}
void*
allocate(
std::size_t n,
@ -69,21 +68,6 @@ public:
return do_deallocate(p, n, align);
}
bool
is_scoped() const noexcept
{
return scoped_;
}
bool
is_equal(
storage const& other) const noexcept
{
return (this == &other) || (
this->id_ != 0 &&
this->id_ == other.id_);
}
friend
bool
operator==(
@ -105,9 +89,13 @@ public:
protected:
// Choose a unique 64-bit random number from here:
// https://www.random.org/cgi-bin/randbyte?nbytes=8&format=h
BOOST_JSON_DECL
explicit
storage(unsigned long long id = 0) noexcept;
storage(
bool need_free,
unsigned long long id = 0) noexcept
: id_(id)
, need_free_(need_free)
{
}
virtual
void*
@ -123,456 +111,7 @@ protected:
std::size_t align) noexcept = 0;
};
//----------------------------------------------------------
/** Manages a type-erased storage object and options for a set of JSON values.
*/
template<class T>
class basic_storage_ptr
{
BOOST_JSON_STATIC_ASSERT(
std::is_base_of<storage, T>::value);
template<class U>
friend class basic_storage_ptr;
template<class U>
friend class scoped_storage;
T* t_ = nullptr;
explicit
basic_storage_ptr(T* t) noexcept
: t_(t)
{
}
public:
/** Construct a null storage pointer
This constructs a null storage pointer.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
basic_storage_ptr() = default;
/** Destroy a storage pointer.
If `this` is not null, the reference counter
on the @ref storage object is decrement. When
the reference count reaches zero, the object
is destroyed.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
~basic_storage_ptr()
{
if(t_)
t_->release();
}
/** Move construct a storage pointer.
After construction, the moved-from pointer
will be null.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param other The storage pointer to construct from.
*/
basic_storage_ptr(
basic_storage_ptr&& other) noexcept
: t_(other.t_)
{
other.t_ = nullptr;
}
/** Copy construct a storage pointer.
If `other` points to a valid @ref storage
object, then this pointer acquires shared
ownership of the storage. Otherwise, the
newly constructed pointer is equal to null.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param other The storage pointer to construct from.
*/
basic_storage_ptr(
basic_storage_ptr const& other) noexcept
: t_(other.t_)
{
if(t_)
t_->addref();
}
template<class U
#ifndef GENERATING_DOCUMENTATION
,class = typename std::enable_if<
std::is_convertible<U*, T*>::value &&
! std::is_same<U, T>::value
>::type
#endif
>
basic_storage_ptr(
basic_storage_ptr<U>&& sp) noexcept
: t_(sp.t_)
{
sp.t_ = nullptr;
}
template<class U
#ifndef GENERATING_DOCUMENTATION
,class = typename std::enable_if<
std::is_convertible<U*, T*>::value &&
! std::is_same<U, T>::value
>::type
#endif
>
basic_storage_ptr(
basic_storage_ptr<U> const& sp) noexcept
: t_(sp.t_)
{
if(t_)
t_->addref();
}
/** Construct an null storage pointer
This constructs a null storage pointer.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
basic_storage_ptr(
std::nullptr_t) noexcept
{
}
/** Move assign a storage pointer.
If `this` points to a valid object, it is
decremented as if by a call to the destructor.
After construction, the moved-from pointer
will be null.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param other The storage pointer to assign from.
*/
basic_storage_ptr&
operator=(
basic_storage_ptr&& other) noexcept
{
if(t_)
t_->release();
t_ = other.t_;
other.t_ = nullptr;
return *this;
}
/** Copy construct a storage pointer.
If `this` points to a valid object, it is
decremented as if by a call to the destructor.
If `other` points to a valid @ref storage
object, then this pointer acquires shared
ownership of the storage. Otherwise, the
newly constructed pointer is equal to null.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param other The storage pointer to assign from.
*/
basic_storage_ptr&
operator=(
basic_storage_ptr const& other) noexcept
{
if(other.t_)
other.t_->addref();
if(t_)
t_->release();
t_ = other.t_;
return *this;
}
/** Return true if this points to a valid storage object.
This function returns true if @ref get() returns
a non-null value.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
explicit
operator bool() const noexcept
{
return t_ != nullptr;
}
/** Return a pointer to the storage object.
If `this` points to a valid storage object,
it is returned. Otherwise the return value
is `nullptr`.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
T*
get() const noexcept
{
return t_;
}
/** Return a pointer to the storage object.
If `this` points to a valid storage object,
it is returned. Otherwise the return value
is `nullptr`.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
T*
operator->() const noexcept
{
return t_;
}
/** Return a reference to the storage object.
If `this` points to a valid storage object,
it is returned. Otherwise the behavior is
undefined.
@par Precondition
`this` points to a valid storage object.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
T&
operator*() const noexcept
{
return *t_;
}
/** Create a new storage object and return a pointer to it.
This functions similarly to `make_shared`.
@par Mandates
`std::is_base_of_v<storage, U>`
@par Complexity
Same as `U(std::forward<Args>(args)...)`.
@par Exception Safety
Strong guarantee.
@param args Parameters forwarded to the constructor of `U`.
@tparam U the type of the storage object to create.
*/
template<class U, class... Args>
friend
basic_storage_ptr<U>
make_storage(Args&&... args);
};
/** Compare two storage pointers
*/
template<class T, class U>
bool
operator==(
basic_storage_ptr<T> const& lhs,
basic_storage_ptr<U> const& rhs) noexcept
{
return lhs.get() == rhs.get();
}
/** Compare two storage pointers
*/
template<class T, class U>
bool
operator!=(
basic_storage_ptr<T> const& lhs,
basic_storage_ptr<U> const& rhs) noexcept
{
return lhs.get() != rhs.get();
}
/** Compare two storage pointers
*/
template<class T>
bool
operator==(
basic_storage_ptr<T> const& lhs,
std::nullptr_t) noexcept
{
return lhs.get() == nullptr;
}
/** Compare two storage pointers
*/
template<class T>
bool
operator!=(
basic_storage_ptr<T> const& lhs,
std::nullptr_t) noexcept
{
return lhs.get() != nullptr;
}
/** Compare two storage pointers
*/
template<class T>
bool
operator==(
std::nullptr_t,
basic_storage_ptr<T> const& rhs) noexcept
{
return rhs.get() == nullptr;
}
/** Compare two storage pointers
*/
template<class T>
bool
operator!=(
std::nullptr_t,
basic_storage_ptr<T> const& rhs) noexcept
{
return rhs.get() != nullptr;
}
/// A type-erased storage pointer.
using storage_ptr = basic_storage_ptr<storage>;
//----------------------------------------------------------
template<class T>
class scoped_storage
{
T t_;
BOOST_JSON_STATIC_ASSERT(
std::is_base_of<storage, T>::value);
public:
template<class... Args>
explicit
scoped_storage(Args&&... args)
: t_(std::forward<Args>(args)...)
{
t_.scoped_ = true;
}
operator storage_ptr() noexcept
{
return storage_ptr(&t_);
}
};
//----------------------------------------------------------
/** Return a pointer to the default storage
This function returns the default storage, which is
used when constructing a container without explicitly
specifying the storage. The default storage uses the
global allocator, equivalent to `std::allocator<char>`.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@par Thread Safety
May be called concurrently.
*/
BOOST_JSON_DECL
storage_ptr const&
default_storage() noexcept;
} // json
} // boost
#include <boost/json/impl/storage.hpp>
#ifdef BOOST_JSON_HEADER_ONLY
#include <boost/json/impl/storage.ipp>
#endif
#endif

View File

@ -0,0 +1,363 @@
//
// 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_STORAGE_PTR_HPP
#define BOOST_JSON_STORAGE_PTR_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/storage.hpp>
#include <cstddef>
#include <type_traits>
#include <utility>
namespace boost {
namespace json {
/** Manages a type-erased storage object and options for a set of JSON values.
*/
class storage_ptr
{
template<class T>
friend class scoped_storage;
storage* p_ = nullptr;
inline
void
release() const noexcept
{
if( p_ &&
p_->counted_ &&
--p_->refs_ == 0)
delete p_;
}
explicit
storage_ptr(storage* p) noexcept
: p_(p)
{
}
public:
/** Construct a default storage pointer
This constructs a default storage pointer.
The default storage is not reference counted,
uses global operator new and delete to obtain
memory, and requires calls to `deallocate`.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
storage_ptr() = default;
storage_ptr(std::nullptr_t) noexcept
{
}
/** Destroy a storage pointer.
This releases the pointed-to storage. If the
storage is reference counted and this is the
last reference. the storage object is destroyed.
If the storage does not require deallocation,
all memory allocated using this storage is
invalidated.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
~storage_ptr()
{
release();
}
/** Move construct a storage pointer.
After construction, the moved-from pointer
will be null.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param other The storage pointer to construct from.
*/
storage_ptr(
storage_ptr&& other) noexcept
: p_(other.p_)
{
other.p_ = nullptr;
}
/** Copy construct a storage pointer.
If `other` points to a valid @ref storage
object, then this pointer acquires shared
ownership of the storage. Otherwise, the
newly constructed pointer is the default
storage pointer.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param other The storage pointer to construct from.
*/
storage_ptr(
storage_ptr const& other) noexcept
: p_(other.p_)
{
if(p_)
++p_->refs_;
}
/** Move assign a storage pointer.
If `this` points to a valid object, it is
decremented as if by a call to the destructor.
After construction, the moved-from pointer
will be null.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param other The storage pointer to assign from.
*/
inline
storage_ptr&
operator=(
storage_ptr&& other) noexcept;
/** Copy construct a storage pointer.
If `this` points to a valid object, it is
decremented as if by a call to the destructor.
If `other` points to a valid @ref storage
object, then this pointer acquires shared
ownership of the storage. Otherwise, the
newly constructed pointer is equal to null.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param other The storage pointer to assign from.
*/
inline
storage_ptr&
operator=(
storage_ptr const& other) noexcept;
/** Return a pointer to the storage object.
If `this` points to a valid storage object,
it is returned. Otherwise the return value
is `nullptr`.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
inline
storage*
get() const noexcept;
/** Return a pointer to the storage object.
If `this` points to a valid storage object,
it is returned. Otherwise the return value
is `nullptr`.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
storage*
operator->() const noexcept
{
return get();
}
/** Return a reference to the storage object.
If `this` points to a valid storage object,
it is returned. Otherwise the behavior is
undefined.
@par Precondition
`this` points to a valid storage object.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
storage&
operator*() const noexcept
{
return *get();
}
/** Create a new storage object and return a pointer to it.
This functions similarly to `make_shared`.
@par Mandates
`std::is_base_of_v<storage, U>`
@par Complexity
Same as `U(std::forward<Args>(args)...)`.
@par Exception Safety
Strong guarantee.
@param args Parameters forwarded to the constructor of `U`.
@tparam U the type of the storage object to create.
*/
template<class U, class... Args>
friend
storage_ptr
make_storage(Args&&... args);
};
#ifndef GENERATING_DOCUMENTATION
inline
bool
operator==(
storage_ptr const& lhs,
storage_ptr const& rhs) noexcept
{
return lhs.get() == rhs.get();
}
inline
bool
operator!=(
storage_ptr const& lhs,
storage_ptr const& rhs) noexcept
{
return lhs.get() != rhs.get();
}
#endif
//----------------------------------------------------------
template<class T>
class scoped_storage
{
T t_;
BOOST_JSON_STATIC_ASSERT(
std::is_base_of<storage, T>::value);
public:
template<class... Args>
explicit
scoped_storage(Args&&... args)
: t_(std::forward<Args>(args)...)
{
t_.counted_ = false;
}
T*
get() noexcept
{
return &t_;
}
T*
operator->() noexcept
{
return get();
}
operator storage_ptr() noexcept
{
return storage_ptr(&t_);
}
};
//----------------------------------------------------------
/** Return a pointer to the default storage
This function returns the default storage, which is
used when constructing a container without explicitly
specifying the storage. The default storage uses the
global allocator, equivalent to `std::allocator<char>`.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@par Thread Safety
May be called concurrently.
*/
inline
storage_ptr
default_storage() noexcept
{
return {};
}
} // json
} // boost
#include <boost/json/impl/storage_ptr.hpp>
#endif

View File

@ -11,7 +11,7 @@
#define BOOST_JSON_STRING_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/storage.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/json/detail/assert.hpp>
#include <boost/json/detail/string.hpp>
#include <boost/pilfer.hpp>

View File

@ -16,7 +16,7 @@
#include <boost/json/kind.hpp>
#include <boost/json/number.hpp>
#include <boost/json/object.hpp>
#include <boost/json/storage.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/json/string.hpp>
#include <boost/json/detail/is_specialized.hpp>
#include <boost/json/detail/value.hpp>

View File

@ -43,6 +43,7 @@ add_executable (json-tests
parser.cpp
serializer.cpp
storage.cpp
storage_ptr.cpp
string.cpp
value.cpp
)

View File

@ -40,6 +40,7 @@ local SOURCES =
parser.cpp
serializer.cpp
storage.cpp
storage_ptr.cpp
string.cpp
value.cpp
ryu/d2fixed_test.cpp

View File

@ -198,7 +198,6 @@ public:
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());
}

View File

@ -10,6 +10,8 @@
// Test that header file is self-contained.
#include <boost/json/block_storage.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
namespace boost {

View File

@ -9,191 +9,3 @@
// Test that header file is self-contained.
#include <boost/json/storage.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include "test.hpp"
namespace boost {
namespace json {
class storage_test : public beast::unit_test::suite
{
public:
struct throwing : storage
{
throwing()
{
throw std::exception{};
}
void*
do_allocate(
std::size_t,
std::size_t) override
{
return nullptr;
}
void
do_deallocate(
void*,
std::size_t,
std::size_t) noexcept override
{
}
};
void
testMembers()
{
auto const dsp = default_storage();
auto const usp =
make_storage<unique_storage>();
// ~storage_ptr()
{
// implied
}
// storage_ptr()
{
storage_ptr sp;
BEAST_EXPECT(! sp);
}
// storage_ptr(storage_ptr&&)
{
storage_ptr sp1 = dsp;
storage_ptr sp2(std::move(sp1));
BEAST_EXPECT(! sp1);
BEAST_EXPECT(*sp2 == *dsp);
}
// storage_ptr(storage_ptr const&)
{
storage_ptr sp1 = dsp;
storage_ptr sp2(sp1);
BEAST_EXPECT(sp1);
BEAST_EXPECT(sp2);
BEAST_EXPECT(sp1 == sp2);
}
// storage_ptr(basic_storage_ptr<U>&&)
{
basic_storage_ptr<unique_storage> sp1 =
make_storage<unique_storage>();
storage_ptr sp2(std::move(sp1));
BEAST_EXPECT(! sp1);
BEAST_EXPECT(sp2);
}
// storage_ptr(basic_storage_ptr<U> const&)
{
basic_storage_ptr<unique_storage> sp1 =
make_storage<unique_storage>();
storage_ptr sp2(sp1);
BEAST_EXPECT(sp1);
BEAST_EXPECT(sp2);
BEAST_EXPECT(*sp1 == *sp2);
}
// storage_ptr(nullptr_t)
{
storage_ptr sp(nullptr);
BEAST_EXPECT(! sp);
}
// operator=(storage_ptr&&)
{
storage_ptr sp1(dsp);
storage_ptr sp2(usp);
sp2 = std::move(sp1);
BEAST_EXPECT(! sp1);
BEAST_EXPECT(*sp2 == *dsp);
}
// operator=(storage_ptr const&)
{
storage_ptr sp1(dsp);
storage_ptr sp2(usp);
sp2 = sp1;
BEAST_EXPECT(sp1);
BEAST_EXPECT(*sp1 == *sp2);
}
// operator bool()
{
storage_ptr sp;
BEAST_EXPECT(! sp);
sp = dsp;
BEAST_EXPECT(sp);
}
// get()
{
storage_ptr sp(dsp);
BEAST_EXPECT(sp.get() == dsp.get());
}
// operator->()
{
storage_ptr sp(dsp);
BEAST_EXPECT(sp.operator->() == dsp.get());
}
// operator*()
{
storage_ptr sp(dsp);
BEAST_EXPECT(&sp.operator*() == dsp.get());
}
// exception in make_storage
{
BEAST_THROWS(
make_storage<throwing>(),
std::exception);
}
}
void
testRelational()
{
basic_storage_ptr<unique_storage> sp1 =
make_storage<unique_storage>();
storage_ptr sp2 = sp1;
basic_storage_ptr<unique_storage> sp3 =
make_storage<unique_storage>();
storage_ptr sp4;
BEAST_EXPECT(sp1 == sp2);
BEAST_EXPECT(sp1 != sp3);
BEAST_EXPECT(sp4 == nullptr);
BEAST_EXPECT(sp3 != nullptr);
BEAST_EXPECT(nullptr == sp4);
BEAST_EXPECT(nullptr != sp3);
}
void
testDefaultStorage()
{
// default_storage()
{
auto sp1 = default_storage();
auto sp2 = default_storage();
BEAST_EXPECT(*sp1 == *sp2);
}
}
void
run() override
{
testMembers();
testRelational();
testDefaultStorage();
}
};
BEAST_DEFINE_TESTSUITE(boost,json,storage);
} // json
} // boost

145
test/storage_ptr.cpp Normal file
View File

@ -0,0 +1,145 @@
//
// 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
//
// Test that header file is self-contained.
#include <boost/json/storage_ptr.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include "test.hpp"
namespace boost {
namespace json {
class storage_ptr_test : public beast::unit_test::suite
{
public:
struct throwing : storage
{
throwing()
: storage(false)
{
throw std::exception{};
}
void*
do_allocate(
std::size_t,
std::size_t) override
{
return nullptr;
}
void
do_deallocate(
void*,
std::size_t,
std::size_t) noexcept override
{
}
};
void
testMembers()
{
auto const dsp = default_storage();
auto const usp =
make_storage<unique_storage>();
// ~storage_ptr()
{
// implied
}
// storage_ptr()
{
storage_ptr sp;
BEAST_EXPECT(sp.get());
}
// storage_ptr(storage_ptr&&)
{
storage_ptr sp1 = dsp;
storage_ptr sp2(std::move(sp1));
BEAST_EXPECT(sp1.get());
BEAST_EXPECT(*sp2 == *dsp);
}
// storage_ptr(storage_ptr const&)
{
storage_ptr sp1 = dsp;
storage_ptr sp2(sp1);
BEAST_EXPECT(sp1 == sp2);
}
// operator=(storage_ptr&&)
{
storage_ptr sp1(dsp);
storage_ptr sp2(usp);
sp2 = std::move(sp1);
BEAST_EXPECT(*sp2 == *dsp);
}
// operator=(storage_ptr const&)
{
storage_ptr sp1(dsp);
storage_ptr sp2(usp);
sp2 = sp1;
BEAST_EXPECT(*sp1 == *sp2);
}
// get()
{
storage_ptr sp(dsp);
BEAST_EXPECT(sp.get() == dsp.get());
}
// operator->()
{
storage_ptr sp(dsp);
BEAST_EXPECT(sp.operator->() == dsp.get());
}
// operator*()
{
storage_ptr sp(dsp);
BEAST_EXPECT(&sp.operator*() == dsp.get());
}
// exception in make_storage
{
BEAST_THROWS(
make_storage<throwing>(),
std::exception);
}
}
void
testDefaultStorage()
{
// default_storage()
{
auto sp1 = default_storage();
auto sp2 = default_storage();
BEAST_EXPECT(*sp1 == *sp2);
}
}
void
run() override
{
testMembers();
testDefaultStorage();
}
};
BEAST_DEFINE_TESTSUITE(boost,json,storage_ptr);
} // json
} // boost

View File

@ -12,7 +12,7 @@
#include <boost/json/basic_parser.hpp>
#include <boost/json/value.hpp>
#include <boost/json/storage.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <cstddef>
#include <iterator>
@ -27,25 +27,26 @@ namespace json {
// unique_storage
struct unique_storage : storage
{
unique_storage()
: storage(true)
{
}
void*
do_allocate(
std::size_t n,
std::size_t) override
{
return std::allocator<
char>{}.allocate(n);
return ::operator new(n);
}
void
do_deallocate(
void* p,
std::size_t n,
std::size_t,
std::size_t) noexcept override
{
auto cp =
reinterpret_cast<char*>(p);
return std::allocator<
char>{}.deallocate(cp, n);
return ::operator delete(p);
}
};
@ -66,7 +67,8 @@ struct fail_storage : storage
std::size_t fail_max = 1;
std::size_t fail = 0;
~fail_storage()
fail_storage()
: storage(true)
{
}
@ -81,39 +83,31 @@ struct fail_storage : storage
fail = 0;
throw test_failure{};
}
return std::allocator<
char>{}.allocate(n);
return ::operator new(n);
}
void
do_deallocate(
void* p,
std::size_t n,
std::size_t,
std::size_t) noexcept override
{
auto cp =
reinterpret_cast<char*>(p);
return std::allocator<
char>{}.deallocate(cp, n);
::operator delete(p);
}
};
//----------------------------------------------------------
namespace detail {
#if 1
template<class F>
void
fail_loop(F&& f)
{
auto sp = make_storage<fail_storage>();
while(sp->fail < 200)
scoped_storage<fail_storage> ss;
while(ss->fail < 200)
{
try
{
f(sp);
f(ss);
}
catch(test_failure const&)
{
@ -121,72 +115,7 @@ fail_loop(F&& f)
}
break;
}
BEAST_EXPECT(sp->fail < 200);
}
#else
template<class F>
typename std::enable_if<
std::is_same<void,
decltype(std::declval<F const&>()(
std::declval<storage_ptr>()))>::value
>::type
fail_loop(F&& f)
{
auto sp = make_storage<fail_storage>();
while(sp->fail < 200)
{
try
{
f(sp);
}
catch(test_failure const&)
{
continue;
}
break;
}
BEAST_EXPECT(sp->fail < 200);
}
template<class F>
typename std::enable_if<
std::is_same<void, decltype(
std::declval<F const&>()())>::value
>::type
fail_loop(F&& f)
{
auto saved = default_storage();
auto sp =
make_storage<fail_storage>();
default_storage(sp);
while(sp->fail < 200)
{
try
{
f();
}
catch(test_failure const&)
{
continue;
}
break;
}
BEAST_EXPECT(sp->fail < 200);
default_storage(saved);
}
#endif
} // detail
template<class F>
void
fail_loop(F&& f)
{
detail::fail_loop(
std::forward<F>(f));
BEAST_EXPECT(ss->fail < 200);
}
//----------------------------------------------------------