diff --git a/CHANGELOG.md b/CHANGELOG.md index b3107a36..38da6722 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Version 69: + +* basic_parser optimizations + +-------------------------------------------------------------------------------- + Version 68: * Split common tests to a new project diff --git a/include/beast/http/detail/basic_parser.hpp b/include/beast/http/detail/basic_parser.hpp index ed1fd9be..c3c534cf 100644 --- a/include/beast/http/detail/basic_parser.hpp +++ b/include/beast/http/detail/basic_parser.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -276,7 +277,7 @@ protected: bool parse_crlf(char const*& it) { - if( it[0] != '\r' && it[1] != '\n') + if( it[0] != '\r' || it[1] != '\n') return false; it += 2; return true; @@ -381,10 +382,31 @@ protected: static char const* find_eol( - char const* first, char const* last, + char const* it, char const* last, error_code& ec) { - auto it = first; +#if 0 + // SLOWER + it = reinterpret_cast( + std::memchr(it, '\r', last - it)); + if(! it) + { + ec.assign(0, ec.category()); + return nullptr; + } + if(it + 2 > last) + { + ec.assign(0, ec.category()); + return nullptr; + } + if(it[1] != '\n') + { + ec = error::bad_line_ending; + return nullptr; + } + ec.assign(0, ec.category()); + return it + 2; +#else for(;;) { if(it == last) @@ -411,61 +433,41 @@ protected: // for lines terminated with a single '\n'? ++it; } +#endif } // VFALCO Can SIMD help this? static char const* - find_eom( - char const* first, char const* last, - error_code& ec) + find_eom(char const* p, char const* last) { - auto it = first; for(;;) { - if(it == last) - { - ec.assign(0, ec.category()); + if(p + 4 > last) return nullptr; - } - if(*it == '\r') + if(p[3] != '\n') { - if(++it == last) - { - ec.assign(0, ec.category()); - return nullptr; - } - if(*it != '\n') - { - ec = error::bad_line_ending; - return nullptr; - } - if(++it == last) - { - ec.assign(0, ec.category()); - return nullptr; - } - if(*it != '\r') - { - ++it; - continue; - } - if(++it == last) - { - ec.assign(0, ec.category()); - return nullptr; - } - if(*it != '\n') - { - ec = error::bad_line_ending; - return nullptr; - } - ec.assign(0, ec.category()); - return ++it; + if(p[3] == '\r') + ++p; + else + p += 4; + } + else if(p[2] != '\r') + { + p += 4; + } + else if(p[1] != '\n') + { + p += 2; + } + else if(p[0] != '\r') + { + p += 2; + } + else + { + return p + 4; } - // VFALCO Should we handle the legacy case - // for lines terminated with a single '\n'? - ++it; } } }; diff --git a/include/beast/http/impl/basic_parser.ipp b/include/beast/http/impl/basic_parser.ipp index 444c54e3..809f06af 100644 --- a/include/beast/http/impl/basic_parser.ipp +++ b/include/beast/http/impl/basic_parser.ipp @@ -21,6 +21,40 @@ namespace beast { namespace http { +namespace detail { + +template +inline +FwdIt +skip_ows2(FwdIt it, FwdIt const& end) +{ + while(it != end) + { + if(*it != ' ' && *it != '\t') + break; + ++it; + } + return it; +} + +template +inline +RanIt +skip_ows_rev2( + RanIt it, RanIt const& first) +{ + while(it != first) + { + auto const c = it[-1]; + if(c != ' ' && c != '\t') + break; + --it; + } + return it; +} + +} // detail + template template basic_parser:: @@ -265,10 +299,8 @@ parse_header(char const*& p, ec = http::error::need_more; return; } - auto const term = find_eom( - p + skip_, p + n, ec); - if(ec) - return; + auto const term = + find_eom(p + skip_, p + n); if(! term) { skip_ = n - 3; @@ -328,7 +360,13 @@ parse_header(char const*& p, char const* term, } auto const version = parse_version(p); - if(version < 0 || ! parse_crlf(p)) + if(version < 0) + { + ec = error::bad_version; + return; + } + + if(! parse_crlf(p)) { ec = error::bad_version; return; @@ -595,9 +633,7 @@ parse_chunk_header(char const*& p0, BOOST_ASSERT(! ec); } - auto eom = find_eom(p0 + skip_, pend, ec); - if(ec) - return; + auto eom = find_eom(p0 + skip_, pend); if(! eom) { BOOST_ASSERT(n >= 3); @@ -688,8 +724,8 @@ parse_fields(char const*& p, *term != '\t') { auto it2 = term - 2; - detail::skip_ows(p, it2); - detail::skip_ows_rev(it2, p); + p = detail::skip_ows2(p, it2); + it2 = detail::skip_ows_rev2(it2, p); auto const f = string_to_field(name); auto const value = make_string(p, it2); do_field(f, value, ec); @@ -706,7 +742,7 @@ parse_fields(char const*& p, for(;;) { auto const it2 = term - 2; - detail::skip_ows(p, it2); + p = detail::skip_ows2(p, it2); if(p != it2) break; p = term; @@ -729,7 +765,7 @@ parse_fields(char const*& p, if(*p != ' ' && *p != '\t') break; s.push_back(' '); - detail::skip_ows(p, term - 2); + p = detail::skip_ows2(p, term - 2); term = find_eol(p, last, ec); if(ec) return; diff --git a/test/http/basic_parser.cpp b/test/http/basic_parser.cpp index 219101f4..84831c6e 100644 --- a/test/http/basic_parser.cpp +++ b/test/http/basic_parser.cpp @@ -378,8 +378,8 @@ public: bad("GET / HTTP/1.0 \r\n" "\r\n", error::bad_version); bad("GET / HTTP/1_0\r\n" "\r\n", error::bad_version); bad("GET / HTTP/1.0\n\r\n" "\r\n", error::bad_version); - bad("GET / HTTP/1.0\n\r\r\n" "\r\n", error::bad_line_ending); - bad("GET / HTTP/1.0\r\r\n" "\r\n", error::bad_line_ending); + bad("GET / HTTP/1.0\n\r\r\n" "\r\n", error::bad_version); + bad("GET / HTTP/1.0\r\r\n" "\r\n", error::bad_version); } void @@ -403,7 +403,7 @@ public: good("HTTP/1.1 200 \x80\x81...\xfe\xff\r\n\r\n"); good("HTTP/1.1 200 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\r\n\r\n"); - bad("\rHTTP/1.0 200 OK\r\n" "\r\n", error::bad_line_ending); + bad("\rHTTP/1.0 200 OK\r\n" "\r\n", error::bad_version); bad("\nHTTP/1.0 200 OK\r\n" "\r\n", error::bad_version); bad(" HTTP/1.0 200 OK\r\n" "\r\n", error::bad_version); bad("_TTP/1.0 200 OK\r\n" "\r\n", error::bad_version); @@ -426,7 +426,7 @@ public: bad("HTTP/1.0 200 \x01\r\n" "\r\n", error::bad_reason); bad("HTTP/1.0 200 \x7f\r\n" "\r\n", error::bad_reason); bad("HTTP/1.0 200 OK\n\r\n" "\r\n", error::bad_reason); - bad("HTTP/1.0 200 OK\r\r\n" "\r\n", error::bad_line_ending); + bad("HTTP/1.0 200 OK\r\r\n" "\r\n", error::bad_reason); } void