feat():initial version

This commit is contained in:
2026-06-09 20:16:47 +08:00
commit 85fbb3188c
14809 changed files with 3044607 additions and 0 deletions

View File

@@ -0,0 +1,554 @@
//
// 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>
BOOST_JSON_NS_BEGIN
//----------------------------------------------------------
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)
{
if(pos >= t_->size)
detail::throw_out_of_range(
BOOST_CURRENT_LOCATION);
return (*t_)[pos];
}
value const&
array::
at(std::size_t pos) const
{
if(pos >= t_->size)
detail::throw_out_of_range(
BOOST_CURRENT_LOCATION);
return (*t_)[pos];
}
value&
array::
operator[](std::size_t pos) noexcept
{
BOOST_ASSERT(pos < t_->size);
return (*t_)[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 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 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);
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();
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,757 @@
//
// 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/json/array.hpp>
#include <boost/json/pilfer.hpp>
#include <boost/json/detail/except.hpp>
#include <cstdlib>
#include <limits>
#include <new>
#include <utility>
BOOST_JSON_NS_BEGIN
//----------------------------------------------------------
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())
detail::throw_length_error(
"array too large",
BOOST_CURRENT_LOCATION);
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())
detail::throw_length_error(
"array too large",
BOOST_CURRENT_LOCATION);
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()
{
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());
auto const p = &(*t_)[0] +
(pos - &(*t_)[0]);
destroy(p, p + 1);
relocate(p, p + 1, 1);
--t_->size;
return p;
}
auto
array::
erase(
const_iterator first,
const_iterator last) noexcept ->
iterator
{
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)
{
BOOST_ASSERT(this != &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())
detail::throw_length_error(
"array too large",
BOOST_CURRENT_LOCATION);
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;
}
BOOST_JSON_NS_END
#endif

View File

@@ -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_ERROR_HPP
#define BOOST_JSON_IMPL_ERROR_HPP
#include <type_traits>
#ifndef BOOST_JSON_STANDALONE
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
#else
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
#endif
BOOST_JSON_NS_BEGIN
BOOST_JSON_DECL
error_code
make_error_code(error e);
BOOST_JSON_DECL
error_condition
make_error_condition(condition c);
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,123 @@
//
// 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>
BOOST_JSON_NS_BEGIN
error_code
make_error_code(error e)
{
struct codes : error_category
{
const char*
name() const noexcept override
{
return "boost.json";
}
std::string
message(int ev) const override
{
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::exception: return "got exception";
case error::not_number: return "not a number";
case error::not_exact: return "not exact";
case error::test_failure: return "test failure";
}
}
error_condition
default_error_condition(
int ev) const noexcept override
{
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::exception:
return condition::parse_error;
case error::not_number:
case error::not_exact:
return condition::assign_error;
}
}
};
static codes const cat{};
return error_code{static_cast<
std::underlying_type<error>::type>(e), cat};
}
error_condition
make_error_condition(condition c)
{
struct codes : error_category
{
const char*
name() const noexcept override
{
return "boost.json";
}
std::string
message(int cv) const override
{
switch(static_cast<condition>(cv))
{
default:
case condition::parse_error:
return "A JSON parse error occurred";
case condition::assign_error:
return "An error occurred during assignment";
}
}
};
static codes const cat{};
return error_condition{static_cast<
std::underlying_type<condition>::type>(c), cat};
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,44 @@
//
// 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>
BOOST_JSON_NS_BEGIN
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;
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,171 @@
//
// 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/align.hpp>
#include <boost/json/detail/except.hpp>
#include <memory>
BOOST_JSON_NS_BEGIN
struct alignas(detail::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 = detail::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 = detail::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;
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,101 @@
//
// 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/json/detail/except.hpp>
BOOST_JSON_NS_BEGIN
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;
/** Destructor
This destroys the resource.
@par Complexity
Constant.
@part Exception Safety
No-throw guarantee.
*/
~null_resource() noexcept = default;
/** 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
{
detail::throw_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;
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,530 @@
//
// 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>
BOOST_JSON_NS_BEGIN
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_;
std::size_t size_;
BOOST_JSON_DECL
void
destroy() noexcept;
public:
explicit
revert_insert(
object& obj) noexcept
: obj_(&obj)
, size_(obj_->size())
{
}
~revert_insert()
{
if(! obj_)
return;
destroy();
obj_->t_->size = static_cast<
index_t>(size_);
}
void
commit() noexcept
{
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;
}
//----------------------------------------------------------
//
// Lookup
//
//----------------------------------------------------------
auto
object::
at(string_view key) ->
value&
{
auto it = find(key);
if(it == end())
detail::throw_out_of_range(
BOOST_CURRENT_LOCATION);
return it->value();
}
auto
object::
at(string_view key) const ->
value const&
{
auto it = find(key);
if(it == end())
detail::throw_out_of_range(
BOOST_CURRENT_LOCATION);
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 insert_impl(pilfer(v));
}
template<class M>
auto
object::
insert_or_assign(
string_view key, M&& m) ->
std::pair<iterator, bool>
{
reserve(size() + 1);
auto const result = find_impl(key);
if(result.first)
{
value(std::forward<M>(m),
sp_).swap(result.first->value());
return { result.first, false };
}
key_value_pair kv(key,
std::forward<M>(m), sp_);
return { insert_impl(pilfer(kv),
result.second), true };
}
template<class Arg>
auto
object::
emplace(
string_view key,
Arg&& arg) ->
std::pair<iterator, bool>
{
reserve(size() + 1);
auto const result = find_impl(key);
if(result.first)
return { result.first, false };
key_value_pair kv(key,
std::forward<Arg>(arg), sp_);
return { insert_impl(pilfer(kv),
result.second), true };
}
//----------------------------------------------------------
//
// (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)
detail::throw_length_error(
"object too large",
BOOST_CURRENT_LOCATION);
reserve(n0 + n);
revert_insert r(*this);
while(first != last)
{
insert(*first);
++first;
}
r.commit();
}
//----------------------------------------------------------
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
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,825 @@
//
// 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/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>
BOOST_JSON_NS_BEGIN
//----------------------------------------------------------
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.data(), key.size(), 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 = find_impl(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()
{
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)
detail::throw_length_error(
"object too large",
BOOST_CURRENT_LOCATION);
reserve(n0 + init.size());
revert_insert r(*this);
if(t_->is_small())
{
for(auto& iv : init)
{
auto result =
find_impl(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
{
auto p = begin() + (pos - begin());
if(t_->is_small())
{
p->~value_type();
--t_->size;
auto const pb = end();
if(p != end())
{
// the casts silence warnings
std::memcpy(
static_cast<void*>(p),
static_cast<void const*>(pb),
sizeof(*p));
}
return p;
}
remove(t_->bucket(p->key()), *p);
p->~value_type();
--t_->size;
auto const pb = end();
if(p != end())
{
auto& head = t_->bucket(pb->key());
remove(head, *pb);
// the casts silence warnings
std::memcpy(
static_cast<void*>(p),
static_cast<void const*>(pb),
sizeof(*p));
access::next(*p) = head;
head = static_cast<
index_t>(p - begin());
}
return p;
}
auto
object::
erase(string_view key) noexcept ->
std::size_t
{
auto it = find(key);
if(it == end())
return 0;
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 =
find_impl(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 =
find_impl(key).first;
if(p)
return p;
return end();
}
bool
object::
contains(
string_view key) const noexcept
{
if(empty())
return false;
return find_impl(
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)
//
//----------------------------------------------------------
auto
object::
find_impl(
string_view key) const noexcept ->
std::pair<
key_value_pair*,
std::size_t>
{
BOOST_ASSERT(t_->capacity > 0);
if(t_->is_small())
{
auto it = &(*t_)[0];
auto const last =
&(*t_)[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;
result.second = t_->digest(key);
auto i = t_->bucket(
result.second);
while(i != null_index_)
{
auto& v = (*t_)[i];
if(v.key() == key)
{
result.first = &v;
return result;
}
i = access::next(v);
}
result.first = nullptr;
return result;
}
auto
object::
insert_impl(
pilfered<key_value_pair> p) ->
std::pair<iterator, bool>
{
// caller is responsible
// for preventing aliasing.
reserve(size() + 1);
auto const result =
find_impl(p.get().key());
if(result.first)
return { result.first, false };
return { insert_impl(
p, result.second), true };
}
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;
}
// rehash to at least `n` buckets
void
object::
rehash(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;
table::deallocate(t_, sp_);
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;
}
}
}
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())
detail::throw_length_error(
"object too large",
BOOST_CURRENT_LOCATION);
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();
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,54 @@
//
// 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>
BOOST_JSON_NS_BEGIN
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,
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,
BOOST_CURRENT_LOCATION);
return jv;
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,136 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/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>
BOOST_JSON_NS_BEGIN
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)
{
error_code ec;
auto const n = write_some(
data, size, ec);
if(ec)
detail::throw_system_error(ec,
BOOST_CURRENT_LOCATION);
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)
{
ec = error::extra_data;
p_.fail(ec);
}
return n;
}
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,
BOOST_CURRENT_LOCATION);
return n;
}
value
parser::
release()
{
if( ! p_.done())
{
// prevent undefined behavior
if(! p_.last_error())
p_.fail(error::incomplete);
detail::throw_system_error(
p_.last_error(),
BOOST_CURRENT_LOCATION);
}
return p_.handler().st.release();
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,191 @@
//
// 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>
BOOST_JSON_NS_BEGIN
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());
for(;;)
{
sv = sr.read(
&s[0] + len,
s.size() - len);
len += sv.size();
if(sr.done())
break;
s.resize(
s.capacity() + 1);
}
s.resize(len);
}
std::string
serialize(
value const& jv)
{
std::string s;
serializer sr;
sr.reset(&jv);
serialize_impl(s, sr);
return s;
}
std::string
serialize(
array const& arr)
{
std::string s;
serializer sr;
sr.reset(&arr);
serialize_impl(s, sr);
return s;
}
std::string
serialize(
object const& obj)
{
std::string s;
serializer sr;
sr.reset(&obj);
serialize_impl(s, sr);
return s;
}
std::string
serialize(
string const& str)
{
std::string s;
serializer sr;
sr.reset(&str);
serialize_impl(s, sr);
return s;
}
// this is here for key_value_pair::key()
std::string
serialize(
string_view sv)
{
std::string s;
serializer sr;
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;
// 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;
sr.reset(&arr);
to_ostream(os, sr);
return os;
}
std::ostream&
operator<<(
std::ostream& os,
object const& obj)
{
serializer sr;
sr.reset(&obj);
to_ostream(os, sr);
return os;
}
std::ostream&
operator<<(
std::ostream& os,
string const& str)
{
serializer sr;
sr.reset(&str);
to_ostream(os, sr);
return os;
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,822 @@
//
// 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
BOOST_JSON_NS_BEGIN
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() noexcept = default;
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()));
return true;
}
cs0_ = { buf_, detail::format_double(
buf_, jv_->get_double()) };
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() noexcept
{
// 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);
}
BOOST_JSON_NS_END
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif

View File

@@ -0,0 +1,78 @@
//
// 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/json/detail/align.hpp>
#include <boost/json/detail/except.hpp>
#include <memory>
BOOST_JSON_NS_BEGIN
static_resource::
~static_resource() noexcept = default;
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 = detail::align(
align, n, p_, n_);
if(! p)
detail::throw_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;
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,148 @@
//
// 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>
BOOST_JSON_NS_BEGIN
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)
{
error_code ec;
auto const n = write_some(
data, size, ec);
if(ec)
detail::throw_system_error(ec,
BOOST_CURRENT_LOCATION);
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)
{
ec = error::extra_data;
p_.fail(ec);
}
return n;
}
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,
BOOST_CURRENT_LOCATION);
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,
BOOST_CURRENT_LOCATION);
}
value
stream_parser::
release()
{
if(! p_.done())
{
// prevent undefined behavior
finish();
}
return p_.handler().st.release();
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,236 @@
//
// 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>
BOOST_JSON_NS_BEGIN
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);
std::copy(first, last,
impl_.append(n, sp_));
}
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());
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,416 @@
//
// 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>
BOOST_JSON_NS_BEGIN
//----------------------------------------------------------
//
// 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(*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())
detail::throw_out_of_range(
BOOST_CURRENT_LOCATION);
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)
{
BOOST_ASSERT(this != &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;
}
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,497 @@
//
// 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/json/value.hpp>
#include <cstring>
#include <limits>
#include <new>
#include <utility>
BOOST_JSON_NS_BEGIN
value::
~value()
{
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
::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));
}
//----------------------------------------------------------
//
// 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;
}
//----------------------------------------------------------
BOOST_JSON_NS_END
#endif

View File

@@ -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
#include <boost/json/value_from.hpp>
BOOST_JSON_NS_BEGIN
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_from(
*reinterpret_cast<
T const*>(p),
std::move(sp));
}
template<class T>
value
value_ref::
from_rvalue(
void* p,
storage_ptr sp)
{
return value_from(
std::move(
*reinterpret_cast<T*>(p)),
std::move(sp));
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,187 @@
//
// 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>
BOOST_JSON_NS_BEGIN
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;
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,472 @@
//
// 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>
BOOST_JSON_NS_BEGIN
//--------------------------------------
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)));
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 + (top_ - begin_);
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)));
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 + (top_ - begin_);
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_);
}
BOOST_JSON_NS_END
#endif

View File

@@ -0,0 +1,59 @@
//
// 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
BOOST_JSON_NS_BEGIN
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());
}
}
BOOST_JSON_NS_END
#endif