json/include/boost/json/impl/array.ipp
Vinnie Falco 68f3df0403 Tidy
2019-11-11 19:21:48 -08:00

668 lines
12 KiB
C++

//
// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/vinniefalco/json
//
#ifndef BOOST_JSON_IMPL_ARRAY_IPP
#define BOOST_JSON_IMPL_ARRAY_IPP
#include <boost/json/array.hpp>
#include <boost/json/detail/assert.hpp>
#include <boost/json/detail/except.hpp>
#include <boost/json/detail/exchange.hpp>
#include <boost/pilfer.hpp>
#include <cstdlib>
#include <limits>
#include <new>
#include <utility>
namespace boost {
namespace json {
//----------------------------------------------------------
array::
impl_type::
impl_type(
std::size_t capacity_,
storage_ptr const& sp)
{
if(capacity_ > max_size())
BOOST_JSON_THROW(
detail::object_too_large_exception());
if(capacity_ > 0)
vec = reinterpret_cast<value*>(
sp->allocate(
capacity_ * sizeof(value),
alignof(value)));
else
vec = nullptr;
capacity = static_cast<
std::uint32_t>(capacity_);
size = 0;
}
array::
impl_type::
impl_type(impl_type&& other) noexcept
: vec(detail::exchange(
other.vec, nullptr))
, size(detail::exchange(
other.size, 0))
, capacity(detail::exchange(
other.capacity, 0))
{
}
auto
array::
impl_type::
operator=(
impl_type&& other) noexcept ->
impl_type&
{
::new(this) impl_type(
std::move(other));
return *this;
}
void
array::
impl_type::
swap(impl_type& rhs) noexcept
{
std::swap(vec, rhs.vec);
std::swap(size, rhs.size);
std::swap(capacity, rhs.capacity);
}
void
array::
impl_type::
destroy(
storage_ptr const& sp) noexcept
{
if(vec && sp->need_free())
{
auto it = vec + size;
while(it != vec)
(*--it).~value();
sp->deallocate(vec,
capacity * sizeof(value),
alignof(value));
}
}
//----------------------------------------------------------
array::
undo_create::
~undo_create()
{
if(! commit)
self_.impl_.destroy(self_.sp_);
}
//----------------------------------------------------------
array::
undo_assign::
undo_assign(array& self)
: self_(self)
, impl_(std::move(self.impl_))
{
}
array::
undo_assign::
~undo_assign()
{
if(! commit)
impl_.swap(self_.impl_);
impl_.destroy(self_.sp_);
}
//----------------------------------------------------------
array::
undo_insert::
undo_insert(
value const* pos_,
std::size_t n,
array& self)
: self_(self)
, n_(static_cast<std::size_t>(n))
, pos(self.impl_.index_of(pos_))
{
if(n > max_size())
BOOST_JSON_THROW(
std::length_error(
"size > max_size()"));
self_.reserve(
self_.impl_.size + n_);
// (iterators invalidated now)
it = self_.impl_.vec + pos;
relocate(it + n_, it,
self_.impl_.size - pos);
self_.impl_.size = static_cast<
std::uint32_t>(
self_.impl_.size + n_);
}
array::
undo_insert::
~undo_insert()
{
if(! commit)
{
auto const first =
self_.impl_.vec + pos;
self_.destroy(first, it);
self_.impl_.size = static_cast<
std::uint32_t>(
self_.impl_.size - n_);
relocate(
first, first + n_,
self_.impl_.size - pos);
}
}
//----------------------------------------------------------
//
// Special Members
//
//----------------------------------------------------------
array::
array(storage_ptr sp) noexcept
: sp_(std::move(sp))
{
}
array::
array(
std::size_t count,
value const& v,
storage_ptr sp)
: sp_(std::move(sp))
{
undo_create u(*this);
reserve(count);
while(impl_.size < count)
{
::new(
impl_.vec +
impl_.size) value(v, sp_);
++impl_.size;
}
u.commit = true;
}
array::
array(
std::size_t count,
storage_ptr sp)
: sp_(std::move(sp))
{
undo_create u(*this);
reserve(count);
while(impl_.size < count)
{
::new(
impl_.vec +
impl_.size) value(sp_);
++impl_.size;
}
u.commit = true;
}
array::
array(array const& other)
: sp_(other.sp_)
{
undo_create u(*this);
copy(other);
u.commit = true;
}
array::
array(
array const& other,
storage_ptr sp)
: sp_(std::move(sp))
{
undo_create u(*this);
copy(other);
u.commit = true;
}
array::
array(pilfered<array> other) noexcept
: sp_(detail::exchange(
other.get().sp_, nullptr))
, impl_(std::move(other.get().impl_))
{
}
array::
array(array&& other) noexcept
: sp_(other.sp_)
, impl_(std::move(other.impl_))
{
}
array::
array(
array&& other,
storage_ptr sp)
: sp_(std::move(sp))
{
if(*sp_ == *other.sp_)
{
impl_.swap(other.impl_);
}
else
{
undo_create u(*this);
copy(other);
u.commit = true;
}
}
array::
array(
std::initializer_list<value> init,
storage_ptr sp)
: sp_(std::move(sp))
{
undo_create u(*this);
copy(init);
u.commit = true;
}
array::
array(unchecked_array&& ua)
: sp_(ua.get_storage())
, impl_(ua.size(), sp_) // exact
{
impl_.size = static_cast<
std::uint32_t>(ua.size());
ua.relocate(impl_.vec);
}
//----------------------------------------------------------
array&
array::
operator=(array const& other)
{
if(this == &other)
return *this;
array tmp(other, sp_);
this->~array();
::new(this) array(pilfer(tmp));
return *this;
}
array&
array::
operator=(array&& other)
{
array tmp(std::move(other), sp_);
this->~array();
::new(this) array(pilfer(tmp));
return *this;
}
array&
array::
operator=(
std::initializer_list<value> init)
{
array tmp(init, sp_);
this->~array();
::new(this) array(pilfer(tmp));
return *this;
}
//----------------------------------------------------------
//
// Capacity
//
//----------------------------------------------------------
void
array::
shrink_to_fit() noexcept
{
if(impl_.capacity <= impl_.size)
return;
if(impl_.size == 0)
{
impl_.destroy(sp_);
impl_ = {};
return;
}
if( impl_.size < min_capacity_ &&
impl_.capacity <= min_capacity_)
return;
#ifndef BOOST_NO_EXCEPTIONS
try
{
#endif
impl_type impl(impl_.size, sp_);
relocate(
impl.vec,
impl_.vec, impl_.size);
impl.size = impl_.size;
impl_.size = 0;
impl_.swap(impl);
impl.destroy(sp_);
#ifndef BOOST_NO_EXCEPTIONS
}
catch(...)
{
// eat the exception
return;
}
#endif
}
//----------------------------------------------------------
//
// Modifiers
//
//----------------------------------------------------------
void
array::
clear() noexcept
{
if(! impl_.vec)
return;
destroy(impl_.vec,
impl_.vec + impl_.size);
impl_.size = 0;
}
auto
array::
insert(
const_iterator pos,
std::size_t count,
value const& v) ->
iterator
{
undo_insert u(pos, count, *this);
while(count--)
u.emplace(v);
u.commit = true;
return impl_.vec + u.pos;
}
auto
array::
insert(
const_iterator pos,
std::initializer_list<value> init) ->
iterator
{
undo_insert u(pos,
static_cast<std::size_t>(
init.size()), *this);
for(auto const& v : init)
u.emplace(v);
u.commit = true;
return impl_.vec + u.pos;
}
auto
array::
erase(
const_iterator pos) noexcept ->
iterator
{
auto p = impl_.vec +
(pos - impl_.vec);
destroy(p, p + 1);
relocate(p, p + 1, 1);
--impl_.size;
return p;
}
auto
array::
erase(
const_iterator first,
const_iterator last) noexcept ->
iterator
{
auto const n = static_cast<
std::size_t>(last - first);
auto const p =
impl_.vec + impl_.index_of(first);
destroy(p, p + n);
relocate(p, p + n,
impl_.size -
impl_.index_of(last));
impl_.size = static_cast<
std::uint32_t>(impl_.size - n);
return p;
}
void
array::
pop_back() noexcept
{
auto const p = &back();
destroy(p, p + 1);
--impl_.size;
}
void
array::
resize(std::size_t count)
{
if(count <= impl_.size)
{
destroy(
impl_.vec + count,
impl_.vec + impl_.size);
impl_.size = static_cast<
std::uint32_t>(count);
return;
}
reserve(count);
auto it =
impl_.vec + impl_.size;
auto const end =
impl_.vec + count;
while(it != end)
::new(it++) value(sp_);
impl_.size = static_cast<
std::uint32_t>(count);
}
void
array::
resize(
std::size_t count,
value const& v)
{
if(count <= size())
{
destroy(
impl_.vec + count,
impl_.vec + impl_.size);
impl_.size = static_cast<
std::uint32_t>(count);
return;
}
reserve(count);
struct undo
{
array& self;
value* it;
value* const end;
~undo()
{
if(it)
self.destroy(end, it);
}
};
auto const end =
impl_.vec + count;
undo u{*this,
impl_.vec + impl_.size,
impl_.vec + impl_.size};
do
{
::new(u.it) value(v, sp_);
++u.it;
}
while(u.it != end);
impl_.size = static_cast<
std::uint32_t>(count);
u.it = nullptr;
}
void
array::
swap(array& other)
{
if(*sp_ == *other.sp_)
{
impl_.swap(other.impl_);
return;
}
array temp1(
std::move(*this),
other.get_storage());
array temp2(
std::move(other),
this->get_storage());
this->~array();
::new(this) array(pilfer(temp2));
other.~array();
::new(&other) array(pilfer(temp1));
}
//----------------------------------------------------------
void
array::
destroy(
value* first, value* last) noexcept
{
if(sp_->need_free())
while(last != first)
(*--last).~value();
}
void
array::
destroy() noexcept
{
auto it = impl_.vec + impl_.size;
while(it != impl_.vec)
(*--it).~value();
sp_->deallocate(impl_.vec,
impl_.capacity * sizeof(value),
alignof(value));
}
void
array::
copy(array const& other)
{
reserve(other.size());
for(auto const& v : other)
{
::new(
impl_.vec +
impl_.size) value(v, sp_);
++impl_.size;
}
}
void
array::
copy(
std::initializer_list<value> init)
{
if(init.size() > max_size())
BOOST_JSON_THROW(
std::length_error(
"size > max_size()"));
reserve(static_cast<
std::size_t>(init.size()));
for(auto const& v : init)
{
::new(
impl_.vec +
impl_.size) value(v, sp_);
++impl_.size;
}
}
void
array::
reserve_impl(std::size_t capacity)
{
if(impl_.vec)
{
// 2x growth
auto const hint =
impl_.capacity * 2;
if(hint < impl_.capacity) // overflow
capacity = max_size();
else if(capacity < hint)
capacity = hint;
}
if( capacity < min_capacity_)
capacity = min_capacity_;
impl_type impl(capacity, sp_);
relocate(
impl.vec,
impl_.vec, impl_.size);
impl.size = impl_.size;
impl_.size = 0;
impl_.destroy(sp_);
impl_ = impl;
}
void
array::
relocate(
value* dest,
value* src,
std::size_t n) noexcept
{
if(n == 0)
return;
std::memmove(
reinterpret_cast<
void*>(dest),
reinterpret_cast<
void const*>(src),
n * sizeof(value));
}
} // json
} // boost
#endif