boost/json/detail/impl/stack.hpp
// // 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_DETAIL_IMPL_STACK_HPP #define BOOST_JSON_DETAIL_IMPL_STACK_HPP #include <boost/align/align.hpp> #include <boost/static_assert.hpp> namespace boost { namespace json { namespace detail { template<> struct stack::non_trivial<void> { using relocate_t = non_trivial* (*) (non_trivial*, void*); relocate_t rel; non_trivial* next; std::size_t offset; BOOST_JSON_DECL non_trivial<>* destroy() noexcept; BOOST_JSON_DECL non_trivial* relocate(void* dst) noexcept; protected: ~non_trivial() = default; }; template< class T > struct stack::non_trivial : stack::non_trivial<void> { T obj; explicit non_trivial(T t, non_trivial<>* next, std::size_t offset) : non_trivial<void>{relocate, next, offset}, obj( std::move(t) ) {} static non_trivial<>* relocate(non_trivial<>* src, void* dest) noexcept { non_trivial* self = static_cast<non_trivial*>(src); non_trivial<>* result = nullptr; if( dest ) result = ::new(dest) non_trivial( std::move(*self) ); self->~non_trivial(); return result; } }; template<class T> void stack:: push_unchecked(T const& t) { constexpr std::size_t n = sizeof(T); BOOST_STATIC_ASSERT( is_trivially_copy_assignable<T>::value ); BOOST_ASSERT( n <= cap_ - size_ ); std::memcpy( base_ + size_, &t, n ); size_ += n; } template<class T> void stack:: peek(T& t) { constexpr std::size_t n = sizeof(T); BOOST_STATIC_ASSERT( is_trivially_copy_assignable<T>::value ); BOOST_ASSERT( size_ >= n ); std::memcpy( &t, base_ + size_ - n, n ); } //-------------------------------------- // trivial template<class T> void stack:: push(T const& t, std::true_type) { if( sizeof(T) > cap_ - size_ ) reserve_impl( sizeof(T) + size_ ); push_unchecked(t); } // non-trivial template<class T> void stack:: push(T&& t, std::false_type) { BOOST_STATIC_ASSERT( ! is_trivially_copy_assignable<T>::value ); using Holder = non_trivial< remove_cvref<T> >; constexpr std::size_t size = sizeof(Holder); constexpr std::size_t alignment = alignof(Holder); void* ptr; std::size_t offset; do { std::size_t space = cap_ - size_; unsigned char* buf = base_ + size_; ptr = buf; if( alignment::align(alignment, size, ptr, space) ) { offset = (reinterpret_cast<unsigned char*>(ptr) - buf) + size; break; } reserve_impl(size_ + size + alignment - 1); } while(true); BOOST_ASSERT( (reinterpret_cast<unsigned char*>(ptr) + size - offset) == (base_ + size_) ); head_ = ::new(ptr) Holder( static_cast<T&&>(t), head_, offset ); size_ += offset; } // trivial template<class T> void stack:: pop(T& t, std::true_type) { BOOST_ASSERT( size_ >= sizeof(T) ); peek(t); size_ -= sizeof(T); } // non-trivial template<class T> void stack:: pop(T& t, std::false_type) { auto next = head_->next; auto offset = head_->offset; using U = remove_cvref<T>; using Holder = non_trivial<U>; auto const head = static_cast<Holder*>(head_); t = std::move( head->obj ); head->~Holder(); head_ = next; size_ -= offset; } } // detail } // json } // boost #endif