// // Copyright (c) 2018 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_CORE_IMPL_ICY_STREAM_IPP #define BOOST_BEAST_CORE_IMPL_ICY_STREAM_IPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace beast { namespace http { namespace detail { template class dynamic_buffer_ref { DynamicBuffer& b_; public: using const_buffers_type = typename DynamicBuffer::const_buffers_type; using mutable_buffers_type = typename DynamicBuffer::mutable_buffers_type; dynamic_buffer_ref(dynamic_buffer_ref&&) = default; explicit dynamic_buffer_ref(DynamicBuffer& b) : b_(b) { } std::size_t size() const { return b_.size(); } std::size_t max_size() const { return b_.max_size(); } std::size_t capacity() const { return b_.capacity(); } const_buffers_type data() const { return b_.data(); } mutable_buffers_type prepare(std::size_t n) { return b_.prepare(n); } void commit(std::size_t n) { b_.commit(n); } void consume(std::size_t n) { b_.consume(n); } }; template typename std::enable_if< boost::asio::is_dynamic_buffer::value, dynamic_buffer_ref>::type ref(DynamicBuffer& b) { return dynamic_buffer_ref(b); } template void buffer_shift(MutableBuffers const& out, ConstBuffers const& in) { using boost::asio::buffer_size; auto in_pos = boost::asio::buffer_sequence_end(in); auto out_pos = boost::asio::buffer_sequence_end(out); auto const in_begin = boost::asio::buffer_sequence_begin(in); auto const out_begin = boost::asio::buffer_sequence_begin(out); BOOST_ASSERT(buffer_size(in) == buffer_size(out)); if(in_pos == in_begin || out_pos == out_begin) return; boost::asio::const_buffer cb{*--in_pos}; boost::asio::mutable_buffer mb{*--out_pos}; for(;;) { if(mb.size() >= cb.size()) { std::memmove( static_cast( mb.data()) + mb.size() - cb.size(), cb.data(), cb.size()); mb = boost::asio::mutable_buffer{ mb.data(), mb.size() - cb.size()}; if(in_pos == in_begin) break; cb = *--in_pos; } else { std::memmove( mb.data(), static_cast( cb.data()) + cb.size() - mb.size(), mb.size()); cb = boost::asio::const_buffer{ cb.data(), cb.size() - mb.size()}; if(out_pos == out_begin) break; mb = *--out_pos; } } } template class match_icy { bool& match_; public: using result_type = std::pair; explicit match_icy(bool& b) : match_(b) { } result_type operator()(FwdIt first, FwdIt last) const { auto it = first; if(it == last) return {first, false}; if(*it != 'I') return {last, true}; if(++it == last) return {first, false}; if(*it != 'C') return {last, true}; if(++it == last) return {first, false}; if(*it != 'Y') return {last, true}; match_ = true; return {last, true}; }; }; } // detail template template class icy_stream::read_op : public boost::asio::coroutine { using alloc_type = typename #if defined(BOOST_NO_CXX11_ALLOCATOR) boost::asio::associated_allocator_t::template rebind::other; #else std::allocator_traits> ::template rebind_alloc; #endif struct data { icy_stream& s; buffers_adapter b; bool match = false; data( Handler const&, icy_stream& s_, MutableBufferSequence const& b_) : s(s_) , b(b_) { } }; handler_ptr d_; public: read_op(read_op&&) = default; read_op(read_op const&) = delete; template read_op( DeducedHandler&& h, icy_stream& s, MutableBufferSequence const& b) : d_(std::forward(h), s, b) { } using allocator_type = boost::asio::associated_allocator_t; allocator_type get_allocator() const noexcept { return (boost::asio::get_associated_allocator)(d_.handler()); } using executor_type = boost::asio::associated_executor_t< Handler, decltype(std::declval().get_executor())>; executor_type get_executor() const noexcept { return (boost::asio::get_associated_executor)( d_.handler(), d_->s.get_executor()); } void operator()( boost::system::error_code ec, std::size_t bytes_transferred); template friend void asio_handler_invoke(Function&& f, read_op* op) { using boost::asio::asio_handler_invoke; asio_handler_invoke(f, std::addressof(op->d_.handler())); } }; template template void icy_stream:: read_op:: operator()( error_code ec, std::size_t bytes_transferred) { using boost::asio::buffer_copy; using boost::asio::buffer_size; using iterator = boost::asio::buffers_iterator< typename detail::dynamic_buffer_ref< buffers_adapter>::const_buffers_type>; auto& d = *d_; BOOST_ASIO_CORO_REENTER(*this) { if(d.b.max_size() == 0) { BOOST_ASIO_CORO_YIELD boost::asio::post(d.s.get_executor(), bind_handler(std::move(*this), ec, 0)); goto upcall; } if(! d.s.detect_) { if(d.s.copy_ > 0) { auto const n = buffer_copy( d.b.prepare(std::min( d.s.copy_, d.b.max_size())), boost::asio::buffer(d.s.buf_)); d.b.commit(n); d.s.copy_ = static_cast( d.s.copy_ - n); if(d.s.copy_ > 0) std::memmove( d.s.buf_, &d.s.buf_[n], d.s.copy_); } if(d.b.size() < d.b.max_size()) { BOOST_ASIO_CORO_YIELD d.s.next_layer().async_read_some( d.b.prepare(d.b.max_size() - d.b.size()), std::move(*this)); d.b.commit(bytes_transferred); } bytes_transferred = d.b.size(); goto upcall; } d.s.detect_ = false; if(d.b.max_size() < 8) { BOOST_ASIO_CORO_YIELD boost::asio::async_read( d.s.next_layer(), boost::asio::buffer(d.s.buf_, 3), std::move(*this)); if(ec) goto upcall; auto n = bytes_transferred; BOOST_ASSERT(n == 3); if( d.s.buf_[0] != 'I' || d.s.buf_[1] != 'C' || d.s.buf_[2] != 'Y') { buffer_copy( d.b.value(), boost::asio::buffer(d.s.buf_, n)); if(d.b.max_size() < 3) { d.s.copy_ = static_cast( 3 - d.b.max_size()); std::memmove( d.s.buf_, &d.s.buf_[d.b.max_size()], d.s.copy_); } bytes_transferred = (std::min)( n, d.b.max_size()); goto upcall; } d.s.copy_ = static_cast( buffer_copy( boost::asio::buffer(d.s.buf_), icy_stream::version() + d.b.max_size())); bytes_transferred = buffer_copy( d.b.value(), icy_stream::version()); goto upcall; } BOOST_ASIO_CORO_YIELD boost::asio::async_read_until( d.s.next_layer(), detail::ref(d.b), detail::match_icy(d.match), std::move(*this)); if(ec) goto upcall; { auto n = bytes_transferred; BOOST_ASSERT(n == d.b.size()); if(! d.match) goto upcall; if(d.b.size() + 5 > d.b.max_size()) { d.s.copy_ = static_cast( n + 5 - d.b.max_size()); std::copy( boost::asio::buffers_begin(d.b.value()) + n - d.s.copy_, boost::asio::buffers_begin(d.b.value()) + n, d.s.buf_); n = d.b.max_size() - 5; } { buffers_suffix> dest( boost::in_place_init, d.b.value()); dest.consume(5); detail::buffer_shift( buffers_prefix(n, dest), buffers_prefix(n, d.b.value())); buffer_copy(d.b.value(), icy_stream::version()); n += 5; bytes_transferred = n; } } upcall: d_.invoke(ec, bytes_transferred); } } //------------------------------------------------------------------------------ template template icy_stream:: icy_stream(Args&&... args) : stream_(std::forward(args)...) { } template template std::size_t icy_stream:: read_some(MutableBufferSequence const& buffers) { static_assert(boost::beast::is_sync_read_stream::value, "SyncReadStream requirements not met"); static_assert(boost::asio::is_mutable_buffer_sequence< MutableBufferSequence>::value, "MutableBufferSequence requirements not met"); error_code ec; auto n = read_some(buffers, ec); if(ec) BOOST_THROW_EXCEPTION(boost::system::system_error{ec}); return n; } template template std::size_t icy_stream:: read_some(MutableBufferSequence const& buffers, error_code& ec) { static_assert(boost::beast::is_sync_read_stream::value, "SyncReadStream requirements not met"); static_assert(boost::asio::is_mutable_buffer_sequence< MutableBufferSequence>::value, "MutableBufferSequence requirements not met"); using boost::asio::buffer_copy; using boost::asio::buffer_size; using iterator = boost::asio::buffers_iterator< typename detail::dynamic_buffer_ref< buffers_adapter>::const_buffers_type>; buffers_adapter b(buffers); if(b.max_size() == 0) { ec.assign(0, ec.category()); return 0; } if(! detect_) { if(copy_ > 0) { auto const n = buffer_copy( b.prepare(std::min( copy_, b.max_size())), boost::asio::buffer(buf_)); b.commit(n); copy_ = static_cast( copy_ - n); if(copy_ > 0) std::memmove( buf_, &buf_[n], copy_); } if(b.size() < b.max_size()) b.commit(stream_.read_some( b.prepare(b.max_size() - b.size()), ec)); return b.size(); } detect_ = false; if(b.max_size() < 8) { auto n = boost::asio::read( stream_, boost::asio::buffer(buf_, 3), ec); if(ec) return 0; BOOST_ASSERT(n == 3); if( buf_[0] != 'I' || buf_[1] != 'C' || buf_[2] != 'Y') { buffer_copy( buffers, boost::asio::buffer(buf_, n)); if(b.max_size() < 3) { copy_ = static_cast( 3 - b.max_size()); std::memmove( buf_, &buf_[b.max_size()], copy_); } return (std::min)(n, b.max_size()); } copy_ = static_cast( buffer_copy( boost::asio::buffer(buf_), version() + b.max_size())); return buffer_copy( buffers, version()); } bool match = false; auto n = boost::asio::read_until( stream_, detail::ref(b), detail::match_icy(match), ec); if(ec) return n; BOOST_ASSERT(n == b.size()); if(! match) return n; if(b.size() + 5 > b.max_size()) { copy_ = static_cast( n + 5 - b.max_size()); std::copy( boost::asio::buffers_begin(buffers) + n - copy_, boost::asio::buffers_begin(buffers) + n, buf_); n = b.max_size() - 5; } buffers_suffix> dest( boost::in_place_init, buffers); dest.consume(5); detail::buffer_shift( buffers_prefix(n, dest), buffers_prefix(n, buffers)); buffer_copy(buffers, version()); n += 5; return n; } template template< class MutableBufferSequence, class ReadHandler> BOOST_ASIO_INITFN_RESULT_TYPE( ReadHandler, void(error_code, std::size_t)) icy_stream:: async_read_some( MutableBufferSequence const& buffers, ReadHandler&& handler) { static_assert(boost::beast::is_async_read_stream::value, "AsyncReadStream requirements not met"); static_assert(boost::asio::is_mutable_buffer_sequence< MutableBufferSequence >::value, "MutableBufferSequence requirements not met"); BOOST_BEAST_HANDLER_INIT( ReadHandler, void(error_code, std::size_t)); read_op< MutableBufferSequence, BOOST_ASIO_HANDLER_TYPE( ReadHandler, void(error_code, std::size_t))>{ std::move(init.completion_handler), *this, buffers}( {}, 0); return init.result.get(); } template template std::size_t icy_stream:: write_some(MutableBufferSequence const& buffers) { static_assert(boost::beast::is_sync_write_stream::value, "SyncWriteStream requirements not met"); static_assert(boost::asio::is_const_buffer_sequence< MutableBufferSequence>::value, "MutableBufferSequence requirements not met"); return stream_.write_some(buffers); } template template std::size_t icy_stream:: write_some(MutableBufferSequence const& buffers, error_code& ec) { static_assert(boost::beast::is_sync_write_stream::value, "SyncWriteStream requirements not met"); static_assert(boost::asio::is_const_buffer_sequence< MutableBufferSequence>::value, "MutableBufferSequence requirements not met"); return stream_.write_some(buffers, ec); } template template< class MutableBufferSequence, class WriteHandler> BOOST_ASIO_INITFN_RESULT_TYPE( WriteHandler, void(error_code, std::size_t)) icy_stream:: async_write_some( MutableBufferSequence const& buffers, WriteHandler&& handler) { static_assert(boost::beast::is_async_write_stream::value, "AsyncWriteStream requirements not met"); static_assert(boost::asio::is_const_buffer_sequence< MutableBufferSequence>::value, "MutableBufferSequence requirements not met"); return stream_.async_write_some(buffers, std::forward(handler)); } } // http } // beast } // boost #endif