diff --git a/include/boost/http/parser.hpp b/include/boost/http/parser.hpp index dba3a064..76858a72 100644 --- a/include/boost/http/parser.hpp +++ b/include/boost/http/parser.hpp @@ -17,8 +17,7 @@ #include #include -#include -#include +#include #include #include #include @@ -26,6 +25,7 @@ #include #include +#include #include #include #include @@ -526,7 +526,7 @@ read(Stream& stream, MB buffers) co_return {{}, 0}; std::size_t total = 0; - auto dest = capy::sans_prefix(buffers, 0); + auto dest = capy::buffer_slice(buffers); for(;;) { @@ -538,12 +538,12 @@ read(Stream& stream, MB buffers) auto body_data = pull_body(); if(capy::buffer_size(body_data) > 0) { - std::size_t copied = capy::buffer_copy(dest, body_data); + std::size_t copied = capy::buffer_copy(dest.data(), body_data); consume_body(copied); total += copied; - dest = capy::sans_prefix(dest, copied); + dest.remove_prefix(copied); - if(capy::buffer_empty(dest)) + if(capy::buffer_empty(dest.data())) co_return {{}, total}; } diff --git a/include/boost/http/serializer.hpp b/include/boost/http/serializer.hpp index be02163d..b29d1594 100644 --- a/include/boost/http/serializer.hpp +++ b/include/boost/http/serializer.hpp @@ -16,13 +16,13 @@ #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -82,7 +82,7 @@ class serializer of mutable buffers for streaming. */ using mutable_buffers_type = - capy::mutable_buffer_pair; + std::array; /** The type used to represent a sequence of constant buffers that refers to the output diff --git a/src/detail/array_of_const_buffers.cpp b/src/detail/array_of_const_buffers.cpp index ce288da9..02daaf4b 100644 --- a/src/detail/array_of_const_buffers.cpp +++ b/src/detail/array_of_const_buffers.cpp @@ -12,8 +12,6 @@ #include -#include - namespace boost { namespace http { namespace detail { @@ -38,7 +36,7 @@ consume(std::size_t n) auto* p = base_ + pos_; if(n < p->size()) { - capy::remove_prefix(*p, n); + *p += n; return; } n -= p->size(); diff --git a/src/detail/filter.cpp b/src/detail/filter.cpp index c6f25fea..8023afd4 100644 --- a/src/detail/filter.cpp +++ b/src/detail/filter.cpp @@ -10,27 +10,48 @@ #include "src/detail/filter.hpp" +#include #include namespace boost { namespace http { namespace detail { +namespace { + +// Returns true if the slice's current data view contains at most one +// non-empty buffer (i.e., we are processing the last logical chunk). +template +bool single_or_empty(Slice const& s) +{ + auto d = s.data(); + auto it = d.begin(); + auto const end_it = d.end(); + if(it == end_it) + return true; + ++it; + return it == end_it; +} + +} // anonymous + auto filter:: process( - capy::slice_of< - boost::span> out, - capy::const_buffer_pair in, + boost::span out_seq, + std::array in_seq, bool more) -> results { + auto out = capy::buffer_slice(out_seq); + auto in = capy::buffer_slice(in_seq); + results rv; bool p_more = true; for(;;) { - if(!more && p_more && in[1].size() == 0) + if(!more && p_more && single_or_empty(in)) { - if(capy::buffer_size(out) < min_out_buffer()) + if(capy::buffer_size(out.data()) < min_out_buffer()) { rv.out_short = true; return rv; @@ -38,8 +59,8 @@ process( p_more = false; } - auto ob = capy::front(out); - auto ib = capy::front(in); + auto ob = capy::front(out.data()); + auto ib = capy::front(in.data()); auto rs = do_process(ob, ib, p_more); rv.in_bytes += rs.in_bytes; @@ -57,13 +78,13 @@ process( return rv; } - capy::remove_prefix(out, rs.out_bytes); - capy::remove_prefix(in, rs.in_bytes); + out.remove_prefix(rs.out_bytes); + in.remove_prefix(rs.in_bytes); - if(capy::buffer_empty(out)) + if(capy::buffer_size(out.data()) == 0) return rv; - if(capy::buffer_empty(in) && rs.out_bytes < ob.size()) + if(capy::buffer_size(in.data()) == 0 && rs.out_bytes < ob.size()) return rv; } } diff --git a/src/detail/filter.hpp b/src/detail/filter.hpp index d511d859..e5dcffad 100644 --- a/src/detail/filter.hpp +++ b/src/detail/filter.hpp @@ -11,11 +11,12 @@ #ifndef BOOST_HTTP_DETAIL_FILTER_HPP #define BOOST_HTTP_DETAIL_FILTER_HPP -#include -#include +#include #include #include +#include + namespace boost { namespace http { namespace detail { @@ -65,9 +66,8 @@ class filter results process( - capy::slice_of< - boost::span> out, - capy::const_buffer_pair in, + boost::span out, + std::array in, bool more); protected: diff --git a/src/parser.cpp b/src/parser.cpp index e23249ee..3db5a4cd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -33,6 +33,7 @@ #include "src/detail/buffer_utils.hpp" #include "src/detail/zlib_filter_base.hpp" +#include #include namespace boost { @@ -123,6 +124,30 @@ Buffer Usage namespace { +// Construct a 2-element const_buffer pair representing the first +// `n` bytes of `src`. Replaces the pre-#262 `capy::prefix(src, n)` +// idiom which yielded a slice convertible to std::array. +inline std::array +prefix_pair( + std::array const& src, + std::size_t n) noexcept +{ + std::array result{}; + if(n <= src[0].size()) + { + result[0] = capy::const_buffer(src[0].data(), n); + } + else + { + result[0] = src[0]; + std::size_t remaining = n - src[0].size(); + if(remaining > src[1].size()) + remaining = src[1].size(); + result[1] = capy::const_buffer(src[1].data(), remaining); + } + return result; +} + class chained_sequence { char const* pos_; @@ -131,7 +156,7 @@ class chained_sequence char const* end_b_; public: - chained_sequence(capy::const_buffer_pair const& cbp) + chained_sequence(std::array const& cbp) : pos_(static_cast(cbp[0].data())) , end_(pos_ + cbp[0].size()) , begin_b_(static_cast(cbp[1].data())) @@ -440,8 +465,8 @@ class parser::impl capy::circular_dynamic_buffer cb0_; capy::circular_dynamic_buffer cb1_; - capy::mutable_buffer_pair mbp_; - capy::const_buffer_pair cbp_; + std::array mbp_; + std::array cbp_; std::unique_ptr filter_; @@ -1113,8 +1138,9 @@ class parser::impl { const std::size_t chunk_avail = clamp(chunk_remain_, cb0_.size()); - const auto chunk = - capy::prefix(cb0_.data(), chunk_avail); + auto cb0_data = cb0_.data(); + auto chunk = capy::buffer_slice( + cb0_data, 0, chunk_avail); if(body_limit_remain() < chunk_avail) { @@ -1127,7 +1153,7 @@ class parser::impl // in_place style auto copied = capy::buffer_copy( cb1_.prepare(cb1_.capacity()), - chunk); + chunk.data()); chunk_remain_ -= copied; body_avail_ += copied; body_total_ += copied; @@ -1243,7 +1269,7 @@ class parser::impl return {}; case state::body: case state::complete: - cbp_ = capy::prefix( + cbp_ = prefix_pair( (is_plain() ? cb0_ : cb1_).data(), body_avail_); return detail::make_span(cbp_); @@ -1342,7 +1368,7 @@ class parser::impl return filter_->process( detail::make_span(cb1_.prepare(n)), - capy::prefix(cb0_.data(), payload_avail), + prefix_pair(cb0_.data(), payload_avail), more); }(); diff --git a/src/serializer.cpp b/src/serializer.cpp index 2f429350..922c0bb9 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -39,6 +40,51 @@ namespace http { namespace { +// Trim n bytes from the front of a 2-element buffer pair, in place. +// Replaces the pre-#262 `capy::remove_prefix(pair, n)` idiom on a +// 2-element buffer sequence. +template +inline void +trim_prefix_pair(std::array& a, std::size_t n) noexcept +{ + if(n >= a[0].size()) + { + n -= a[0].size(); + a[0] = Buf(); + if(n >= a[1].size()) + a[1] = Buf(); + else + a[1] = Buf( + static_cast(const_cast( + static_cast(a[1].data()))) + n, + a[1].size() - n); + } + else + { + a[0] += n; + } +} + +// Trim n bytes from the back of a 2-element buffer pair, in place. +template +inline void +trim_suffix_pair(std::array& a, std::size_t n) noexcept +{ + if(n >= a[1].size()) + { + n -= a[1].size(); + a[1] = Buf(); + if(n >= a[0].size()) + a[0] = Buf(); + else + a[0] = Buf(a[0].data(), a[0].size() - n); + } + else + { + a[1] = Buf(a[1].data(), a[1].size() - n); + } +} + const capy::const_buffer crlf_and_final_chunk = {"\r\n0\r\n\r\n", 7}; @@ -64,7 +110,7 @@ chunk_header_len( void write_chunk_header( - const capy::mutable_buffer_pair& mbs, + const std::array& mbs, std::size_t size) noexcept { static constexpr char hexdig[] = @@ -584,7 +630,7 @@ class serializer::impl return out_capacity(); } - capy::mutable_buffer_pair + std::array stream_prepare() { if(state_ == state::start) @@ -667,15 +713,15 @@ class serializer::impl detail::throw_length_error(); } - capy::mutable_buffer_pair + std::array out_prepare() noexcept { auto mbp = out_.prepare(out_.capacity()); if(is_chunked_) { - capy::remove_prefix( + trim_prefix_pair( mbp, chunk_header_len_); - capy::remove_suffix( + trim_suffix_pair( mbp, crlf_and_final_chunk.size()); } return mbp; diff --git a/test/unit/serializer.cpp b/test/unit/serializer.cpp index 7cf2ef31..cb33d84a 100644 --- a/test/unit/serializer.cpp +++ b/test/unit/serializer.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include #include @@ -60,17 +60,15 @@ struct serializer_test std::string read_some(serializer& sr) { - capy::slice_of< - serializer::const_buffers_type> cbs - = sr.prepare().value(); + auto bufs = sr.prepare().value(); + auto cbs = capy::buffer_slice(bufs, 0, 256); BOOST_TEST(!sr.is_done()); // We limit buffer consumption to necessitate // multiple calls to serializer::prepare() and // serializer::consume(), allowing tests to cover // state management within these functions std::string s; - capy::keep_prefix(cbs, 256); - for( auto buf : cbs) + for( auto buf : cbs.data()) { s.append( reinterpret_cast(buf.data()), @@ -336,7 +334,7 @@ struct serializer_test while( buf.size() > 0 ) { auto n = capy::buffer_copy(out_buf, buf); - capy::remove_prefix(buf, n); + buf += n; s.insert( s.end(),