// // Copyright (c) 2016-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/boostorg/beast // #ifndef BOOST_BEAST_IMPL_BUFFERS_ADAPTOR_HPP #define BOOST_BEAST_IMPL_BUFFERS_ADAPTOR_HPP #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace beast { //------------------------------------------------------------------------------ #if BOOST_WORKAROUND(BOOST_MSVC, < 1910) # pragma warning (push) # pragma warning (disable: 4521) // multiple copy constructors specified # pragma warning (disable: 4522) // multiple assignment operators specified #endif template template class buffers_adaptor::subrange { public: using value_type = typename std::conditional< isMutable, net::mutable_buffer, net::const_buffer>::type; struct iterator; // construct from two iterators plus optionally subrange definition subrange( iter_type first, // iterator to first buffer in storage iter_type last, // iterator to last buffer in storage std::size_t pos = 0, // the offset in bytes from the beginning of the storage std::size_t n = // the total length of the subrange (std::numeric_limits::max)()); #if BOOST_WORKAROUND(BOOST_MSVC, < 1910) subrange( subrange const& other) : first_(other.first_) , last_(other.last_) , first_offset_(other.first_offset_) , last_size_(other.last_size_) { } subrange& operator=( subrange const& other) { first_ = other.first_; last_ = other.last_; first_offset_ = other.first_offset_; last_size_ = other.last_size_; return *this; } #else subrange( subrange const&) = default; subrange& operator=( subrange const&) = default; #endif // allow conversion from mutable to const template::type * = nullptr> subrange(subrange const &other) : first_(other.first_) , last_(other.last_) , first_offset_(other.first_offset_) , last_size_(other.last_size_) { } iterator begin() const; iterator end() const; private: friend subrange; void adjust( std::size_t pos, std::size_t n); private: // points to the first buffer in the sequence iter_type first_; // Points to one past the end of the underlying buffer sequence iter_type last_; // The initial offset into the first buffer std::size_t first_offset_; // how many bytes in the penultimate buffer are used (if any) std::size_t last_size_; }; #if BOOST_WORKAROUND(BOOST_MSVC, < 1910) # pragma warning (pop) #endif //------------------------------------------------------------------------------ template template struct buffers_adaptor:: subrange:: iterator { using iterator_category = std::bidirectional_iterator_tag; using value_type = typename buffers_adaptor:: template subrange:: value_type; using reference = value_type&; using pointer = value_type*; using difference_type = std::ptrdiff_t; iterator( subrange const *parent, iter_type it); iterator(); value_type operator*() const; pointer operator->() const = delete; iterator & operator++(); iterator operator++(int); iterator & operator--(); iterator operator--(int); bool operator==(iterator const &b) const; bool operator!=(iterator const &b) const; private: subrange const *parent_; iter_type it_; }; //------------------------------------------------------------------------------ template auto buffers_adaptor:: end_impl() const -> iter_type { return out_ == end_ ? end_ : std::next(out_); } template buffers_adaptor:: buffers_adaptor( buffers_adaptor const& other, std::size_t nbegin, std::size_t nout, std::size_t nend) : bs_(other.bs_) , begin_(std::next(bs_.begin(), nbegin)) , out_(std::next(bs_.begin(), nout)) , end_(std::next(bs_.begin(), nend)) , max_size_(other.max_size_) , in_pos_(other.in_pos_) , in_size_(other.in_size_) , out_pos_(other.out_pos_) , out_end_(other.out_end_) { } template buffers_adaptor:: buffers_adaptor(MutableBufferSequence const& bs) : bs_(bs) , begin_(net::buffer_sequence_begin(bs_)) , out_ (net::buffer_sequence_begin(bs_)) , end_ (net::buffer_sequence_begin(bs_)) , max_size_( [&bs] { return buffer_bytes(bs); }()) { } template template buffers_adaptor:: buffers_adaptor( boost::in_place_init_t, Args&&... args) : bs_{std::forward(args)...} , begin_(net::buffer_sequence_begin(bs_)) , out_ (net::buffer_sequence_begin(bs_)) , end_ (net::buffer_sequence_begin(bs_)) , max_size_( [&] { return buffer_bytes(bs_); }()) { } template buffers_adaptor:: buffers_adaptor(buffers_adaptor const& other) : buffers_adaptor( other, std::distance( net::buffer_sequence_begin(other.bs_), other.begin_), std::distance( net::buffer_sequence_begin(other.bs_), other.out_), std::distance( net::buffer_sequence_begin(other.bs_), other.end_)) { } template auto buffers_adaptor:: operator=(buffers_adaptor const& other) -> buffers_adaptor& { if(this == &other) return *this; auto const nbegin = std::distance( net::buffer_sequence_begin(other.bs_), other.begin_); auto const nout = std::distance( net::buffer_sequence_begin(other.bs_), other.out_); auto const nend = std::distance( net::buffer_sequence_begin(other.bs_), other.end_); bs_ = other.bs_; begin_ = std::next( net::buffer_sequence_begin(bs_), nbegin); out_ = std::next( net::buffer_sequence_begin(bs_), nout); end_ = std::next( net::buffer_sequence_begin(bs_), nend); max_size_ = other.max_size_; in_pos_ = other.in_pos_; in_size_ = other.in_size_; out_pos_ = other.out_pos_; out_end_ = other.out_end_; return *this; } // template auto buffers_adaptor:: data() const noexcept -> const_buffers_type { return const_buffers_type( begin_, end_, in_pos_, in_size_); } template auto buffers_adaptor:: data() noexcept -> mutable_buffers_type { return mutable_buffers_type( begin_, end_, in_pos_, in_size_); } template auto buffers_adaptor:: prepare(std::size_t n) -> mutable_buffers_type { auto prepared = n; end_ = out_; if(end_ != net::buffer_sequence_end(bs_)) { auto size = buffer_bytes(*end_) - out_pos_; if(n > size) { n -= size; while(++end_ != net::buffer_sequence_end(bs_)) { size = buffer_bytes(*end_); if(n < size) { out_end_ = n; n = 0; ++end_; break; } n -= size; out_end_ = size; } } else { ++end_; out_end_ = out_pos_ + n; n = 0; } } if(n > 0) BOOST_THROW_EXCEPTION(std::length_error{ "buffers_adaptor too long"}); return mutable_buffers_type(out_, end_, out_pos_, prepared); } template void buffers_adaptor:: commit(std::size_t n) noexcept { if(out_ == end_) return; auto const last = std::prev(end_); while(out_ != last) { auto const avail = buffer_bytes(*out_) - out_pos_; if(n < avail) { out_pos_ += n; in_size_ += n; return; } ++out_; n -= avail; out_pos_ = 0; in_size_ += avail; } n = std::min( n, out_end_ - out_pos_); out_pos_ += n; in_size_ += n; if(out_pos_ == buffer_bytes(*out_)) { ++out_; out_pos_ = 0; out_end_ = 0; } } template void buffers_adaptor:: consume(std::size_t n) noexcept { while(begin_ != out_) { auto const avail = buffer_bytes(*begin_) - in_pos_; if(n < avail) { in_size_ -= n; in_pos_ += n; return; } n -= avail; in_size_ -= avail; in_pos_ = 0; ++begin_; } auto const avail = out_pos_ - in_pos_; if(n < avail) { in_size_ -= n; in_pos_ += n; } else { in_size_ -= avail; in_pos_ = out_pos_; } } template auto buffers_adaptor:: make_subrange(std::size_t pos, std::size_t n) -> subrange { return subrange( begin_, net::buffer_sequence_end(bs_), in_pos_ + pos, n); } template auto buffers_adaptor:: make_subrange(std::size_t pos, std::size_t n) const -> subrange { return subrange( begin_, net::buffer_sequence_end(bs_), in_pos_ + pos, n); } // ------------------------------------------------------------------------- // subrange template template buffers_adaptor:: subrange:: subrange( iter_type first, // iterator to first buffer in storage iter_type last, // iterator to last buffer in storage std::size_t pos, // the offset in bytes from the beginning of the storage std::size_t n) // the total length of the subrange : first_(first) , last_(last) , first_offset_(0) , last_size_((std::numeric_limits::max)()) { adjust(pos, n); } template template void buffers_adaptor:: subrange:: adjust( std::size_t pos, std::size_t n) { if (n == 0) last_ = first_; if (first_ == last_) { first_offset_ = 0; last_size_ = 0; return; } auto is_last = [this](iter_type iter) { return std::next(iter) == last_; }; pos += first_offset_; while (pos) { auto adjust = (std::min)(pos, first_->size()); if (adjust >= first_->size()) { ++first_; first_offset_ = 0; pos -= adjust; } else { first_offset_ = adjust; pos = 0; break; } } auto current = first_; auto max_elem = current->size() - first_offset_; if (is_last(current)) { // both first and last element last_size_ = (std::min)(max_elem, n); last_ = std::next(current); return; } else if (max_elem >= n) { last_ = std::next(current); last_size_ = n; } else { n -= max_elem; ++current; } for (;;) { max_elem = current->size(); if (is_last(current)) { last_size_ = (std::min)(n, last_size_); return; } else if (max_elem < n) { n -= max_elem; ++current; } else { last_size_ = n; last_ = std::next(current); return; } } } template template auto buffers_adaptor:: subrange:: begin() const -> iterator { return iterator(this, first_); } template template auto buffers_adaptor:: subrange:: end() const -> iterator { return iterator(this, last_); } // ------------------------------------------------------------------------- // buffers_adaptor::subrange::iterator template template buffers_adaptor:: subrange:: iterator:: iterator() : parent_(nullptr) , it_() { } template template buffers_adaptor:: subrange:: iterator:: iterator(subrange const *parent, iter_type it) : parent_(parent) , it_(it) { } template template auto buffers_adaptor:: subrange:: iterator:: operator*() const -> value_type { value_type result = *it_; if (it_ == parent_->first_) result += parent_->first_offset_; if (std::next(it_) == parent_->last_) { result = value_type( result.data(), (std::min)( parent_->last_size_, result.size())); } return result; } template template auto buffers_adaptor:: subrange:: iterator:: operator++() -> iterator & { ++it_; return *this; } template template auto buffers_adaptor:: subrange:: iterator:: operator++(int) -> iterator { auto result = *this; ++it_; return result; } template template auto buffers_adaptor:: subrange:: iterator:: operator--() -> iterator & { --it_; return *this; } template template auto buffers_adaptor:: subrange:: iterator:: operator--(int) -> iterator { auto result = *this; --it_; return result; } template template auto buffers_adaptor:: subrange:: iterator:: operator==(iterator const &b) const -> bool { return it_ == b.it_; } template template auto buffers_adaptor:: subrange:: iterator:: operator!=(iterator const &b) const -> bool { return !(*this == b); } } // beast } // boost #endif