From ff8be0e931471b45b514d2a0ab4f5afafeae195b Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sun, 11 Jun 2017 17:55:05 -0700 Subject: [PATCH] Avoid a parser allocation using non-flat buffer: When the parser is presented with an input buffer sequence with length greater than one, the input is flattened using stack space instead of dynamic allocation if the buffer size is no greater than 8192. --- CHANGELOG.md | 1 + include/beast/http/basic_parser.hpp | 10 ++-- include/beast/http/impl/basic_parser.ipp | 67 +++++++++++++----------- 3 files changed, 45 insertions(+), 33 deletions(-) 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