storage_ptr uses memory_resource

This commit is contained in:
Vinnie Falco 2020-04-04 11:56:04 -07:00
parent 33e8e468cb
commit a47b0f3fc1
35 changed files with 606 additions and 797 deletions

View File

@ -197,20 +197,22 @@ jobs:
- COMMENT=standalone
- STANDALONE=1
- B2_VARIANT=variant=debug
- B2_TOOLSET=gcc-7
- B2_TOOLSET=gcc-9
- B2_CXXSTD=17
- B2_DEFINES="define=BOOST_JSON_STANDALONE=1"
addons: *gcc-7
addons: *gcc-9
- os: "linux"
env:
- COMMENT=standalone
- STANDALONE=1
- B2_VARIANT=variant=debug
- B2_TOOLSET=clang-7
- B2_CXXSTD=17
- B2_DEFINES="define=BOOST_JSON_STANDALONE=1"
addons: *clang-7
# VFALCO This needs to use a later version of
# libstdc++ which has <memory_resource>
#- os: "linux"
# env:
# - COMMENT=standalone
# - STANDALONE=1
# - B2_VARIANT=variant=debug
# - B2_TOOLSET=clang-9
# - B2_CXXSTD=17
# - B2_DEFINES="define=BOOST_JSON_STANDALONE=1"
# addons: *clang-9
# libstdc++
- { os: "linux", dist: "trusty", # xenial has libstdc++ from gcc 5.4.0 with newer ABI

View File

@ -40,13 +40,14 @@ return types in public interfaces.
The design of the library also achieves these goals:
* Requires only C++11
* Support stateful allocators
* Top performance of general libraries
* Uniform interface on all C++ versions
* Strict parser and serializer which work incrementally
* Security-aware treatment of untrusted inputs
* Fast compilation performance
* Requires only C++11.
* Support stateful allocators.
* Top performance of general libraries.
* Uniform interface on all C++ versions.
* Key lookup in objects has constant average complexity.
* Strict parser and serializer which work incrementally.
* Security-aware treatment of untrusted inputs.
* Fast compilation performance.
## CMake

View File

@ -320,8 +320,8 @@ public:
parser p;
while(repeat--)
{
scoped_storage<pool> ss;
p.start(ss);
monotonic_resource mr;
p.start(&mr);
error_code ec;
p.write(s.data(), s.size(), ec);
if(! ec)
@ -335,8 +335,8 @@ public:
string_view s,
std::size_t repeat) const override
{
scoped_storage<pool> sp;
auto jv = json::parse(s, sp);
monotonic_resource mr;
auto jv = json::parse(s, &mr);
serializer sr;
string out;
out.reserve(512);

View File

@ -45,6 +45,7 @@ The design of the library also achieves these goals:
* Safe, easy to use interfaces.
* Stateful allocator support.
* Uniform interface on all C++ versions.
* Key lookup in objects has constant average complexity.
* Strict parser and serializer which work incrementally.
* Security-aware treatment of untrusted inputs.
* Fast compilation performance.

View File

@ -15,12 +15,11 @@
#include <boost/json/array.hpp>
#include <boost/json/error.hpp>
#include <boost/json/kind.hpp>
#include <boost/json/monotonic_resource.hpp>
#include <boost/json/number_cast.hpp>
#include <boost/json/object.hpp>
#include <boost/json/parser.hpp>
#include <boost/json/pool.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/to_value.hpp>

View File

@ -149,9 +149,10 @@ public:
*/
~array()
{
if( impl_.data() &&
sp_->need_free())
destroy();
if( ! impl_.data() ||
sp_.is_not_counted_and_deallocate_is_null())
return;
destroy();
}
#ifndef BOOST_JSON_DOCS

View File

@ -40,6 +40,9 @@ generic_category();
using boost::system::generic_category;
#endif
/// The type of memory_resource used by the library.
using memory_resource = boost::container::pmr::memory_resource;
#else
using error_code = std::error_code;
@ -48,6 +51,7 @@ using error_condition = std::error_condition;
using string_view = std::string_view;
using system_error = std::system_error;
using std::generic_category;
using memory_resource = std::pmr::memory_resource;
#endif

View File

@ -13,12 +13,14 @@
#ifndef BOOST_JSON_STANDALONE
# include <boost/config.hpp>
# include <boost/assert.hpp>
# include <boost/container/pmr/memory_resource.hpp>
# include <boost/system/error_code.hpp>
# include <boost/system/system_error.hpp>
# include <boost/utility/string_view.hpp>
# include <boost/throw_exception.hpp>
#else
# include <cassert>
# include <memory_resource>
# include <string_view>
# include <system_error>
#endif

View File

@ -7,36 +7,38 @@
// Official repository: https://github.com/vinniefalco/json
//
#ifndef BOOST_JSON_IMPL_STORAGE_HPP
#define BOOST_JSON_IMPL_STORAGE_HPP
#ifndef BOOST_JSON_DETAIL_COUNTED_RESOURCE_HPP
#define BOOST_JSON_DETAIL_COUNTED_RESOURCE_HPP
#include <utility>
#include <boost/json/config.hpp>
#include <atomic>
namespace boost {
namespace json {
namespace detail {
struct counted_resource
: memory_resource
{
std::atomic<std::size_t> refs{ 1 };
};
template<class T>
struct storage_impl : storage
class counted_resource_impl final
: public counted_resource
{
T t;
public:
template<class... Args>
constexpr
explicit
storage_impl(
bool counted,
counted_resource_impl(
Args&&... args)
: storage(
T::id,
T::need_free,
counted)
, t(std::forward<Args>(args)...)
: t(std::forward<Args>(args)...)
{
}
void*
allocate(
do_allocate(
std::size_t n,
std::size_t align) override
{
@ -44,12 +46,20 @@ struct storage_impl : storage
}
void
deallocate(
do_deallocate(
void* p,
std::size_t n,
std::size_t align) noexcept override
std::size_t align) override
{
t.deallocate(p, n, align);
return t.deallocate(p, n, align);
}
bool
do_is_equal(
memory_resource const& mr) const noexcept override
{
// VFALCO Is always false ok?
return false;
}
};

View File

@ -0,0 +1,56 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.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_DEFAULT_RESOURCE_HPP
#define BOOST_JSON_DEFAULT_RESOURCE_HPP
#include <boost/json/config.hpp>
#include <new>
namespace boost {
namespace json {
namespace detail {
/** A simple memory resource that uses operator new and delete.
*/
class default_resource final
: public memory_resource
{
public:
void*
do_allocate(
std::size_t n,
std::size_t align) override
{
return ::operator new(n);
}
void
do_deallocate(
void* p,
std::size_t,
std::size_t) override
{
::operator delete(p);
}
bool
do_is_equal(
memory_resource const& mr) const noexcept
{
return dynamic_cast<
default_resource const*>(&mr) != nullptr;
}
};
} // detail
} // json
} // boost
#endif

View File

@ -28,10 +28,12 @@ index_of(value const* pos) const noexcept ->
unchecked_array::
~unchecked_array()
{
if(data_ && sp_->need_free())
for(unsigned long i = 0;
i < size_; ++i)
data_[i].~value();
if(! data_ ||
sp_.is_not_counted_and_deallocate_is_null())
return;
for(unsigned long i = 0;
i < size_; ++i)
data_[i].~value();
}
void

View File

@ -96,8 +96,10 @@ array_impl::
destroy(
storage_ptr const& sp) noexcept
{
if(tab_ && sp->need_free())
destroy_impl(sp);
if(! tab_ ||
sp.is_not_counted_and_deallocate_is_null())
return;
destroy_impl(sp);
}
} // detail

View File

@ -132,10 +132,12 @@ destroy(
key_value_pair* p,
std::size_t n) noexcept
{
// VFALCO We check need_free here even
// VFALCO We check again here even
// though some callers already check it.
if( n == 0 ||
! p->value().storage()->need_free())
if(n == 0 || ! p)
return;
auto const& sp = p->value().storage();
if(sp.is_not_counted_and_deallocate_is_null())
return;
p += n;
while(n--)

View File

@ -53,7 +53,7 @@ public:
destroy(storage_ptr const& sp) noexcept
{
if( tab_ == nullptr ||
! sp->need_free())
sp.is_not_counted_and_deallocate_is_null())
return;
do_destroy(sp);
}

View File

@ -22,7 +22,6 @@ namespace boost {
namespace json {
class value;
class string_test;
namespace detail {

View File

@ -464,9 +464,10 @@ array::
destroy(
value* first, value* last) noexcept
{
if(sp_->need_free())
while(last != first)
(*--last).~value();
if(sp_.is_not_counted_and_deallocate_is_null())
return;
while(last != first)
(*--last).~value();
}
void

View File

@ -0,0 +1,136 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.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_MONOTONIC_RESOURCE_IPP
#define BOOST_JSON_IMPL_MONOTONIC_RESOURCE_IPP
#include <boost/json/monotonic_resource.hpp>
namespace boost {
namespace json {
struct monotonic_resource::block
{
std::size_t const size;
std::uintptr_t top;
block* next;
block(
std::size_t size_,
block* next_)
: size(size_)
, top(reinterpret_cast<
std::uintptr_t>(this+1))
, next(next_)
{
}
void*
alloc(
std::size_t n,
std::size_t align) noexcept
{
// must be power of 2
BOOST_ASSERT(
(align & (align - 1)) == 0);
auto i = top;
if((i & (align - 1)) != 0)
i = (i | (align - 1)) + 1;
if(i + n > (reinterpret_cast<
std::uintptr_t>(this+1) + size))
return nullptr;
top = i + n;
return reinterpret_cast<void*>(i);
}
};
auto
monotonic_resource::
alloc_block(std::size_t size) ->
block&
{
auto const n = (
size + sizeof(block) - 1) /
sizeof(block);
auto const bytes =
(n + 1) * sizeof(block);
auto& b = *::new(
::operator new(bytes)) block(
n * sizeof(block), head_);
head_ = &b;
return b;
}
monotonic_resource::
~monotonic_resource()
{
for(auto b = head_; b;)
{
auto next = b->next;
::operator delete(b);
b = next;
}
}
monotonic_resource::
monotonic_resource() noexcept
: block_size_(64 * 1024)
{
}
monotonic_resource::
monotonic_resource(
std::size_t block_size) noexcept
: block_size_(block_size)
{
}
void*
monotonic_resource::
do_allocate(
std::size_t n,
std::size_t align)
{
if(head_)
{
auto p = head_->alloc(n, align);
if(p)
return p;
}
if(n > block_size_ - 2 * align)
alloc_block(n + 2 * align);
else
alloc_block(block_size_);
auto p = head_->alloc(n, align);
BOOST_ASSERT(p);
return p;
}
void
monotonic_resource::
do_deallocate(
void*,
std::size_t,
std::size_t)
{
// nothing
}
bool
monotonic_resource::
do_is_equal(
memory_resource const& mr) const noexcept
{
return false;
}
} // json
} // boost
#endif

View File

@ -1,86 +0,0 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.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 <new>
#include <utility>
namespace boost {
namespace json {
//----------------------------------------------------------
namespace detail {
struct default_impl
{
static
constexpr
std::uint64_t
id = 0x3b88990852d58ae4;
static
constexpr
bool
need_free = true;
void*
allocate(
std::size_t n,
std::size_t)
{
return ::operator new(n);
}
void
deallocate(
void* p,
std::size_t,
std::size_t) noexcept
{
::operator delete(p);
}
};
} // detail
//----------------------------------------------------------
storage*
storage_ptr::
get() const noexcept
{
BOOST_JSON_REQUIRE_CONST_INIT
static scoped_storage<
detail::default_impl> ss;
return p_ ? p_ : &ss.impl_;
}
//----------------------------------------------------------
template<class Storage, class... Args>
storage_ptr
make_storage(Args&&... args)
{
// If this generates an error, it means that your
// type `Storage` does not meet the named requirements.
//
static_assert(is_storage<Storage>::value,
"Storage requirements not met");
return storage_ptr(new
detail::storage_impl<Storage>(true,
std::forward<Args>(args)...));
}
} // json
} // boost
#endif

View File

@ -371,8 +371,9 @@ key_value_pair::
~key_value_pair()
{
auto const& sp = value_.storage();
if(sp->need_free())
sp->deallocate(key_, len_ + 1, 1);
if(sp.is_not_counted_and_deallocate_is_null())
return;
sp->deallocate(key_, len_ + 1, 1);
}
key_value_pair::

View File

@ -0,0 +1,80 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.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_MONOTONIC_RESOURCE_HPP
#define BOOST_JSON_MONOTONIC_RESOURCE_HPP
#include <boost/json/config.hpp>
#include <boost/json/storage_ptr.hpp>
namespace boost {
namespace json {
/** A fast memory resource using many large fixed size blocks.
*/
class monotonic_resource final
: public memory_resource
{
struct block;
std::size_t const block_size_ = 64 * 1024;
block* head_ = nullptr;
inline block& alloc_block(std::size_t size);
public:
BOOST_JSON_DECL
~monotonic_resource();
BOOST_JSON_DECL
monotonic_resource() noexcept;
BOOST_JSON_DECL
monotonic_resource(
std::size_t block_size) noexcept;
BOOST_JSON_DECL
void*
do_allocate(
std::size_t n,
std::size_t align) override;
BOOST_JSON_DECL
void
do_deallocate(
void* p,
std::size_t n,
std::size_t align) override;
BOOST_JSON_DECL
bool
do_is_equal(
memory_resource const& mr) const noexcept override;
};
template<>
struct is_deallocate_null<
monotonic_resource>
{
static
constexpr
bool
deallocate_is_null() noexcept
{
return true;
}
};
} // json
} // boost
#ifdef BOOST_JSON_HEADER_ONLY
#include <boost/json/impl/monotonic_resource.ipp>
#endif
#endif

View File

@ -11,7 +11,7 @@
#define BOOST_JSON_PARSER_HPP
#include <boost/json/config.hpp>
#include <boost/json/storage.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/json/value.hpp>
#include <boost/json/string.hpp>
#include <boost/json/detail/basic_parser.hpp>

View File

@ -1,139 +0,0 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.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_POOL_HPP
#define BOOST_JSON_POOL_HPP
#include <boost/json/config.hpp>
#include <boost/json/storage.hpp>
#include <cstdint>
#include <new>
namespace boost {
namespace json {
/** A storage which uses a multiple fixed size blocks
*/
class pool
{
struct block
{
std::size_t const size;
std::uintptr_t top;
block* next;
block(
std::size_t size_,
block* next_)
: size(size_)
, top(reinterpret_cast<
std::uintptr_t>(this+1))
, next(next_)
{
}
void*
alloc(
std::size_t n,
std::size_t align) noexcept
{
// must be power of 2
BOOST_ASSERT(
(align & (align - 1)) == 0);
auto i = top;
if((i & (align - 1)) != 0)
i = (i | (align - 1)) + 1;
if(i + n > (reinterpret_cast<
std::uintptr_t>(this+1) + size))
return nullptr;
top = i + n;
return reinterpret_cast<void*>(i);
}
};
std::size_t const block_size_;
block* head_ = nullptr;
public:
static
constexpr
std::uint64_t
id = 0;
static
constexpr
bool
need_free = false;
~pool()
{
for(auto b = head_; b;)
{
auto next = b->next;
::operator delete(b);
b = next;
}
}
explicit
pool(
std::size_t block_size = 64 * 1024)
: block_size_(block_size)
{
}
void*
allocate(
std::size_t n,
std::size_t align)
{
if(head_)
{
auto p = head_->alloc(n, align);
if(p)
return p;
}
if(n > block_size_ - 2 * align)
alloc_block(n + 2 * align);
else
alloc_block(block_size_);
auto p = head_->alloc(n, align);
BOOST_ASSERT(p);
return p;
}
void
deallocate(
void*,
std::size_t,
std::size_t) noexcept
{
}
private:
block&
alloc_block(std::size_t size)
{
auto const n = (
size + sizeof(block) - 1) /
sizeof(block);
auto const bytes =
(n + 1) * sizeof(block);
auto& b = *::new(
::operator new(bytes)) block(
n * sizeof(block), head_);
head_ = &b;
return b;
}
};
} // json
} // boost
#endif

View File

@ -29,6 +29,7 @@ in a translation unit of the program.
#include <boost/json/impl/array.ipp>
#include <boost/json/impl/error.ipp>
#include <boost/json/impl/monotonic_resource.ipp>
#include <boost/json/impl/object.ipp>
#include <boost/json/impl/parser.ipp>
#include <boost/json/impl/serializer.ipp>

View File

@ -1,219 +0,0 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.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_HPP
#define BOOST_JSON_STORAGE_HPP
#include <boost/json/config.hpp>
#include <atomic>
#include <cstddef>
#include <stddef.h> // for ::max_align_t
namespace boost {
namespace json {
namespace detail {
template<class T>
struct storage_impl;
} // detail
/** Abstract interface to a memory resource used with JSON.
This interface is modeled similarly to
`std::pmr::memory_resource` with some notable
differences:
@li Instances may be reference counted.
@li The function @ref is_equal is implemented
as a non-virtual member which does not require
RTTI or `typeinfo`.
@li The function @ref need_free is provided to
allow the implementation to optionally inform
callers that calls to deallocate memory are not
required.
*/
class storage
{
std::atomic<std::size_t> refs_{ 1 };
std::uint64_t const id_ = 0;
bool const need_free_ ;
bool const counted_;
friend class storage_ptr;
template<class T>
friend struct detail::storage_impl;
// Choose a unique 64-bit random number from here:
// https://www.random.org/cgi-bin/randbyte?nbytes=8&format=h
constexpr
explicit
storage(
unsigned long long id,
bool need_free,
bool counted) noexcept
: id_(id)
, need_free_(need_free || counted)
, counted_(counted)
{
}
public:
virtual
~storage() = default;
/** Returns `true` if calls to `deallocate` are required.
*/
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_);
}
/** Allocate memory.
Allocates storage with for space of at
least `bytes` octets. The returned storage
is aligned to the specified alignment is
supported, and to `alignof(max_align_t)`
otherwise.
@throw std::exception if storage of the
requested size and alignment cannot be
obtained.
*/
BOOST_JSON_NODISCARD
virtual
void*
allocate(
std::size_t n,
std::size_t align =
alignof(max_align_t)) = 0;
/** Deallocate memory.
Deallocates the storage pointed to by `p`.
@par Preconditions
`p` was returned by a prior call to
`u.allocate( bytes, align )` where
`this->is_equal(u)`, and the storage
`p` points to was not previously
deallocated.
*/
virtual
void
deallocate(
void* p,
std::size_t n,
std::size_t align =
alignof(max_align_t)) = 0;
friend
bool
operator==(
storage const& lhs,
storage const& rhs) noexcept;
friend
bool
operator!=(
storage const& lhs,
storage const& rhs) noexcept;
};
/** Return true if lhs equals rhs.
*/
inline
bool
operator==(
storage const& lhs,
storage const& rhs) noexcept
{
return lhs.is_equal(rhs);
}
/** Return true if lhs does not equal rhs.
*/
inline
bool
operator!=(
storage const& lhs,
storage const& rhs) noexcept
{
return ! lhs.is_equal(rhs);
}
/** Metafunction to determine if a type meets the requirements of Storage.
This type alias is `std::true_type` if the type
`T` satisfies the syntactic requirements of <em>Storage</em>,
otherwise it is equivalent to `std::false_type`.
@par Exemplar
For the following declaration,
`is_storage<Storage>` is `std::true_type`:
@code
struct Storage
{
static constexpr std::uint64_t id = 0;
static_constexpr bool need_free = true;
void* allocate( std::size_t bytes, std::size_t align );
void deallocate( void* p, std::size_t bytes, std::size_t align );
};
@endcode
@tparam T The type to check.
*/
#if BOOST_JSON_DOCS
template<class T>
using is_storage = __see_below__;
#else
template<class T, class = void>
struct is_storage : std::false_type {};
template<class T>
struct is_storage<T, detail::void_t<decltype(
std::declval<std::uint64_t&>() = T::id,
std::declval<bool&>() = T::need_free,
std::declval<void*&>() =
std::declval<T&>().allocate(
std::declval<std::size_t>(),
std::declval<std::size_t>()),
std::declval<T&>().deallocate(
std::declval<void*>(),
std::declval<std::size_t>(),
std::declval<std::size_t>())
) > > : std::true_type
{
};
#endif
} // json
} // boost
#include <boost/json/impl/storage.hpp>
#endif

View File

@ -11,14 +11,29 @@
#define BOOST_JSON_STORAGE_PTR_HPP
#include <boost/json/config.hpp>
#include <boost/json/storage.hpp>
#include <boost/json/detail/counted_resource.hpp>
#include <boost/json/detail/default_resource.hpp>
#include <cstddef>
#include <cstdint>
#include <new>
#include <type_traits>
#include <utility>
namespace boost {
namespace json {
template<class T>
struct is_deallocate_null
{
static
constexpr
bool
deallocate_is_null() noexcept
{
return false;
}
};
/** Manages a type-erased storage object.
This container is used to hold a shared reference
@ -26,75 +41,60 @@ namespace json {
*/
class storage_ptr
{
template<class T>
friend class scoped_storage;
friend struct detail::counted_resource;
using counted_resource =
detail::counted_resource;
storage* p_ = nullptr;
std::uintptr_t i_ = 0;
static
memory_resource*
get_default() noexcept
{
BOOST_JSON_REQUIRE_CONST_INIT
static detail::default_resource impl;
return &impl;
}
counted_resource*
get_counted() const noexcept
{
return reinterpret_cast<
counted_resource*>(i_ & ~3);
}
inline
void
addref() const noexcept
{
if(p_ && p_->counted_)
++p_->refs_;
if(is_counted())
++get_counted()->refs;
}
inline
void
release() const noexcept
{
if( p_ && p_->counted_ &&
--p_->refs_ == 0)
delete p_;
if(is_counted())
{
auto const p = get_counted();
if(--p->refs == 0)
delete p;
}
}
explicit
template<class T>
storage_ptr(
storage* p) noexcept
: p_(p)
detail::counted_resource_impl<T>* p) noexcept
: i_(reinterpret_cast<
std::uintptr_t>(p) + 1 +
(is_deallocate_null<
T>::deallocate_is_null() ? 2 : 0))
{
BOOST_ASSERT(p);
}
public:
/** Default constructor.
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;
/** Construct a pointer to default storage.
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(
std::nullptr_t) noexcept
: storage_ptr()
{
}
/** Destructor.
This releases the pointed-to storage. If the
@ -117,6 +117,63 @@ public:
release();
}
/** Default constructor.
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() noexcept
: i_(0)
{
}
/** Construct a pointer to default storage.
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(
std::nullptr_t) noexcept
: storage_ptr()
{
}
/** Construct a pointer to a memory resource.
*/
template<class T, class =
typename std::enable_if<
std::is_convertible<
T*, memory_resource*>::value>>
storage_ptr(T* p) noexcept
: i_(reinterpret_cast<
std::uintptr_t>(p) +
(is_deallocate_null<
T>::deallocate_is_null() ?
2 : 0))
{
BOOST_ASSERT(p);
}
/** Move constructor.
After construction, the moved-from object
@ -134,8 +191,8 @@ public:
*/
storage_ptr(
storage_ptr&& other) noexcept
: p_(detail::exchange(
other.p_, nullptr))
: i_(detail::exchange(
other.i_, 0))
{
}
@ -156,7 +213,7 @@ public:
*/
storage_ptr(
storage_ptr const& other) noexcept
: p_(other.p_)
: i_(other.i_)
{
addref();
}
@ -184,9 +241,8 @@ public:
storage_ptr&& other) noexcept
{
release();
p_ = detail::exchange(
other.p_,
nullptr);
i_ = detail::exchange(
other.i_, 0);
return *this;
}
@ -212,10 +268,34 @@ public:
{
other.addref();
release();
p_ = other.p_;
i_ = other.i_;
return *this;
}
/** Return `true` if the storage pointer has shared ownership of the memory resource.
*/
bool
is_counted() const noexcept
{
return (i_ & 1) != 0;
}
/** Return `true` if the memory resource does not require deallocate.
*/
bool
deallocate_is_null() const noexcept
{
return (i_ & 2) != 0;
}
/** Return `true` if ownership of the memory resource is not shared and deallocate is null.
*/
bool
is_not_counted_and_deallocate_is_null() const noexcept
{
return (i_ & 3) == 2;
}
/** Return a pointer to the storage object.
@par Complexity
@ -226,9 +306,14 @@ public:
No-throw guarantee.
*/
inline
storage*
get() const noexcept;
memory_resource*
get() const noexcept
{
if(i_ != 0)
return reinterpret_cast<
memory_resource*>(i_ & ~3);
return get_default();
}
/** Return a pointer to the storage object.
@ -240,7 +325,7 @@ public:
No-throw guarantee.
*/
storage*
memory_resource*
operator->() const noexcept
{
return get();
@ -260,7 +345,7 @@ public:
No-throw guarantee.
*/
storage&
memory_resource&
operator*() const noexcept
{
return *get();
@ -269,16 +354,16 @@ public:
template<class U, class... Args>
friend
storage_ptr
make_storage(Args&&... args);
make_counted_resource(Args&&... args);
};
/** Create a new, counted storage object and return a pointer to it.
/** Create a new, counted memory resource and return a pointer to it.
This functions similarly to `make_shared`.
@par Mandates
@code
is_storage<T>::value == true
std::is_base_of< memory_resource, T >::value == true
@endcode
@par Complexity
@ -291,11 +376,22 @@ public:
@param args Parameters forwarded to the constructor of `T`.
@tparam T the type of the storage object to create.
@tparam T the concrete type of the memory resource to create.
*/
template<class T, class... Args>
storage_ptr
make_storage(Args&&... args);
make_counted_resource(Args&&... args)
{
// If this generates an error, it means that your
// type `Storage` does not meet the named requirements.
//
BOOST_STATIC_ASSERT(
std::is_base_of<
memory_resource, T>::value);
return storage_ptr(new
detail::counted_resource_impl<T>(
std::forward<Args>(args)...));
}
/** Return true if lhs equals rhs.
@ -327,101 +423,7 @@ operator!=(
return lhs.get() != rhs.get();
}
//----------------------------------------------------------
/** A wrapper to provide deterministic lifetime to a @ref storage object.
This wrapper enables the caller to construct a
@ref storage objects whose lifetime is controlled
by the lifetime of the wrapper instead of a
reference counted.
@par Example
This example creates a @ref pool with
bounded lifetime and uses it to parse a JSON,
then print it to `std::cout`.
@code
{
scoped_storage<pool> sp;
auto jv = parse( str, sp );
}
@endcode
*/
template<class Storage>
class scoped_storage
{
friend storage_ptr;
detail::storage_impl<Storage> impl_;
// If this generates an error, it means that your
// type `Storage` does not meet the named requirements.
//
static_assert(is_storage<Storage>::value,
"Storage requirements not met");
public:
/** Constructor.
@par Exception Safety
Any exceptions thrown by `Storage::Storage`.
@param args Arguments forwarded to the
constructor of the storage object.
*/
template<class... Args>
constexpr
explicit
scoped_storage(Args&&... args)
: impl_(false,
std::forward<Args>(args)...)
{
}
/** Return a pointer to the Storage object.
*/
Storage*
get() noexcept
{
return &impl_;
}
/** Return a pointer to the Storage object.
*/
Storage*
operator->() noexcept
{
return &impl_.t;
}
/** Return a storage pointer to the Storage object.
*/
storage_ptr
get_storage_ptr() noexcept
{
return storage_ptr(&impl_);
}
/** Implicit conversion to @ref storage_ptr.
This function allows `*this` to be passed
wherever a @ref storage_ptr is expected.
*/
operator storage_ptr() noexcept
{
return get_storage_ptr();
}
};
} // json
} // boost
#include <boost/json/impl/storage_ptr.hpp>
#endif

View File

@ -30,13 +30,12 @@ local SOURCES =
error.cpp
json.cpp
kind.cpp
monotonic_resource.cpp
number_cast.cpp
object.cpp
parser.cpp
pool.cpp
serializer.cpp
snippets.cpp
storage.cpp
storage_ptr.cpp
string.cpp
to_value.cpp

View File

@ -8,7 +8,7 @@
//
// Test that header file is self-contained.
#include <boost/json/pool.hpp>
#include <boost/json/monotonic_resource.hpp>
#include <boost/json/parser.hpp>
@ -46,7 +46,7 @@ R"xx({
}
}
})xx"
, make_storage<pool>());
, make_counted_resource<monotonic_resource>());
BOOST_TEST_PASS();
}
@ -57,7 +57,7 @@ R"xx({
}
};
TEST_SUITE(pool_test, "boost.json.pool");
TEST_SUITE(pool_test, "boost.json.monotonic_resource");
} // json
} // boost

View File

@ -10,7 +10,7 @@
// Test that header file is self-contained.
#include <boost/json/object.hpp>
#include <boost/json/pool.hpp>
#include <boost/json/monotonic_resource.hpp>
#include <cmath>
#include <type_traits>
@ -256,7 +256,7 @@ public:
// object(pilfered<object>)
{
auto const sp =
make_storage<unique_storage>();
make_counted_resource<unique_resource>();
object o1({
{"a", 1},
{"b", true},
@ -271,7 +271,7 @@ public:
check(o2, 3);
}
auto const sp = make_storage<unique_storage>();
auto const sp = make_counted_resource<unique_resource>();
auto const sp0 = storage_ptr{};
// object(object const&)
@ -929,12 +929,12 @@ public:
// destroy key_value_pair array with need_free=false
{
scoped_storage<pool> sp;
monotonic_resource mr;
object o({
{"a", 1},
{"b", true},
{"b", {1,2,3}},
{"c", "hello"}}, sp);
{"c", "hello"}}, &mr);
}
}

View File

@ -10,7 +10,7 @@
// Test that header file is self-contained.
#include <boost/json/parser.hpp>
#include <boost/json/pool.hpp>
#include <boost/json/monotonic_resource.hpp>
#include <boost/json/serializer.hpp>
#include <sstream>
@ -99,12 +99,11 @@ public:
for(std::size_t i = 1;
i < s.size(); ++i)
{
scoped_storage<
fail_storage> ss;
ss->fail_max = 0;
fail_resource mr;
mr.fail_max = 0;
parser p;
error_code ec;
p.start(ss);
p.start(&mr);
p.write(s.data(), i, ec);
if(BOOST_TEST(! ec))
p.write(
@ -762,16 +761,16 @@ public:
{
{
error_code ec;
scoped_storage<pool> sp;
auto jv = parse(js, ec, sp);
monotonic_resource mr;
auto jv = parse(js, ec, &mr);
BOOST_TEST(! ec);
check_round_trip(jv);
}
{
error_code ec;
scoped_storage<pool> sp;
auto jv = parse("xxx", ec, sp);
monotonic_resource mr;
auto jv = parse("xxx", ec, &mr);
BOOST_TEST(ec);
BOOST_TEST(jv.is_null());
}
@ -795,15 +794,15 @@ public:
// parse(string_view, storage_ptr)
{
{
scoped_storage<pool> sp;
check_round_trip(parse(js, sp));
monotonic_resource mr;
check_round_trip(parse(js, &mr));
}
{
scoped_storage<pool> sp;
monotonic_resource mr;
value jv;
BOOST_TEST_THROWS(
jv = parse("xxx", sp),
jv = parse("xxx", &mr),
system_error);
}
}

View File

@ -27,7 +27,7 @@ usingStrings()
string str1; // empty string, default storage
string str2( make_storage<pool>() ); // empty string, pool storage
string str2( make_counted_resource<monotonic_resource>() ); // empty string, pool storage
//]
}
@ -95,7 +95,7 @@ usingArrays()
array arr1; // empty array, default storage
array arr2( make_storage<pool>() ); // empty array, pool storage
array arr2( make_counted_resource<monotonic_resource>() ); // empty array, pool storage
//]
}
@ -138,7 +138,7 @@ usingObjects()
object obj1; // empty object, default storage
object obj2( make_storage<pool>() ); // empty object, pool storage
object obj2( make_counted_resource<monotonic_resource>() ); // empty object, pool storage
//]
}
@ -202,7 +202,7 @@ usingStorage()
value parse_fast( string_view s )
{
return parse( s, make_storage<pool>() );
return parse( s, make_counted_resource<monotonic_resource>() );
}
//]
@ -213,9 +213,9 @@ void do_json(value const&) {}
void do_rpc( string_view cmd )
{
scoped_storage<pool> sp;
monotonic_resource mr;
value const jv = parse( cmd, sp );
value const jv = parse( cmd, &mr );
do_json( jv );
}
@ -224,20 +224,10 @@ void do_rpc( string_view cmd )
//[snippet_storage_4
struct Storage
{
static constexpr std::uint64_t id = 0;
static constexpr bool need_free = true;
void* allocate( std::size_t bytes, std::size_t align );
void deallocate( void* p, std::size_t bytes, std::size_t align );
};
// TODO
//]
BOOST_STATIC_ASSERT(
is_storage<Storage>::value);
//----------------------------------------------------------
void
@ -263,7 +253,7 @@ usingParsing()
{
//[snippet_parsing_3
value jv = parse( "[1,2,3,4,5]", make_storage<pool>() );
value jv = parse( "[1,2,3,4,5]", make_counted_resource<monotonic_resource>() );
//]
}
@ -322,10 +312,10 @@ usingParsing()
error_code ec;
// Declare a new, scoped instance of the block storage
scoped_storage< pool > sp;
monotonic_resource mr;
// Use the scoped instance for the parsed value
p.start( sp );
p.start( &mr );
// Write the entire JSON
p.write( "[1,2,3,4,5]", 11, ec );

View File

@ -1,11 +0,0 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.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.hpp>

View File

@ -19,60 +19,36 @@ namespace json {
class storage_ptr_test
{
public:
struct not_storage
{
void*
allocate(
std::size_t,
std::size_t)
{
return nullptr;
}
void
deallocate(
void*,
std::size_t,
std::size_t) noexcept
{
}
};
BOOST_STATIC_ASSERT(
! is_storage<not_storage>::value);
struct throwing
: memory_resource
{
static
constexpr
std::uint64_t
id = 0;
static
constexpr
bool
need_free = true;
throwing()
{
throw std::exception{};
}
void*
allocate(
do_allocate(
std::size_t,
std::size_t)
std::size_t) override
{
return nullptr;
}
void
deallocate(
do_deallocate(
void*,
std::size_t,
std::size_t) noexcept
std::size_t) noexcept override
{
}
bool
do_is_equal(
memory_resource const&) const noexcept override
{
return false;
}
};
void
@ -80,7 +56,7 @@ public:
{
auto const dsp = storage_ptr{};
auto const usp =
make_storage<unique_storage>();
make_counted_resource<unique_resource>();
// ~storage_ptr()
{
@ -145,7 +121,7 @@ public:
// exception in make_storage
{
BOOST_TEST_THROWS(
make_storage<throwing>(),
make_counted_resource<throwing>(),
std::exception);
}
}

View File

@ -92,7 +92,7 @@ public:
// string(storage_ptr)
{
auto const sp =
make_storage<unique_storage>();
make_counted_resource<unique_resource>();
string s(sp);
BOOST_TEST(s.empty());
BOOST_TEST(*s.storage() == *sp.get());

View File

@ -38,31 +38,22 @@ struct test_failure : std::exception
}
};
struct fail_storage
struct fail_resource
: memory_resource
{
static
constexpr
std::uint64_t
id = 0;
static
constexpr
bool
need_free = true;
std::size_t fail_max = 0;
std::size_t fail = 0;
std::size_t nalloc = 0;
~fail_storage()
~fail_resource()
{
BOOST_TEST(nalloc == 0);
}
void*
allocate(
do_allocate(
std::size_t n,
std::size_t)
std::size_t) override
{
if(++fail == fail_max)
{
@ -76,28 +67,35 @@ struct fail_storage
}
void
deallocate(
do_deallocate(
void* p,
std::size_t,
std::size_t) noexcept
std::size_t) noexcept override
{
if(BOOST_TEST(nalloc > 0))
--nalloc;
::operator delete(p);
}
bool
do_is_equal(
memory_resource const& mr) const noexcept override
{
return false;
}
};
template<class F>
void
fail_loop(F&& f)
{
scoped_storage<fail_storage> ss;
ss->fail_max = 1;
while(ss->fail < 200)
fail_resource ss;
ss.fail_max = 1;
while(ss.fail < 200)
{
try
{
f(ss);
f(&ss);
}
catch(test_failure const&)
{
@ -105,39 +103,39 @@ fail_loop(F&& f)
}
break;
}
BOOST_TEST(ss->fail < 200);
BOOST_TEST(ss.fail < 200);
}
//----------------------------------------------------------
struct unique_storage
struct unique_resource
: memory_resource
{
static
constexpr
std::uint64_t
id = 0;
static
constexpr
bool
need_free = true;
unique_resource() = default;
void*
allocate(
do_allocate(
std::size_t n,
std::size_t)
std::size_t) override
{
return ::operator new(n);
}
void
deallocate(
do_deallocate(
void* p,
std::size_t,
std::size_t) noexcept
std::size_t) noexcept override
{
return ::operator delete(p);
}
bool
do_is_equal(
memory_resource const& mr) const noexcept override
{
return false;
}
};
//----------------------------------------------------------

View File

@ -43,7 +43,7 @@ public:
testSpecial()
{
auto dsp = storage_ptr{};
auto sp = make_storage<unique_storage>();
auto sp = make_counted_resource<unique_resource>();
// ~value()
{
@ -468,7 +468,7 @@ public:
testConversion()
{
auto dsp = storage_ptr{};
auto sp = make_storage<unique_storage>();
auto sp = make_counted_resource<unique_resource>();
// value(object)
// value(object, storage_ptr)
@ -1050,7 +1050,7 @@ public:
testGetStorage()
{
auto const sp =
make_storage<unique_storage>();
make_counted_resource<unique_resource>();
value obj(object{}, sp);
value arr(array{}, sp);