libcarla/include/system/boost/json/impl/value_stack.ipp
2024-10-18 13:19:59 +08:00

473 lines
8.6 KiB
C++

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