diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a8b5d82..7a47a705 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Version 55: * Don't allocate memory to handle obs-fold * Add Beast CMake interface target +* Avoid a parser allocation using non-flat buffer -------------------------------------------------------------------------------- diff --git a/include/beast/http/basic_parser.hpp b/include/beast/http/basic_parser.hpp index 6777abaf..292aa050 100644 --- a/include/beast/http/basic_parser.hpp +++ b/include/beast/http/basic_parser.hpp @@ -74,6 +74,9 @@ class basic_parser // limit on the size of the obs-fold buffer static std::size_t constexpr max_obs_fold = 4096; + // limit on the size of the stack flat buffer + static std::size_t constexpr max_stack_buffer = 8192; + // Message will be complete after reading header static unsigned constexpr flagSkipBody = 1<< 0; @@ -363,9 +366,10 @@ private: } template - string_view - maybe_flatten( - ConstBufferSequence const& buffers); + std::size_t + put_from_stack(std::size_t size, + ConstBufferSequence const& buffers, + error_code& ec); void parse_header(char const*& p, diff --git a/include/beast/http/impl/basic_parser.ipp b/include/beast/http/impl/basic_parser.ipp index 84dfeac0..24408767 100644 --- a/include/beast/http/impl/basic_parser.ipp +++ b/include/beast/http/impl/basic_parser.ipp @@ -88,9 +88,35 @@ put(ConstBufferSequence const& buffers, static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); - auto const buffer = maybe_flatten(buffers); + using boost::asio::buffer_cast; + using boost::asio::buffer_copy; + using boost::asio::buffer_size; + auto const p = buffers.begin(); + auto const last = buffers.end(); + if(p == last) + return 0; + if(std::next(p) == last) + { + // single buffer + auto const b = *p; + return put(boost::asio::const_buffers_1{ + buffer_cast(b), + buffer_size(b)}, ec); + } + auto const size = buffer_size(buffers); + if(size <= max_stack_buffer) + return put_from_stack(size, buffers, ec); + if(size > buf_len_) + { + // reallocate + buf_.reset(new char[size]); + buf_len_ = size; + } + // flatten + buffer_copy(boost::asio::buffer( + buf_.get(), buf_len_), buffers); return put(boost::asio::const_buffers_1{ - buffer.data(), buffer.size()}, ec); + buf_.get(), buf_len_}, ec); } template @@ -209,37 +235,18 @@ put_eof(error_code& ec) template template -inline -string_view +std::size_t basic_parser:: -maybe_flatten( - ConstBufferSequence const& buffers) +put_from_stack(std::size_t size, + ConstBufferSequence const& buffers, + error_code& ec) { - using boost::asio::buffer_cast; + char buf[max_stack_buffer]; + using boost::asio::buffer; using boost::asio::buffer_copy; - using boost::asio::buffer_size; - auto const p = buffers.begin(); - auto const last = buffers.end(); - if(p == last) - return {nullptr, 0}; - if(std::next(p) == last) - { - // single buffer - auto const b = *p; - return {buffer_cast(b), - buffer_size(b)}; - } - auto const len = buffer_size(buffers); - if(len > buf_len_) - { - // reallocate - buf_.reset(new char[len]); - buf_len_ = len; - } - // flatten - buffer_copy(boost::asio::buffer( - buf_.get(), buf_len_), buffers); - return {buf_.get(), len}; + buffer_copy(buffer(buf, sizeof(buf)), buffers); + return put(boost::asio::const_buffers_1{ + buf, size}, ec); } template