From 522d3bf378a7f4407b6d19b6c8dd9c2cc0f6c6a5 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Fri, 23 Jun 2017 09:33:28 -0700 Subject: [PATCH] basic_fields optimizations --- CHANGELOG.md | 1 + include/beast/http/fields.hpp | 4 + include/beast/http/impl/fields.ipp | 230 ++++++++++++++--------------- 3 files changed, 112 insertions(+), 123 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 564c9ae8..b482c87a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Version 66: * Handle bad_alloc in parser * Tidy up message piecewise ctors * Add header aliases +* basic_fields optimizations -------------------------------------------------------------------------------- diff --git a/include/beast/http/fields.hpp b/include/beast/http/fields.hpp index dcbce060..1d5d87be 100644 --- a/include/beast/http/fields.hpp +++ b/include/beast/http/fields.hpp @@ -624,6 +624,10 @@ private: void realloc_string(string_view& dest, string_view s); + void + realloc_target( + string_view& dest, string_view s); + template void copy_all(basic_fields const&); diff --git a/include/beast/http/impl/fields.ipp b/include/beast/http/impl/fields.ipp index 474510a9..ccac2979 100644 --- a/include/beast/http/impl/fields.ipp +++ b/include/beast/http/impl/fields.ipp @@ -139,26 +139,17 @@ public: bool get_keep_alive(int version) const; - template - void - prepare(String& s, basic_fields const& f, - unsigned version, verb v); - - template - void - prepare(String&s, basic_fields const& f, - unsigned version, unsigned code); - basic_fields const& f_; - static_string ss_; - string_view sv_; - std::string s_; + boost::asio::const_buffer cb_[3]; + char buf_[13]; bool chunked_; bool keep_alive_; public: using const_buffers_type = buffer_cat_view< + boost::asio::const_buffers_1, + boost::asio::const_buffers_1, boost::asio::const_buffers_1, field_range, boost::asio::const_buffers_1>; @@ -185,7 +176,9 @@ public: get() const { return buffer_cat( - boost::asio::buffer(sv_.data(), sv_.size()), + boost::asio::const_buffers_1{cb_[0]}, + boost::asio::const_buffers_1{cb_[1]}, + boost::asio::const_buffers_1{cb_[2]}, field_range(f_.list_.begin(), f_.list_.end()), detail::chunk_crlf()); } @@ -226,89 +219,6 @@ get_keep_alive(int version) const return ! token_list{it->value()}.exists("close"); } -template -template -void -basic_fields::reader:: -prepare(String& s, basic_fields const&, - unsigned version, verb v) -{ - if(v == verb::unknown) - { - auto const sv = - f_.get_method_impl(); - s.append(sv.data(), sv.size()); - } - else - { - auto const sv = to_string(v); - s.append(sv.data(), sv.size()); - } - s.push_back(' '); - { - auto const sv = f_.get_target_impl(); - s.append(sv.data(), sv.size()); - } - if(version == 11) - { - s.append(" HTTP/1.1\r\n"); - } - else if(version == 10) - { - s.append(" HTTP/1.0\r\n"); - } - else - { - s.append(" HTTP/"); - s.push_back('0' + ((version / 10) % 10)); - s.push_back('.'); - s.push_back('0' + (version % 10)); - s.append("\r\n"); - } -} - -template -template -void -basic_fields::reader:: -prepare(String& s, basic_fields const&, - unsigned version, unsigned code) -{ - if(version == 11) - { - s.append("HTTP/1.1 "); - } - else if(version == 10) - { - s.append("HTTP/1.0 "); - } - else - { - s.append("HTTP/"); - s.push_back('0' + ((version / 10) % 10)); - s.push_back('.'); - s.push_back('0' + (version % 10)); - s.push_back(' '); - } - { - auto const ss = to_static_string(code); - s.append(ss.data(), ss.size()); - } - s.push_back(' '); - if(int_to_status(code) == status::unknown) - { - auto const sv = f_.get_reason_impl(); - s.append(sv.data(), sv.size()); - } - else - { - auto const sv = - obsolete_reason(int_to_status(code)); - s.append(sv.data(), sv.size()); - } - s.append("\r\n"); -} - template basic_fields::reader:: reader(basic_fields const& f, @@ -317,16 +227,36 @@ reader(basic_fields const& f, , chunked_(get_chunked()) , keep_alive_(get_keep_alive(version)) { - try - { - prepare(ss_, f, version, v); - sv_ = ss_; - } - catch(std::length_error const&) - { - prepare(s_, f, version, v); - sv_ = s_; - } +/* + request + "" + " " + " HTTP/X.Y\r\n" (11 chars) +*/ + string_view sv; + if(v == verb::unknown) + sv = f_.get_method_impl(); + else + sv = to_string(v); + cb_[0] = {sv.data(), sv.size()}; + + // target_or_reason_ has a leading SP + cb_[1] = { + f_.target_or_reason_.data(), + f_.target_or_reason_.size()}; + + buf_[0] = ' '; + buf_[1] = 'H'; + buf_[2] = 'T'; + buf_[3] = 'T'; + buf_[4] = 'P'; + buf_[5] = '/'; + buf_[6] = '0' + static_cast(version / 10); + buf_[7] = '.'; + buf_[8] = '0' + static_cast(version % 10); + buf_[9] = '\r'; + buf_[10]= '\n'; + cb_[2] = {buf_, 11}; } template @@ -337,16 +267,35 @@ reader(basic_fields const& f, , chunked_(get_chunked()) , keep_alive_(get_keep_alive(version)) { - try - { - prepare(ss_, f, version, code); - sv_ = ss_; - } - catch(std::length_error const&) - { - prepare(s_, f, version, code); - sv_ = s_; - } +/* + response + "HTTP/X.Y ### " (13 chars) + "" + "\r\n" +*/ + buf_[0] = 'H'; + buf_[1] = 'T'; + buf_[2] = 'T'; + buf_[3] = 'P'; + buf_[4] = '/'; + buf_[5] = '0' + static_cast(version / 10); + buf_[6] = '.'; + buf_[7] = '0' + static_cast(version % 10); + buf_[8] = ' '; + buf_[9] = '0' + static_cast(code / 100); + buf_[10]= '0' + static_cast((code / 10) % 10); + buf_[11]= '0' + static_cast(code % 10); + buf_[12]= ' '; + cb_[0] = {buf_, 13}; + + string_view sv; + if(! f_.target_or_reason_.empty()) + sv = f_.target_or_reason_; + else + sv = obsolete_reason(static_cast(code)); + cb_[1] = {sv.data(), sv.size()}; + + cb_[2] = detail::chunk_crlf(); } //------------------------------------------------------------------------------ @@ -425,7 +374,8 @@ basic_fields:: { delete_list(); realloc_string(method_, {}); - realloc_string(target_or_reason_, {}); + realloc_string( + target_or_reason_, {}); } template @@ -828,7 +778,8 @@ void basic_fields:: set_target_impl(string_view s) { - realloc_string(target_or_reason_, s); + realloc_target( + target_or_reason_, s); } template @@ -837,7 +788,8 @@ void basic_fields:: set_reason_impl(string_view s) { - realloc_string(target_or_reason_, s); + realloc_string( + target_or_reason_, s); } template @@ -855,7 +807,11 @@ string_view basic_fields:: get_target_impl() const { - return target_or_reason_; + if(target_or_reason_.empty()) + return target_or_reason_; + return { + target_or_reason_.data() + 1, + target_or_reason_.size() - 1}; } template @@ -869,7 +825,7 @@ get_reason_impl() const namespace detail { -// Builds a new string with "chunked" maybe taken off the end +// Builds a new string with "chunked" taken off the end if present template void without_chunked_last(String& s, string_view const& tokens) @@ -1059,7 +1015,6 @@ void basic_fields:: realloc_string(string_view& dest, string_view s) { - s = detail::trim(s); if(dest.empty() && s.empty()) return; auto a = typename std::allocator_traits< @@ -1079,6 +1034,35 @@ realloc_string(string_view& dest, string_view s) } } +template +void +basic_fields:: +realloc_target( + string_view& dest, string_view s) +{ + // The target string are stored with an + // extra space at the beginning to help + // the reader class. + if(dest.empty() && s.empty()) + return; + auto a = typename std::allocator_traits< + Allocator>::template rebind_alloc< + char>(alloc_); + if(! dest.empty()) + { + a.deallocate(const_cast( + dest.data()), dest.size()); + dest.clear(); + } + if(! s.empty()) + { + auto const p = a.allocate(1 + s.size()); + p[0] = ' '; + std::memcpy(p + 1, s.data(), s.size()); + dest = {p, 1 + s.size()}; + } +} + template template void