mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-08-01 04:57:46 +00:00
Added thirdparty: boost library
This commit is contained in:
+590
@@ -0,0 +1,590 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_ARRAY_HPP
|
||||
#define BOOST_JSON_IMPL_ARRAY_HPP
|
||||
|
||||
#include <boost/json/value.hpp>
|
||||
#include <boost/json/detail/except.hpp>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
struct alignas(value)
|
||||
array::table
|
||||
{
|
||||
std::uint32_t size = 0;
|
||||
std::uint32_t capacity = 0;
|
||||
|
||||
constexpr table();
|
||||
|
||||
value&
|
||||
operator[](std::size_t pos) noexcept
|
||||
{
|
||||
return (reinterpret_cast<
|
||||
value*>(this + 1))[pos];
|
||||
}
|
||||
|
||||
BOOST_JSON_DECL
|
||||
static
|
||||
table*
|
||||
allocate(
|
||||
std::size_t capacity,
|
||||
storage_ptr const& sp);
|
||||
|
||||
BOOST_JSON_DECL
|
||||
static
|
||||
void
|
||||
deallocate(
|
||||
table* p,
|
||||
storage_ptr const& sp);
|
||||
};
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
class array::revert_construct
|
||||
{
|
||||
array* arr_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
revert_construct(
|
||||
array& arr) noexcept
|
||||
: arr_(&arr)
|
||||
{
|
||||
}
|
||||
|
||||
~revert_construct()
|
||||
{
|
||||
if(! arr_)
|
||||
return;
|
||||
arr_->destroy();
|
||||
}
|
||||
|
||||
void
|
||||
commit() noexcept
|
||||
{
|
||||
arr_ = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
class array::revert_insert
|
||||
{
|
||||
array* arr_;
|
||||
std::size_t const i_;
|
||||
std::size_t const n_;
|
||||
|
||||
public:
|
||||
value* p;
|
||||
|
||||
BOOST_JSON_DECL
|
||||
revert_insert(
|
||||
const_iterator pos,
|
||||
std::size_t n,
|
||||
array& arr);
|
||||
|
||||
BOOST_JSON_DECL
|
||||
~revert_insert();
|
||||
|
||||
value*
|
||||
commit() noexcept
|
||||
{
|
||||
auto it =
|
||||
arr_->data() + i_;
|
||||
arr_ = nullptr;
|
||||
return it;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
array::
|
||||
relocate(
|
||||
value* dest,
|
||||
value* src,
|
||||
std::size_t n) noexcept
|
||||
{
|
||||
if(n == 0)
|
||||
return;
|
||||
std::memmove(
|
||||
static_cast<void*>(dest),
|
||||
static_cast<void const*>(src),
|
||||
n * sizeof(value));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Construction
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
template<class InputIt, class>
|
||||
array::
|
||||
array(
|
||||
InputIt first, InputIt last,
|
||||
storage_ptr sp)
|
||||
: array(
|
||||
first, last,
|
||||
std::move(sp),
|
||||
iter_cat<InputIt>{})
|
||||
{
|
||||
BOOST_STATIC_ASSERT(
|
||||
std::is_constructible<value,
|
||||
decltype(*first)>::value);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Modifiers
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
template<class InputIt, class>
|
||||
auto
|
||||
array::
|
||||
insert(
|
||||
const_iterator pos,
|
||||
InputIt first, InputIt last) ->
|
||||
iterator
|
||||
{
|
||||
BOOST_STATIC_ASSERT(
|
||||
std::is_constructible<value,
|
||||
decltype(*first)>::value);
|
||||
return insert(pos, first, last,
|
||||
iter_cat<InputIt>{});
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
auto
|
||||
array::
|
||||
emplace(
|
||||
const_iterator pos,
|
||||
Arg&& arg) ->
|
||||
iterator
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
pos >= begin() &&
|
||||
pos <= end());
|
||||
value jv(
|
||||
std::forward<Arg>(arg),
|
||||
storage());
|
||||
return insert(pos, pilfer(jv));
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
value&
|
||||
array::
|
||||
emplace_back(Arg&& arg)
|
||||
{
|
||||
value jv(
|
||||
std::forward<Arg>(arg),
|
||||
storage());
|
||||
return push_back(pilfer(jv));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Element access
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
value&
|
||||
array::
|
||||
at(std::size_t pos) &
|
||||
{
|
||||
auto const& self = *this;
|
||||
return const_cast< value& >( self.at(pos) );
|
||||
}
|
||||
|
||||
value&&
|
||||
array::
|
||||
at(std::size_t pos) &&
|
||||
{
|
||||
return std::move( at(pos) );
|
||||
}
|
||||
|
||||
value const&
|
||||
array::
|
||||
at(std::size_t pos) const&
|
||||
{
|
||||
if(pos >= t_->size)
|
||||
{
|
||||
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
|
||||
detail::throw_system_error( error::out_of_range, &loc );
|
||||
}
|
||||
return (*t_)[pos];
|
||||
}
|
||||
|
||||
value&
|
||||
array::
|
||||
operator[](std::size_t pos) & noexcept
|
||||
{
|
||||
BOOST_ASSERT(pos < t_->size);
|
||||
return (*t_)[pos];
|
||||
}
|
||||
|
||||
value&&
|
||||
array::
|
||||
operator[](std::size_t pos) && noexcept
|
||||
{
|
||||
return std::move( (*this)[pos] );
|
||||
}
|
||||
|
||||
value const&
|
||||
array::
|
||||
operator[](std::size_t pos) const& noexcept
|
||||
{
|
||||
BOOST_ASSERT(pos < t_->size);
|
||||
return (*t_)[pos];
|
||||
}
|
||||
|
||||
value&
|
||||
array::
|
||||
front() & noexcept
|
||||
{
|
||||
BOOST_ASSERT(t_->size > 0);
|
||||
return (*t_)[0];
|
||||
}
|
||||
|
||||
value&&
|
||||
array::
|
||||
front() && noexcept
|
||||
{
|
||||
return std::move( front() );
|
||||
}
|
||||
|
||||
value const&
|
||||
array::
|
||||
front() const& noexcept
|
||||
{
|
||||
BOOST_ASSERT(t_->size > 0);
|
||||
return (*t_)[0];
|
||||
}
|
||||
|
||||
value&
|
||||
array::
|
||||
back() & noexcept
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
t_->size > 0);
|
||||
return (*t_)[t_->size - 1];
|
||||
}
|
||||
|
||||
value&&
|
||||
array::
|
||||
back() && noexcept
|
||||
{
|
||||
return std::move( back() );
|
||||
}
|
||||
|
||||
value const&
|
||||
array::
|
||||
back() const& noexcept
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
t_->size > 0);
|
||||
return (*t_)[t_->size - 1];
|
||||
}
|
||||
|
||||
value*
|
||||
array::
|
||||
data() noexcept
|
||||
{
|
||||
return &(*t_)[0];
|
||||
}
|
||||
|
||||
value const*
|
||||
array::
|
||||
data() const noexcept
|
||||
{
|
||||
return &(*t_)[0];
|
||||
}
|
||||
|
||||
value const*
|
||||
array::
|
||||
if_contains(
|
||||
std::size_t pos) const noexcept
|
||||
{
|
||||
if( pos < t_->size )
|
||||
return &(*t_)[pos];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
value*
|
||||
array::
|
||||
if_contains(
|
||||
std::size_t pos) noexcept
|
||||
{
|
||||
if( pos < t_->size )
|
||||
return &(*t_)[pos];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Iterators
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
auto
|
||||
array::
|
||||
begin() noexcept ->
|
||||
iterator
|
||||
{
|
||||
return &(*t_)[0];
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
begin() const noexcept ->
|
||||
const_iterator
|
||||
{
|
||||
return &(*t_)[0];
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
cbegin() const noexcept ->
|
||||
const_iterator
|
||||
{
|
||||
return &(*t_)[0];
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
end() noexcept ->
|
||||
iterator
|
||||
{
|
||||
return &(*t_)[t_->size];
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
end() const noexcept ->
|
||||
const_iterator
|
||||
{
|
||||
return &(*t_)[t_->size];
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
cend() const noexcept ->
|
||||
const_iterator
|
||||
{
|
||||
return &(*t_)[t_->size];
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
rbegin() noexcept ->
|
||||
reverse_iterator
|
||||
{
|
||||
return reverse_iterator(end());
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
rbegin() const noexcept ->
|
||||
const_reverse_iterator
|
||||
{
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
crbegin() const noexcept ->
|
||||
const_reverse_iterator
|
||||
{
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
rend() noexcept ->
|
||||
reverse_iterator
|
||||
{
|
||||
return reverse_iterator(begin());
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
rend() const noexcept ->
|
||||
const_reverse_iterator
|
||||
{
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
crend() const noexcept ->
|
||||
const_reverse_iterator
|
||||
{
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Capacity
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
std::size_t
|
||||
array::
|
||||
size() const noexcept
|
||||
{
|
||||
return t_->size;
|
||||
}
|
||||
|
||||
constexpr
|
||||
std::size_t
|
||||
array::
|
||||
max_size() noexcept
|
||||
{
|
||||
// max_size depends on the address model
|
||||
using min = std::integral_constant<std::size_t,
|
||||
(std::size_t(-1) - sizeof(table)) / sizeof(value)>;
|
||||
return min::value < BOOST_JSON_MAX_STRUCTURED_SIZE ?
|
||||
min::value : BOOST_JSON_MAX_STRUCTURED_SIZE;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
array::
|
||||
capacity() const noexcept
|
||||
{
|
||||
return t_->capacity;
|
||||
}
|
||||
|
||||
bool
|
||||
array::
|
||||
empty() const noexcept
|
||||
{
|
||||
return t_->size == 0;
|
||||
}
|
||||
|
||||
void
|
||||
array::
|
||||
reserve(
|
||||
std::size_t new_capacity)
|
||||
{
|
||||
// never shrink
|
||||
if(new_capacity <= t_->capacity)
|
||||
return;
|
||||
reserve_impl(new_capacity);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// private
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
template<class InputIt>
|
||||
array::
|
||||
array(
|
||||
InputIt first, InputIt last,
|
||||
storage_ptr sp,
|
||||
std::input_iterator_tag)
|
||||
: sp_(std::move(sp))
|
||||
, t_(&empty_)
|
||||
{
|
||||
revert_construct r(*this);
|
||||
while(first != last)
|
||||
{
|
||||
reserve(size() + 1);
|
||||
::new(end()) value(
|
||||
*first++, sp_);
|
||||
++t_->size;
|
||||
}
|
||||
r.commit();
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
array::
|
||||
array(
|
||||
InputIt first, InputIt last,
|
||||
storage_ptr sp,
|
||||
std::forward_iterator_tag)
|
||||
: sp_(std::move(sp))
|
||||
{
|
||||
std::size_t n =
|
||||
std::distance(first, last);
|
||||
if( n == 0 )
|
||||
{
|
||||
t_ = &empty_;
|
||||
return;
|
||||
}
|
||||
|
||||
t_ = table::allocate(n, sp_);
|
||||
t_->size = 0;
|
||||
revert_construct r(*this);
|
||||
while(n--)
|
||||
{
|
||||
::new(end()) value(
|
||||
*first++, sp_);
|
||||
++t_->size;
|
||||
}
|
||||
r.commit();
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
auto
|
||||
array::
|
||||
insert(
|
||||
const_iterator pos,
|
||||
InputIt first, InputIt last,
|
||||
std::input_iterator_tag) ->
|
||||
iterator
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
pos >= begin() && pos <= end());
|
||||
if(first == last)
|
||||
return data() + (pos - data());
|
||||
array temp(first, last, sp_);
|
||||
revert_insert r(
|
||||
pos, temp.size(), *this);
|
||||
relocate(
|
||||
r.p,
|
||||
temp.data(),
|
||||
temp.size());
|
||||
temp.t_->size = 0;
|
||||
return r.commit();
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
auto
|
||||
array::
|
||||
insert(
|
||||
const_iterator pos,
|
||||
InputIt first, InputIt last,
|
||||
std::forward_iterator_tag) ->
|
||||
iterator
|
||||
{
|
||||
std::size_t n =
|
||||
std::distance(first, last);
|
||||
revert_insert r(pos, n, *this);
|
||||
while(n--)
|
||||
{
|
||||
::new(r.p) value(*first++);
|
||||
++r.p;
|
||||
}
|
||||
return r.commit();
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+776
@@ -0,0 +1,776 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_ARRAY_IPP
|
||||
#define BOOST_JSON_IMPL_ARRAY_IPP
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/json/array.hpp>
|
||||
#include <boost/json/pilfer.hpp>
|
||||
#include <boost/json/detail/except.hpp>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
constexpr array::table::table() = default;
|
||||
|
||||
// empty arrays point here
|
||||
BOOST_JSON_REQUIRE_CONST_INIT
|
||||
array::table array::empty_;
|
||||
|
||||
auto
|
||||
array::
|
||||
table::
|
||||
allocate(
|
||||
std::size_t capacity,
|
||||
storage_ptr const& sp) ->
|
||||
table*
|
||||
{
|
||||
BOOST_ASSERT(capacity > 0);
|
||||
if(capacity > array::max_size())
|
||||
{
|
||||
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
|
||||
detail::throw_system_error( error::array_too_large, &loc );
|
||||
}
|
||||
auto p = reinterpret_cast<
|
||||
table*>(sp->allocate(
|
||||
sizeof(table) +
|
||||
capacity * sizeof(value),
|
||||
alignof(value)));
|
||||
p->capacity = static_cast<
|
||||
std::uint32_t>(capacity);
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
array::
|
||||
table::
|
||||
deallocate(
|
||||
table* p,
|
||||
storage_ptr const& sp)
|
||||
{
|
||||
if(p->capacity == 0)
|
||||
return;
|
||||
sp->deallocate(p,
|
||||
sizeof(table) +
|
||||
p->capacity * sizeof(value),
|
||||
alignof(value));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
array::
|
||||
revert_insert::
|
||||
revert_insert(
|
||||
const_iterator pos,
|
||||
std::size_t n,
|
||||
array& arr)
|
||||
: arr_(&arr)
|
||||
, i_(pos - arr_->data())
|
||||
, n_(n)
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
pos >= arr_->begin() &&
|
||||
pos <= arr_->end());
|
||||
if( n_ <= arr_->capacity() -
|
||||
arr_->size())
|
||||
{
|
||||
// fast path
|
||||
p = arr_->data() + i_;
|
||||
if(n_ == 0)
|
||||
return;
|
||||
relocate(
|
||||
p + n_,
|
||||
p,
|
||||
arr_->size() - i_);
|
||||
arr_->t_->size = static_cast<
|
||||
std::uint32_t>(
|
||||
arr_->t_->size + n_);
|
||||
return;
|
||||
}
|
||||
if(n_ > max_size() - arr_->size())
|
||||
{
|
||||
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
|
||||
detail::throw_system_error( error::array_too_large, &loc );
|
||||
}
|
||||
auto t = table::allocate(
|
||||
arr_->growth(arr_->size() + n_),
|
||||
arr_->sp_);
|
||||
t->size = static_cast<std::uint32_t>(
|
||||
arr_->size() + n_);
|
||||
p = &(*t)[0] + i_;
|
||||
relocate(
|
||||
&(*t)[0],
|
||||
arr_->data(),
|
||||
i_);
|
||||
relocate(
|
||||
&(*t)[i_ + n_],
|
||||
arr_->data() + i_,
|
||||
arr_->size() - i_);
|
||||
t = detail::exchange(arr_->t_, t);
|
||||
table::deallocate(t, arr_->sp_);
|
||||
}
|
||||
|
||||
array::
|
||||
revert_insert::
|
||||
~revert_insert()
|
||||
{
|
||||
if(! arr_)
|
||||
return;
|
||||
BOOST_ASSERT(n_ != 0);
|
||||
auto const pos =
|
||||
arr_->data() + i_;
|
||||
arr_->destroy(pos, p);
|
||||
arr_->t_->size = static_cast<
|
||||
std::uint32_t>(
|
||||
arr_->t_->size - n_);
|
||||
relocate(
|
||||
pos,
|
||||
pos + n_,
|
||||
arr_->size() - i_);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
array::
|
||||
destroy(
|
||||
value* first, value* last) noexcept
|
||||
{
|
||||
if(sp_.is_not_shared_and_deallocate_is_trivial())
|
||||
return;
|
||||
while(last-- != first)
|
||||
last->~value();
|
||||
}
|
||||
|
||||
void
|
||||
array::
|
||||
destroy() noexcept
|
||||
{
|
||||
if(sp_.is_not_shared_and_deallocate_is_trivial())
|
||||
return;
|
||||
auto last = end();
|
||||
auto const first = begin();
|
||||
while(last-- != first)
|
||||
last->~value();
|
||||
table::deallocate(t_, sp_);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Special Members
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
array::
|
||||
array(detail::unchecked_array&& ua)
|
||||
: sp_(ua.storage())
|
||||
{
|
||||
BOOST_STATIC_ASSERT(
|
||||
alignof(table) == alignof(value));
|
||||
if(ua.size() == 0)
|
||||
{
|
||||
t_ = &empty_;
|
||||
return;
|
||||
}
|
||||
t_= table::allocate(
|
||||
ua.size(), sp_);
|
||||
t_->size = static_cast<
|
||||
std::uint32_t>(ua.size());
|
||||
ua.relocate(data());
|
||||
}
|
||||
|
||||
array::
|
||||
~array() noexcept
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
array::
|
||||
array(
|
||||
std::size_t count,
|
||||
value const& v,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
{
|
||||
if(count == 0)
|
||||
{
|
||||
t_ = &empty_;
|
||||
return;
|
||||
}
|
||||
t_= table::allocate(
|
||||
count, sp_);
|
||||
t_->size = 0;
|
||||
revert_construct r(*this);
|
||||
while(count--)
|
||||
{
|
||||
::new(end()) value(v, sp_);
|
||||
++t_->size;
|
||||
}
|
||||
r.commit();
|
||||
}
|
||||
|
||||
array::
|
||||
array(
|
||||
std::size_t count,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
{
|
||||
if(count == 0)
|
||||
{
|
||||
t_ = &empty_;
|
||||
return;
|
||||
}
|
||||
t_ = table::allocate(
|
||||
count, sp_);
|
||||
t_->size = static_cast<
|
||||
std::uint32_t>(count);
|
||||
auto p = data();
|
||||
do
|
||||
{
|
||||
::new(p++) value(sp_);
|
||||
}
|
||||
while(--count);
|
||||
}
|
||||
|
||||
array::
|
||||
array(array const& other)
|
||||
: array(other, other.sp_)
|
||||
{
|
||||
}
|
||||
|
||||
array::
|
||||
array(
|
||||
array const& other,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
{
|
||||
if(other.empty())
|
||||
{
|
||||
t_ = &empty_;
|
||||
return;
|
||||
}
|
||||
t_ = table::allocate(
|
||||
other.size(), sp_);
|
||||
t_->size = 0;
|
||||
revert_construct r(*this);
|
||||
auto src = other.data();
|
||||
auto dest = data();
|
||||
auto const n = other.size();
|
||||
do
|
||||
{
|
||||
::new(dest++) value(
|
||||
*src++, sp_);
|
||||
++t_->size;
|
||||
}
|
||||
while(t_->size < n);
|
||||
r.commit();
|
||||
}
|
||||
|
||||
array::
|
||||
array(
|
||||
array&& other,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
{
|
||||
if(*sp_ == *other.sp_)
|
||||
{
|
||||
// same resource
|
||||
t_ = detail::exchange(
|
||||
other.t_, &empty_);
|
||||
return;
|
||||
}
|
||||
else if(other.empty())
|
||||
{
|
||||
t_ = &empty_;
|
||||
return;
|
||||
}
|
||||
// copy
|
||||
t_ = table::allocate(
|
||||
other.size(), sp_);
|
||||
t_->size = 0;
|
||||
revert_construct r(*this);
|
||||
auto src = other.data();
|
||||
auto dest = data();
|
||||
auto const n = other.size();
|
||||
do
|
||||
{
|
||||
::new(dest++) value(
|
||||
*src++, sp_);
|
||||
++t_->size;
|
||||
}
|
||||
while(t_->size < n);
|
||||
r.commit();
|
||||
}
|
||||
|
||||
array::
|
||||
array(
|
||||
std::initializer_list<
|
||||
value_ref> init,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
{
|
||||
if(init.size() == 0)
|
||||
{
|
||||
t_ = &empty_;
|
||||
return;
|
||||
}
|
||||
t_ = table::allocate(
|
||||
init.size(), sp_);
|
||||
t_->size = 0;
|
||||
revert_construct r(*this);
|
||||
value_ref::write_array(
|
||||
data(), init, sp_);
|
||||
t_->size = static_cast<
|
||||
std::uint32_t>(init.size());
|
||||
r.commit();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
array&
|
||||
array::
|
||||
operator=(array const& other)
|
||||
{
|
||||
array(other,
|
||||
storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
array&
|
||||
array::
|
||||
operator=(array&& other)
|
||||
{
|
||||
array(std::move(other),
|
||||
storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
array&
|
||||
array::
|
||||
operator=(
|
||||
std::initializer_list<value_ref> init)
|
||||
{
|
||||
array(init,
|
||||
storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Capacity
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
array::
|
||||
shrink_to_fit() noexcept
|
||||
{
|
||||
if(capacity() <= size())
|
||||
return;
|
||||
if(size() == 0)
|
||||
{
|
||||
table::deallocate(t_, sp_);
|
||||
t_ = &empty_;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try
|
||||
{
|
||||
#endif
|
||||
auto t = table::allocate(
|
||||
size(), sp_);
|
||||
relocate(
|
||||
&(*t)[0],
|
||||
data(),
|
||||
size());
|
||||
t->size = static_cast<
|
||||
std::uint32_t>(size());
|
||||
t = detail::exchange(
|
||||
t_, t);
|
||||
table::deallocate(t, sp_);
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// eat the exception
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Modifiers
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
array::
|
||||
clear() noexcept
|
||||
{
|
||||
if(size() == 0)
|
||||
return;
|
||||
destroy(
|
||||
begin(), end());
|
||||
t_->size = 0;
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
insert(
|
||||
const_iterator pos,
|
||||
value const& v) ->
|
||||
iterator
|
||||
{
|
||||
return emplace(pos, v);
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
insert(
|
||||
const_iterator pos,
|
||||
value&& v) ->
|
||||
iterator
|
||||
{
|
||||
return emplace(pos, std::move(v));
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
insert(
|
||||
const_iterator pos,
|
||||
std::size_t count,
|
||||
value const& v) ->
|
||||
iterator
|
||||
{
|
||||
revert_insert r(
|
||||
pos, count, *this);
|
||||
while(count--)
|
||||
{
|
||||
::new(r.p) value(v, sp_);
|
||||
++r.p;
|
||||
}
|
||||
return r.commit();
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
insert(
|
||||
const_iterator pos,
|
||||
std::initializer_list<
|
||||
value_ref> init) ->
|
||||
iterator
|
||||
{
|
||||
revert_insert r(
|
||||
pos, init.size(), *this);
|
||||
value_ref::write_array(
|
||||
r.p, init, sp_);
|
||||
return r.commit();
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
erase(
|
||||
const_iterator pos) noexcept ->
|
||||
iterator
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
pos >= begin() &&
|
||||
pos <= end());
|
||||
return erase(pos, pos + 1);
|
||||
}
|
||||
|
||||
auto
|
||||
array::
|
||||
erase(
|
||||
const_iterator first,
|
||||
const_iterator last) noexcept ->
|
||||
iterator
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
first >= begin() &&
|
||||
last >= first &&
|
||||
last <= end());
|
||||
std::size_t const n =
|
||||
last - first;
|
||||
auto const p = &(*t_)[0] +
|
||||
(first - &(*t_)[0]);
|
||||
destroy(p, p + n);
|
||||
relocate(p, p + n,
|
||||
t_->size - (last -
|
||||
&(*t_)[0]));
|
||||
t_->size = static_cast<
|
||||
std::uint32_t>(t_->size - n);
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
array::
|
||||
push_back(value const& v)
|
||||
{
|
||||
emplace_back(v);
|
||||
}
|
||||
|
||||
void
|
||||
array::
|
||||
push_back(value&& v)
|
||||
{
|
||||
emplace_back(std::move(v));
|
||||
}
|
||||
|
||||
void
|
||||
array::
|
||||
pop_back() noexcept
|
||||
{
|
||||
auto const p = &back();
|
||||
destroy(p, p + 1);
|
||||
--t_->size;
|
||||
}
|
||||
|
||||
void
|
||||
array::
|
||||
resize(std::size_t count)
|
||||
{
|
||||
if(count <= t_->size)
|
||||
{
|
||||
// shrink
|
||||
destroy(
|
||||
&(*t_)[0] + count,
|
||||
&(*t_)[0] + t_->size);
|
||||
t_->size = static_cast<
|
||||
std::uint32_t>(count);
|
||||
return;
|
||||
}
|
||||
|
||||
reserve(count);
|
||||
auto p = &(*t_)[t_->size];
|
||||
auto const end = &(*t_)[count];
|
||||
while(p != end)
|
||||
::new(p++) value(sp_);
|
||||
t_->size = static_cast<
|
||||
std::uint32_t>(count);
|
||||
}
|
||||
|
||||
void
|
||||
array::
|
||||
resize(
|
||||
std::size_t count,
|
||||
value const& v)
|
||||
{
|
||||
if(count <= size())
|
||||
{
|
||||
// shrink
|
||||
destroy(
|
||||
data() + count,
|
||||
data() + size());
|
||||
t_->size = static_cast<
|
||||
std::uint32_t>(count);
|
||||
return;
|
||||
}
|
||||
count -= size();
|
||||
revert_insert r(
|
||||
end(), count, *this);
|
||||
while(count--)
|
||||
{
|
||||
::new(r.p) value(v, sp_);
|
||||
++r.p;
|
||||
}
|
||||
r.commit();
|
||||
}
|
||||
|
||||
void
|
||||
array::
|
||||
swap(array& other)
|
||||
{
|
||||
if(*sp_ == *other.sp_)
|
||||
{
|
||||
t_ = detail::exchange(
|
||||
other.t_, t_);
|
||||
return;
|
||||
}
|
||||
array temp1(
|
||||
std::move(*this),
|
||||
other.storage());
|
||||
array temp2(
|
||||
std::move(other),
|
||||
this->storage());
|
||||
this->~array();
|
||||
::new(this) array(
|
||||
pilfer(temp2));
|
||||
other.~array();
|
||||
::new(&other) array(
|
||||
pilfer(temp1));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Private
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
std::size_t
|
||||
array::
|
||||
growth(
|
||||
std::size_t new_size) const
|
||||
{
|
||||
if(new_size > max_size())
|
||||
{
|
||||
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
|
||||
detail::throw_system_error( error::array_too_large, &loc );
|
||||
}
|
||||
std::size_t const old = capacity();
|
||||
if(old > max_size() - old / 2)
|
||||
return new_size;
|
||||
std::size_t const g =
|
||||
old + old / 2; // 1.5x
|
||||
if(g < new_size)
|
||||
return new_size;
|
||||
return g;
|
||||
}
|
||||
|
||||
// precondition: new_capacity > capacity()
|
||||
void
|
||||
array::
|
||||
reserve_impl(
|
||||
std::size_t new_capacity)
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
new_capacity > t_->capacity);
|
||||
auto t = table::allocate(
|
||||
growth(new_capacity), sp_);
|
||||
relocate(
|
||||
&(*t)[0],
|
||||
&(*t_)[0],
|
||||
t_->size);
|
||||
t->size = t_->size;
|
||||
t = detail::exchange(t_, t);
|
||||
table::deallocate(t, sp_);
|
||||
}
|
||||
|
||||
// precondition: pv is not aliased
|
||||
value&
|
||||
array::
|
||||
push_back(
|
||||
pilfered<value> pv)
|
||||
{
|
||||
auto const n = t_->size;
|
||||
if(n < t_->capacity)
|
||||
{
|
||||
// fast path
|
||||
auto& v = *::new(
|
||||
&(*t_)[n]) value(pv);
|
||||
++t_->size;
|
||||
return v;
|
||||
}
|
||||
auto const t =
|
||||
detail::exchange(t_,
|
||||
table::allocate(
|
||||
growth(n + 1),
|
||||
sp_));
|
||||
auto& v = *::new(
|
||||
&(*t_)[n]) value(pv);
|
||||
relocate(
|
||||
&(*t_)[0],
|
||||
&(*t)[0],
|
||||
n);
|
||||
t_->size = n + 1;
|
||||
table::deallocate(t, sp_);
|
||||
return v;
|
||||
}
|
||||
|
||||
// precondition: pv is not aliased
|
||||
auto
|
||||
array::
|
||||
insert(
|
||||
const_iterator pos,
|
||||
pilfered<value> pv) ->
|
||||
iterator
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
pos >= begin() &&
|
||||
pos <= end());
|
||||
std::size_t const n =
|
||||
t_->size;
|
||||
std::size_t const i =
|
||||
pos - &(*t_)[0];
|
||||
if(n < t_->capacity)
|
||||
{
|
||||
// fast path
|
||||
auto const p =
|
||||
&(*t_)[i];
|
||||
relocate(
|
||||
p + 1,
|
||||
p,
|
||||
n - i);
|
||||
::new(p) value(pv);
|
||||
++t_->size;
|
||||
return p;
|
||||
}
|
||||
auto t =
|
||||
table::allocate(
|
||||
growth(n + 1), sp_);
|
||||
auto const p = &(*t)[i];
|
||||
::new(p) value(pv);
|
||||
relocate(
|
||||
&(*t)[0],
|
||||
&(*t_)[0],
|
||||
i);
|
||||
relocate(
|
||||
p + 1,
|
||||
&(*t_)[i],
|
||||
n - i);
|
||||
t->size = static_cast<
|
||||
std::uint32_t>(size() + 1);
|
||||
t = detail::exchange(t_, t);
|
||||
table::deallocate(t, sp_);
|
||||
return p;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
bool
|
||||
array::
|
||||
equal(
|
||||
array const& other) const noexcept
|
||||
{
|
||||
if(size() != other.size())
|
||||
return false;
|
||||
for(std::size_t i = 0; i < size(); ++i)
|
||||
if((*this)[i] != other[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// std::hash specialization
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
std::size_t
|
||||
std::hash<::boost::json::array>::operator()(
|
||||
::boost::json::array const& ja) const noexcept
|
||||
{
|
||||
return ::boost::hash< ::boost::json::array >()( ja );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
#endif
|
||||
+545
@@ -0,0 +1,545 @@
|
||||
//
|
||||
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
|
||||
// Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru)
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_CONVERSION_HPP
|
||||
#define BOOST_JSON_IMPL_CONVERSION_HPP
|
||||
|
||||
#include <boost/json/fwd.hpp>
|
||||
#include <boost/json/value.hpp>
|
||||
#include <boost/json/string_view.hpp>
|
||||
#include <boost/describe/enumerators.hpp>
|
||||
#include <boost/describe/members.hpp>
|
||||
#include <boost/describe/bases.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#ifndef BOOST_NO_CXX17_HDR_VARIANT
|
||||
# include <variant>
|
||||
#endif // BOOST_NO_CXX17_HDR_VARIANT
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
namespace detail {
|
||||
|
||||
#ifdef __cpp_lib_nonmember_container_access
|
||||
using std::size;
|
||||
#endif
|
||||
|
||||
template<std::size_t I, class T>
|
||||
using tuple_element_t = typename std::tuple_element<I, T>::type;
|
||||
|
||||
template<class T>
|
||||
using iterator_type = decltype(std::begin(std::declval<T&>()));
|
||||
template<class T>
|
||||
using iterator_traits = std::iterator_traits< iterator_type<T> >;
|
||||
|
||||
template<class T>
|
||||
using value_type = typename iterator_traits<T>::value_type;
|
||||
template<class T>
|
||||
using mapped_type = tuple_element_t< 1, value_type<T> >;
|
||||
|
||||
// had to make the metafunction always succeeding in order to make it work
|
||||
// with msvc 14.0
|
||||
template<class T>
|
||||
using key_type_helper = tuple_element_t< 0, value_type<T> >;
|
||||
template<class T>
|
||||
using key_type = mp11::mp_eval_or<
|
||||
void,
|
||||
key_type_helper,
|
||||
T>;
|
||||
|
||||
template<class T>
|
||||
using are_begin_and_end_same = std::is_same<
|
||||
iterator_type<T>,
|
||||
decltype(std::end(std::declval<T&>()))>;
|
||||
|
||||
template<class T>
|
||||
using begin_iterator_category = typename std::iterator_traits<
|
||||
iterator_type<T>>::iterator_category;
|
||||
|
||||
template<class T>
|
||||
using has_positive_tuple_size = mp11::mp_bool<
|
||||
(std::tuple_size<T>::value > 0) >;
|
||||
|
||||
template<class T>
|
||||
using has_unique_keys = has_positive_tuple_size<decltype(
|
||||
std::declval<T&>().emplace(
|
||||
std::declval<value_type<T>>()))>;
|
||||
|
||||
template<class T>
|
||||
struct is_value_type_pair_helper : std::false_type
|
||||
{ };
|
||||
template<class T1, class T2>
|
||||
struct is_value_type_pair_helper<std::pair<T1, T2>> : std::true_type
|
||||
{ };
|
||||
template<class T>
|
||||
using is_value_type_pair = is_value_type_pair_helper<value_type<T>>;
|
||||
|
||||
template<class T>
|
||||
using has_size_member_helper
|
||||
= std::is_convertible<decltype(std::declval<T&>().size()), std::size_t>;
|
||||
template<class T>
|
||||
using has_size_member = mp11::mp_valid_and_true<has_size_member_helper, T>;
|
||||
template<class T>
|
||||
using has_free_size_helper
|
||||
= std::is_convertible<
|
||||
decltype(size(std::declval<T const&>())),
|
||||
std::size_t>;
|
||||
template<class T>
|
||||
using has_free_size = mp11::mp_valid_and_true<has_free_size_helper, T>;
|
||||
template<class T>
|
||||
using size_implementation = mp11::mp_cond<
|
||||
has_size_member<T>, mp11::mp_int<3>,
|
||||
has_free_size<T>, mp11::mp_int<2>,
|
||||
std::is_array<T>, mp11::mp_int<1>,
|
||||
mp11::mp_true, mp11::mp_int<0>>;
|
||||
|
||||
template<class T>
|
||||
std::size_t
|
||||
try_size(T&& cont, mp11::mp_int<3>)
|
||||
{
|
||||
return cont.size();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::size_t
|
||||
try_size(T& cont, mp11::mp_int<2>)
|
||||
{
|
||||
return size(cont);
|
||||
}
|
||||
|
||||
template<class T, std::size_t N>
|
||||
std::size_t
|
||||
try_size(T(&)[N], mp11::mp_int<1>)
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::size_t
|
||||
try_size(T&, mp11::mp_int<0>)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
using has_push_back_helper
|
||||
= decltype(std::declval<T&>().push_back(std::declval<value_type<T>>()));
|
||||
template<class T>
|
||||
using has_push_back = mp11::mp_valid<has_push_back_helper, T>;
|
||||
template<class T>
|
||||
using inserter_implementation = mp11::mp_cond<
|
||||
is_tuple_like<T>, mp11::mp_int<2>,
|
||||
has_push_back<T>, mp11::mp_int<1>,
|
||||
mp11::mp_true, mp11::mp_int<0>>;
|
||||
|
||||
template<class T>
|
||||
iterator_type<T>
|
||||
inserter(
|
||||
T& target,
|
||||
mp11::mp_int<2>)
|
||||
{
|
||||
return target.begin();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::back_insert_iterator<T>
|
||||
inserter(
|
||||
T& target,
|
||||
mp11::mp_int<1>)
|
||||
{
|
||||
return std::back_inserter(target);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::insert_iterator<T>
|
||||
inserter(
|
||||
T& target,
|
||||
mp11::mp_int<0>)
|
||||
{
|
||||
return std::inserter( target, target.end() );
|
||||
}
|
||||
|
||||
using value_from_conversion = mp11::mp_true;
|
||||
using value_to_conversion = mp11::mp_false;
|
||||
|
||||
struct user_conversion_tag { };
|
||||
struct context_conversion_tag : user_conversion_tag { };
|
||||
struct full_context_conversion_tag : context_conversion_tag { };
|
||||
struct native_conversion_tag { };
|
||||
struct value_conversion_tag : native_conversion_tag { };
|
||||
struct object_conversion_tag : native_conversion_tag { };
|
||||
struct array_conversion_tag : native_conversion_tag { };
|
||||
struct string_conversion_tag : native_conversion_tag { };
|
||||
struct bool_conversion_tag : native_conversion_tag { };
|
||||
struct number_conversion_tag : native_conversion_tag { };
|
||||
struct integral_conversion_tag : number_conversion_tag { };
|
||||
struct floating_point_conversion_tag : number_conversion_tag { };
|
||||
struct null_like_conversion_tag { };
|
||||
struct string_like_conversion_tag { };
|
||||
struct map_like_conversion_tag { };
|
||||
struct sequence_conversion_tag { };
|
||||
struct tuple_conversion_tag { };
|
||||
struct described_class_conversion_tag { };
|
||||
struct described_enum_conversion_tag { };
|
||||
struct variant_conversion_tag { };
|
||||
struct optional_conversion_tag { };
|
||||
struct no_conversion_tag { };
|
||||
|
||||
template<class... Args>
|
||||
using supports_tag_invoke = decltype(tag_invoke( std::declval<Args>()... ));
|
||||
|
||||
template<class T>
|
||||
using has_user_conversion_from_impl = supports_tag_invoke<
|
||||
value_from_tag, value&, T&& >;
|
||||
template<class T>
|
||||
using has_user_conversion_to_impl = supports_tag_invoke<
|
||||
value_to_tag<T>, value const& >;
|
||||
template<class T>
|
||||
using has_nonthrowing_user_conversion_to_impl = supports_tag_invoke<
|
||||
try_value_to_tag<T>, value const& >;
|
||||
template< class T, class Dir >
|
||||
using has_user_conversion1 = mp11::mp_if<
|
||||
std::is_same<Dir, value_from_conversion>,
|
||||
mp11::mp_valid<has_user_conversion_from_impl, T>,
|
||||
mp11::mp_or<
|
||||
mp11::mp_valid<has_user_conversion_to_impl, T>,
|
||||
mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>>>;
|
||||
|
||||
template< class Ctx, class T >
|
||||
using has_context_conversion_from_impl = supports_tag_invoke<
|
||||
value_from_tag, value&, T&&, Ctx const& >;
|
||||
template< class Ctx, class T >
|
||||
using has_context_conversion_to_impl = supports_tag_invoke<
|
||||
value_to_tag<T>, value const&, Ctx const& >;
|
||||
template< class Ctx, class T >
|
||||
using has_nonthrowing_context_conversion_to_impl = supports_tag_invoke<
|
||||
try_value_to_tag<T>, value const&, Ctx const& >;
|
||||
template< class Ctx, class T, class Dir >
|
||||
using has_user_conversion2 = mp11::mp_if<
|
||||
std::is_same<Dir, value_from_conversion>,
|
||||
mp11::mp_valid<has_context_conversion_from_impl, Ctx, T>,
|
||||
mp11::mp_or<
|
||||
mp11::mp_valid<has_context_conversion_to_impl, Ctx, T>,
|
||||
mp11::mp_valid<has_nonthrowing_context_conversion_to_impl, Ctx, T>>>;
|
||||
|
||||
template< class Ctx, class T >
|
||||
using has_full_context_conversion_from_impl = supports_tag_invoke<
|
||||
value_from_tag, value&, T&&, Ctx const&, Ctx const& >;
|
||||
template< class Ctx, class T >
|
||||
using has_full_context_conversion_to_impl = supports_tag_invoke<
|
||||
value_to_tag<T>, value const&, Ctx const&, Ctx const& >;
|
||||
template< class Ctx, class T >
|
||||
using has_nonthrowing_full_context_conversion_to_impl = supports_tag_invoke<
|
||||
try_value_to_tag<T>, value const&, Ctx const&, Ctx const& >;
|
||||
template< class Ctx, class T, class Dir >
|
||||
using has_user_conversion3 = mp11::mp_if<
|
||||
std::is_same<Dir, value_from_conversion>,
|
||||
mp11::mp_valid<has_full_context_conversion_from_impl, Ctx, T>,
|
||||
mp11::mp_or<
|
||||
mp11::mp_valid<has_full_context_conversion_to_impl, Ctx, T>,
|
||||
mp11::mp_valid<
|
||||
has_nonthrowing_full_context_conversion_to_impl, Ctx, T>>>;
|
||||
|
||||
template< class T >
|
||||
using described_non_public_members = describe::describe_members<
|
||||
T, describe::mod_private | describe::mod_protected>;
|
||||
template< class T >
|
||||
using described_bases = describe::describe_bases<
|
||||
T, describe::mod_any_access>;
|
||||
|
||||
#if defined(BOOST_MSVC) && BOOST_MSVC < 1920
|
||||
|
||||
template< class T >
|
||||
struct described_member_t_impl;
|
||||
|
||||
template< class T, class C >
|
||||
struct described_member_t_impl<T C::*>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template< class T, class D >
|
||||
using described_member_t = remove_cvref<
|
||||
typename described_member_t_impl<
|
||||
remove_cvref<decltype(D::pointer)> >::type>;
|
||||
|
||||
#else
|
||||
|
||||
template< class T, class D >
|
||||
using described_member_t = remove_cvref<decltype(
|
||||
std::declval<T&>().* D::pointer )>;
|
||||
|
||||
#endif
|
||||
|
||||
// user conversion (via tag_invoke)
|
||||
template< class Ctx, class T, class Dir >
|
||||
using user_conversion_category = mp11::mp_cond<
|
||||
has_user_conversion3<Ctx, T, Dir>, full_context_conversion_tag,
|
||||
has_user_conversion2<Ctx, T, Dir>, context_conversion_tag,
|
||||
has_user_conversion1<T, Dir>, user_conversion_tag>;
|
||||
|
||||
// native conversions (constructors and member functions of value)
|
||||
template< class T >
|
||||
using native_conversion_category = mp11::mp_cond<
|
||||
std::is_same<T, value>, value_conversion_tag,
|
||||
std::is_same<T, array>, array_conversion_tag,
|
||||
std::is_same<T, object>, object_conversion_tag,
|
||||
std::is_same<T, string>, string_conversion_tag>;
|
||||
|
||||
// generic conversions
|
||||
template< class T >
|
||||
using generic_conversion_category = mp11::mp_cond<
|
||||
std::is_same<T, bool>, bool_conversion_tag,
|
||||
std::is_integral<T>, integral_conversion_tag,
|
||||
std::is_floating_point<T>, floating_point_conversion_tag,
|
||||
is_null_like<T>, null_like_conversion_tag,
|
||||
is_string_like<T>, string_like_conversion_tag,
|
||||
is_map_like<T>, map_like_conversion_tag,
|
||||
is_sequence_like<T>, sequence_conversion_tag,
|
||||
is_tuple_like<T>, tuple_conversion_tag,
|
||||
is_described_class<T>, described_class_conversion_tag,
|
||||
is_described_enum<T>, described_enum_conversion_tag,
|
||||
is_variant_like<T>, variant_conversion_tag,
|
||||
is_optional_like<T>, optional_conversion_tag,
|
||||
// failed to find a suitable implementation
|
||||
mp11::mp_true, no_conversion_tag>;
|
||||
|
||||
template< class T >
|
||||
using nested_type = typename T::type;
|
||||
template< class T1, class T2 >
|
||||
using conversion_category_impl_helper = mp11::mp_eval_if_not<
|
||||
std::is_same<detail::no_conversion_tag, T1>,
|
||||
T1,
|
||||
mp11::mp_eval_or_q, T1, mp11::mp_quote<nested_type>, T2>;
|
||||
template< class Ctx, class T, class Dir >
|
||||
struct conversion_category_impl
|
||||
{
|
||||
using type = mp11::mp_fold<
|
||||
mp11::mp_list<
|
||||
mp11::mp_defer<user_conversion_category, Ctx, T, Dir>,
|
||||
mp11::mp_defer<native_conversion_category, T>,
|
||||
mp11::mp_defer<generic_conversion_category, T>>,
|
||||
no_conversion_tag,
|
||||
conversion_category_impl_helper>;
|
||||
};
|
||||
template< class Ctx, class T, class Dir >
|
||||
using conversion_category =
|
||||
typename conversion_category_impl< Ctx, T, Dir >::type;
|
||||
|
||||
template< class T >
|
||||
using any_conversion_tag = mp11::mp_not<
|
||||
std::is_same< T, no_conversion_tag > >;
|
||||
|
||||
template< class T, class Dir, class... Ctxs >
|
||||
struct conversion_category_impl< std::tuple<Ctxs...>, T, Dir >
|
||||
{
|
||||
using ctxs = mp11::mp_list< remove_cvref<Ctxs>... >;
|
||||
using cats = mp11::mp_list<
|
||||
conversion_category<remove_cvref<Ctxs>, T, Dir>... >;
|
||||
|
||||
template< class I >
|
||||
using exists = mp11::mp_less< I, mp11::mp_size<cats> >;
|
||||
|
||||
using context2 = mp11::mp_find< cats, full_context_conversion_tag >;
|
||||
using context1 = mp11::mp_find< cats, context_conversion_tag >;
|
||||
using context0 = mp11::mp_find< cats, user_conversion_tag >;
|
||||
using index = mp11::mp_cond<
|
||||
exists<context2>, context2,
|
||||
exists<context1>, context1,
|
||||
exists<context0>, context0,
|
||||
mp11::mp_true, mp11::mp_find_if< cats, any_conversion_tag > >;
|
||||
using type = mp11::mp_eval_or<
|
||||
no_conversion_tag,
|
||||
mp11::mp_at, cats, index >;
|
||||
};
|
||||
|
||||
struct no_context
|
||||
{};
|
||||
|
||||
struct allow_exceptions
|
||||
{};
|
||||
|
||||
template <class T, class Dir>
|
||||
using can_convert = mp11::mp_not<
|
||||
std::is_same<
|
||||
detail::conversion_category<no_context, T, Dir>,
|
||||
detail::no_conversion_tag>>;
|
||||
|
||||
template<class Impl1, class Impl2>
|
||||
using conversion_round_trips_helper = mp11::mp_or<
|
||||
std::is_same<Impl1, Impl2>,
|
||||
std::is_base_of<user_conversion_tag, Impl1>,
|
||||
std::is_base_of<user_conversion_tag, Impl2>>;
|
||||
template< class Ctx, class T, class Dir >
|
||||
using conversion_round_trips = conversion_round_trips_helper<
|
||||
conversion_category<Ctx, T, Dir>,
|
||||
conversion_category<Ctx, T, mp11::mp_not<Dir>>>;
|
||||
|
||||
template< class T1, class T2 >
|
||||
struct copy_cref_helper
|
||||
{
|
||||
using type = remove_cvref<T2>;
|
||||
};
|
||||
template< class T1, class T2 >
|
||||
using copy_cref = typename copy_cref_helper< T1, T2 >::type;
|
||||
|
||||
template< class T1, class T2 >
|
||||
struct copy_cref_helper<T1 const, T2>
|
||||
{
|
||||
using type = remove_cvref<T2> const;
|
||||
};
|
||||
template< class T1, class T2 >
|
||||
struct copy_cref_helper<T1&, T2>
|
||||
{
|
||||
using type = copy_cref<T1, T2>&;
|
||||
};
|
||||
template< class T1, class T2 >
|
||||
struct copy_cref_helper<T1&&, T2>
|
||||
{
|
||||
using type = copy_cref<T1, T2>&&;
|
||||
};
|
||||
|
||||
template< class Rng, class Traits >
|
||||
using forwarded_value_helper = mp11::mp_if<
|
||||
std::is_convertible<
|
||||
typename Traits::reference,
|
||||
copy_cref<Rng, typename Traits::value_type> >,
|
||||
copy_cref<Rng, typename Traits::value_type>,
|
||||
typename Traits::value_type >;
|
||||
|
||||
template< class Rng >
|
||||
using forwarded_value = forwarded_value_helper<
|
||||
Rng, iterator_traits< Rng > >;
|
||||
|
||||
template< class Ctx, class T, class Dir >
|
||||
struct supported_context
|
||||
{
|
||||
using type = Ctx;
|
||||
|
||||
static
|
||||
type const&
|
||||
get( Ctx const& ctx ) noexcept
|
||||
{
|
||||
return ctx;
|
||||
}
|
||||
};
|
||||
|
||||
template< class T, class Dir, class... Ctxs >
|
||||
struct supported_context< std::tuple<Ctxs...>, T, Dir >
|
||||
{
|
||||
using Ctx = std::tuple<Ctxs...>;
|
||||
using impl = conversion_category_impl<Ctx, T, Dir>;
|
||||
using index = typename impl::index;
|
||||
using next_supported = supported_context<
|
||||
mp11::mp_at< typename impl::ctxs, index >, T, Dir >;
|
||||
using type = typename next_supported::type;
|
||||
|
||||
static
|
||||
type const&
|
||||
get( Ctx const& ctx ) noexcept
|
||||
{
|
||||
return next_supported::get( std::get<index::value>( ctx ) );
|
||||
}
|
||||
};
|
||||
|
||||
template< class T >
|
||||
using value_result_type = typename std::decay<
|
||||
decltype( std::declval<T&>().value() )>::type;
|
||||
|
||||
template< class T >
|
||||
using can_reset = decltype( std::declval<T&>().reset() );
|
||||
|
||||
template< class T >
|
||||
using has_valueless_by_exception =
|
||||
decltype( std::declval<T const&>().valueless_by_exception() );
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <class T>
|
||||
struct result_for<T, value>
|
||||
{
|
||||
using type = result< detail::remove_cvref<T> >;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct is_string_like
|
||||
: std::is_convertible<T, string_view>
|
||||
{ };
|
||||
|
||||
template<class T>
|
||||
struct is_sequence_like
|
||||
: mp11::mp_all<
|
||||
mp11::mp_valid_and_true<detail::are_begin_and_end_same, T>,
|
||||
mp11::mp_valid<detail::begin_iterator_category, T>>
|
||||
{ };
|
||||
|
||||
template<class T>
|
||||
struct is_map_like
|
||||
: mp11::mp_all<
|
||||
is_sequence_like<T>,
|
||||
mp11::mp_valid_and_true<detail::is_value_type_pair, T>,
|
||||
is_string_like<detail::key_type<T>>,
|
||||
mp11::mp_valid_and_true<detail::has_unique_keys, T>>
|
||||
{ };
|
||||
|
||||
template<class T>
|
||||
struct is_tuple_like
|
||||
: mp11::mp_valid_and_true<detail::has_positive_tuple_size, T>
|
||||
{ };
|
||||
|
||||
template<>
|
||||
struct is_null_like<std::nullptr_t>
|
||||
: std::true_type
|
||||
{ };
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_VARIANT
|
||||
template<>
|
||||
struct is_null_like<std::monostate>
|
||||
: std::true_type
|
||||
{ };
|
||||
#endif // BOOST_NO_CXX17_HDR_VARIANT
|
||||
|
||||
template<class T>
|
||||
struct is_described_class
|
||||
: mp11::mp_and<
|
||||
describe::has_describe_members<T>,
|
||||
mp11::mp_not< std::is_union<T> >,
|
||||
mp11::mp_empty<
|
||||
mp11::mp_eval_or<
|
||||
mp11::mp_list<>, detail::described_non_public_members, T>>,
|
||||
mp11::mp_empty<
|
||||
mp11::mp_eval_or<mp11::mp_list<>, detail::described_bases, T>>>
|
||||
{ };
|
||||
|
||||
template<class T>
|
||||
struct is_described_enum
|
||||
: describe::has_describe_enumerators<T>
|
||||
{ };
|
||||
|
||||
template<class T>
|
||||
struct is_variant_like : mp11::mp_valid<detail::has_valueless_by_exception, T>
|
||||
{ };
|
||||
|
||||
template<class T>
|
||||
struct is_optional_like
|
||||
: mp11::mp_and<
|
||||
mp11::mp_not<std::is_void<
|
||||
mp11::mp_eval_or<void, detail::value_result_type, T>>>,
|
||||
mp11::mp_valid<detail::can_reset, T>>
|
||||
{ };
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_JSON_IMPL_CONVERSION_HPP
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_ERROR_HPP
|
||||
#define BOOST_JSON_IMPL_ERROR_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
template<>
|
||||
struct is_error_code_enum< ::boost::json::error >
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
template<>
|
||||
struct is_error_condition_enum< ::boost::json::condition >
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // system
|
||||
} // boost
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct is_error_code_enum< ::boost::json::error >
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
template<>
|
||||
struct is_error_condition_enum< ::boost::json::condition >
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // std
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
namespace detail {
|
||||
|
||||
struct error_code_category_t
|
||||
: error_category
|
||||
{
|
||||
constexpr
|
||||
error_code_category_t()
|
||||
: error_category(0xB9A9B9922177C772)
|
||||
{}
|
||||
|
||||
BOOST_JSON_DECL
|
||||
const char*
|
||||
name() const noexcept override;
|
||||
|
||||
BOOST_JSON_DECL
|
||||
char const*
|
||||
message( int ev, char* buf, std::size_t len ) const noexcept override;
|
||||
|
||||
BOOST_JSON_DECL
|
||||
std::string
|
||||
message( int ev ) const override;
|
||||
|
||||
BOOST_JSON_DECL
|
||||
error_condition
|
||||
default_error_condition( int ev ) const noexcept override;
|
||||
};
|
||||
|
||||
extern
|
||||
BOOST_JSON_DECL
|
||||
error_code_category_t error_code_category;
|
||||
|
||||
struct error_condition_category_t
|
||||
: error_category
|
||||
{
|
||||
constexpr
|
||||
error_condition_category_t()
|
||||
: error_category(0x37CEF5A036D24FD1)
|
||||
{}
|
||||
|
||||
BOOST_JSON_DECL
|
||||
const char*
|
||||
name() const noexcept override;
|
||||
|
||||
BOOST_JSON_DECL
|
||||
char const*
|
||||
message( int ev, char*, std::size_t ) const noexcept override;
|
||||
|
||||
BOOST_JSON_DECL
|
||||
std::string
|
||||
message( int cv ) const override;
|
||||
};
|
||||
|
||||
extern
|
||||
BOOST_JSON_DECL
|
||||
error_condition_category_t error_condition_category;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
inline
|
||||
BOOST_SYSTEM_CONSTEXPR
|
||||
error_code
|
||||
make_error_code(error e) noexcept
|
||||
{
|
||||
|
||||
return error_code(
|
||||
static_cast<std::underlying_type<error>::type>(e),
|
||||
detail::error_code_category );
|
||||
}
|
||||
|
||||
inline
|
||||
BOOST_SYSTEM_CONSTEXPR
|
||||
error_condition
|
||||
make_error_condition(condition c) noexcept
|
||||
{
|
||||
return error_condition(
|
||||
static_cast<std::underlying_type<condition>::type>(c),
|
||||
detail::error_condition_category );
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_ERROR_IPP
|
||||
#define BOOST_JSON_IMPL_ERROR_IPP
|
||||
|
||||
#include <boost/json/error.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
namespace detail {
|
||||
|
||||
// msvc 14.0 has a bug that warns about inability to use constexpr
|
||||
// construction here, even though there's no constexpr construction
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1900
|
||||
# pragma warning( push )
|
||||
# pragma warning( disable : 4592 )
|
||||
#endif
|
||||
BOOST_JSON_CONSTINIT
|
||||
error_code_category_t error_code_category;
|
||||
|
||||
BOOST_JSON_CONSTINIT
|
||||
error_condition_category_t error_condition_category;
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1900
|
||||
# pragma warning( pop )
|
||||
#endif
|
||||
|
||||
char const*
|
||||
error_code_category_t::name() const noexcept
|
||||
{
|
||||
return "boost.json";
|
||||
}
|
||||
|
||||
char const*
|
||||
error_code_category_t::message( int ev, char*, std::size_t ) const noexcept
|
||||
{
|
||||
switch(static_cast<error>(ev))
|
||||
{
|
||||
default:
|
||||
case error::syntax: return "syntax error";
|
||||
case error::extra_data: return "extra data";
|
||||
case error::incomplete: return "incomplete JSON";
|
||||
case error::exponent_overflow: return "exponent overflow";
|
||||
case error::too_deep: return "too deep";
|
||||
case error::illegal_leading_surrogate: return "illegal leading surrogate";
|
||||
case error::illegal_trailing_surrogate: return "illegal trailing surrogate";
|
||||
case error::expected_hex_digit: return "expected hex digit";
|
||||
case error::expected_utf16_escape: return "expected utf16 escape";
|
||||
case error::object_too_large: return "object too large";
|
||||
case error::array_too_large: return "array too large";
|
||||
case error::key_too_large: return "key too large";
|
||||
case error::string_too_large: return "string too large";
|
||||
case error::number_too_large: return "number too large";
|
||||
case error::input_error: return "input error";
|
||||
|
||||
case error::exception: return "got exception";
|
||||
case error::out_of_range: return "out of range";
|
||||
case error::test_failure: return "test failure";
|
||||
|
||||
case error::missing_slash: return "missing slash character";
|
||||
case error::invalid_escape: return "invalid escape sequence";
|
||||
case error::token_not_number: return "token is not a number";
|
||||
case error::value_is_scalar: return "current value is scalar";
|
||||
case error::not_found: return "no referenced value";
|
||||
case error::token_overflow: return "token overflow";
|
||||
case error::past_the_end: return "past-the-end token not supported";
|
||||
|
||||
case error::not_number: return "not a number";
|
||||
case error::not_exact: return "not exact";
|
||||
case error::not_null: return "value is not null";
|
||||
case error::not_bool: return "value is not boolean";
|
||||
case error::not_array: return "value is not an array";
|
||||
case error::not_object: return "value is not an object";
|
||||
case error::not_string: return "value is not a string";
|
||||
case error::not_int64: return "value is not a std::int64_t number";
|
||||
case error::not_uint64: return "value is not a std::uint64_t number";
|
||||
case error::not_double: return "value is not a double";
|
||||
case error::not_integer: return "value is not integer";
|
||||
case error::size_mismatch: return "source composite size does not match target size";
|
||||
case error::exhausted_variants: return "exhausted all variants";
|
||||
case error::unknown_name: return "unknown name";
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
error_code_category_t::message( int ev ) const
|
||||
{
|
||||
return message( ev, nullptr, 0 );
|
||||
}
|
||||
|
||||
error_condition
|
||||
error_code_category_t::default_error_condition( int ev) const noexcept
|
||||
{
|
||||
switch(static_cast<error>(ev))
|
||||
{
|
||||
default:
|
||||
return {ev, *this};
|
||||
|
||||
case error::syntax:
|
||||
case error::extra_data:
|
||||
case error::incomplete:
|
||||
case error::exponent_overflow:
|
||||
case error::too_deep:
|
||||
case error::illegal_leading_surrogate:
|
||||
case error::illegal_trailing_surrogate:
|
||||
case error::expected_hex_digit:
|
||||
case error::expected_utf16_escape:
|
||||
case error::object_too_large:
|
||||
case error::array_too_large:
|
||||
case error::key_too_large:
|
||||
case error::string_too_large:
|
||||
case error::number_too_large:
|
||||
case error::input_error:
|
||||
return condition::parse_error;
|
||||
|
||||
case error::missing_slash:
|
||||
case error::invalid_escape:
|
||||
return condition::pointer_parse_error;
|
||||
|
||||
case error::token_not_number:
|
||||
case error::value_is_scalar:
|
||||
case error::not_found:
|
||||
case error::token_overflow:
|
||||
case error::past_the_end:
|
||||
return condition::pointer_use_error;
|
||||
|
||||
case error::not_number:
|
||||
case error::not_exact:
|
||||
case error::not_null:
|
||||
case error::not_bool:
|
||||
case error::not_array:
|
||||
case error::not_object:
|
||||
case error::not_string:
|
||||
case error::not_int64:
|
||||
case error::not_uint64:
|
||||
case error::not_double:
|
||||
case error::not_integer:
|
||||
case error::size_mismatch:
|
||||
case error::exhausted_variants:
|
||||
case error::unknown_name:
|
||||
return condition::conversion_error;
|
||||
|
||||
case error::exception:
|
||||
case error::out_of_range:
|
||||
return condition::generic_error;
|
||||
}
|
||||
}
|
||||
|
||||
char const*
|
||||
error_condition_category_t::name() const noexcept
|
||||
{
|
||||
return "boost.json";
|
||||
}
|
||||
|
||||
char const*
|
||||
error_condition_category_t::message( int cv, char*, std::size_t ) const noexcept
|
||||
{
|
||||
switch(static_cast<condition>(cv))
|
||||
{
|
||||
default:
|
||||
case condition::parse_error:
|
||||
return "A JSON parse error occurred";
|
||||
case condition::pointer_parse_error:
|
||||
return "A JSON Pointer parse error occurred";
|
||||
case condition::pointer_use_error:
|
||||
return "An error occurred when JSON Pointer was used with"
|
||||
" a value";
|
||||
case condition::conversion_error:
|
||||
return "An error occurred during conversion";
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
error_condition_category_t::message( int cv ) const
|
||||
{
|
||||
return message( cv, nullptr, 0 );
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_KIND_IPP
|
||||
#define BOOST_JSON_IMPL_KIND_IPP
|
||||
|
||||
#include <boost/json/kind.hpp>
|
||||
#include <ostream>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
string_view
|
||||
to_string(kind k) noexcept
|
||||
{
|
||||
switch(k)
|
||||
{
|
||||
case kind::array: return "array";
|
||||
case kind::object: return "object";
|
||||
case kind::string: return "string";
|
||||
case kind::int64: return "int64";
|
||||
case kind::uint64: return "uint64";
|
||||
case kind::double_: return "double";
|
||||
case kind::bool_: return "bool";
|
||||
default: // satisfy warnings
|
||||
case kind::null: return "null";
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, kind k)
|
||||
{
|
||||
os << to_string(k);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
//
|
||||
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
|
||||
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_MONOTONIC_RESOURCE_IPP
|
||||
#define BOOST_JSON_IMPL_MONOTONIC_RESOURCE_IPP
|
||||
|
||||
#include <boost/json/monotonic_resource.hpp>
|
||||
#include <boost/json/detail/except.hpp>
|
||||
#include <boost/align/align.hpp>
|
||||
#include <boost/core/max_align.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
struct alignas(core::max_align_t)
|
||||
monotonic_resource::block : block_base
|
||||
{
|
||||
};
|
||||
|
||||
constexpr
|
||||
std::size_t
|
||||
monotonic_resource::
|
||||
max_size()
|
||||
{
|
||||
return std::size_t(-1) - sizeof(block);
|
||||
}
|
||||
|
||||
// lowest power of 2 greater than or equal to n
|
||||
std::size_t
|
||||
monotonic_resource::
|
||||
round_pow2(
|
||||
std::size_t n) noexcept
|
||||
{
|
||||
if(n & (n - 1))
|
||||
return next_pow2(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
// lowest power of 2 greater than n
|
||||
std::size_t
|
||||
monotonic_resource::
|
||||
next_pow2(
|
||||
std::size_t n) noexcept
|
||||
{
|
||||
std::size_t result = min_size_;
|
||||
while(result <= n)
|
||||
{
|
||||
if(result >= max_size() - result)
|
||||
{
|
||||
// overflow
|
||||
result = max_size();
|
||||
break;
|
||||
}
|
||||
result *= 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
monotonic_resource::
|
||||
~monotonic_resource()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
monotonic_resource::
|
||||
monotonic_resource(
|
||||
std::size_t initial_size,
|
||||
storage_ptr upstream) noexcept
|
||||
: buffer_{
|
||||
nullptr, 0, 0, nullptr}
|
||||
, next_size_(round_pow2(initial_size))
|
||||
, upstream_(std::move(upstream))
|
||||
{
|
||||
}
|
||||
|
||||
monotonic_resource::
|
||||
monotonic_resource(
|
||||
unsigned char* buffer,
|
||||
std::size_t size,
|
||||
storage_ptr upstream) noexcept
|
||||
: buffer_{
|
||||
buffer, size, size, nullptr}
|
||||
, next_size_(next_pow2(size))
|
||||
, upstream_(std::move(upstream))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
monotonic_resource::
|
||||
release() noexcept
|
||||
{
|
||||
auto p = head_;
|
||||
while(p != &buffer_)
|
||||
{
|
||||
auto next = p->next;
|
||||
upstream_->deallocate(p, p->size);
|
||||
p = next;
|
||||
}
|
||||
buffer_.p = reinterpret_cast<
|
||||
unsigned char*>(buffer_.p) - (
|
||||
buffer_.size - buffer_.avail);
|
||||
buffer_.avail = buffer_.size;
|
||||
head_ = &buffer_;
|
||||
}
|
||||
|
||||
void*
|
||||
monotonic_resource::
|
||||
do_allocate(
|
||||
std::size_t n,
|
||||
std::size_t align)
|
||||
{
|
||||
auto p = alignment::align(
|
||||
align, n, head_->p, head_->avail);
|
||||
if(p)
|
||||
{
|
||||
head_->p = reinterpret_cast<
|
||||
unsigned char*>(p) + n;
|
||||
head_->avail -= n;
|
||||
return p;
|
||||
}
|
||||
|
||||
if(next_size_ < n)
|
||||
next_size_ = round_pow2(n);
|
||||
auto b = ::new(upstream_->allocate(
|
||||
sizeof(block) + next_size_)) block;
|
||||
b->p = b + 1;
|
||||
b->avail = next_size_;
|
||||
b->size = next_size_;
|
||||
b->next = head_;
|
||||
head_ = b;
|
||||
next_size_ = next_pow2(next_size_);
|
||||
|
||||
p = alignment::align(
|
||||
align, n, head_->p, head_->avail);
|
||||
BOOST_ASSERT(p);
|
||||
head_->p = reinterpret_cast<
|
||||
unsigned char*>(p) + n;
|
||||
head_->avail -= n;
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
monotonic_resource::
|
||||
do_deallocate(
|
||||
void*,
|
||||
std::size_t,
|
||||
std::size_t)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
bool
|
||||
monotonic_resource::
|
||||
do_is_equal(
|
||||
memory_resource const& mr) const noexcept
|
||||
{
|
||||
return this == &mr;
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_NULL_RESOURCE_IPP
|
||||
#define BOOST_JSON_IMPL_NULL_RESOURCE_IPP
|
||||
|
||||
#include <boost/json/null_resource.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/** A resource which always fails.
|
||||
|
||||
This memory resource always throws the exception
|
||||
`std::bad_alloc` in calls to `allocate`.
|
||||
*/
|
||||
class null_resource final
|
||||
: public memory_resource
|
||||
{
|
||||
public:
|
||||
/// Copy constructor (deleted)
|
||||
null_resource(
|
||||
null_resource const&) = delete;
|
||||
|
||||
/// Copy assignment (deleted)
|
||||
null_resource& operator=(
|
||||
null_resource const&) = delete;
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs the resource.
|
||||
|
||||
@par Complexity
|
||||
Constant.
|
||||
|
||||
@par Exception Safety
|
||||
No-throw guarantee.
|
||||
*/
|
||||
/** @{ */
|
||||
null_resource() noexcept = default;
|
||||
|
||||
protected:
|
||||
void*
|
||||
do_allocate(
|
||||
std::size_t,
|
||||
std::size_t) override
|
||||
{
|
||||
throw_exception( std::bad_alloc(), BOOST_CURRENT_LOCATION );
|
||||
}
|
||||
|
||||
void
|
||||
do_deallocate(
|
||||
void*,
|
||||
std::size_t,
|
||||
std::size_t) override
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
bool
|
||||
do_is_equal(
|
||||
memory_resource const& mr
|
||||
) const noexcept override
|
||||
{
|
||||
return this == &mr;
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
memory_resource*
|
||||
get_null_resource() noexcept
|
||||
{
|
||||
static detail::null_resource mr;
|
||||
return &mr;
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+587
@@ -0,0 +1,587 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_OBJECT_HPP
|
||||
#define BOOST_JSON_IMPL_OBJECT_HPP
|
||||
|
||||
#include <boost/json/value.hpp>
|
||||
#include <iterator>
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Objects with size less than or equal
|
||||
// to this number will use a linear search
|
||||
// instead of the more expensive hash function.
|
||||
static
|
||||
constexpr
|
||||
std::size_t
|
||||
small_object_size_ = 18;
|
||||
|
||||
BOOST_STATIC_ASSERT(
|
||||
small_object_size_ <
|
||||
BOOST_JSON_MAX_STRUCTURED_SIZE);
|
||||
|
||||
} // detail
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
struct alignas(key_value_pair)
|
||||
object::table
|
||||
{
|
||||
std::uint32_t size = 0;
|
||||
std::uint32_t capacity = 0;
|
||||
std::uintptr_t salt = 0;
|
||||
|
||||
#if defined(_MSC_VER) && BOOST_JSON_ARCH == 32
|
||||
// VFALCO If we make key_value_pair smaller,
|
||||
// then we might want to revisit this
|
||||
// padding.
|
||||
BOOST_STATIC_ASSERT(
|
||||
sizeof(key_value_pair) == 32);
|
||||
char pad[4] = {}; // silence warnings
|
||||
#endif
|
||||
|
||||
constexpr table();
|
||||
|
||||
// returns true if we use a linear
|
||||
// search instead of the hash table.
|
||||
bool is_small() const noexcept
|
||||
{
|
||||
return capacity <=
|
||||
detail::small_object_size_;
|
||||
}
|
||||
|
||||
key_value_pair&
|
||||
operator[](
|
||||
std::size_t pos) noexcept
|
||||
{
|
||||
return reinterpret_cast<
|
||||
key_value_pair*>(
|
||||
this + 1)[pos];
|
||||
}
|
||||
|
||||
// VFALCO This is exported for tests
|
||||
BOOST_JSON_DECL
|
||||
std::size_t
|
||||
digest(string_view key) const noexcept;
|
||||
|
||||
inline
|
||||
index_t&
|
||||
bucket(std::size_t hash) noexcept;
|
||||
|
||||
inline
|
||||
index_t&
|
||||
bucket(string_view key) noexcept;
|
||||
|
||||
inline
|
||||
void
|
||||
clear() noexcept;
|
||||
|
||||
static
|
||||
inline
|
||||
table*
|
||||
allocate(
|
||||
std::size_t capacity,
|
||||
std::uintptr_t salt,
|
||||
storage_ptr const& sp);
|
||||
|
||||
static
|
||||
void
|
||||
deallocate(
|
||||
table* p,
|
||||
storage_ptr const& sp) noexcept
|
||||
{
|
||||
if(p->capacity == 0)
|
||||
return;
|
||||
if(! p->is_small())
|
||||
sp->deallocate(p,
|
||||
sizeof(table) + p->capacity * (
|
||||
sizeof(key_value_pair) +
|
||||
sizeof(index_t)));
|
||||
else
|
||||
sp->deallocate(p,
|
||||
sizeof(table) + p->capacity *
|
||||
sizeof(key_value_pair));
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
class object::revert_construct
|
||||
{
|
||||
object* obj_;
|
||||
|
||||
BOOST_JSON_DECL
|
||||
void
|
||||
destroy() noexcept;
|
||||
|
||||
public:
|
||||
explicit
|
||||
revert_construct(
|
||||
object& obj) noexcept
|
||||
: obj_(&obj)
|
||||
{
|
||||
}
|
||||
|
||||
~revert_construct()
|
||||
{
|
||||
if(! obj_)
|
||||
return;
|
||||
destroy();
|
||||
}
|
||||
|
||||
void
|
||||
commit() noexcept
|
||||
{
|
||||
obj_ = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
class object::revert_insert
|
||||
{
|
||||
object* obj_;
|
||||
table* t_ = nullptr;
|
||||
std::size_t size_;
|
||||
|
||||
BOOST_JSON_DECL
|
||||
void
|
||||
destroy() noexcept;
|
||||
|
||||
public:
|
||||
explicit
|
||||
revert_insert(
|
||||
object& obj,
|
||||
std::size_t capacity)
|
||||
: obj_(&obj)
|
||||
, size_(obj_->size())
|
||||
{
|
||||
if( capacity > obj_->capacity() )
|
||||
t_ = obj_->reserve_impl(capacity);
|
||||
}
|
||||
|
||||
~revert_insert()
|
||||
{
|
||||
if(! obj_)
|
||||
return;
|
||||
|
||||
destroy();
|
||||
if( t_ )
|
||||
{
|
||||
table::deallocate( obj_->t_, obj_->sp_ );
|
||||
obj_->t_ = t_;
|
||||
}
|
||||
else
|
||||
{
|
||||
obj_->t_->size = static_cast<index_t>(size_);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
commit() noexcept
|
||||
{
|
||||
BOOST_ASSERT(obj_);
|
||||
if( t_ )
|
||||
table::deallocate( t_, obj_->sp_ );
|
||||
obj_ = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Iterators
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
auto
|
||||
object::
|
||||
begin() noexcept ->
|
||||
iterator
|
||||
{
|
||||
return &(*t_)[0];
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
begin() const noexcept ->
|
||||
const_iterator
|
||||
{
|
||||
return &(*t_)[0];
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
cbegin() const noexcept ->
|
||||
const_iterator
|
||||
{
|
||||
return &(*t_)[0];
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
end() noexcept ->
|
||||
iterator
|
||||
{
|
||||
return &(*t_)[t_->size];
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
end() const noexcept ->
|
||||
const_iterator
|
||||
{
|
||||
return &(*t_)[t_->size];
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
cend() const noexcept ->
|
||||
const_iterator
|
||||
{
|
||||
return &(*t_)[t_->size];
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
rbegin() noexcept ->
|
||||
reverse_iterator
|
||||
{
|
||||
return reverse_iterator(end());
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
rbegin() const noexcept ->
|
||||
const_reverse_iterator
|
||||
{
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
crbegin() const noexcept ->
|
||||
const_reverse_iterator
|
||||
{
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
rend() noexcept ->
|
||||
reverse_iterator
|
||||
{
|
||||
return reverse_iterator(begin());
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
rend() const noexcept ->
|
||||
const_reverse_iterator
|
||||
{
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
crend() const noexcept ->
|
||||
const_reverse_iterator
|
||||
{
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Capacity
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
bool
|
||||
object::
|
||||
empty() const noexcept
|
||||
{
|
||||
return t_->size == 0;
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
size() const noexcept ->
|
||||
std::size_t
|
||||
{
|
||||
return t_->size;
|
||||
}
|
||||
|
||||
constexpr
|
||||
std::size_t
|
||||
object::
|
||||
max_size() noexcept
|
||||
{
|
||||
// max_size depends on the address model
|
||||
using min = std::integral_constant<std::size_t,
|
||||
(std::size_t(-1) - sizeof(table)) /
|
||||
(sizeof(key_value_pair) + sizeof(index_t))>;
|
||||
return min::value < BOOST_JSON_MAX_STRUCTURED_SIZE ?
|
||||
min::value : BOOST_JSON_MAX_STRUCTURED_SIZE;
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
capacity() const noexcept ->
|
||||
std::size_t
|
||||
{
|
||||
return t_->capacity;
|
||||
}
|
||||
|
||||
void
|
||||
object::
|
||||
reserve(std::size_t new_capacity)
|
||||
{
|
||||
if( new_capacity <= capacity() )
|
||||
return;
|
||||
table* const old_table = reserve_impl(new_capacity);
|
||||
table::deallocate( old_table, sp_ );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Lookup
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
auto
|
||||
object::
|
||||
at(string_view key) & ->
|
||||
value&
|
||||
{
|
||||
auto const& self = *this;
|
||||
return const_cast< value& >( self.at(key) );
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
at(string_view key) && ->
|
||||
value&&
|
||||
{
|
||||
return std::move( at(key) );
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
at(string_view key) const& ->
|
||||
value const&
|
||||
{
|
||||
auto it = find(key);
|
||||
if(it == end())
|
||||
{
|
||||
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
|
||||
detail::throw_system_error( error::out_of_range, &loc );
|
||||
}
|
||||
return it->value();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
template<class P, class>
|
||||
auto
|
||||
object::
|
||||
insert(P&& p) ->
|
||||
std::pair<iterator, bool>
|
||||
{
|
||||
key_value_pair v(
|
||||
std::forward<P>(p), sp_);
|
||||
return emplace_impl( v.key(), pilfer(v) );
|
||||
}
|
||||
|
||||
template<class M>
|
||||
auto
|
||||
object::
|
||||
insert_or_assign(
|
||||
string_view key, M&& m) ->
|
||||
std::pair<iterator, bool>
|
||||
{
|
||||
std::pair<iterator, bool> result = emplace_impl(
|
||||
key, key, static_cast<M&&>(m) );
|
||||
if( !result.second )
|
||||
{
|
||||
value(static_cast<M>(m), sp_).swap(
|
||||
result.first->value());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
auto
|
||||
object::
|
||||
emplace(
|
||||
string_view key,
|
||||
Arg&& arg) ->
|
||||
std::pair<iterator, bool>
|
||||
{
|
||||
return emplace_impl( key, key, static_cast<Arg&&>(arg) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// (private)
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
template<class InputIt>
|
||||
void
|
||||
object::
|
||||
construct(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
std::size_t min_capacity,
|
||||
std::input_iterator_tag)
|
||||
{
|
||||
reserve(min_capacity);
|
||||
revert_construct r(*this);
|
||||
while(first != last)
|
||||
{
|
||||
insert(*first);
|
||||
++first;
|
||||
}
|
||||
r.commit();
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
void
|
||||
object::
|
||||
construct(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
std::size_t min_capacity,
|
||||
std::forward_iterator_tag)
|
||||
{
|
||||
auto n = static_cast<
|
||||
std::size_t>(std::distance(
|
||||
first, last));
|
||||
if( n < min_capacity)
|
||||
n = min_capacity;
|
||||
reserve(n);
|
||||
revert_construct r(*this);
|
||||
while(first != last)
|
||||
{
|
||||
insert(*first);
|
||||
++first;
|
||||
}
|
||||
r.commit();
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
void
|
||||
object::
|
||||
insert(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
std::input_iterator_tag)
|
||||
{
|
||||
// Since input iterators cannot be rewound,
|
||||
// we keep inserted elements on an exception.
|
||||
//
|
||||
while(first != last)
|
||||
{
|
||||
insert(*first);
|
||||
++first;
|
||||
}
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
void
|
||||
object::
|
||||
insert(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
std::forward_iterator_tag)
|
||||
{
|
||||
auto const n =
|
||||
static_cast<std::size_t>(
|
||||
std::distance(first, last));
|
||||
auto const n0 = size();
|
||||
if(n > max_size() - n0)
|
||||
{
|
||||
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
|
||||
detail::throw_system_error( error::object_too_large, &loc );
|
||||
}
|
||||
revert_insert r( *this, n0 + n );
|
||||
while(first != last)
|
||||
{
|
||||
insert(*first);
|
||||
++first;
|
||||
}
|
||||
r.commit();
|
||||
}
|
||||
|
||||
template< class... Args >
|
||||
std::pair<object::iterator, bool>
|
||||
object::
|
||||
emplace_impl( string_view key, Args&& ... args )
|
||||
{
|
||||
std::pair<iterator, std::size_t> search_result(nullptr, 0);
|
||||
if( !empty() )
|
||||
{
|
||||
search_result = detail::find_in_object(*this, key);
|
||||
if( search_result.first )
|
||||
return { search_result.first, false };
|
||||
}
|
||||
|
||||
// we create the new value before reserving, in case it is a reference to
|
||||
// a subobject of the current object
|
||||
key_value_pair kv( static_cast<Args&&>(args)..., sp_ );
|
||||
// the key might get deallocated too
|
||||
key = kv.key();
|
||||
|
||||
std::size_t const old_capacity = capacity();
|
||||
reserve(size() + 1);
|
||||
if( (empty() && capacity() > detail::small_object_size_)
|
||||
|| (capacity() != old_capacity) )
|
||||
search_result.second = detail::digest(
|
||||
key.begin(), key.end(), t_->salt);
|
||||
|
||||
BOOST_ASSERT(
|
||||
t_->is_small() ||
|
||||
(search_result.second ==
|
||||
detail::digest(key.begin(), key.end(), t_->salt)) );
|
||||
|
||||
return { insert_impl(pilfer(kv), search_result.second), true };
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
unchecked_object::
|
||||
~unchecked_object()
|
||||
{
|
||||
if(! data_)
|
||||
return;
|
||||
if(sp_.is_not_shared_and_deallocate_is_trivial())
|
||||
return;
|
||||
value* p = data_;
|
||||
while(size_--)
|
||||
{
|
||||
p[0].~value();
|
||||
p[1].~value();
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+897
@@ -0,0 +1,897 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_OBJECT_IPP
|
||||
#define BOOST_JSON_IMPL_OBJECT_IPP
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/json/object.hpp>
|
||||
#include <boost/json/detail/digest.hpp>
|
||||
#include <boost/json/detail/except.hpp>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
namespace detail {
|
||||
|
||||
template<class CharRange>
|
||||
std::pair<key_value_pair*, std::size_t>
|
||||
find_in_object(
|
||||
object const& obj,
|
||||
CharRange key) noexcept
|
||||
{
|
||||
BOOST_ASSERT(obj.t_->capacity > 0);
|
||||
if(obj.t_->is_small())
|
||||
{
|
||||
auto it = &(*obj.t_)[0];
|
||||
auto const last =
|
||||
&(*obj.t_)[obj.t_->size];
|
||||
for(;it != last; ++it)
|
||||
if( key == it->key() )
|
||||
return { it, 0 };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
std::pair<
|
||||
key_value_pair*,
|
||||
std::size_t> result;
|
||||
BOOST_ASSERT(obj.t_->salt != 0);
|
||||
result.second = detail::digest(key.begin(), key.end(), obj.t_->salt);
|
||||
auto i = obj.t_->bucket(
|
||||
result.second);
|
||||
while(i != object::null_index_)
|
||||
{
|
||||
auto& v = (*obj.t_)[i];
|
||||
if( key == v.key() )
|
||||
{
|
||||
result.first = &v;
|
||||
return result;
|
||||
}
|
||||
i = access::next(v);
|
||||
}
|
||||
result.first = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template
|
||||
std::pair<key_value_pair*, std::size_t>
|
||||
find_in_object<string_view>(
|
||||
object const& obj,
|
||||
string_view key) noexcept;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
constexpr object::table::table() = default;
|
||||
|
||||
// empty objects point here
|
||||
BOOST_JSON_REQUIRE_CONST_INIT
|
||||
object::table object::empty_;
|
||||
|
||||
std::size_t
|
||||
object::table::
|
||||
digest(string_view key) const noexcept
|
||||
{
|
||||
BOOST_ASSERT(salt != 0);
|
||||
return detail::digest(
|
||||
key.begin(), key.end(), salt);
|
||||
}
|
||||
|
||||
auto
|
||||
object::table::
|
||||
bucket(std::size_t hash) noexcept ->
|
||||
index_t&
|
||||
{
|
||||
return reinterpret_cast<
|
||||
index_t*>(&(*this)[capacity])[
|
||||
hash % capacity];
|
||||
}
|
||||
|
||||
auto
|
||||
object::table::
|
||||
bucket(string_view key) noexcept ->
|
||||
index_t&
|
||||
{
|
||||
return bucket(digest(key));
|
||||
}
|
||||
|
||||
void
|
||||
object::table::
|
||||
clear() noexcept
|
||||
{
|
||||
BOOST_ASSERT(! is_small());
|
||||
// initialize buckets
|
||||
std::memset(
|
||||
reinterpret_cast<index_t*>(
|
||||
&(*this)[capacity]),
|
||||
0xff, // null_index_
|
||||
capacity * sizeof(index_t));
|
||||
}
|
||||
|
||||
object::table*
|
||||
object::table::
|
||||
allocate(
|
||||
std::size_t capacity,
|
||||
std::uintptr_t salt,
|
||||
storage_ptr const& sp)
|
||||
{
|
||||
BOOST_STATIC_ASSERT(
|
||||
alignof(key_value_pair) >=
|
||||
alignof(index_t));
|
||||
BOOST_ASSERT(capacity > 0);
|
||||
BOOST_ASSERT(capacity <= max_size());
|
||||
table* p;
|
||||
if(capacity <= detail::small_object_size_)
|
||||
{
|
||||
p = reinterpret_cast<
|
||||
table*>(sp->allocate(
|
||||
sizeof(table) + capacity *
|
||||
sizeof(key_value_pair)));
|
||||
p->capacity = static_cast<
|
||||
std::uint32_t>(capacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = reinterpret_cast<
|
||||
table*>(sp->allocate(
|
||||
sizeof(table) + capacity * (
|
||||
sizeof(key_value_pair) +
|
||||
sizeof(index_t))));
|
||||
p->capacity = static_cast<
|
||||
std::uint32_t>(capacity);
|
||||
p->clear();
|
||||
}
|
||||
if(salt)
|
||||
{
|
||||
p->salt = salt;
|
||||
}
|
||||
else
|
||||
{
|
||||
// VFALCO This would be better if it
|
||||
// was random, but maybe this
|
||||
// is good enough.
|
||||
p->salt = reinterpret_cast<
|
||||
std::uintptr_t>(p);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
object::
|
||||
revert_construct::
|
||||
destroy() noexcept
|
||||
{
|
||||
obj_->destroy();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
object::
|
||||
revert_insert::
|
||||
destroy() noexcept
|
||||
{
|
||||
obj_->destroy(
|
||||
&(*obj_->t_)[size_],
|
||||
obj_->end());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Construction
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
object::
|
||||
object(detail::unchecked_object&& uo)
|
||||
: sp_(uo.storage())
|
||||
{
|
||||
if(uo.size() == 0)
|
||||
{
|
||||
t_ = &empty_;
|
||||
return;
|
||||
}
|
||||
// should already be checked
|
||||
BOOST_ASSERT(
|
||||
uo.size() <= max_size());
|
||||
t_ = table::allocate(
|
||||
uo.size(), 0, sp_);
|
||||
|
||||
// insert all elements, keeping
|
||||
// the last of any duplicate keys.
|
||||
auto dest = begin();
|
||||
auto src = uo.release();
|
||||
auto const end = src + 2 * uo.size();
|
||||
if(t_->is_small())
|
||||
{
|
||||
t_->size = 0;
|
||||
while(src != end)
|
||||
{
|
||||
access::construct_key_value_pair(
|
||||
dest, pilfer(src[0]), pilfer(src[1]));
|
||||
src += 2;
|
||||
auto result = detail::find_in_object(*this, dest->key());
|
||||
if(! result.first)
|
||||
{
|
||||
++dest;
|
||||
++t_->size;
|
||||
continue;
|
||||
}
|
||||
// handle duplicate
|
||||
auto& v = *result.first;
|
||||
// don't bother to check if
|
||||
// storage deallocate is trivial
|
||||
v.~key_value_pair();
|
||||
// trivial relocate
|
||||
std::memcpy(
|
||||
static_cast<void*>(&v),
|
||||
dest, sizeof(v));
|
||||
}
|
||||
return;
|
||||
}
|
||||
while(src != end)
|
||||
{
|
||||
access::construct_key_value_pair(
|
||||
dest, pilfer(src[0]), pilfer(src[1]));
|
||||
src += 2;
|
||||
auto& head = t_->bucket(dest->key());
|
||||
auto i = head;
|
||||
for(;;)
|
||||
{
|
||||
if(i == null_index_)
|
||||
{
|
||||
// end of bucket
|
||||
access::next(
|
||||
*dest) = head;
|
||||
head = static_cast<index_t>(
|
||||
dest - begin());
|
||||
++dest;
|
||||
break;
|
||||
}
|
||||
auto& v = (*t_)[i];
|
||||
if(v.key() != dest->key())
|
||||
{
|
||||
i = access::next(v);
|
||||
continue;
|
||||
}
|
||||
|
||||
// handle duplicate
|
||||
access::next(*dest) =
|
||||
access::next(v);
|
||||
// don't bother to check if
|
||||
// storage deallocate is trivial
|
||||
v.~key_value_pair();
|
||||
// trivial relocate
|
||||
std::memcpy(
|
||||
static_cast<void*>(&v),
|
||||
dest, sizeof(v));
|
||||
break;
|
||||
}
|
||||
}
|
||||
t_->size = static_cast<
|
||||
index_t>(dest - begin());
|
||||
}
|
||||
|
||||
object::
|
||||
~object() noexcept
|
||||
{
|
||||
if(sp_.is_not_shared_and_deallocate_is_trivial())
|
||||
return;
|
||||
if(t_->capacity == 0)
|
||||
return;
|
||||
destroy();
|
||||
}
|
||||
|
||||
object::
|
||||
object(
|
||||
std::size_t min_capacity,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
, t_(&empty_)
|
||||
{
|
||||
reserve(min_capacity);
|
||||
}
|
||||
|
||||
object::
|
||||
object(object&& other) noexcept
|
||||
: sp_(other.sp_)
|
||||
, t_(detail::exchange(
|
||||
other.t_, &empty_))
|
||||
{
|
||||
}
|
||||
|
||||
object::
|
||||
object(
|
||||
object&& other,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
{
|
||||
if(*sp_ == *other.sp_)
|
||||
{
|
||||
t_ = detail::exchange(
|
||||
other.t_, &empty_);
|
||||
return;
|
||||
}
|
||||
|
||||
t_ = &empty_;
|
||||
object(other, sp_).swap(*this);
|
||||
}
|
||||
|
||||
object::
|
||||
object(
|
||||
object const& other,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
, t_(&empty_)
|
||||
{
|
||||
reserve(other.size());
|
||||
revert_construct r(*this);
|
||||
if(t_->is_small())
|
||||
{
|
||||
for(auto const& v : other)
|
||||
{
|
||||
::new(end())
|
||||
key_value_pair(v, sp_);
|
||||
++t_->size;
|
||||
}
|
||||
r.commit();
|
||||
return;
|
||||
}
|
||||
for(auto const& v : other)
|
||||
{
|
||||
// skip duplicate checking
|
||||
auto& head =
|
||||
t_->bucket(v.key());
|
||||
auto pv = ::new(end())
|
||||
key_value_pair(v, sp_);
|
||||
access::next(*pv) = head;
|
||||
head = t_->size;
|
||||
++t_->size;
|
||||
}
|
||||
r.commit();
|
||||
}
|
||||
|
||||
object::
|
||||
object(
|
||||
std::initializer_list<std::pair<
|
||||
string_view, value_ref>> init,
|
||||
std::size_t min_capacity,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
, t_(&empty_)
|
||||
{
|
||||
if( min_capacity < init.size())
|
||||
min_capacity = init.size();
|
||||
reserve(min_capacity);
|
||||
revert_construct r(*this);
|
||||
insert(init);
|
||||
r.commit();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Assignment
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
object&
|
||||
object::
|
||||
operator=(object const& other)
|
||||
{
|
||||
object tmp(other, sp_);
|
||||
this->~object();
|
||||
::new(this) object(pilfer(tmp));
|
||||
return *this;
|
||||
}
|
||||
|
||||
object&
|
||||
object::
|
||||
operator=(object&& other)
|
||||
{
|
||||
object tmp(std::move(other), sp_);
|
||||
this->~object();
|
||||
::new(this) object(pilfer(tmp));
|
||||
return *this;
|
||||
}
|
||||
|
||||
object&
|
||||
object::
|
||||
operator=(
|
||||
std::initializer_list<std::pair<
|
||||
string_view, value_ref>> init)
|
||||
{
|
||||
object tmp(init, sp_);
|
||||
this->~object();
|
||||
::new(this) object(pilfer(tmp));
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Modifiers
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
object::
|
||||
clear() noexcept
|
||||
{
|
||||
if(empty())
|
||||
return;
|
||||
if(! sp_.is_not_shared_and_deallocate_is_trivial())
|
||||
destroy(begin(), end());
|
||||
if(! t_->is_small())
|
||||
t_->clear();
|
||||
t_->size = 0;
|
||||
}
|
||||
|
||||
void
|
||||
object::
|
||||
insert(
|
||||
std::initializer_list<std::pair<
|
||||
string_view, value_ref>> init)
|
||||
{
|
||||
auto const n0 = size();
|
||||
if(init.size() > max_size() - n0)
|
||||
{
|
||||
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
|
||||
detail::throw_system_error( error::object_too_large, &loc );
|
||||
}
|
||||
revert_insert r( *this, n0 + init.size() );
|
||||
if(t_->is_small())
|
||||
{
|
||||
for(auto& iv : init)
|
||||
{
|
||||
auto result =
|
||||
detail::find_in_object(*this, iv.first);
|
||||
if(result.first)
|
||||
{
|
||||
// ignore duplicate
|
||||
continue;
|
||||
}
|
||||
::new(end()) key_value_pair(
|
||||
iv.first,
|
||||
iv.second.make_value(sp_));
|
||||
++t_->size;
|
||||
}
|
||||
r.commit();
|
||||
return;
|
||||
}
|
||||
for(auto& iv : init)
|
||||
{
|
||||
auto& head = t_->bucket(iv.first);
|
||||
auto i = head;
|
||||
for(;;)
|
||||
{
|
||||
if(i == null_index_)
|
||||
{
|
||||
// VFALCO value_ref should construct
|
||||
// a key_value_pair using placement
|
||||
auto& v = *::new(end())
|
||||
key_value_pair(
|
||||
iv.first,
|
||||
iv.second.make_value(sp_));
|
||||
access::next(v) = head;
|
||||
head = static_cast<index_t>(
|
||||
t_->size);
|
||||
++t_->size;
|
||||
break;
|
||||
}
|
||||
auto& v = (*t_)[i];
|
||||
if(v.key() == iv.first)
|
||||
{
|
||||
// ignore duplicate
|
||||
break;
|
||||
}
|
||||
i = access::next(v);
|
||||
}
|
||||
}
|
||||
r.commit();
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
erase(const_iterator pos) noexcept ->
|
||||
iterator
|
||||
{
|
||||
return do_erase(pos,
|
||||
[this](iterator p) {
|
||||
// the casts silence warnings
|
||||
std::memcpy(
|
||||
static_cast<void*>(p),
|
||||
static_cast<void const*>(end()),
|
||||
sizeof(*p));
|
||||
},
|
||||
[this](iterator p) {
|
||||
reindex_relocate(end(), p);
|
||||
});
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
erase(string_view key) noexcept ->
|
||||
std::size_t
|
||||
{
|
||||
auto it = find(key);
|
||||
if(it == end())
|
||||
return 0;
|
||||
erase(it);
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
stable_erase(const_iterator pos) noexcept ->
|
||||
iterator
|
||||
{
|
||||
return do_erase(pos,
|
||||
[this](iterator p) {
|
||||
// the casts silence warnings
|
||||
std::memmove(
|
||||
static_cast<void*>(p),
|
||||
static_cast<void const*>(p + 1),
|
||||
sizeof(*p) * (end() - p));
|
||||
},
|
||||
[this](iterator p) {
|
||||
for (; p != end(); ++p)
|
||||
{
|
||||
reindex_relocate(p + 1, p);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
stable_erase(string_view key) noexcept ->
|
||||
std::size_t
|
||||
{
|
||||
auto it = find(key);
|
||||
if(it == end())
|
||||
return 0;
|
||||
stable_erase(it);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
object::
|
||||
swap(object& other)
|
||||
{
|
||||
if(*sp_ == *other.sp_)
|
||||
{
|
||||
t_ = detail::exchange(
|
||||
other.t_, t_);
|
||||
return;
|
||||
}
|
||||
object temp1(
|
||||
std::move(*this),
|
||||
other.storage());
|
||||
object temp2(
|
||||
std::move(other),
|
||||
this->storage());
|
||||
other.~object();
|
||||
::new(&other) object(pilfer(temp1));
|
||||
this->~object();
|
||||
::new(this) object(pilfer(temp2));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Lookup
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
auto
|
||||
object::
|
||||
operator[](string_view key) ->
|
||||
value&
|
||||
{
|
||||
auto const result =
|
||||
emplace(key, nullptr);
|
||||
return result.first->value();
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
count(string_view key) const noexcept ->
|
||||
std::size_t
|
||||
{
|
||||
if(find(key) == end())
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
find(string_view key) noexcept ->
|
||||
iterator
|
||||
{
|
||||
if(empty())
|
||||
return end();
|
||||
auto const p =
|
||||
detail::find_in_object(*this, key).first;
|
||||
if(p)
|
||||
return p;
|
||||
return end();
|
||||
}
|
||||
|
||||
auto
|
||||
object::
|
||||
find(string_view key) const noexcept ->
|
||||
const_iterator
|
||||
{
|
||||
if(empty())
|
||||
return end();
|
||||
auto const p =
|
||||
detail::find_in_object(*this, key).first;
|
||||
if(p)
|
||||
return p;
|
||||
return end();
|
||||
}
|
||||
|
||||
bool
|
||||
object::
|
||||
contains(
|
||||
string_view key) const noexcept
|
||||
{
|
||||
if(empty())
|
||||
return false;
|
||||
return detail::find_in_object(*this, key).first
|
||||
!= nullptr;
|
||||
}
|
||||
|
||||
value const*
|
||||
object::
|
||||
if_contains(
|
||||
string_view key) const noexcept
|
||||
{
|
||||
auto const it = find(key);
|
||||
if(it != end())
|
||||
return &it->value();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
value*
|
||||
object::
|
||||
if_contains(
|
||||
string_view key) noexcept
|
||||
{
|
||||
auto const it = find(key);
|
||||
if(it != end())
|
||||
return &it->value();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// (private)
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
key_value_pair*
|
||||
object::
|
||||
insert_impl(
|
||||
pilfered<key_value_pair> p,
|
||||
std::size_t hash)
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
capacity() > size());
|
||||
if(t_->is_small())
|
||||
{
|
||||
auto const pv = ::new(end())
|
||||
key_value_pair(p);
|
||||
++t_->size;
|
||||
return pv;
|
||||
}
|
||||
auto& head =
|
||||
t_->bucket(hash);
|
||||
auto const pv = ::new(end())
|
||||
key_value_pair(p);
|
||||
access::next(*pv) = head;
|
||||
head = t_->size;
|
||||
++t_->size;
|
||||
return pv;
|
||||
}
|
||||
|
||||
// allocate new table, copy elements there, and rehash them
|
||||
object::table*
|
||||
object::
|
||||
reserve_impl(std::size_t new_capacity)
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
new_capacity > t_->capacity);
|
||||
auto t = table::allocate(
|
||||
growth(new_capacity),
|
||||
t_->salt, sp_);
|
||||
if(! empty())
|
||||
std::memcpy(
|
||||
static_cast<
|
||||
void*>(&(*t)[0]),
|
||||
begin(),
|
||||
size() * sizeof(
|
||||
key_value_pair));
|
||||
t->size = t_->size;
|
||||
std::swap(t_, t);
|
||||
|
||||
if(! t_->is_small())
|
||||
{
|
||||
// rebuild hash table,
|
||||
// without dup checks
|
||||
auto p = end();
|
||||
index_t i = t_->size;
|
||||
while(i-- > 0)
|
||||
{
|
||||
--p;
|
||||
auto& head =
|
||||
t_->bucket(p->key());
|
||||
access::next(*p) = head;
|
||||
head = i;
|
||||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
bool
|
||||
object::
|
||||
equal(object const& other) const noexcept
|
||||
{
|
||||
if(size() != other.size())
|
||||
return false;
|
||||
auto const end_ = other.end();
|
||||
for(auto e : *this)
|
||||
{
|
||||
auto it = other.find(e.key());
|
||||
if(it == end_)
|
||||
return false;
|
||||
if(it->value() != e.value())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
object::
|
||||
growth(
|
||||
std::size_t new_size) const
|
||||
{
|
||||
if(new_size > max_size())
|
||||
{
|
||||
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
|
||||
detail::throw_system_error( error::object_too_large, &loc );
|
||||
}
|
||||
std::size_t const old = capacity();
|
||||
if(old > max_size() - old / 2)
|
||||
return new_size;
|
||||
std::size_t const g =
|
||||
old + old / 2; // 1.5x
|
||||
if(g < new_size)
|
||||
return new_size;
|
||||
return g;
|
||||
}
|
||||
|
||||
void
|
||||
object::
|
||||
remove(
|
||||
index_t& head,
|
||||
key_value_pair& v) noexcept
|
||||
{
|
||||
BOOST_ASSERT(! t_->is_small());
|
||||
auto const i = static_cast<
|
||||
index_t>(&v - begin());
|
||||
if(head == i)
|
||||
{
|
||||
head = access::next(v);
|
||||
return;
|
||||
}
|
||||
auto* pn =
|
||||
&access::next((*t_)[head]);
|
||||
while(*pn != i)
|
||||
pn = &access::next((*t_)[*pn]);
|
||||
*pn = access::next(v);
|
||||
}
|
||||
|
||||
void
|
||||
object::
|
||||
destroy() noexcept
|
||||
{
|
||||
BOOST_ASSERT(t_->capacity > 0);
|
||||
BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
|
||||
destroy(begin(), end());
|
||||
table::deallocate(t_, sp_);
|
||||
}
|
||||
|
||||
void
|
||||
object::
|
||||
destroy(
|
||||
key_value_pair* first,
|
||||
key_value_pair* last) noexcept
|
||||
{
|
||||
BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
|
||||
while(last != first)
|
||||
(--last)->~key_value_pair();
|
||||
}
|
||||
|
||||
template<class FS, class FB>
|
||||
auto
|
||||
object::
|
||||
do_erase(
|
||||
const_iterator pos,
|
||||
FS small_reloc,
|
||||
FB big_reloc) noexcept
|
||||
-> iterator
|
||||
{
|
||||
auto p = begin() + (pos - begin());
|
||||
if(t_->is_small())
|
||||
{
|
||||
p->~value_type();
|
||||
--t_->size;
|
||||
if(p != end())
|
||||
{
|
||||
small_reloc(p);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
remove(t_->bucket(p->key()), *p);
|
||||
p->~value_type();
|
||||
--t_->size;
|
||||
if(p != end())
|
||||
{
|
||||
big_reloc(p);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
object::
|
||||
reindex_relocate(
|
||||
key_value_pair* src,
|
||||
key_value_pair* dst) noexcept
|
||||
{
|
||||
BOOST_ASSERT(! t_->is_small());
|
||||
auto& head = t_->bucket(src->key());
|
||||
remove(head, *src);
|
||||
// the casts silence warnings
|
||||
std::memcpy(
|
||||
static_cast<void*>(dst),
|
||||
static_cast<void const*>(src),
|
||||
sizeof(*dst));
|
||||
access::next(*dst) = head;
|
||||
head = static_cast<
|
||||
index_t>(dst - begin());
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// std::hash specialization
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
std::size_t
|
||||
std::hash<::boost::json::object>::operator()(
|
||||
::boost::json::object const& jo) const noexcept
|
||||
{
|
||||
return ::boost::hash< ::boost::json::object >()( jo );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
|
||||
#endif
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
//
|
||||
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
|
||||
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_PARSE_IPP
|
||||
#define BOOST_JSON_IMPL_PARSE_IPP
|
||||
|
||||
#include <boost/json/parse.hpp>
|
||||
#include <boost/json/parser.hpp>
|
||||
#include <boost/json/detail/except.hpp>
|
||||
|
||||
#include <istream>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
value
|
||||
parse(
|
||||
string_view s,
|
||||
error_code& ec,
|
||||
storage_ptr sp,
|
||||
const parse_options& opt)
|
||||
{
|
||||
unsigned char temp[
|
||||
BOOST_JSON_STACK_BUFFER_SIZE];
|
||||
parser p(storage_ptr(), opt, temp);
|
||||
p.reset(std::move(sp));
|
||||
p.write(s, ec);
|
||||
if(ec)
|
||||
return nullptr;
|
||||
return p.release();
|
||||
}
|
||||
|
||||
value
|
||||
parse(
|
||||
string_view s,
|
||||
std::error_code& ec,
|
||||
storage_ptr sp,
|
||||
parse_options const& opt)
|
||||
{
|
||||
error_code jec;
|
||||
value result = parse(s, jec, std::move(sp), opt);
|
||||
ec = jec;
|
||||
return result;
|
||||
}
|
||||
|
||||
value
|
||||
parse(
|
||||
string_view s,
|
||||
storage_ptr sp,
|
||||
const parse_options& opt)
|
||||
{
|
||||
error_code ec;
|
||||
auto jv = parse(
|
||||
s, ec, std::move(sp), opt);
|
||||
if(ec)
|
||||
detail::throw_system_error( ec );
|
||||
return jv;
|
||||
}
|
||||
|
||||
value
|
||||
parse(
|
||||
std::istream& is,
|
||||
error_code& ec,
|
||||
storage_ptr sp,
|
||||
parse_options const& opt)
|
||||
{
|
||||
unsigned char parser_buffer[BOOST_JSON_STACK_BUFFER_SIZE / 2];
|
||||
stream_parser p(storage_ptr(), opt, parser_buffer);
|
||||
p.reset(std::move(sp));
|
||||
|
||||
char read_buffer[BOOST_JSON_STACK_BUFFER_SIZE / 2];
|
||||
do
|
||||
{
|
||||
if( is.eof() )
|
||||
{
|
||||
p.finish(ec);
|
||||
break;
|
||||
}
|
||||
|
||||
if( !is )
|
||||
{
|
||||
BOOST_JSON_FAIL( ec, error::input_error );
|
||||
break;
|
||||
}
|
||||
|
||||
is.read(read_buffer, sizeof(read_buffer));
|
||||
auto const consumed = is.gcount();
|
||||
|
||||
p.write( read_buffer, static_cast<std::size_t>(consumed), ec );
|
||||
}
|
||||
while( !ec.failed() );
|
||||
|
||||
if( ec.failed() )
|
||||
return nullptr;
|
||||
|
||||
return p.release();
|
||||
}
|
||||
|
||||
value
|
||||
parse(
|
||||
std::istream& is,
|
||||
std::error_code& ec,
|
||||
storage_ptr sp,
|
||||
parse_options const& opt)
|
||||
{
|
||||
error_code jec;
|
||||
value result = parse(is, jec, std::move(sp), opt);
|
||||
ec = jec;
|
||||
return result;
|
||||
}
|
||||
|
||||
value
|
||||
parse(
|
||||
std::istream& is,
|
||||
storage_ptr sp,
|
||||
parse_options const& opt)
|
||||
{
|
||||
error_code ec;
|
||||
auto jv = parse(
|
||||
is, ec, std::move(sp), opt);
|
||||
if(ec)
|
||||
detail::throw_system_error( ec );
|
||||
return jv;
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
|
||||
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_PARSE_INTO_HPP
|
||||
#define BOOST_JSON_IMPL_PARSE_INTO_HPP
|
||||
|
||||
#include <boost/json/basic_parser_impl.hpp>
|
||||
#include <boost/json/error.hpp>
|
||||
#include <istream>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
template<class V>
|
||||
void
|
||||
parse_into(
|
||||
V& v,
|
||||
string_view sv,
|
||||
error_code& ec,
|
||||
parse_options const& opt )
|
||||
{
|
||||
parser_for<V> p( opt, &v );
|
||||
|
||||
std::size_t n = p.write_some( false, sv.data(), sv.size(), ec );
|
||||
|
||||
if( !ec && n < sv.size() )
|
||||
{
|
||||
BOOST_JSON_FAIL( ec, error::extra_data );
|
||||
}
|
||||
}
|
||||
|
||||
template<class V>
|
||||
void
|
||||
parse_into(
|
||||
V& v,
|
||||
string_view sv,
|
||||
std::error_code& ec,
|
||||
parse_options const& opt )
|
||||
{
|
||||
error_code jec;
|
||||
parse_into(v, sv, jec, opt);
|
||||
ec = jec;
|
||||
}
|
||||
|
||||
template<class V>
|
||||
void
|
||||
parse_into(
|
||||
V& v,
|
||||
string_view sv,
|
||||
parse_options const& opt )
|
||||
{
|
||||
error_code ec;
|
||||
parse_into(v, sv, ec, opt);
|
||||
if( ec.failed() )
|
||||
detail::throw_system_error( ec );
|
||||
}
|
||||
|
||||
template<class V>
|
||||
void
|
||||
parse_into(
|
||||
V& v,
|
||||
std::istream& is,
|
||||
error_code& ec,
|
||||
parse_options const& opt )
|
||||
{
|
||||
parser_for<V> p( opt, &v );
|
||||
|
||||
char read_buffer[BOOST_JSON_STACK_BUFFER_SIZE];
|
||||
do
|
||||
{
|
||||
if( is.eof() )
|
||||
{
|
||||
p.write_some(false, nullptr, 0, ec);
|
||||
break;
|
||||
}
|
||||
|
||||
if( !is )
|
||||
{
|
||||
BOOST_JSON_FAIL( ec, error::input_error );
|
||||
break;
|
||||
}
|
||||
|
||||
is.read(read_buffer, sizeof(read_buffer));
|
||||
std::size_t const consumed = static_cast<std::size_t>( is.gcount() );
|
||||
|
||||
std::size_t const n = p.write_some( true, read_buffer, consumed, ec );
|
||||
if( !ec.failed() && n < consumed )
|
||||
{
|
||||
BOOST_JSON_FAIL( ec, error::extra_data );
|
||||
}
|
||||
}
|
||||
while( !ec.failed() );
|
||||
}
|
||||
|
||||
template<class V>
|
||||
void
|
||||
parse_into(
|
||||
V& v,
|
||||
std::istream& is,
|
||||
std::error_code& ec,
|
||||
parse_options const& opt )
|
||||
{
|
||||
error_code jec;
|
||||
parse_into(v, is, jec, opt);
|
||||
ec = jec;
|
||||
}
|
||||
|
||||
template<class V>
|
||||
void
|
||||
parse_into(
|
||||
V& v,
|
||||
std::istream& is,
|
||||
parse_options const& opt )
|
||||
{
|
||||
error_code ec;
|
||||
parse_into(v, is, ec, opt);
|
||||
if( ec.failed() )
|
||||
detail::throw_system_error( ec );
|
||||
}
|
||||
|
||||
} // namespace boost
|
||||
} // namespace json
|
||||
|
||||
#endif
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_PARSER_IPP
|
||||
#define BOOST_JSON_IMPL_PARSER_IPP
|
||||
|
||||
#include <boost/json/parser.hpp>
|
||||
#include <boost/json/basic_parser_impl.hpp>
|
||||
#include <boost/json/error.hpp>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
parser::
|
||||
parser(
|
||||
storage_ptr sp,
|
||||
parse_options const& opt,
|
||||
unsigned char* buffer,
|
||||
std::size_t size) noexcept
|
||||
: p_(
|
||||
opt,
|
||||
std::move(sp),
|
||||
buffer,
|
||||
size)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
parser::
|
||||
parser(
|
||||
storage_ptr sp,
|
||||
parse_options const& opt) noexcept
|
||||
: p_(
|
||||
opt,
|
||||
std::move(sp),
|
||||
nullptr,
|
||||
0)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void
|
||||
parser::
|
||||
reset(storage_ptr sp) noexcept
|
||||
{
|
||||
p_.reset();
|
||||
p_.handler().st.reset(sp);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
parser::
|
||||
write_some(
|
||||
char const* data,
|
||||
std::size_t size,
|
||||
error_code& ec)
|
||||
{
|
||||
auto const n = p_.write_some(
|
||||
false, data, size, ec);
|
||||
BOOST_ASSERT(ec || p_.done());
|
||||
return n;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
parser::
|
||||
write_some(
|
||||
char const* data,
|
||||
std::size_t size,
|
||||
std::error_code& ec)
|
||||
{
|
||||
error_code jec;
|
||||
std::size_t const result = write_some(data, size, jec);
|
||||
ec = jec;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
parser::
|
||||
write_some(
|
||||
char const* data,
|
||||
std::size_t size)
|
||||
{
|
||||
error_code ec;
|
||||
auto const n = write_some(
|
||||
data, size, ec);
|
||||
if(ec)
|
||||
detail::throw_system_error( ec );
|
||||
return n;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
parser::
|
||||
write(
|
||||
char const* data,
|
||||
std::size_t size,
|
||||
error_code& ec)
|
||||
{
|
||||
auto const n = write_some(
|
||||
data, size, ec);
|
||||
if(! ec && n < size)
|
||||
{
|
||||
BOOST_JSON_FAIL(ec, error::extra_data);
|
||||
p_.fail(ec);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
parser::
|
||||
write(
|
||||
char const* data,
|
||||
std::size_t size,
|
||||
std::error_code& ec)
|
||||
{
|
||||
error_code jec;
|
||||
std::size_t const result = write(data, size, jec);
|
||||
ec = jec;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
parser::
|
||||
write(
|
||||
char const* data,
|
||||
std::size_t size)
|
||||
{
|
||||
error_code ec;
|
||||
auto const n = write(
|
||||
data, size, ec);
|
||||
if(ec)
|
||||
detail::throw_system_error( ec );
|
||||
return n;
|
||||
}
|
||||
|
||||
value
|
||||
parser::
|
||||
release()
|
||||
{
|
||||
if( ! p_.done())
|
||||
{
|
||||
// prevent undefined behavior
|
||||
if(! p_.last_error())
|
||||
{
|
||||
error_code ec;
|
||||
BOOST_JSON_FAIL(ec, error::incomplete);
|
||||
p_.fail(ec);
|
||||
}
|
||||
detail::throw_system_error(
|
||||
p_.last_error());
|
||||
}
|
||||
return p_.handler().st.release();
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+501
@@ -0,0 +1,501 @@
|
||||
//
|
||||
// Copyright (c) 2022 Dmitry Arkhipov (grisumbras@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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_POINTER_IPP
|
||||
#define BOOST_JSON_IMPL_POINTER_IPP
|
||||
|
||||
#include <boost/json/value.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class pointer_token
|
||||
{
|
||||
public:
|
||||
class iterator;
|
||||
|
||||
pointer_token(
|
||||
string_view sv) noexcept
|
||||
: b_( sv.begin() + 1 )
|
||||
, e_( sv.end() )
|
||||
{
|
||||
BOOST_ASSERT( !sv.empty() );
|
||||
BOOST_ASSERT( *sv.data() == '/' );
|
||||
}
|
||||
|
||||
iterator begin() const noexcept;
|
||||
iterator end() const noexcept;
|
||||
|
||||
private:
|
||||
char const* b_;
|
||||
char const* e_;
|
||||
};
|
||||
|
||||
class pointer_token::iterator
|
||||
{
|
||||
public:
|
||||
using value_type = char;
|
||||
using reference = char;
|
||||
using pointer = value_type*;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
explicit iterator(char const* base) noexcept
|
||||
: base_(base)
|
||||
{
|
||||
}
|
||||
|
||||
char operator*() const noexcept
|
||||
{
|
||||
switch( char c = *base_ )
|
||||
{
|
||||
case '~':
|
||||
c = base_[1];
|
||||
if( '0' == c )
|
||||
return '~';
|
||||
BOOST_ASSERT('1' == c);
|
||||
return '/';
|
||||
default:
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
iterator& operator++() noexcept
|
||||
{
|
||||
if( '~' == *base_ )
|
||||
base_ += 2;
|
||||
else
|
||||
++base_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) noexcept
|
||||
{
|
||||
iterator result = *this;
|
||||
++(*this);
|
||||
return result;
|
||||
}
|
||||
|
||||
char const* base() const noexcept
|
||||
{
|
||||
return base_;
|
||||
}
|
||||
|
||||
private:
|
||||
char const* base_;
|
||||
};
|
||||
|
||||
bool operator==(pointer_token::iterator l, pointer_token::iterator r) noexcept
|
||||
{
|
||||
return l.base() == r.base();
|
||||
}
|
||||
|
||||
bool operator!=(pointer_token::iterator l, pointer_token::iterator r) noexcept
|
||||
{
|
||||
return l.base() != r.base();
|
||||
}
|
||||
|
||||
pointer_token::iterator pointer_token::begin() const noexcept
|
||||
{
|
||||
return iterator(b_);
|
||||
}
|
||||
|
||||
pointer_token::iterator pointer_token::end() const noexcept
|
||||
{
|
||||
return iterator(e_);
|
||||
}
|
||||
|
||||
bool operator==(pointer_token token, string_view sv) noexcept
|
||||
{
|
||||
auto t_b = token.begin();
|
||||
auto const t_e = token.end();
|
||||
auto s_b = sv.begin();
|
||||
auto const s_e = sv.end();
|
||||
while( s_b != s_e )
|
||||
{
|
||||
if( t_e == t_b )
|
||||
return false;
|
||||
if( *t_b != *s_b )
|
||||
return false;
|
||||
++t_b;
|
||||
++s_b;
|
||||
}
|
||||
return t_b == t_e;
|
||||
}
|
||||
|
||||
bool is_invalid_zero(
|
||||
char const* b,
|
||||
char const* e) noexcept
|
||||
{
|
||||
// in JSON Pointer only zero index can start character '0'
|
||||
if( *b != '0' )
|
||||
return false;
|
||||
|
||||
// if an index token starts with '0', then it should not have any more
|
||||
// characters: either the string should end, or new token should start
|
||||
++b;
|
||||
if( b == e )
|
||||
return false;
|
||||
|
||||
BOOST_ASSERT( *b != '/' );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_past_the_end_token(
|
||||
char const* b,
|
||||
char const* e) noexcept
|
||||
{
|
||||
if( *b != '-' )
|
||||
return false;
|
||||
|
||||
++b;
|
||||
BOOST_ASSERT( (b == e) || (*b != '/') );
|
||||
return b == e;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
parse_number_token(
|
||||
string_view sv,
|
||||
error_code& ec) noexcept
|
||||
{
|
||||
BOOST_ASSERT( !sv.empty() );
|
||||
|
||||
char const* b = sv.begin();
|
||||
BOOST_ASSERT( *b == '/' );
|
||||
|
||||
++b;
|
||||
char const* const e = sv.end();
|
||||
if( ( b == e )
|
||||
|| is_invalid_zero(b, e) )
|
||||
{
|
||||
BOOST_JSON_FAIL(ec, error::token_not_number);
|
||||
return {};
|
||||
}
|
||||
|
||||
if( is_past_the_end_token(b, e) )
|
||||
{
|
||||
++b;
|
||||
BOOST_JSON_FAIL(ec, error::past_the_end);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::size_t result = 0;
|
||||
for( ; b != e; ++b )
|
||||
{
|
||||
char const c = *b;
|
||||
BOOST_ASSERT( c != '/' );
|
||||
|
||||
unsigned d = c - '0';
|
||||
if( d > 9 )
|
||||
{
|
||||
BOOST_JSON_FAIL(ec, error::token_not_number);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::size_t new_result = result * 10 + d;
|
||||
if( new_result < result )
|
||||
{
|
||||
BOOST_JSON_FAIL(ec, error::token_overflow);
|
||||
return {};
|
||||
}
|
||||
|
||||
result = new_result;
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
string_view
|
||||
next_segment(
|
||||
string_view& sv,
|
||||
error_code& ec) noexcept
|
||||
{
|
||||
if( sv.empty() )
|
||||
return sv;
|
||||
|
||||
char const* const start = sv.begin();
|
||||
char const* b = start;
|
||||
if( *b++ != '/' )
|
||||
{
|
||||
BOOST_JSON_FAIL( ec, error::missing_slash );
|
||||
return {};
|
||||
}
|
||||
|
||||
char const* e = sv.end();
|
||||
for( ; b < e; ++b )
|
||||
{
|
||||
char const c = *b;
|
||||
if( '/' == c )
|
||||
break;
|
||||
|
||||
if( '~' == c )
|
||||
{
|
||||
if( ++b == e )
|
||||
{
|
||||
BOOST_JSON_FAIL( ec, error::invalid_escape );
|
||||
break;
|
||||
}
|
||||
|
||||
switch (*b)
|
||||
{
|
||||
case '0': // fall through
|
||||
case '1':
|
||||
// valid escape sequence
|
||||
continue;
|
||||
default: {
|
||||
BOOST_JSON_FAIL( ec, error::invalid_escape );
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sv.remove_prefix( b - start );
|
||||
return string_view( start, b );
|
||||
}
|
||||
|
||||
value*
|
||||
if_contains_token(object const& obj, pointer_token token)
|
||||
{
|
||||
if( obj.empty() )
|
||||
return nullptr;
|
||||
|
||||
auto const it = detail::find_in_object(obj, token).first;
|
||||
if( !it )
|
||||
return nullptr;
|
||||
|
||||
return &it->value();
|
||||
}
|
||||
|
||||
template<
|
||||
class Value,
|
||||
class OnObject,
|
||||
class OnArray,
|
||||
class OnScalar >
|
||||
Value*
|
||||
walk_pointer(
|
||||
Value& jv,
|
||||
string_view sv,
|
||||
error_code& ec,
|
||||
OnObject on_object,
|
||||
OnArray on_array,
|
||||
OnScalar on_scalar)
|
||||
{
|
||||
ec.clear();
|
||||
|
||||
string_view segment = detail::next_segment( sv, ec );
|
||||
|
||||
Value* result = &jv;
|
||||
while( true )
|
||||
{
|
||||
if( ec.failed() )
|
||||
return nullptr;
|
||||
|
||||
if( !result )
|
||||
{
|
||||
BOOST_JSON_FAIL(ec, error::not_found);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if( segment.empty() )
|
||||
break;
|
||||
|
||||
switch( result->kind() )
|
||||
{
|
||||
case kind::object: {
|
||||
auto& obj = result->get_object();
|
||||
|
||||
detail::pointer_token const token( segment );
|
||||
segment = detail::next_segment( sv, ec );
|
||||
|
||||
result = on_object( obj, token );
|
||||
break;
|
||||
}
|
||||
case kind::array: {
|
||||
auto const index = detail::parse_number_token( segment, ec );
|
||||
segment = detail::next_segment( sv, ec );
|
||||
|
||||
auto& arr = result->get_array();
|
||||
result = on_array( arr, index, ec );
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if( on_scalar( *result, segment ) )
|
||||
break;
|
||||
BOOST_JSON_FAIL( ec, error::value_is_scalar );
|
||||
}}
|
||||
}
|
||||
|
||||
BOOST_ASSERT( result );
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
value const&
|
||||
value::at_pointer(string_view ptr) const&
|
||||
{
|
||||
error_code ec;
|
||||
auto const found = find_pointer(ptr, ec);
|
||||
if( !found )
|
||||
detail::throw_system_error( ec );
|
||||
return *found;
|
||||
}
|
||||
|
||||
value const*
|
||||
value::find_pointer( string_view sv, error_code& ec ) const noexcept
|
||||
{
|
||||
return detail::walk_pointer(
|
||||
*this,
|
||||
sv,
|
||||
ec,
|
||||
[]( object const& obj, detail::pointer_token token )
|
||||
{
|
||||
return detail::if_contains_token(obj, token);
|
||||
},
|
||||
[]( array const& arr, std::size_t index, error_code& ec )
|
||||
-> value const*
|
||||
{
|
||||
if( ec )
|
||||
return nullptr;
|
||||
|
||||
return arr.if_contains(index);
|
||||
},
|
||||
[]( value const&, string_view)
|
||||
{
|
||||
return std::false_type();
|
||||
});
|
||||
}
|
||||
|
||||
value*
|
||||
value::find_pointer(string_view ptr, error_code& ec) noexcept
|
||||
{
|
||||
value const& self = *this;
|
||||
return const_cast<value*>(self.find_pointer(ptr, ec));
|
||||
}
|
||||
|
||||
value const*
|
||||
value::find_pointer(string_view ptr, std::error_code& ec) const noexcept
|
||||
{
|
||||
error_code jec;
|
||||
value const* result = find_pointer(ptr, jec);
|
||||
ec = jec;
|
||||
return result;
|
||||
}
|
||||
|
||||
value*
|
||||
value::find_pointer(string_view ptr, std::error_code& ec) noexcept
|
||||
{
|
||||
value const& self = *this;
|
||||
return const_cast<value*>(self.find_pointer(ptr, ec));
|
||||
}
|
||||
|
||||
value*
|
||||
value::set_at_pointer(
|
||||
string_view sv,
|
||||
value_ref ref,
|
||||
error_code& ec,
|
||||
set_pointer_options const& opts )
|
||||
{
|
||||
value* result = detail::walk_pointer(
|
||||
*this,
|
||||
sv,
|
||||
ec,
|
||||
[]( object& obj, detail::pointer_token token)
|
||||
{
|
||||
if( !obj.empty() )
|
||||
{
|
||||
key_value_pair* kv = detail::find_in_object( obj, token ).first;
|
||||
if( kv )
|
||||
return &kv->value();
|
||||
}
|
||||
|
||||
string key( token.begin(), token.end(), obj.storage() );
|
||||
return &obj.emplace( std::move(key), nullptr ).first->value();
|
||||
},
|
||||
[ &opts ]( array& arr, std::size_t index, error_code& ec ) -> value*
|
||||
{
|
||||
if( ec == error::past_the_end )
|
||||
index = arr.size();
|
||||
else if( ec.failed() )
|
||||
return nullptr;
|
||||
|
||||
if( index >= arr.size() )
|
||||
{
|
||||
std::size_t const n = index - arr.size();
|
||||
if( n >= opts.max_created_elements )
|
||||
return nullptr;
|
||||
|
||||
arr.resize( arr.size() + n + 1 );
|
||||
}
|
||||
|
||||
ec.clear();
|
||||
return arr.data() + index;
|
||||
},
|
||||
[ &opts ]( value& jv, string_view segment )
|
||||
{
|
||||
if( jv.is_null() || opts.replace_any_scalar )
|
||||
{
|
||||
if( opts.create_arrays )
|
||||
{
|
||||
error_code ec;
|
||||
detail::parse_number_token( segment, ec );
|
||||
if( !ec.failed() || ec == error::past_the_end )
|
||||
{
|
||||
jv = array( jv.storage() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if( opts.create_objects )
|
||||
{
|
||||
jv = object( jv.storage() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if( result )
|
||||
*result = ref.make_value( storage() );
|
||||
return result;
|
||||
}
|
||||
|
||||
value*
|
||||
value::set_at_pointer(
|
||||
string_view sv,
|
||||
value_ref ref,
|
||||
std::error_code& ec,
|
||||
set_pointer_options const& opts )
|
||||
{
|
||||
error_code jec;
|
||||
value* result = set_at_pointer( sv, ref, jec, opts );
|
||||
ec = jec;
|
||||
return result;
|
||||
}
|
||||
|
||||
value&
|
||||
value::set_at_pointer(
|
||||
string_view sv, value_ref ref, set_pointer_options const& opts )
|
||||
{
|
||||
error_code ec;
|
||||
value* result = set_at_pointer( sv, ref, ec, opts );
|
||||
if( !result )
|
||||
detail::throw_system_error( ec );
|
||||
return *result;
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_JSON_IMPL_POINTER_IPP
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_SERIALIZE_IPP
|
||||
#define BOOST_JSON_IMPL_SERIALIZE_IPP
|
||||
|
||||
#include <boost/json/serialize.hpp>
|
||||
#include <boost/json/serializer.hpp>
|
||||
#include <ostream>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
namespace {
|
||||
|
||||
int serialize_xalloc = std::ios::xalloc();
|
||||
|
||||
enum class serialize_stream_flags : long
|
||||
{
|
||||
allow_infinity_and_nan = 1,
|
||||
};
|
||||
|
||||
std::underlying_type<serialize_stream_flags>::type
|
||||
to_bitmask( serialize_options const& opts )
|
||||
{
|
||||
using E = serialize_stream_flags;
|
||||
using I = std::underlying_type<E>::type;
|
||||
return (opts.allow_infinity_and_nan
|
||||
? static_cast<I>(E::allow_infinity_and_nan) : 0);
|
||||
}
|
||||
|
||||
serialize_options
|
||||
get_stream_flags( std::ostream& os )
|
||||
{
|
||||
auto const flags = os.iword(serialize_xalloc);
|
||||
|
||||
serialize_options opts;
|
||||
using E = serialize_stream_flags;
|
||||
using I = std::underlying_type<E>::type;
|
||||
opts.allow_infinity_and_nan =
|
||||
flags & static_cast<I>(E::allow_infinity_and_nan);
|
||||
return opts;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static
|
||||
void
|
||||
serialize_impl(
|
||||
std::string& s,
|
||||
serializer& sr)
|
||||
{
|
||||
// serialize to a small buffer to avoid
|
||||
// the first few allocations in std::string
|
||||
char buf[BOOST_JSON_STACK_BUFFER_SIZE];
|
||||
string_view sv;
|
||||
sv = sr.read(buf);
|
||||
if(sr.done())
|
||||
{
|
||||
// fast path
|
||||
s.append(
|
||||
sv.data(), sv.size());
|
||||
return;
|
||||
}
|
||||
std::size_t len = sv.size();
|
||||
s.reserve(len * 2);
|
||||
s.resize(s.capacity());
|
||||
BOOST_ASSERT(
|
||||
s.size() >= len * 2);
|
||||
std::memcpy(&s[0],
|
||||
sv.data(), sv.size());
|
||||
auto const lim =
|
||||
s.max_size() / 2;
|
||||
for(;;)
|
||||
{
|
||||
sv = sr.read(
|
||||
&s[0] + len,
|
||||
s.size() - len);
|
||||
len += sv.size();
|
||||
if(sr.done())
|
||||
break;
|
||||
// growth factor 2x
|
||||
if(s.size() < lim)
|
||||
s.resize(s.size() * 2);
|
||||
else
|
||||
s.resize(2 * lim);
|
||||
}
|
||||
s.resize(len);
|
||||
}
|
||||
|
||||
std::string
|
||||
serialize(
|
||||
value const& jv,
|
||||
serialize_options const& opts)
|
||||
{
|
||||
unsigned char buf[256];
|
||||
serializer sr(
|
||||
storage_ptr(),
|
||||
buf,
|
||||
sizeof(buf),
|
||||
opts);
|
||||
sr.reset(&jv);
|
||||
std::string s;
|
||||
serialize_impl(s, sr);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string
|
||||
serialize(
|
||||
array const& arr,
|
||||
serialize_options const& opts)
|
||||
{
|
||||
unsigned char buf[256];
|
||||
serializer sr(
|
||||
storage_ptr(),
|
||||
buf,
|
||||
sizeof(buf),
|
||||
opts);
|
||||
std::string s;
|
||||
sr.reset(&arr);
|
||||
serialize_impl(s, sr);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string
|
||||
serialize(
|
||||
object const& obj,
|
||||
serialize_options const& opts)
|
||||
{
|
||||
unsigned char buf[256];
|
||||
serializer sr(
|
||||
storage_ptr(),
|
||||
buf,
|
||||
sizeof(buf),
|
||||
opts);
|
||||
std::string s;
|
||||
sr.reset(&obj);
|
||||
serialize_impl(s, sr);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string
|
||||
serialize(
|
||||
string const& str,
|
||||
serialize_options const& opts)
|
||||
{
|
||||
return serialize( str.subview(), opts );
|
||||
}
|
||||
|
||||
// this is here for key_value_pair::key()
|
||||
std::string
|
||||
serialize(
|
||||
string_view sv,
|
||||
serialize_options const& opts)
|
||||
{
|
||||
unsigned char buf[256];
|
||||
serializer sr(
|
||||
storage_ptr(),
|
||||
buf,
|
||||
sizeof(buf),
|
||||
opts);
|
||||
std::string s;
|
||||
sr.reset(sv);
|
||||
serialize_impl(s, sr);
|
||||
return s;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
//[example_operator_lt__lt_
|
||||
// Serialize a value into an output stream
|
||||
|
||||
std::ostream&
|
||||
operator<<( std::ostream& os, value const& jv )
|
||||
{
|
||||
// Create a serializer
|
||||
serializer sr( get_stream_flags(os) );
|
||||
|
||||
// Set the serializer up for our value
|
||||
sr.reset( &jv );
|
||||
|
||||
// Loop until all output is produced.
|
||||
while( ! sr.done() )
|
||||
{
|
||||
// Use a local buffer to avoid allocation.
|
||||
char buf[ BOOST_JSON_STACK_BUFFER_SIZE ];
|
||||
|
||||
// Fill our buffer with serialized characters and write it to the output stream.
|
||||
os << sr.read( buf );
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
//]
|
||||
|
||||
static
|
||||
void
|
||||
to_ostream(
|
||||
std::ostream& os,
|
||||
serializer& sr)
|
||||
{
|
||||
while(! sr.done())
|
||||
{
|
||||
char buf[BOOST_JSON_STACK_BUFFER_SIZE];
|
||||
auto s = sr.read(buf);
|
||||
os.write(s.data(), s.size());
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(
|
||||
std::ostream& os,
|
||||
array const& arr)
|
||||
{
|
||||
serializer sr( get_stream_flags(os) );
|
||||
sr.reset(&arr);
|
||||
to_ostream(os, sr);
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(
|
||||
std::ostream& os,
|
||||
object const& obj)
|
||||
{
|
||||
serializer sr( get_stream_flags(os) );
|
||||
sr.reset(&obj);
|
||||
to_ostream(os, sr);
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(
|
||||
std::ostream& os,
|
||||
string const& str)
|
||||
{
|
||||
serializer sr( get_stream_flags(os) );
|
||||
sr.reset(&str);
|
||||
to_ostream(os, sr);
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<( std::ostream& os, serialize_options const& opts )
|
||||
{
|
||||
os.iword(serialize_xalloc) = to_bitmask(opts);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+839
@@ -0,0 +1,839 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_SERIALIZER_IPP
|
||||
#define BOOST_JSON_IMPL_SERIALIZER_IPP
|
||||
|
||||
#include <boost/json/serializer.hpp>
|
||||
#include <boost/json/detail/format.hpp>
|
||||
#include <boost/json/detail/sse2.hpp>
|
||||
#include <ostream>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
enum class serializer::state : char
|
||||
{
|
||||
nul1, nul2, nul3, nul4,
|
||||
tru1, tru2, tru3, tru4,
|
||||
fal1, fal2, fal3, fal4, fal5,
|
||||
str1, str2, str3, str4, esc1,
|
||||
utf1, utf2, utf3, utf4, utf5,
|
||||
num,
|
||||
arr1, arr2, arr3, arr4,
|
||||
obj1, obj2, obj3, obj4, obj5, obj6
|
||||
};
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
serializer::
|
||||
serializer(
|
||||
storage_ptr sp,
|
||||
unsigned char* buf,
|
||||
std::size_t buf_size,
|
||||
serialize_options const& opts) noexcept
|
||||
: st_(
|
||||
std::move(sp),
|
||||
buf,
|
||||
buf_size)
|
||||
, opts_(opts)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
serializer::
|
||||
suspend(state st)
|
||||
{
|
||||
st_.push(st);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
serializer::
|
||||
suspend(
|
||||
state st,
|
||||
array::const_iterator it,
|
||||
array const* pa)
|
||||
{
|
||||
st_.push(pa);
|
||||
st_.push(it);
|
||||
st_.push(st);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
serializer::
|
||||
suspend(
|
||||
state st,
|
||||
object::const_iterator it,
|
||||
object const* po)
|
||||
{
|
||||
st_.push(po);
|
||||
st_.push(it);
|
||||
st_.push(st);
|
||||
return false;
|
||||
}
|
||||
|
||||
template<bool StackEmpty>
|
||||
bool
|
||||
serializer::
|
||||
write_null(stream& ss0)
|
||||
{
|
||||
local_stream ss(ss0);
|
||||
if(! StackEmpty && ! st_.empty())
|
||||
{
|
||||
state st;
|
||||
st_.pop(st);
|
||||
switch(st)
|
||||
{
|
||||
default:
|
||||
case state::nul1: goto do_nul1;
|
||||
case state::nul2: goto do_nul2;
|
||||
case state::nul3: goto do_nul3;
|
||||
case state::nul4: goto do_nul4;
|
||||
}
|
||||
}
|
||||
do_nul1:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('n');
|
||||
else
|
||||
return suspend(state::nul1);
|
||||
do_nul2:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('u');
|
||||
else
|
||||
return suspend(state::nul2);
|
||||
do_nul3:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('l');
|
||||
else
|
||||
return suspend(state::nul3);
|
||||
do_nul4:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('l');
|
||||
else
|
||||
return suspend(state::nul4);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<bool StackEmpty>
|
||||
bool
|
||||
serializer::
|
||||
write_true(stream& ss0)
|
||||
{
|
||||
local_stream ss(ss0);
|
||||
if(! StackEmpty && ! st_.empty())
|
||||
{
|
||||
state st;
|
||||
st_.pop(st);
|
||||
switch(st)
|
||||
{
|
||||
default:
|
||||
case state::tru1: goto do_tru1;
|
||||
case state::tru2: goto do_tru2;
|
||||
case state::tru3: goto do_tru3;
|
||||
case state::tru4: goto do_tru4;
|
||||
}
|
||||
}
|
||||
do_tru1:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('t');
|
||||
else
|
||||
return suspend(state::tru1);
|
||||
do_tru2:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('r');
|
||||
else
|
||||
return suspend(state::tru2);
|
||||
do_tru3:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('u');
|
||||
else
|
||||
return suspend(state::tru3);
|
||||
do_tru4:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('e');
|
||||
else
|
||||
return suspend(state::tru4);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<bool StackEmpty>
|
||||
bool
|
||||
serializer::
|
||||
write_false(stream& ss0)
|
||||
{
|
||||
local_stream ss(ss0);
|
||||
if(! StackEmpty && ! st_.empty())
|
||||
{
|
||||
state st;
|
||||
st_.pop(st);
|
||||
switch(st)
|
||||
{
|
||||
default:
|
||||
case state::fal1: goto do_fal1;
|
||||
case state::fal2: goto do_fal2;
|
||||
case state::fal3: goto do_fal3;
|
||||
case state::fal4: goto do_fal4;
|
||||
case state::fal5: goto do_fal5;
|
||||
}
|
||||
}
|
||||
do_fal1:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('f');
|
||||
else
|
||||
return suspend(state::fal1);
|
||||
do_fal2:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('a');
|
||||
else
|
||||
return suspend(state::fal2);
|
||||
do_fal3:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('l');
|
||||
else
|
||||
return suspend(state::fal3);
|
||||
do_fal4:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('s');
|
||||
else
|
||||
return suspend(state::fal4);
|
||||
do_fal5:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('e');
|
||||
else
|
||||
return suspend(state::fal5);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<bool StackEmpty>
|
||||
bool
|
||||
serializer::
|
||||
write_string(stream& ss0)
|
||||
{
|
||||
local_stream ss(ss0);
|
||||
local_const_stream cs(cs0_);
|
||||
if(! StackEmpty && ! st_.empty())
|
||||
{
|
||||
state st;
|
||||
st_.pop(st);
|
||||
switch(st)
|
||||
{
|
||||
default:
|
||||
case state::str1: goto do_str1;
|
||||
case state::str2: goto do_str2;
|
||||
case state::str3: goto do_str3;
|
||||
case state::str4: goto do_str4;
|
||||
case state::esc1: goto do_esc1;
|
||||
case state::utf1: goto do_utf1;
|
||||
case state::utf2: goto do_utf2;
|
||||
case state::utf3: goto do_utf3;
|
||||
case state::utf4: goto do_utf4;
|
||||
case state::utf5: goto do_utf5;
|
||||
}
|
||||
}
|
||||
static constexpr char hex[] = "0123456789abcdef";
|
||||
static constexpr char esc[] =
|
||||
"uuuuuuuubtnufruuuuuuuuuuuuuuuuuu"
|
||||
"\0\0\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\\\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||
|
||||
// opening quote
|
||||
do_str1:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('\x22'); // '"'
|
||||
else
|
||||
return suspend(state::str1);
|
||||
|
||||
// fast loop,
|
||||
// copy unescaped
|
||||
do_str2:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
{
|
||||
std::size_t n = cs.remain();
|
||||
if(BOOST_JSON_LIKELY(n > 0))
|
||||
{
|
||||
if(ss.remain() > n)
|
||||
n = detail::count_unescaped(
|
||||
cs.data(), n);
|
||||
else
|
||||
n = detail::count_unescaped(
|
||||
cs.data(), ss.remain());
|
||||
if(n > 0)
|
||||
{
|
||||
ss.append(cs.data(), n);
|
||||
cs.skip(n);
|
||||
if(! ss)
|
||||
return suspend(state::str2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ss.append('\x22'); // '"'
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return suspend(state::str2);
|
||||
}
|
||||
|
||||
// slow loop,
|
||||
// handle escapes
|
||||
do_str3:
|
||||
while(BOOST_JSON_LIKELY(ss))
|
||||
{
|
||||
if(BOOST_JSON_LIKELY(cs))
|
||||
{
|
||||
auto const ch = *cs;
|
||||
auto const c = esc[static_cast<
|
||||
unsigned char>(ch)];
|
||||
++cs;
|
||||
if(! c)
|
||||
{
|
||||
ss.append(ch);
|
||||
}
|
||||
else if(c != 'u')
|
||||
{
|
||||
ss.append('\\');
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
{
|
||||
ss.append(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
buf_[0] = c;
|
||||
return suspend(
|
||||
state::esc1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(BOOST_JSON_LIKELY(
|
||||
ss.remain() >= 6))
|
||||
{
|
||||
ss.append("\\u00", 4);
|
||||
ss.append(hex[static_cast<
|
||||
unsigned char>(ch) >> 4]);
|
||||
ss.append(hex[static_cast<
|
||||
unsigned char>(ch) & 15]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ss.append('\\');
|
||||
buf_[0] = hex[static_cast<
|
||||
unsigned char>(ch) >> 4];
|
||||
buf_[1] = hex[static_cast<
|
||||
unsigned char>(ch) & 15];
|
||||
goto do_utf1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ss.append('\x22'); // '"'
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return suspend(state::str3);
|
||||
|
||||
do_str4:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('\x22'); // '"'
|
||||
else
|
||||
return suspend(state::str4);
|
||||
|
||||
do_esc1:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append(buf_[0]);
|
||||
else
|
||||
return suspend(state::esc1);
|
||||
goto do_str3;
|
||||
|
||||
do_utf1:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('u');
|
||||
else
|
||||
return suspend(state::utf1);
|
||||
do_utf2:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('0');
|
||||
else
|
||||
return suspend(state::utf2);
|
||||
do_utf3:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('0');
|
||||
else
|
||||
return suspend(state::utf3);
|
||||
do_utf4:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append(buf_[0]);
|
||||
else
|
||||
return suspend(state::utf4);
|
||||
do_utf5:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append(buf_[1]);
|
||||
else
|
||||
return suspend(state::utf5);
|
||||
goto do_str3;
|
||||
}
|
||||
|
||||
template<bool StackEmpty>
|
||||
bool
|
||||
serializer::
|
||||
write_number(stream& ss0)
|
||||
{
|
||||
local_stream ss(ss0);
|
||||
if(StackEmpty || st_.empty())
|
||||
{
|
||||
switch(jv_->kind())
|
||||
{
|
||||
default:
|
||||
case kind::int64:
|
||||
if(BOOST_JSON_LIKELY(
|
||||
ss.remain() >=
|
||||
detail::max_number_chars))
|
||||
{
|
||||
ss.advance(detail::format_int64(
|
||||
ss.data(), jv_->get_int64()));
|
||||
return true;
|
||||
}
|
||||
cs0_ = { buf_, detail::format_int64(
|
||||
buf_, jv_->get_int64()) };
|
||||
break;
|
||||
|
||||
case kind::uint64:
|
||||
if(BOOST_JSON_LIKELY(
|
||||
ss.remain() >=
|
||||
detail::max_number_chars))
|
||||
{
|
||||
ss.advance(detail::format_uint64(
|
||||
ss.data(), jv_->get_uint64()));
|
||||
return true;
|
||||
}
|
||||
cs0_ = { buf_, detail::format_uint64(
|
||||
buf_, jv_->get_uint64()) };
|
||||
break;
|
||||
|
||||
case kind::double_:
|
||||
if(BOOST_JSON_LIKELY(
|
||||
ss.remain() >=
|
||||
detail::max_number_chars))
|
||||
{
|
||||
ss.advance(
|
||||
detail::format_double(
|
||||
ss.data(),
|
||||
jv_->get_double(),
|
||||
opts_.allow_infinity_and_nan));
|
||||
return true;
|
||||
}
|
||||
cs0_ = { buf_, detail::format_double(
|
||||
buf_, jv_->get_double(), opts_.allow_infinity_and_nan) };
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state st;
|
||||
st_.pop(st);
|
||||
BOOST_ASSERT(
|
||||
st == state::num);
|
||||
}
|
||||
auto const n = ss.remain();
|
||||
if(n < cs0_.remain())
|
||||
{
|
||||
ss.append(cs0_.data(), n);
|
||||
cs0_.skip(n);
|
||||
return suspend(state::num);
|
||||
}
|
||||
ss.append(
|
||||
cs0_.data(), cs0_.remain());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<bool StackEmpty>
|
||||
bool
|
||||
serializer::
|
||||
write_array(stream& ss0)
|
||||
{
|
||||
array const* pa;
|
||||
local_stream ss(ss0);
|
||||
array::const_iterator it;
|
||||
array::const_iterator end;
|
||||
if(StackEmpty || st_.empty())
|
||||
{
|
||||
pa = pa_;
|
||||
it = pa->begin();
|
||||
end = pa->end();
|
||||
}
|
||||
else
|
||||
{
|
||||
state st;
|
||||
st_.pop(st);
|
||||
st_.pop(it);
|
||||
st_.pop(pa);
|
||||
end = pa->end();
|
||||
switch(st)
|
||||
{
|
||||
default:
|
||||
case state::arr1: goto do_arr1;
|
||||
case state::arr2: goto do_arr2;
|
||||
case state::arr3: goto do_arr3;
|
||||
case state::arr4: goto do_arr4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
do_arr1:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('[');
|
||||
else
|
||||
return suspend(
|
||||
state::arr1, it, pa);
|
||||
if(it == end)
|
||||
goto do_arr4;
|
||||
for(;;)
|
||||
{
|
||||
do_arr2:
|
||||
jv_ = &*it;
|
||||
if(! write_value<StackEmpty>(ss))
|
||||
return suspend(
|
||||
state::arr2, it, pa);
|
||||
if(BOOST_JSON_UNLIKELY(
|
||||
++it == end))
|
||||
break;
|
||||
do_arr3:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append(',');
|
||||
else
|
||||
return suspend(
|
||||
state::arr3, it, pa);
|
||||
}
|
||||
do_arr4:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append(']');
|
||||
else
|
||||
return suspend(
|
||||
state::arr4, it, pa);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<bool StackEmpty>
|
||||
bool
|
||||
serializer::
|
||||
write_object(stream& ss0)
|
||||
{
|
||||
object const* po;
|
||||
local_stream ss(ss0);
|
||||
object::const_iterator it;
|
||||
object::const_iterator end;
|
||||
if(StackEmpty || st_.empty())
|
||||
{
|
||||
po = po_;
|
||||
it = po->begin();
|
||||
end = po->end();
|
||||
}
|
||||
else
|
||||
{
|
||||
state st;
|
||||
st_.pop(st);
|
||||
st_.pop(it);
|
||||
st_.pop(po);
|
||||
end = po->end();
|
||||
switch(st)
|
||||
{
|
||||
default:
|
||||
case state::obj1: goto do_obj1;
|
||||
case state::obj2: goto do_obj2;
|
||||
case state::obj3: goto do_obj3;
|
||||
case state::obj4: goto do_obj4;
|
||||
case state::obj5: goto do_obj5;
|
||||
case state::obj6: goto do_obj6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
do_obj1:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append('{');
|
||||
else
|
||||
return suspend(
|
||||
state::obj1, it, po);
|
||||
if(BOOST_JSON_UNLIKELY(
|
||||
it == end))
|
||||
goto do_obj6;
|
||||
for(;;)
|
||||
{
|
||||
cs0_ = {
|
||||
it->key().data(),
|
||||
it->key().size() };
|
||||
do_obj2:
|
||||
if(BOOST_JSON_UNLIKELY(
|
||||
! write_string<StackEmpty>(ss)))
|
||||
return suspend(
|
||||
state::obj2, it, po);
|
||||
do_obj3:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append(':');
|
||||
else
|
||||
return suspend(
|
||||
state::obj3, it, po);
|
||||
do_obj4:
|
||||
jv_ = &it->value();
|
||||
if(BOOST_JSON_UNLIKELY(
|
||||
! write_value<StackEmpty>(ss)))
|
||||
return suspend(
|
||||
state::obj4, it, po);
|
||||
++it;
|
||||
if(BOOST_JSON_UNLIKELY(it == end))
|
||||
break;
|
||||
do_obj5:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
ss.append(',');
|
||||
else
|
||||
return suspend(
|
||||
state::obj5, it, po);
|
||||
}
|
||||
do_obj6:
|
||||
if(BOOST_JSON_LIKELY(ss))
|
||||
{
|
||||
ss.append('}');
|
||||
return true;
|
||||
}
|
||||
return suspend(
|
||||
state::obj6, it, po);
|
||||
}
|
||||
|
||||
template<bool StackEmpty>
|
||||
bool
|
||||
serializer::
|
||||
write_value(stream& ss)
|
||||
{
|
||||
if(StackEmpty || st_.empty())
|
||||
{
|
||||
auto const& jv(*jv_);
|
||||
switch(jv.kind())
|
||||
{
|
||||
default:
|
||||
case kind::object:
|
||||
po_ = &jv.get_object();
|
||||
return write_object<true>(ss);
|
||||
|
||||
case kind::array:
|
||||
pa_ = &jv.get_array();
|
||||
return write_array<true>(ss);
|
||||
|
||||
case kind::string:
|
||||
{
|
||||
auto const& js = jv.get_string();
|
||||
cs0_ = { js.data(), js.size() };
|
||||
return write_string<true>(ss);
|
||||
}
|
||||
|
||||
case kind::int64:
|
||||
case kind::uint64:
|
||||
case kind::double_:
|
||||
return write_number<true>(ss);
|
||||
|
||||
case kind::bool_:
|
||||
if(jv.get_bool())
|
||||
{
|
||||
if(BOOST_JSON_LIKELY(
|
||||
ss.remain() >= 4))
|
||||
{
|
||||
ss.append("true", 4);
|
||||
return true;
|
||||
}
|
||||
return write_true<true>(ss);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(BOOST_JSON_LIKELY(
|
||||
ss.remain() >= 5))
|
||||
{
|
||||
ss.append("false", 5);
|
||||
return true;
|
||||
}
|
||||
return write_false<true>(ss);
|
||||
}
|
||||
|
||||
case kind::null:
|
||||
if(BOOST_JSON_LIKELY(
|
||||
ss.remain() >= 4))
|
||||
{
|
||||
ss.append("null", 4);
|
||||
return true;
|
||||
}
|
||||
return write_null<true>(ss);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state st;
|
||||
st_.peek(st);
|
||||
switch(st)
|
||||
{
|
||||
default:
|
||||
case state::nul1: case state::nul2:
|
||||
case state::nul3: case state::nul4:
|
||||
return write_null<StackEmpty>(ss);
|
||||
|
||||
case state::tru1: case state::tru2:
|
||||
case state::tru3: case state::tru4:
|
||||
return write_true<StackEmpty>(ss);
|
||||
|
||||
case state::fal1: case state::fal2:
|
||||
case state::fal3: case state::fal4:
|
||||
case state::fal5:
|
||||
return write_false<StackEmpty>(ss);
|
||||
|
||||
case state::str1: case state::str2:
|
||||
case state::str3: case state::str4:
|
||||
case state::esc1:
|
||||
case state::utf1: case state::utf2:
|
||||
case state::utf3: case state::utf4:
|
||||
case state::utf5:
|
||||
return write_string<StackEmpty>(ss);
|
||||
|
||||
case state::num:
|
||||
return write_number<StackEmpty>(ss);
|
||||
|
||||
case state::arr1: case state::arr2:
|
||||
case state::arr3: case state::arr4:
|
||||
return write_array<StackEmpty>(ss);
|
||||
|
||||
case state::obj1: case state::obj2:
|
||||
case state::obj3: case state::obj4:
|
||||
case state::obj5: case state::obj6:
|
||||
return write_object<StackEmpty>(ss);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string_view
|
||||
serializer::
|
||||
read_some(
|
||||
char* dest, std::size_t size)
|
||||
{
|
||||
// If this goes off it means you forgot
|
||||
// to call reset() before seriailzing a
|
||||
// new value, or you never checked done()
|
||||
// to see if you should stop.
|
||||
BOOST_ASSERT(! done_);
|
||||
|
||||
stream ss(dest, size);
|
||||
if(st_.empty())
|
||||
(this->*fn0_)(ss);
|
||||
else
|
||||
(this->*fn1_)(ss);
|
||||
if(st_.empty())
|
||||
{
|
||||
done_ = true;
|
||||
jv_ = nullptr;
|
||||
}
|
||||
return string_view(
|
||||
dest, ss.used(dest));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
serializer::
|
||||
serializer( serialize_options const& opts ) noexcept
|
||||
: opts_(opts)
|
||||
{
|
||||
// ensure room for \uXXXX escape plus one
|
||||
BOOST_STATIC_ASSERT(
|
||||
sizeof(serializer::buf_) >= 7);
|
||||
}
|
||||
|
||||
void
|
||||
serializer::
|
||||
reset(value const* p) noexcept
|
||||
{
|
||||
pv_ = p;
|
||||
fn0_ = &serializer::write_value<true>;
|
||||
fn1_ = &serializer::write_value<false>;
|
||||
|
||||
jv_ = p;
|
||||
st_.clear();
|
||||
done_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
serializer::
|
||||
reset(array const* p) noexcept
|
||||
{
|
||||
pa_ = p;
|
||||
fn0_ = &serializer::write_array<true>;
|
||||
fn1_ = &serializer::write_array<false>;
|
||||
st_.clear();
|
||||
done_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
serializer::
|
||||
reset(object const* p) noexcept
|
||||
{
|
||||
po_ = p;
|
||||
fn0_ = &serializer::write_object<true>;
|
||||
fn1_ = &serializer::write_object<false>;
|
||||
st_.clear();
|
||||
done_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
serializer::
|
||||
reset(string const* p) noexcept
|
||||
{
|
||||
cs0_ = { p->data(), p->size() };
|
||||
fn0_ = &serializer::write_string<true>;
|
||||
fn1_ = &serializer::write_string<false>;
|
||||
st_.clear();
|
||||
done_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
serializer::
|
||||
reset(string_view sv) noexcept
|
||||
{
|
||||
cs0_ = { sv.data(), sv.size() };
|
||||
fn0_ = &serializer::write_string<true>;
|
||||
fn1_ = &serializer::write_string<false>;
|
||||
st_.clear();
|
||||
done_ = false;
|
||||
}
|
||||
|
||||
string_view
|
||||
serializer::
|
||||
read(char* dest, std::size_t size)
|
||||
{
|
||||
if(! jv_)
|
||||
{
|
||||
static value const null;
|
||||
jv_ = &null;
|
||||
}
|
||||
return read_some(dest, size);
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_STATIC_RESOURCE_IPP
|
||||
#define BOOST_JSON_IMPL_STATIC_RESOURCE_IPP
|
||||
|
||||
#include <boost/json/static_resource.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <boost/align/align.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
static_resource::
|
||||
static_resource(
|
||||
unsigned char* buffer,
|
||||
std::size_t size) noexcept
|
||||
: p_(buffer)
|
||||
, n_(size)
|
||||
, size_(size)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
static_resource::
|
||||
release() noexcept
|
||||
{
|
||||
p_ = reinterpret_cast<
|
||||
char*>(p_) - (size_ - n_);
|
||||
n_ = size_;
|
||||
}
|
||||
|
||||
void*
|
||||
static_resource::
|
||||
do_allocate(
|
||||
std::size_t n,
|
||||
std::size_t align)
|
||||
{
|
||||
auto p = alignment::align(
|
||||
align, n, p_, n_);
|
||||
if(! p)
|
||||
throw_exception( std::bad_alloc(), BOOST_CURRENT_LOCATION );
|
||||
p_ = reinterpret_cast<char*>(p) + n;
|
||||
n_ -= n;
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
static_resource::
|
||||
do_deallocate(
|
||||
void*,
|
||||
std::size_t,
|
||||
std::size_t)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
bool
|
||||
static_resource::
|
||||
do_is_equal(
|
||||
memory_resource const& mr) const noexcept
|
||||
{
|
||||
return this == &mr;
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_STREAM_PARSER_IPP
|
||||
#define BOOST_JSON_IMPL_STREAM_PARSER_IPP
|
||||
|
||||
#include <boost/json/stream_parser.hpp>
|
||||
#include <boost/json/basic_parser_impl.hpp>
|
||||
#include <boost/json/error.hpp>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
stream_parser::
|
||||
stream_parser(
|
||||
storage_ptr sp,
|
||||
parse_options const& opt,
|
||||
unsigned char* buffer,
|
||||
std::size_t size) noexcept
|
||||
: p_(
|
||||
opt,
|
||||
std::move(sp),
|
||||
buffer,
|
||||
size)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
stream_parser::
|
||||
stream_parser(
|
||||
storage_ptr sp,
|
||||
parse_options const& opt) noexcept
|
||||
: p_(
|
||||
opt,
|
||||
std::move(sp),
|
||||
nullptr,
|
||||
0)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void
|
||||
stream_parser::
|
||||
reset(storage_ptr sp) noexcept
|
||||
{
|
||||
p_.reset();
|
||||
p_.handler().st.reset(sp);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
stream_parser::
|
||||
write_some(
|
||||
char const* data,
|
||||
std::size_t size,
|
||||
error_code& ec)
|
||||
{
|
||||
return p_.write_some(
|
||||
true, data, size, ec);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
stream_parser::
|
||||
write_some(
|
||||
char const* data,
|
||||
std::size_t size,
|
||||
std::error_code& ec)
|
||||
{
|
||||
error_code jec;
|
||||
std::size_t const result = write_some(data, size, jec);
|
||||
ec = jec;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
stream_parser::
|
||||
write_some(
|
||||
char const* data,
|
||||
std::size_t size)
|
||||
{
|
||||
error_code ec;
|
||||
auto const n = write_some(
|
||||
data, size, ec);
|
||||
if(ec)
|
||||
detail::throw_system_error( ec );
|
||||
return n;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
stream_parser::
|
||||
write(
|
||||
char const* data,
|
||||
std::size_t size,
|
||||
error_code& ec)
|
||||
{
|
||||
auto const n = write_some(
|
||||
data, size, ec);
|
||||
if(! ec && n < size)
|
||||
{
|
||||
BOOST_JSON_FAIL(ec, error::extra_data);
|
||||
p_.fail(ec);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
stream_parser::
|
||||
write(
|
||||
char const* data,
|
||||
std::size_t size,
|
||||
std::error_code& ec)
|
||||
{
|
||||
error_code jec;
|
||||
std::size_t const result = write(data, size, jec);
|
||||
ec = jec;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
stream_parser::
|
||||
write(
|
||||
char const* data,
|
||||
std::size_t size)
|
||||
{
|
||||
error_code ec;
|
||||
auto const n = write(
|
||||
data, size, ec);
|
||||
if(ec)
|
||||
detail::throw_system_error( ec );
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
stream_parser::
|
||||
finish(error_code& ec)
|
||||
{
|
||||
p_.write_some(false, nullptr, 0, ec);
|
||||
}
|
||||
|
||||
void
|
||||
stream_parser::
|
||||
finish()
|
||||
{
|
||||
error_code ec;
|
||||
finish(ec);
|
||||
if(ec)
|
||||
detail::throw_system_error( ec );
|
||||
}
|
||||
|
||||
void
|
||||
stream_parser::
|
||||
finish(std::error_code& ec)
|
||||
{
|
||||
error_code jec;
|
||||
finish(jec);
|
||||
ec = jec;
|
||||
}
|
||||
|
||||
value
|
||||
stream_parser::
|
||||
release()
|
||||
{
|
||||
if(! p_.done())
|
||||
{
|
||||
// prevent undefined behavior
|
||||
finish();
|
||||
}
|
||||
return p_.handler().st.release();
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_STRING_HPP
|
||||
#define BOOST_JSON_IMPL_STRING_HPP
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
string::
|
||||
string(
|
||||
detail::key_t const&,
|
||||
string_view s,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
, impl_(detail::key_t{},
|
||||
s, sp_)
|
||||
{
|
||||
}
|
||||
|
||||
string::
|
||||
string(
|
||||
detail::key_t const&,
|
||||
string_view s1,
|
||||
string_view s2,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
, impl_(detail::key_t{},
|
||||
s1, s2, sp_)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt, class>
|
||||
string::
|
||||
string(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
, impl_(first, last, sp_,
|
||||
iter_cat<InputIt>{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt, class>
|
||||
string&
|
||||
string::
|
||||
assign(
|
||||
InputIt first,
|
||||
InputIt last)
|
||||
{
|
||||
assign(first, last,
|
||||
iter_cat<InputIt>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class InputIt, class>
|
||||
string&
|
||||
string::
|
||||
append(InputIt first, InputIt last)
|
||||
{
|
||||
append(first, last,
|
||||
iter_cat<InputIt>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
// KRYSTIAN TODO: this can be done without copies when
|
||||
// reallocation is not needed, when the iterator is a
|
||||
// FowardIterator or better, as we can use std::distance
|
||||
template<class InputIt, class>
|
||||
auto
|
||||
string::
|
||||
insert(
|
||||
size_type pos,
|
||||
InputIt first,
|
||||
InputIt last) ->
|
||||
string&
|
||||
{
|
||||
struct cleanup
|
||||
{
|
||||
detail::string_impl& s;
|
||||
storage_ptr const& sp;
|
||||
|
||||
~cleanup()
|
||||
{
|
||||
s.destroy(sp);
|
||||
}
|
||||
};
|
||||
|
||||
// We use the default storage because
|
||||
// the allocation is immediately freed.
|
||||
storage_ptr dsp;
|
||||
detail::string_impl tmp(
|
||||
first, last, dsp,
|
||||
iter_cat<InputIt>{});
|
||||
cleanup c{tmp, dsp};
|
||||
std::memcpy(
|
||||
impl_.insert_unchecked(pos, tmp.size(), sp_),
|
||||
tmp.data(),
|
||||
tmp.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
// KRYSTIAN TODO: this can be done without copies when
|
||||
// reallocation is not needed, when the iterator is a
|
||||
// FowardIterator or better, as we can use std::distance
|
||||
template<class InputIt, class>
|
||||
auto
|
||||
string::
|
||||
replace(
|
||||
const_iterator first,
|
||||
const_iterator last,
|
||||
InputIt first2,
|
||||
InputIt last2) ->
|
||||
string&
|
||||
{
|
||||
struct cleanup
|
||||
{
|
||||
detail::string_impl& s;
|
||||
storage_ptr const& sp;
|
||||
|
||||
~cleanup()
|
||||
{
|
||||
s.destroy(sp);
|
||||
}
|
||||
};
|
||||
|
||||
// We use the default storage because
|
||||
// the allocation is immediately freed.
|
||||
storage_ptr dsp;
|
||||
detail::string_impl tmp(
|
||||
first2, last2, dsp,
|
||||
iter_cat<InputIt>{});
|
||||
cleanup c{tmp, dsp};
|
||||
std::memcpy(
|
||||
impl_.replace_unchecked(
|
||||
first - begin(),
|
||||
last - first,
|
||||
tmp.size(),
|
||||
sp_),
|
||||
tmp.data(),
|
||||
tmp.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
template<class InputIt>
|
||||
void
|
||||
string::
|
||||
assign(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
std::random_access_iterator_tag)
|
||||
{
|
||||
auto dest = impl_.assign(static_cast<
|
||||
size_type>(last - first), sp_);
|
||||
while(first != last)
|
||||
*dest++ = *first++;
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
void
|
||||
string::
|
||||
assign(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
std::input_iterator_tag)
|
||||
{
|
||||
if(first == last)
|
||||
{
|
||||
impl_.term(0);
|
||||
return;
|
||||
}
|
||||
detail::string_impl tmp(
|
||||
first, last, sp_,
|
||||
std::input_iterator_tag{});
|
||||
impl_.destroy(sp_);
|
||||
impl_ = tmp;
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
void
|
||||
string::
|
||||
append(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
std::random_access_iterator_tag)
|
||||
{
|
||||
|
||||
auto const n = static_cast<
|
||||
size_type>(last - first);
|
||||
char* out = impl_.append(n, sp_);
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1900
|
||||
while( first != last )
|
||||
*out++ = *first++;
|
||||
#else
|
||||
std::copy(first, last, out);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
void
|
||||
string::
|
||||
append(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
std::input_iterator_tag)
|
||||
{
|
||||
struct cleanup
|
||||
{
|
||||
detail::string_impl& s;
|
||||
storage_ptr const& sp;
|
||||
|
||||
~cleanup()
|
||||
{
|
||||
s.destroy(sp);
|
||||
}
|
||||
};
|
||||
|
||||
// We use the default storage because
|
||||
// the allocation is immediately freed.
|
||||
storage_ptr dsp;
|
||||
detail::string_impl tmp(
|
||||
first, last, dsp,
|
||||
std::input_iterator_tag{});
|
||||
cleanup c{tmp, dsp};
|
||||
std::memcpy(
|
||||
impl_.append(tmp.size(), sp_),
|
||||
tmp.data(), tmp.size());
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+431
@@ -0,0 +1,431 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_STRING_IPP
|
||||
#define BOOST_JSON_IMPL_STRING_IPP
|
||||
|
||||
#include <boost/json/detail/except.hpp>
|
||||
#include <algorithm>
|
||||
#include <new>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Construction
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
string::
|
||||
string(
|
||||
std::size_t count,
|
||||
char ch,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
{
|
||||
assign(count, ch);
|
||||
}
|
||||
|
||||
string::
|
||||
string(
|
||||
char const* s,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
{
|
||||
assign(s);
|
||||
}
|
||||
|
||||
string::
|
||||
string(
|
||||
char const* s,
|
||||
std::size_t count,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
{
|
||||
assign(s, count);
|
||||
}
|
||||
|
||||
string::
|
||||
string(string const& other)
|
||||
: sp_(other.sp_)
|
||||
{
|
||||
assign(other);
|
||||
}
|
||||
|
||||
string::
|
||||
string(
|
||||
string const& other,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
{
|
||||
assign(other);
|
||||
}
|
||||
|
||||
string::
|
||||
string(
|
||||
string&& other,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
{
|
||||
assign(std::move(other));
|
||||
}
|
||||
|
||||
string::
|
||||
string(
|
||||
string_view s,
|
||||
storage_ptr sp)
|
||||
: sp_(std::move(sp))
|
||||
{
|
||||
assign(s);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Assignment
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
string&
|
||||
string::
|
||||
operator=(string const& other)
|
||||
{
|
||||
return assign(other);
|
||||
}
|
||||
|
||||
string&
|
||||
string::
|
||||
operator=(string&& other)
|
||||
{
|
||||
return assign(std::move(other));
|
||||
}
|
||||
|
||||
string&
|
||||
string::
|
||||
operator=(char const* s)
|
||||
{
|
||||
return assign(s);
|
||||
}
|
||||
|
||||
string&
|
||||
string::
|
||||
operator=(string_view s)
|
||||
{
|
||||
return assign(s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
string&
|
||||
string::
|
||||
assign(
|
||||
size_type count,
|
||||
char ch)
|
||||
{
|
||||
std::char_traits<char>::assign(
|
||||
impl_.assign(count, sp_),
|
||||
count,
|
||||
ch);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string&
|
||||
string::
|
||||
assign(
|
||||
string const& other)
|
||||
{
|
||||
if(this == &other)
|
||||
return *this;
|
||||
return assign(
|
||||
other.data(),
|
||||
other.size());
|
||||
}
|
||||
|
||||
string&
|
||||
string::
|
||||
assign(string&& other)
|
||||
{
|
||||
if( &other == this )
|
||||
return *this;
|
||||
|
||||
if(*sp_ == *other.sp_)
|
||||
{
|
||||
impl_.destroy(sp_);
|
||||
impl_ = other.impl_;
|
||||
::new(&other.impl_) detail::string_impl();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// copy
|
||||
return assign(other);
|
||||
}
|
||||
|
||||
string&
|
||||
string::
|
||||
assign(
|
||||
char const* s,
|
||||
size_type count)
|
||||
{
|
||||
std::char_traits<char>::copy(
|
||||
impl_.assign(count, sp_),
|
||||
s, count);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string&
|
||||
string::
|
||||
assign(
|
||||
char const* s)
|
||||
{
|
||||
return assign(s, std::char_traits<
|
||||
char>::length(s));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Capacity
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
string::
|
||||
shrink_to_fit()
|
||||
{
|
||||
impl_.shrink_to_fit(sp_);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Operations
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
string::
|
||||
clear() noexcept
|
||||
{
|
||||
impl_.term(0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
string::
|
||||
push_back(char ch)
|
||||
{
|
||||
*impl_.append(1, sp_) = ch;
|
||||
}
|
||||
|
||||
void
|
||||
string::
|
||||
pop_back()
|
||||
{
|
||||
back() = 0;
|
||||
impl_.size(impl_.size() - 1);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
string&
|
||||
string::
|
||||
append(size_type count, char ch)
|
||||
{
|
||||
std::char_traits<char>::assign(
|
||||
impl_.append(count, sp_),
|
||||
count, ch);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string&
|
||||
string::
|
||||
append(string_view sv)
|
||||
{
|
||||
std::char_traits<char>::copy(
|
||||
impl_.append(sv.size(), sp_),
|
||||
sv.data(), sv.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
string&
|
||||
string::
|
||||
insert(
|
||||
size_type pos,
|
||||
string_view sv)
|
||||
{
|
||||
impl_.insert(pos, sv.data(), sv.size(), sp_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string&
|
||||
string::
|
||||
insert(
|
||||
std::size_t pos,
|
||||
std::size_t count,
|
||||
char ch)
|
||||
{
|
||||
std::char_traits<char>::assign(
|
||||
impl_.insert_unchecked(pos, count, sp_),
|
||||
count, ch);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
string&
|
||||
string::
|
||||
replace(
|
||||
std::size_t pos,
|
||||
std::size_t count,
|
||||
string_view sv)
|
||||
{
|
||||
impl_.replace(pos, count, sv.data(), sv.size(), sp_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string&
|
||||
string::
|
||||
replace(
|
||||
std::size_t pos,
|
||||
std::size_t count,
|
||||
std::size_t count2,
|
||||
char ch)
|
||||
{
|
||||
std::char_traits<char>::assign(
|
||||
impl_.replace_unchecked(pos, count, count2, sp_),
|
||||
count2, ch);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
string&
|
||||
string::
|
||||
erase(
|
||||
size_type pos,
|
||||
size_type count)
|
||||
{
|
||||
if(pos > impl_.size())
|
||||
{
|
||||
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
|
||||
detail::throw_system_error( error::out_of_range, &loc );
|
||||
}
|
||||
if( count > impl_.size() - pos)
|
||||
count = impl_.size() - pos;
|
||||
std::char_traits<char>::move(
|
||||
impl_.data() + pos,
|
||||
impl_.data() + pos + count,
|
||||
impl_.size() - pos - count + 1);
|
||||
impl_.term(impl_.size() - count);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto
|
||||
string::
|
||||
erase(const_iterator pos) ->
|
||||
iterator
|
||||
{
|
||||
return erase(pos, pos+1);
|
||||
}
|
||||
|
||||
auto
|
||||
string::
|
||||
erase(
|
||||
const_iterator first,
|
||||
const_iterator last) ->
|
||||
iterator
|
||||
{
|
||||
auto const pos = first - begin();
|
||||
auto const count = last - first;
|
||||
erase(pos, count);
|
||||
return data() + pos;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
string::
|
||||
resize(size_type count, char ch)
|
||||
{
|
||||
if(count <= impl_.size())
|
||||
{
|
||||
impl_.term(count);
|
||||
return;
|
||||
}
|
||||
|
||||
reserve(count);
|
||||
std::char_traits<char>::assign(
|
||||
impl_.end(),
|
||||
count - impl_.size(),
|
||||
ch);
|
||||
grow(count - size());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
string::
|
||||
swap(string& other)
|
||||
{
|
||||
if(*sp_ == *other.sp_)
|
||||
{
|
||||
std::swap(impl_, other.impl_);
|
||||
return;
|
||||
}
|
||||
string temp1(
|
||||
std::move(*this), other.sp_);
|
||||
string temp2(
|
||||
std::move(other), sp_);
|
||||
this->~string();
|
||||
::new(this) string(pilfer(temp2));
|
||||
other.~string();
|
||||
::new(&other) string(pilfer(temp1));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
string::
|
||||
reserve_impl(size_type new_cap)
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
new_cap >= impl_.capacity());
|
||||
if(new_cap > impl_.capacity())
|
||||
{
|
||||
// grow
|
||||
new_cap = detail::string_impl::growth(
|
||||
new_cap, impl_.capacity());
|
||||
detail::string_impl tmp(new_cap, sp_);
|
||||
std::char_traits<char>::copy(tmp.data(),
|
||||
impl_.data(), impl_.size() + 1);
|
||||
tmp.size(impl_.size());
|
||||
impl_.destroy(sp_);
|
||||
impl_ = tmp;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
std::size_t
|
||||
std::hash< ::boost::json::string >::operator()(
|
||||
::boost::json::string const& js ) const noexcept
|
||||
{
|
||||
return ::boost::hash< ::boost::json::string >()( js );
|
||||
}
|
||||
|
||||
#endif
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru)
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_VALUE_HPP
|
||||
#define BOOST_JSON_IMPL_VALUE_HPP
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
value&
|
||||
value::at_pointer(string_view ptr) &
|
||||
{
|
||||
auto const& self = *this;
|
||||
return const_cast<value&>( self.at_pointer(ptr) );
|
||||
}
|
||||
|
||||
value&&
|
||||
value::at_pointer(string_view ptr) &&
|
||||
{
|
||||
return std::move( this->at_pointer(ptr) );
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_JSON_IMPL_VALUE_HPP
|
||||
+714
@@ -0,0 +1,714 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_VALUE_IPP
|
||||
#define BOOST_JSON_IMPL_VALUE_IPP
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/json/value.hpp>
|
||||
#include <boost/json/parser.hpp>
|
||||
#include <cstring>
|
||||
#include <istream>
|
||||
#include <limits>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
int parse_depth_xalloc = std::ios::xalloc();
|
||||
int parse_flags_xalloc = std::ios::xalloc();
|
||||
|
||||
struct value_hasher
|
||||
{
|
||||
std::size_t& seed;
|
||||
|
||||
template< class T >
|
||||
void operator()( T&& t ) const noexcept
|
||||
{
|
||||
boost::hash_combine( seed, t );
|
||||
}
|
||||
};
|
||||
|
||||
enum class stream_parse_flags
|
||||
{
|
||||
allow_comments = 1 << 0,
|
||||
allow_trailing_commas = 1 << 1,
|
||||
allow_invalid_utf8 = 1 << 2,
|
||||
};
|
||||
|
||||
long
|
||||
to_bitmask( parse_options const& opts )
|
||||
{
|
||||
using E = stream_parse_flags;
|
||||
return
|
||||
(opts.allow_comments ?
|
||||
static_cast<long>(E::allow_comments) : 0) |
|
||||
(opts.allow_trailing_commas ?
|
||||
static_cast<long>(E::allow_trailing_commas) : 0) |
|
||||
(opts.allow_invalid_utf8 ?
|
||||
static_cast<long>(E::allow_invalid_utf8) : 0);
|
||||
}
|
||||
|
||||
parse_options
|
||||
get_parse_options( std::istream& is )
|
||||
{
|
||||
long const flags = is.iword(parse_flags_xalloc);
|
||||
|
||||
using E = stream_parse_flags;
|
||||
parse_options opts;
|
||||
opts.allow_comments =
|
||||
flags & static_cast<long>(E::allow_comments) ? true : false;
|
||||
opts.allow_trailing_commas =
|
||||
flags & static_cast<long>(E::allow_trailing_commas) ? true : false;
|
||||
opts.allow_invalid_utf8 =
|
||||
flags & static_cast<long>(E::allow_invalid_utf8) ? true : false;
|
||||
return opts;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
value::
|
||||
~value() noexcept
|
||||
{
|
||||
switch(kind())
|
||||
{
|
||||
case json::kind::null:
|
||||
case json::kind::bool_:
|
||||
case json::kind::int64:
|
||||
case json::kind::uint64:
|
||||
case json::kind::double_:
|
||||
sca_.~scalar();
|
||||
break;
|
||||
|
||||
case json::kind::string:
|
||||
str_.~string();
|
||||
break;
|
||||
|
||||
case json::kind::array:
|
||||
arr_.~array();
|
||||
break;
|
||||
|
||||
case json::kind::object:
|
||||
obj_.~object();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
value::
|
||||
value(
|
||||
value const& other,
|
||||
storage_ptr sp)
|
||||
{
|
||||
switch(other.kind())
|
||||
{
|
||||
case json::kind::null:
|
||||
::new(&sca_) scalar(
|
||||
std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::bool_:
|
||||
::new(&sca_) scalar(
|
||||
other.sca_.b,
|
||||
std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::int64:
|
||||
::new(&sca_) scalar(
|
||||
other.sca_.i,
|
||||
std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::uint64:
|
||||
::new(&sca_) scalar(
|
||||
other.sca_.u,
|
||||
std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::double_:
|
||||
::new(&sca_) scalar(
|
||||
other.sca_.d,
|
||||
std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::string:
|
||||
::new(&str_) string(
|
||||
other.str_,
|
||||
std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::array:
|
||||
::new(&arr_) array(
|
||||
other.arr_,
|
||||
std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::object:
|
||||
::new(&obj_) object(
|
||||
other.obj_,
|
||||
std::move(sp));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
value::
|
||||
value(value&& other) noexcept
|
||||
{
|
||||
relocate(this, other);
|
||||
::new(&other.sca_) scalar(sp_);
|
||||
}
|
||||
|
||||
value::
|
||||
value(
|
||||
value&& other,
|
||||
storage_ptr sp)
|
||||
{
|
||||
switch(other.kind())
|
||||
{
|
||||
case json::kind::null:
|
||||
::new(&sca_) scalar(
|
||||
std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::bool_:
|
||||
::new(&sca_) scalar(
|
||||
other.sca_.b, std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::int64:
|
||||
::new(&sca_) scalar(
|
||||
other.sca_.i, std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::uint64:
|
||||
::new(&sca_) scalar(
|
||||
other.sca_.u, std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::double_:
|
||||
::new(&sca_) scalar(
|
||||
other.sca_.d, std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::string:
|
||||
::new(&str_) string(
|
||||
std::move(other.str_),
|
||||
std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::array:
|
||||
::new(&arr_) array(
|
||||
std::move(other.arr_),
|
||||
std::move(sp));
|
||||
break;
|
||||
|
||||
case json::kind::object:
|
||||
::new(&obj_) object(
|
||||
std::move(other.obj_),
|
||||
std::move(sp));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Conversion
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
value::
|
||||
value(
|
||||
std::initializer_list<value_ref> init,
|
||||
storage_ptr sp)
|
||||
{
|
||||
if(value_ref::maybe_object(init))
|
||||
{
|
||||
::new(&obj_) object(
|
||||
value_ref::make_object(
|
||||
init, std::move(sp)));
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef BOOST_JSON_LEGACY_INIT_LIST_BEHAVIOR
|
||||
if( init.size() == 1 )
|
||||
{
|
||||
::new(&sca_) scalar();
|
||||
value temp = init.begin()->make_value( std::move(sp) );
|
||||
swap(temp);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
::new(&arr_) array(
|
||||
value_ref::make_array(
|
||||
init, std::move(sp)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Assignment
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
value&
|
||||
value::
|
||||
operator=(value const& other)
|
||||
{
|
||||
value(other,
|
||||
storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
value&
|
||||
value::
|
||||
operator=(value&& other)
|
||||
{
|
||||
value(std::move(other),
|
||||
storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
value&
|
||||
value::
|
||||
operator=(
|
||||
std::initializer_list<value_ref> init)
|
||||
{
|
||||
value(init,
|
||||
storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
value&
|
||||
value::
|
||||
operator=(string_view s)
|
||||
{
|
||||
value(s, storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
value&
|
||||
value::
|
||||
operator=(char const* s)
|
||||
{
|
||||
value(s, storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
value&
|
||||
value::
|
||||
operator=(string const& str)
|
||||
{
|
||||
value(str, storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
value&
|
||||
value::
|
||||
operator=(string&& str)
|
||||
{
|
||||
value(std::move(str),
|
||||
storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
value&
|
||||
value::
|
||||
operator=(array const& arr)
|
||||
{
|
||||
value(arr, storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
value&
|
||||
value::
|
||||
operator=(array&& arr)
|
||||
{
|
||||
value(std::move(arr),
|
||||
storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
value&
|
||||
value::
|
||||
operator=(object const& obj)
|
||||
{
|
||||
value(obj, storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
value&
|
||||
value::
|
||||
operator=(object&& obj)
|
||||
{
|
||||
value(std::move(obj),
|
||||
storage()).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// Modifiers
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
string&
|
||||
value::
|
||||
emplace_string() noexcept
|
||||
{
|
||||
return *::new(&str_) string(destroy());
|
||||
}
|
||||
|
||||
array&
|
||||
value::
|
||||
emplace_array() noexcept
|
||||
{
|
||||
return *::new(&arr_) array(destroy());
|
||||
}
|
||||
|
||||
object&
|
||||
value::
|
||||
emplace_object() noexcept
|
||||
{
|
||||
return *::new(&obj_) object(destroy());
|
||||
}
|
||||
|
||||
void
|
||||
value::
|
||||
swap(value& other)
|
||||
{
|
||||
if(*storage() == *other.storage())
|
||||
{
|
||||
// fast path
|
||||
union U
|
||||
{
|
||||
value tmp;
|
||||
U(){}
|
||||
~U(){}
|
||||
};
|
||||
U u;
|
||||
relocate(&u.tmp, *this);
|
||||
relocate(this, other);
|
||||
relocate(&other, u.tmp);
|
||||
return;
|
||||
}
|
||||
|
||||
// copy
|
||||
value temp1(
|
||||
std::move(*this),
|
||||
other.storage());
|
||||
value temp2(
|
||||
std::move(other),
|
||||
this->storage());
|
||||
other.~value();
|
||||
::new(&other) value(pilfer(temp1));
|
||||
this->~value();
|
||||
::new(this) value(pilfer(temp2));
|
||||
}
|
||||
|
||||
std::istream&
|
||||
operator>>(
|
||||
std::istream& is,
|
||||
value& jv)
|
||||
{
|
||||
using Traits = std::istream::traits_type;
|
||||
|
||||
// sentry prepares the stream for reading and finalizes it in destructor
|
||||
std::istream::sentry sentry(is);
|
||||
if( !sentry )
|
||||
return is;
|
||||
|
||||
parse_options opts = get_parse_options( is );
|
||||
if( auto depth = static_cast<std::size_t>( is.iword(parse_depth_xalloc) ) )
|
||||
opts.max_depth = depth;
|
||||
|
||||
unsigned char parser_buf[BOOST_JSON_STACK_BUFFER_SIZE / 2];
|
||||
stream_parser p( {}, opts, parser_buf );
|
||||
p.reset( jv.storage() );
|
||||
|
||||
char read_buf[BOOST_JSON_STACK_BUFFER_SIZE / 2];
|
||||
std::streambuf& buf = *is.rdbuf();
|
||||
std::ios::iostate err = std::ios::goodbit;
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try
|
||||
#endif
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
error_code ec;
|
||||
|
||||
// we peek the buffer; this either makes sure that there's no
|
||||
// more input, or makes sure there's something in the internal
|
||||
// buffer (so in_avail will return a positive number)
|
||||
std::istream::int_type c = is.rdbuf()->sgetc();
|
||||
// if we indeed reached EOF, we check if we parsed a full JSON
|
||||
// document; if not, we error out
|
||||
if( Traits::eq_int_type(c, Traits::eof()) )
|
||||
{
|
||||
err |= std::ios::eofbit;
|
||||
p.finish(ec);
|
||||
if( ec.failed() )
|
||||
break;
|
||||
}
|
||||
|
||||
// regardless of reaching EOF, we might have parsed a full JSON
|
||||
// document; if so, we successfully finish
|
||||
if( p.done() )
|
||||
{
|
||||
jv = p.release();
|
||||
return is;
|
||||
}
|
||||
|
||||
// at this point we definitely have more input, specifically in
|
||||
// buf's internal buffer; we also definitely haven't parsed a whole
|
||||
// document
|
||||
std::streamsize available = buf.in_avail();
|
||||
// if this assert fails, the streambuf is buggy
|
||||
BOOST_ASSERT( available > 0 );
|
||||
|
||||
available = ( std::min )(
|
||||
static_cast<std::size_t>(available), sizeof(read_buf) );
|
||||
// we read from the internal buffer of buf into our buffer
|
||||
available = buf.sgetn( read_buf, available );
|
||||
|
||||
std::size_t consumed = p.write_some(
|
||||
read_buf, static_cast<std::size_t>(available), ec );
|
||||
// if the parser hasn't consumed the entire input we've took from
|
||||
// buf, we put the remaining data back; this should succeed,
|
||||
// because we only read data from buf's internal buffer
|
||||
while( consumed++ < static_cast<std::size_t>(available) )
|
||||
{
|
||||
std::istream::int_type const status = buf.sungetc();
|
||||
BOOST_ASSERT( status != Traits::eof() );
|
||||
(void)status;
|
||||
}
|
||||
|
||||
if( ec.failed() )
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
catch(...)
|
||||
{
|
||||
try
|
||||
{
|
||||
is.setstate(std::ios::badbit);
|
||||
}
|
||||
// we ignore the exception, because we need to throw the original
|
||||
// exception instead
|
||||
catch( std::ios::failure const& ) { }
|
||||
|
||||
if( is.exceptions() & std::ios::badbit )
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
|
||||
is.setstate(err | std::ios::failbit);
|
||||
return is;
|
||||
}
|
||||
|
||||
std::istream&
|
||||
operator>>(
|
||||
std::istream& is,
|
||||
parse_options const& opts)
|
||||
{
|
||||
is.iword(parse_flags_xalloc) = to_bitmask(opts);
|
||||
is.iword(parse_depth_xalloc) = static_cast<long>(opts.max_depth);
|
||||
return is;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// private
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
storage_ptr
|
||||
value::
|
||||
destroy() noexcept
|
||||
{
|
||||
switch(kind())
|
||||
{
|
||||
case json::kind::null:
|
||||
case json::kind::bool_:
|
||||
case json::kind::int64:
|
||||
case json::kind::uint64:
|
||||
case json::kind::double_:
|
||||
break;
|
||||
|
||||
case json::kind::string:
|
||||
{
|
||||
auto sp = str_.storage();
|
||||
str_.~string();
|
||||
return sp;
|
||||
}
|
||||
|
||||
case json::kind::array:
|
||||
{
|
||||
auto sp = arr_.storage();
|
||||
arr_.~array();
|
||||
return sp;
|
||||
}
|
||||
|
||||
case json::kind::object:
|
||||
{
|
||||
auto sp = obj_.storage();
|
||||
obj_.~object();
|
||||
return sp;
|
||||
}
|
||||
|
||||
}
|
||||
return std::move(sp_);
|
||||
}
|
||||
|
||||
bool
|
||||
value::
|
||||
equal(value const& other) const noexcept
|
||||
{
|
||||
switch(kind())
|
||||
{
|
||||
default: // unreachable()?
|
||||
case json::kind::null:
|
||||
return other.kind() == json::kind::null;
|
||||
|
||||
case json::kind::bool_:
|
||||
return
|
||||
other.kind() == json::kind::bool_ &&
|
||||
get_bool() == other.get_bool();
|
||||
|
||||
case json::kind::int64:
|
||||
switch(other.kind())
|
||||
{
|
||||
case json::kind::int64:
|
||||
return get_int64() == other.get_int64();
|
||||
case json::kind::uint64:
|
||||
if(get_int64() < 0)
|
||||
return false;
|
||||
return static_cast<std::uint64_t>(
|
||||
get_int64()) == other.get_uint64();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
case json::kind::uint64:
|
||||
switch(other.kind())
|
||||
{
|
||||
case json::kind::uint64:
|
||||
return get_uint64() == other.get_uint64();
|
||||
case json::kind::int64:
|
||||
if(other.get_int64() < 0)
|
||||
return false;
|
||||
return static_cast<std::uint64_t>(
|
||||
other.get_int64()) == get_uint64();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
case json::kind::double_:
|
||||
return
|
||||
other.kind() == json::kind::double_ &&
|
||||
get_double() == other.get_double();
|
||||
|
||||
case json::kind::string:
|
||||
return
|
||||
other.kind() == json::kind::string &&
|
||||
get_string() == other.get_string();
|
||||
|
||||
case json::kind::array:
|
||||
return
|
||||
other.kind() == json::kind::array &&
|
||||
get_array() == other.get_array();
|
||||
|
||||
case json::kind::object:
|
||||
return
|
||||
other.kind() == json::kind::object &&
|
||||
get_object() == other.get_object();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// key_value_pair
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
// empty keys point here
|
||||
BOOST_JSON_REQUIRE_CONST_INIT
|
||||
char const
|
||||
key_value_pair::empty_[1] = { 0 };
|
||||
|
||||
key_value_pair::
|
||||
key_value_pair(
|
||||
pilfered<json::value> key,
|
||||
pilfered<json::value> value) noexcept
|
||||
: value_(value)
|
||||
{
|
||||
std::size_t len;
|
||||
key_ = access::release_key(key.get(), len);
|
||||
len_ = static_cast<std::uint32_t>(len);
|
||||
}
|
||||
|
||||
key_value_pair::
|
||||
key_value_pair(
|
||||
key_value_pair const& other,
|
||||
storage_ptr sp)
|
||||
: value_(other.value_, std::move(sp))
|
||||
{
|
||||
auto p = reinterpret_cast<
|
||||
char*>(value_.storage()->
|
||||
allocate(other.len_ + 1,
|
||||
alignof(char)));
|
||||
std::memcpy(
|
||||
p, other.key_, other.len_);
|
||||
len_ = other.len_;
|
||||
p[len_] = 0;
|
||||
key_ = p;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
std::size_t
|
||||
hash_value_impl( value const& jv ) noexcept
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
|
||||
kind const k = jv.kind();
|
||||
boost::hash_combine( seed, k != kind::int64 ? k : kind::uint64 );
|
||||
|
||||
visit( value_hasher{seed}, jv );
|
||||
return seed;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
// std::hash specialization
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
|
||||
std::size_t
|
||||
std::hash<::boost::json::value>::operator()(
|
||||
::boost::json::value const& jv) const noexcept
|
||||
{
|
||||
return ::boost::hash< ::boost::json::value >()( jv );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
#endif
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_VALUE_REF_HPP
|
||||
#define BOOST_JSON_IMPL_VALUE_REF_HPP
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
template<class T>
|
||||
value
|
||||
value_ref::
|
||||
from_builtin(
|
||||
void const* p,
|
||||
storage_ptr sp) noexcept
|
||||
{
|
||||
return value(
|
||||
*reinterpret_cast<
|
||||
T const*>(p),
|
||||
std::move(sp));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
value
|
||||
value_ref::
|
||||
from_const(
|
||||
void const* p,
|
||||
storage_ptr sp)
|
||||
{
|
||||
return value(
|
||||
*reinterpret_cast<
|
||||
T const*>(p),
|
||||
std::move(sp));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
value
|
||||
value_ref::
|
||||
from_rvalue(
|
||||
void* p,
|
||||
storage_ptr sp)
|
||||
{
|
||||
return value(
|
||||
std::move(
|
||||
*reinterpret_cast<T*>(p)),
|
||||
std::move(sp));
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_VALUE_REF_IPP
|
||||
#define BOOST_JSON_IMPL_VALUE_REF_IPP
|
||||
|
||||
#include <boost/json/value_ref.hpp>
|
||||
#include <boost/json/array.hpp>
|
||||
#include <boost/json/value.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
value_ref::
|
||||
operator
|
||||
value() const
|
||||
{
|
||||
return make_value({});
|
||||
}
|
||||
|
||||
value
|
||||
value_ref::
|
||||
from_init_list(
|
||||
void const* p,
|
||||
storage_ptr sp)
|
||||
{
|
||||
return make_value(
|
||||
*reinterpret_cast<
|
||||
init_list const*>(p),
|
||||
std::move(sp));
|
||||
}
|
||||
|
||||
bool
|
||||
value_ref::
|
||||
is_key_value_pair() const noexcept
|
||||
{
|
||||
if(what_ != what::ini)
|
||||
return false;
|
||||
if(arg_.init_list_.size() != 2)
|
||||
return false;
|
||||
auto const& e =
|
||||
*arg_.init_list_.begin();
|
||||
if( e.what_ != what::str &&
|
||||
e.what_ != what::strfunc)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
value_ref::
|
||||
maybe_object(
|
||||
std::initializer_list<
|
||||
value_ref> init) noexcept
|
||||
{
|
||||
for(auto const& e : init)
|
||||
if(! e.is_key_value_pair())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
string_view
|
||||
value_ref::
|
||||
get_string() const noexcept
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
what_ == what::str ||
|
||||
what_ == what::strfunc);
|
||||
if (what_ == what::strfunc)
|
||||
return *static_cast<const string*>(f_.p);
|
||||
return arg_.str_;
|
||||
}
|
||||
|
||||
value
|
||||
value_ref::
|
||||
make_value(
|
||||
storage_ptr sp) const
|
||||
{
|
||||
switch(what_)
|
||||
{
|
||||
default:
|
||||
case what::str:
|
||||
return string(
|
||||
arg_.str_,
|
||||
std::move(sp));
|
||||
|
||||
case what::ini:
|
||||
return make_value(
|
||||
arg_.init_list_,
|
||||
std::move(sp));
|
||||
|
||||
case what::func:
|
||||
return f_.f(f_.p,
|
||||
std::move(sp));
|
||||
|
||||
case what::strfunc:
|
||||
return f_.f(f_.p,
|
||||
std::move(sp));
|
||||
|
||||
case what::cfunc:
|
||||
return cf_.f(cf_.p,
|
||||
std::move(sp));
|
||||
}
|
||||
}
|
||||
|
||||
value
|
||||
value_ref::
|
||||
make_value(
|
||||
std::initializer_list<
|
||||
value_ref> init,
|
||||
storage_ptr sp)
|
||||
{
|
||||
if(maybe_object(init))
|
||||
return make_object(
|
||||
init, std::move(sp));
|
||||
return make_array(
|
||||
init, std::move(sp));
|
||||
}
|
||||
|
||||
object
|
||||
value_ref::
|
||||
make_object(
|
||||
std::initializer_list<value_ref> init,
|
||||
storage_ptr sp)
|
||||
{
|
||||
object obj(std::move(sp));
|
||||
obj.reserve(init.size());
|
||||
for(auto const& e : init)
|
||||
obj.emplace(
|
||||
e.arg_.init_list_.begin()[0].get_string(),
|
||||
e.arg_.init_list_.begin()[1].make_value(
|
||||
obj.storage()));
|
||||
return obj;
|
||||
}
|
||||
|
||||
array
|
||||
value_ref::
|
||||
make_array(
|
||||
std::initializer_list<
|
||||
value_ref> init,
|
||||
storage_ptr sp)
|
||||
{
|
||||
array arr(std::move(sp));
|
||||
arr.reserve(init.size());
|
||||
for(auto const& e : init)
|
||||
arr.emplace_back(
|
||||
e.make_value(
|
||||
arr.storage()));
|
||||
return arr;
|
||||
}
|
||||
|
||||
void
|
||||
value_ref::
|
||||
write_array(
|
||||
value* dest,
|
||||
std::initializer_list<
|
||||
value_ref> init,
|
||||
storage_ptr const& sp)
|
||||
{
|
||||
struct undo
|
||||
{
|
||||
value* const base;
|
||||
value* pos;
|
||||
~undo()
|
||||
{
|
||||
if(pos)
|
||||
while(pos > base)
|
||||
(--pos)->~value();
|
||||
}
|
||||
};
|
||||
undo u{dest, dest};
|
||||
for(auto const& e : init)
|
||||
{
|
||||
::new(u.pos) value(
|
||||
e.make_value(sp));
|
||||
++u.pos;
|
||||
}
|
||||
u.pos = nullptr;
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+476
@@ -0,0 +1,476 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_VALUE_STACK_IPP
|
||||
#define BOOST_JSON_IMPL_VALUE_STACK_IPP
|
||||
|
||||
#include <boost/json/value_stack.hpp>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
value_stack::
|
||||
stack::
|
||||
~stack()
|
||||
{
|
||||
clear();
|
||||
if( begin_ != temp_ &&
|
||||
begin_ != nullptr)
|
||||
sp_->deallocate(
|
||||
begin_,
|
||||
(end_ - begin_) *
|
||||
sizeof(value));
|
||||
}
|
||||
|
||||
value_stack::
|
||||
stack::
|
||||
stack(
|
||||
storage_ptr sp,
|
||||
void* temp,
|
||||
std::size_t size) noexcept
|
||||
: sp_(std::move(sp))
|
||||
, temp_(temp)
|
||||
{
|
||||
if(size >= min_size_ *
|
||||
sizeof(value))
|
||||
{
|
||||
begin_ = reinterpret_cast<
|
||||
value*>(temp);
|
||||
top_ = begin_;
|
||||
end_ = begin_ +
|
||||
size / sizeof(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
begin_ = nullptr;
|
||||
top_ = nullptr;
|
||||
end_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
value_stack::
|
||||
stack::
|
||||
run_dtors(bool b) noexcept
|
||||
{
|
||||
run_dtors_ = b;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
value_stack::
|
||||
stack::
|
||||
size() const noexcept
|
||||
{
|
||||
return top_ - begin_;
|
||||
}
|
||||
|
||||
bool
|
||||
value_stack::
|
||||
stack::
|
||||
has_chars()
|
||||
{
|
||||
return chars_ != 0;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
// destroy the values but
|
||||
// not the stack allocation.
|
||||
void
|
||||
value_stack::
|
||||
stack::
|
||||
clear() noexcept
|
||||
{
|
||||
if(top_ != begin_)
|
||||
{
|
||||
if(run_dtors_)
|
||||
for(auto it = top_;
|
||||
it-- != begin_;)
|
||||
it->~value();
|
||||
top_ = begin_;
|
||||
}
|
||||
chars_ = 0;
|
||||
}
|
||||
|
||||
void
|
||||
value_stack::
|
||||
stack::
|
||||
maybe_grow()
|
||||
{
|
||||
if(top_ >= end_)
|
||||
grow_one();
|
||||
}
|
||||
|
||||
// make room for at least one more value
|
||||
void
|
||||
value_stack::
|
||||
stack::
|
||||
grow_one()
|
||||
{
|
||||
BOOST_ASSERT(chars_ == 0);
|
||||
std::size_t const capacity =
|
||||
end_ - begin_;
|
||||
std::size_t new_cap = min_size_;
|
||||
// VFALCO check overflow here
|
||||
while(new_cap < capacity + 1)
|
||||
new_cap <<= 1;
|
||||
auto const begin =
|
||||
reinterpret_cast<value*>(
|
||||
sp_->allocate(
|
||||
new_cap * sizeof(value)));
|
||||
std::size_t const cur_size = top_ - begin_;
|
||||
if(begin_)
|
||||
{
|
||||
std::memcpy(
|
||||
reinterpret_cast<char*>(begin),
|
||||
reinterpret_cast<char*>(begin_),
|
||||
size() * sizeof(value));
|
||||
if(begin_ != temp_)
|
||||
sp_->deallocate(begin_,
|
||||
capacity * sizeof(value));
|
||||
}
|
||||
// book-keeping
|
||||
top_ = begin + cur_size;
|
||||
end_ = begin + new_cap;
|
||||
begin_ = begin;
|
||||
}
|
||||
|
||||
// make room for nchars additional characters.
|
||||
void
|
||||
value_stack::
|
||||
stack::
|
||||
grow(std::size_t nchars)
|
||||
{
|
||||
// needed capacity in values
|
||||
std::size_t const needed =
|
||||
size() +
|
||||
1 +
|
||||
((chars_ + nchars +
|
||||
sizeof(value) - 1) /
|
||||
sizeof(value));
|
||||
std::size_t const capacity =
|
||||
end_ - begin_;
|
||||
BOOST_ASSERT(
|
||||
needed > capacity);
|
||||
std::size_t new_cap = min_size_;
|
||||
// VFALCO check overflow here
|
||||
while(new_cap < needed)
|
||||
new_cap <<= 1;
|
||||
auto const begin =
|
||||
reinterpret_cast<value*>(
|
||||
sp_->allocate(
|
||||
new_cap * sizeof(value)));
|
||||
std::size_t const cur_size = top_ - begin_;
|
||||
if(begin_)
|
||||
{
|
||||
std::size_t amount =
|
||||
size() * sizeof(value);
|
||||
if(chars_ > 0)
|
||||
amount += sizeof(value) + chars_;
|
||||
std::memcpy(
|
||||
reinterpret_cast<char*>(begin),
|
||||
reinterpret_cast<char*>(begin_),
|
||||
amount);
|
||||
if(begin_ != temp_)
|
||||
sp_->deallocate(begin_,
|
||||
capacity * sizeof(value));
|
||||
}
|
||||
// book-keeping
|
||||
top_ = begin + cur_size;
|
||||
end_ = begin + new_cap;
|
||||
begin_ = begin;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
void
|
||||
value_stack::
|
||||
stack::
|
||||
append(string_view s)
|
||||
{
|
||||
std::size_t const bytes_avail =
|
||||
reinterpret_cast<
|
||||
char const*>(end_) -
|
||||
reinterpret_cast<
|
||||
char const*>(top_);
|
||||
// make sure there is room for
|
||||
// pushing one more value without
|
||||
// clobbering the string.
|
||||
if(sizeof(value) + chars_ +
|
||||
s.size() > bytes_avail)
|
||||
grow(s.size());
|
||||
|
||||
// copy the new piece
|
||||
std::memcpy(
|
||||
reinterpret_cast<char*>(
|
||||
top_ + 1) + chars_,
|
||||
s.data(), s.size());
|
||||
chars_ += s.size();
|
||||
|
||||
// ensure a pushed value cannot
|
||||
// clobber the released string.
|
||||
BOOST_ASSERT(
|
||||
reinterpret_cast<char*>(
|
||||
top_ + 1) + chars_ <=
|
||||
reinterpret_cast<char*>(
|
||||
end_));
|
||||
}
|
||||
|
||||
string_view
|
||||
value_stack::
|
||||
stack::
|
||||
release_string() noexcept
|
||||
{
|
||||
// ensure a pushed value cannot
|
||||
// clobber the released string.
|
||||
BOOST_ASSERT(
|
||||
reinterpret_cast<char*>(
|
||||
top_ + 1) + chars_ <=
|
||||
reinterpret_cast<char*>(
|
||||
end_));
|
||||
auto const n = chars_;
|
||||
chars_ = 0;
|
||||
return { reinterpret_cast<
|
||||
char const*>(top_ + 1), n };
|
||||
}
|
||||
|
||||
// transfer ownership of the top n
|
||||
// elements of the stack to the caller
|
||||
value*
|
||||
value_stack::
|
||||
stack::
|
||||
release(std::size_t n) noexcept
|
||||
{
|
||||
BOOST_ASSERT(n <= size());
|
||||
BOOST_ASSERT(chars_ == 0);
|
||||
top_ -= n;
|
||||
return top_;
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
value&
|
||||
value_stack::
|
||||
stack::
|
||||
push(Args&&... args)
|
||||
{
|
||||
BOOST_ASSERT(chars_ == 0);
|
||||
if(top_ >= end_)
|
||||
grow_one();
|
||||
value& jv = detail::access::
|
||||
construct_value(top_,
|
||||
std::forward<Args>(args)...);
|
||||
++top_;
|
||||
return jv;
|
||||
}
|
||||
|
||||
template<class Unchecked>
|
||||
void
|
||||
value_stack::
|
||||
stack::
|
||||
exchange(Unchecked&& u)
|
||||
{
|
||||
BOOST_ASSERT(chars_ == 0);
|
||||
union U
|
||||
{
|
||||
value v;
|
||||
U() {}
|
||||
~U() {}
|
||||
} jv;
|
||||
// construct value on the stack
|
||||
// to avoid clobbering top_[0],
|
||||
// which belongs to `u`.
|
||||
detail::access::
|
||||
construct_value(
|
||||
&jv.v, std::move(u));
|
||||
std::memcpy(
|
||||
reinterpret_cast<
|
||||
char*>(top_),
|
||||
&jv.v, sizeof(value));
|
||||
++top_;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
value_stack::
|
||||
~value_stack()
|
||||
{
|
||||
// default dtor is here so the
|
||||
// definition goes in the library
|
||||
// instead of the caller's TU.
|
||||
}
|
||||
|
||||
value_stack::
|
||||
value_stack(
|
||||
storage_ptr sp,
|
||||
unsigned char* temp_buffer,
|
||||
std::size_t temp_size) noexcept
|
||||
: st_(
|
||||
std::move(sp),
|
||||
temp_buffer,
|
||||
temp_size)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
value_stack::
|
||||
reset(storage_ptr sp) noexcept
|
||||
{
|
||||
st_.clear();
|
||||
|
||||
sp_.~storage_ptr();
|
||||
::new(&sp_) storage_ptr(
|
||||
pilfer(sp));
|
||||
|
||||
// `stack` needs this
|
||||
// to clean up correctly
|
||||
st_.run_dtors(
|
||||
! sp_.is_not_shared_and_deallocate_is_trivial());
|
||||
}
|
||||
|
||||
value
|
||||
value_stack::
|
||||
release() noexcept
|
||||
{
|
||||
// This means the caller did not
|
||||
// cause a single top level element
|
||||
// to be produced.
|
||||
BOOST_ASSERT(st_.size() == 1);
|
||||
|
||||
// give up shared ownership
|
||||
sp_ = {};
|
||||
|
||||
return pilfer(*st_.release(1));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
void
|
||||
value_stack::
|
||||
push_array(std::size_t n)
|
||||
{
|
||||
// we already have room if n > 0
|
||||
if(BOOST_JSON_UNLIKELY(n == 0))
|
||||
st_.maybe_grow();
|
||||
detail::unchecked_array ua(
|
||||
st_.release(n), n, sp_);
|
||||
st_.exchange(std::move(ua));
|
||||
}
|
||||
|
||||
void
|
||||
value_stack::
|
||||
push_object(std::size_t n)
|
||||
{
|
||||
// we already have room if n > 0
|
||||
if(BOOST_JSON_UNLIKELY(n == 0))
|
||||
st_.maybe_grow();
|
||||
detail::unchecked_object uo(
|
||||
st_.release(n * 2), n, sp_);
|
||||
st_.exchange(std::move(uo));
|
||||
}
|
||||
|
||||
void
|
||||
value_stack::
|
||||
push_chars(
|
||||
string_view s)
|
||||
{
|
||||
st_.append(s);
|
||||
}
|
||||
|
||||
void
|
||||
value_stack::
|
||||
push_key(
|
||||
string_view s)
|
||||
{
|
||||
if(! st_.has_chars())
|
||||
{
|
||||
st_.push(detail::key_t{}, s, sp_);
|
||||
return;
|
||||
}
|
||||
auto part = st_.release_string();
|
||||
st_.push(detail::key_t{}, part, s, sp_);
|
||||
}
|
||||
|
||||
void
|
||||
value_stack::
|
||||
push_string(
|
||||
string_view s)
|
||||
{
|
||||
if(! st_.has_chars())
|
||||
{
|
||||
// fast path
|
||||
st_.push(s, sp_);
|
||||
return;
|
||||
}
|
||||
|
||||
// VFALCO We could add a special
|
||||
// private ctor to string that just
|
||||
// creates uninitialized space,
|
||||
// to reduce member function calls.
|
||||
auto part = st_.release_string();
|
||||
auto& str = st_.push(
|
||||
string_kind, sp_).get_string();
|
||||
str.reserve(
|
||||
part.size() + s.size());
|
||||
std::memcpy(
|
||||
str.data(),
|
||||
part.data(), part.size());
|
||||
std::memcpy(
|
||||
str.data() + part.size(),
|
||||
s.data(), s.size());
|
||||
str.grow(part.size() + s.size());
|
||||
}
|
||||
|
||||
void
|
||||
value_stack::
|
||||
push_int64(
|
||||
int64_t i)
|
||||
{
|
||||
st_.push(i, sp_);
|
||||
}
|
||||
|
||||
void
|
||||
value_stack::
|
||||
push_uint64(
|
||||
uint64_t u)
|
||||
{
|
||||
st_.push(u, sp_);
|
||||
}
|
||||
|
||||
void
|
||||
value_stack::
|
||||
push_double(
|
||||
double d)
|
||||
{
|
||||
st_.push(d, sp_);
|
||||
}
|
||||
|
||||
void
|
||||
value_stack::
|
||||
push_bool(
|
||||
bool b)
|
||||
{
|
||||
st_.push(b, sp_);
|
||||
}
|
||||
|
||||
void
|
||||
value_stack::
|
||||
push_null()
|
||||
{
|
||||
st_.push(nullptr, sp_);
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// 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/boostorg/json
|
||||
//
|
||||
|
||||
#ifndef BOOST_JSON_IMPL_VISIT_HPP
|
||||
#define BOOST_JSON_IMPL_VISIT_HPP
|
||||
|
||||
namespace boost {
|
||||
namespace json {
|
||||
|
||||
template<class Visitor>
|
||||
auto
|
||||
visit(
|
||||
Visitor&& v,
|
||||
value& jv) -> decltype(
|
||||
std::declval<Visitor>()(nullptr))
|
||||
{
|
||||
switch(jv.kind())
|
||||
{
|
||||
default: // unreachable()?
|
||||
case kind::null: return std::forward<Visitor>(v)(nullptr);
|
||||
case kind::bool_: return std::forward<Visitor>(v)(jv.get_bool());
|
||||
case kind::int64: return std::forward<Visitor>(v)(jv.get_int64());
|
||||
case kind::uint64: return std::forward<Visitor>(v)(jv.get_uint64());
|
||||
case kind::double_: return std::forward<Visitor>(v)(jv.get_double());
|
||||
case kind::string: return std::forward<Visitor>(v)(jv.get_string());
|
||||
case kind::array: return std::forward<Visitor>(v)(jv.get_array());
|
||||
case kind::object: return std::forward<Visitor>(v)(jv.get_object());
|
||||
}
|
||||
}
|
||||
|
||||
template<class Visitor>
|
||||
auto
|
||||
visit(
|
||||
Visitor&& v,
|
||||
value const& jv) -> decltype(
|
||||
std::declval<Visitor>()(nullptr))
|
||||
{
|
||||
switch (jv.kind())
|
||||
{
|
||||
default: // unreachable()?
|
||||
case kind::null: return std::forward<Visitor>(v)(nullptr);
|
||||
case kind::bool_: return std::forward<Visitor>(v)(jv.get_bool());
|
||||
case kind::int64: return std::forward<Visitor>(v)(jv.get_int64());
|
||||
case kind::uint64: return std::forward<Visitor>(v)(jv.get_uint64());
|
||||
case kind::double_: return std::forward<Visitor>(v)(jv.get_double());
|
||||
case kind::string: return std::forward<Visitor>(v)(jv.get_string());
|
||||
case kind::array: return std::forward<Visitor>(v)(jv.get_array());
|
||||
case kind::object: return std::forward<Visitor>(v)(jv.get_object());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user