From 2cfe3ba1b80c3db219b8712cb1559de8f57239fc Mon Sep 17 00:00:00 2001 From: Damian Jarek Date: Fri, 12 Apr 2019 01:32:36 +0200 Subject: [PATCH] Replace static_string in parser close #1574 This change yields a modest performance improment of 1-2% by replacing the exception-based handling of buffer overflow with one based on regular conditional checks. Signed-off-by: Damian Jarek --- CHANGELOG.md | 1 + include/boost/beast/core/static_string.hpp | 3 +- .../boost/beast/http/detail/basic_parser.hpp | 56 +++++++++++- .../boost/beast/http/detail/basic_parser.ipp | 89 +++++++++---------- .../boost/beast/http/impl/basic_parser.ipp | 3 +- 5 files changed, 100 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1e0aaa7..866e22e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Version 251: * detect_ssl uses bool * launder pointers * Fix compilation on MSVC with std::string_view +* Replace static_string in parser -------------------------------------------------------------------------------- diff --git a/include/boost/beast/core/static_string.hpp b/include/boost/beast/core/static_string.hpp index fc60a72e..c1cea6c3 100644 --- a/include/boost/beast/core/static_string.hpp +++ b/include/boost/beast/core/static_string.hpp @@ -16,8 +16,7 @@ #include #include #include -#include -#include +#include #include #include #include diff --git a/include/boost/beast/http/detail/basic_parser.hpp b/include/boost/beast/http/detail/basic_parser.hpp index f4e9083f..49596521 100644 --- a/include/boost/beast/http/detail/basic_parser.hpp +++ b/include/boost/beast/http/detail/basic_parser.hpp @@ -10,7 +10,6 @@ #ifndef BOOST_BEAST_HTTP_DETAIL_BASIC_PARSER_HPP #define BOOST_BEAST_HTTP_DETAIL_BASIC_PARSER_HPP -#include #include #include #include @@ -24,6 +23,59 @@ namespace beast { namespace http { namespace detail { +template +class char_buffer +{ +public: + bool try_push_back(char c) + { + if (size_ == N) + return false; + buf_[size_++] = c; + return true; + } + + bool try_append(char const* first, char const* last) + { + std::size_t const n = last - first; + if (n > N - size_) + return false; + std::memmove(&buf_[size_], first, n); + size_ += n; + return true; + } + + void clear() noexcept + { + size_ = 0; + } + + char* data() noexcept + { + return buf_; + } + + char const* data() const noexcept + { + return buf_; + } + + std::size_t size() const noexcept + { + return size_; + } + + bool empty() const noexcept + { + return size_ == 0; + } + +private: + std::size_t size_= 0; + char buf_[N]; +}; + + struct basic_parser_base { // limit on the size of the obs-fold buffer @@ -182,7 +234,7 @@ struct basic_parser_base char const* last, string_view& name, string_view& value, - static_string& buf, + char_buffer& buf, error_code& ec); BOOST_BEAST_DECL diff --git a/include/boost/beast/http/detail/basic_parser.ipp b/include/boost/beast/http/detail/basic_parser.ipp index 934d0dc5..8aeb6833 100644 --- a/include/boost/beast/http/detail/basic_parser.ipp +++ b/include/boost/beast/http/detail/basic_parser.ipp @@ -491,7 +491,7 @@ parse_field( char const* last, string_view& name, string_view& value, - static_string& buf, + char_buffer& buf, error_code& ec) { /* header-field = field-name ":" OWS field-value OWS @@ -607,63 +607,60 @@ parse_field( if(token_last != first) break; } - buf.resize(0); - buf.append(first, token_last); - BOOST_ASSERT(! buf.empty()); -#ifndef BOOST_NO_EXCEPTIONS - try -#endif + buf.clear(); + if (!buf.try_append(first, token_last)) { - for(;;) + ec = error::header_limit; + return; + } + + BOOST_ASSERT(! buf.empty()); + for(;;) + { + // eat leading ' ' and '\t' + for(;;++p) { - // eat leading ' ' and '\t' - for(;;++p) - { - if(p + 1 > last) - { - ec = error::need_more; - return; - } - if(! (*p == ' ' || *p == '\t')) - break; - } - // parse to CRLF - first = p; - p = parse_token_to_eol(p, last, token_last, ec); - if(ec) - return; - if(! p) - { - ec = error::bad_value; - return; - } - // Look 1 char past the CRLF to handle obs-fold. if(p + 1 > last) { ec = error::need_more; return; } - token_last = trim_back(token_last, first); - if(first != token_last) + if(! (*p == ' ' || *p == '\t')) + break; + } + // parse to CRLF + first = p; + p = parse_token_to_eol(p, last, token_last, ec); + if(ec) + return; + if(! p) + { + ec = error::bad_value; + return; + } + // Look 1 char past the CRLF to handle obs-fold. + if(p + 1 > last) + { + ec = error::need_more; + return; + } + token_last = trim_back(token_last, first); + if(first != token_last) + { + if (!buf.try_push_back(' ') || + !buf.try_append(first, token_last)) { - buf.push_back(' '); - buf.append(first, token_last); - } - if(*p != ' ' && *p != '\t') - { - value = {buf.data(), buf.size()}; + ec = error::header_limit; return; } - ++p; } + if(*p != ' ' && *p != '\t') + { + value = {buf.data(), buf.size()}; + return; + } + ++p; } -#ifndef BOOST_NO_EXCEPTIONS - catch(std::length_error const&) - { - ec = error::header_limit; - return; - } -#endif } diff --git a/include/boost/beast/http/impl/basic_parser.ipp b/include/boost/beast/http/impl/basic_parser.ipp index 0d9e3a9f..3e443697 100644 --- a/include/boost/beast/http/impl/basic_parser.ipp +++ b/include/boost/beast/http/impl/basic_parser.ipp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -405,7 +404,7 @@ parse_fields(char const*& in, string_view name; string_view value; // https://stackoverflow.com/questions/686217/maximum-on-http-header-values - static_string buf; + detail::char_buffer buf; auto p = in; for(;;) {