// // 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 #include #include #include 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( sp_->allocate( new_cap * sizeof(value))); if(begin_) { std::memcpy( reinterpret_cast(begin), reinterpret_cast(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( 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(begin), reinterpret_cast(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( top_ + 1) + chars_, s.data(), s.size()); chars_ += s.size(); // ensure a pushed value cannot // clobber the released string. BOOST_ASSERT( reinterpret_cast( top_ + 1) + chars_ <= reinterpret_cast( end_)); } string_view value_stack:: stack:: release_string() noexcept { // ensure a pushed value cannot // clobber the released string. BOOST_ASSERT( reinterpret_cast( top_ + 1) + chars_ <= reinterpret_cast( 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 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)...); ++top_; return jv; } template 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