diff --git a/CHANGELOG.md b/CHANGELOG.md index ccb5e0f4..d4ac224f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Version 259: * Remove unused includes from `test::stream` * Move `char_buffer` into a separate file * Fix coverage collection in AzP CI +* Improve performance of `http::string_to_verb` -------------------------------------------------------------------------------- diff --git a/include/boost/beast/core/detail/string.hpp b/include/boost/beast/core/detail/string.hpp new file mode 100644 index 00000000..6245e127 --- /dev/null +++ b/include/boost/beast/core/detail/string.hpp @@ -0,0 +1,57 @@ +// +// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_STRING_HPP +#define BOOST_BEAST_DETAIL_STRING_HPP + +#include +#include + +#if defined(BOOST_BEAST_USE_STD_STRING_VIEW) +#include +#else +#include +#endif + +namespace boost { +namespace beast { + +namespace detail { + +#if defined(BOOST_BEAST_USE_STD_STRING_VIEW) + using string_view = std::string_view; + + template + using basic_string_view = + std::basic_string_view; +#else + using string_view = boost::string_view; + + template + using basic_string_view = + boost::basic_string_view; +#endif + +inline string_view operator "" _sv(char const* p, std::size_t n) +{ + return string_view{p, n}; +} + +inline +char +ascii_tolower(char c) +{ + return ((static_cast(c) - 65U) < 26) ? + c + 'a' - 'A' : c; +} +} // detail +} // beast +} // boost + +#endif diff --git a/include/boost/beast/core/impl/string.ipp b/include/boost/beast/core/impl/string.ipp new file mode 100644 index 00000000..c3aaacdc --- /dev/null +++ b/include/boost/beast/core/impl/string.ipp @@ -0,0 +1,71 @@ +// +// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_IMPL_STRING_IPP +#define BOOST_BEAST_IMPL_STRING_IPP + +#include + +#include + +namespace boost { +namespace beast { + +bool +iequals( + beast::string_view lhs, + beast::string_view rhs) +{ + auto n = lhs.size(); + if(rhs.size() != n) + return false; + auto p1 = lhs.data(); + auto p2 = rhs.data(); + char a, b; + // fast loop + while(n--) + { + a = *p1++; + b = *p2++; + if(a != b) + goto slow; + } + return true; +slow: + do + { + if(detail::ascii_tolower(a) != detail::ascii_tolower(b)) + return false; + a = *p1++; + b = *p2++; + } + while(n--); + return true; +} + +bool +iless::operator()( + string_view lhs, + string_view rhs) const +{ + using std::begin; + using std::end; + return std::lexicographical_compare( + begin(lhs), end(lhs), begin(rhs), end(rhs), + [](char c1, char c2) + { + return detail::ascii_tolower(c1) < detail::ascii_tolower(c2); + } + ); +} + +} // beast +} // boost + +#endif diff --git a/include/boost/beast/core/string.hpp b/include/boost/beast/core/string.hpp index 976bfca4..5a91ed8f 100644 --- a/include/boost/beast/core/string.hpp +++ b/include/boost/beast/core/string.hpp @@ -10,82 +10,17 @@ #ifndef BOOST_BEAST_STRING_HPP #define BOOST_BEAST_STRING_HPP -#include -#include - -#if defined(BOOST_BEAST_USE_STD_STRING_VIEW) -#include -#else -#include -#endif - -#include +#include namespace boost { namespace beast { -#if defined(BOOST_BEAST_USE_STD_STRING_VIEW) - /// The type of string view used by the library - using string_view = std::string_view; +/// The type of string view used by the library +using detail::string_view; - /// The type of basic string view used by the library - template - using basic_string_view = - std::basic_string_view; -#else - /// The type of string view used by the library - using string_view = boost::string_view; - - /// The type of basic string view used by the library - template - using basic_string_view = - boost::basic_string_view; -#endif - -namespace detail { - -inline -char -ascii_tolower(char c) -{ - return ((static_cast(c) - 65U) < 26) ? - c + 'a' - 'A' : c; -} - -template -bool -iequals( - beast::string_view lhs, - beast::string_view rhs) -{ - auto n = lhs.size(); - if(rhs.size() != n) - return false; - auto p1 = lhs.data(); - auto p2 = rhs.data(); - char a, b; - // fast loop - while(n--) - { - a = *p1++; - b = *p2++; - if(a != b) - goto slow; - } - return true; -slow: - do - { - if(ascii_tolower(a) != ascii_tolower(b)) - return false; - a = *p1++; - b = *p2++; - } - while(n--); - return true; -} - -} // detail +/// The type of basic string view used by the library +template +using basic_string_view = detail::basic_string_view; /** Returns `true` if two strings are equal, using a case-insensitive comparison. @@ -95,14 +30,11 @@ slow: @param rhs The string on the right side of the equality */ -inline +BOOST_BEAST_DECL bool iequals( beast::string_view lhs, - beast::string_view rhs) -{ - return detail::iequals(lhs, rhs); -} + beast::string_view rhs); /** A case-insensitive less predicate for strings. @@ -110,21 +42,11 @@ iequals( */ struct iless { + BOOST_BEAST_DECL bool operator()( string_view lhs, - string_view rhs) const - { - using std::begin; - using std::end; - return std::lexicographical_compare( - begin(lhs), end(lhs), begin(rhs), end(rhs), - [](char c1, char c2) - { - return detail::ascii_tolower(c1) < detail::ascii_tolower(c2); - } - ); - } + string_view rhs) const; }; /** A case-insensitive equality predicate for strings. @@ -145,4 +67,8 @@ struct iequal } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + #endif diff --git a/include/boost/beast/http/impl/verb.ipp b/include/boost/beast/http/impl/verb.ipp index 20ef47c5..7e8dd882 100644 --- a/include/boost/beast/http/impl/verb.ipp +++ b/include/boost/beast/http/impl/verb.ipp @@ -21,50 +21,51 @@ namespace http { string_view to_string(verb v) { + using beast::detail::operator "" _sv; switch(v) { - case verb::delete_: return "DELETE"; - case verb::get: return "GET"; - case verb::head: return "HEAD"; - case verb::post: return "POST"; - case verb::put: return "PUT"; - case verb::connect: return "CONNECT"; - case verb::options: return "OPTIONS"; - case verb::trace: return "TRACE"; + case verb::delete_: return "DELETE"_sv; + case verb::get: return "GET"_sv; + case verb::head: return "HEAD"_sv; + case verb::post: return "POST"_sv; + case verb::put: return "PUT"_sv; + case verb::connect: return "CONNECT"_sv; + case verb::options: return "OPTIONS"_sv; + case verb::trace: return "TRACE"_sv; - case verb::copy: return "COPY"; - case verb::lock: return "LOCK"; - case verb::mkcol: return "MKCOL"; - case verb::move: return "MOVE"; - case verb::propfind: return "PROPFIND"; - case verb::proppatch: return "PROPPATCH"; - case verb::search: return "SEARCH"; - case verb::unlock: return "UNLOCK"; - case verb::bind: return "BIND"; - case verb::rebind: return "REBIND"; - case verb::unbind: return "UNBIND"; - case verb::acl: return "ACL"; + case verb::copy: return "COPY"_sv; + case verb::lock: return "LOCK"_sv; + case verb::mkcol: return "MKCOL"_sv; + case verb::move: return "MOVE"_sv; + case verb::propfind: return "PROPFIND"_sv; + case verb::proppatch: return "PROPPATCH"_sv; + case verb::search: return "SEARCH"_sv; + case verb::unlock: return "UNLOCK"_sv; + case verb::bind: return "BIND"_sv; + case verb::rebind: return "REBIND"_sv; + case verb::unbind: return "UNBIND"_sv; + case verb::acl: return "ACL"_sv; - case verb::report: return "REPORT"; - case verb::mkactivity: return "MKACTIVITY"; - case verb::checkout: return "CHECKOUT"; - case verb::merge: return "MERGE"; + case verb::report: return "REPORT"_sv; + case verb::mkactivity: return "MKACTIVITY"_sv; + case verb::checkout: return "CHECKOUT"_sv; + case verb::merge: return "MERGE"_sv; - case verb::msearch: return "M-SEARCH"; - case verb::notify: return "NOTIFY"; - case verb::subscribe: return "SUBSCRIBE"; - case verb::unsubscribe: return "UNSUBSCRIBE"; + case verb::msearch: return "M-SEARCH"_sv; + case verb::notify: return "NOTIFY"_sv; + case verb::subscribe: return "SUBSCRIBE"_sv; + case verb::unsubscribe: return "UNSUBSCRIBE"_sv; - case verb::patch: return "PATCH"; - case verb::purge: return "PURGE"; + case verb::patch: return "PATCH"_sv; + case verb::purge: return "PURGE"_sv; - case verb::mkcalendar: return "MKCALENDAR"; + case verb::mkcalendar: return "MKCALENDAR"_sv; + + case verb::link: return "LINK"_sv; + case verb::unlink: return "UNLINK"_sv; - case verb::link: return "LINK"; - case verb::unlink: return "UNLINK"; - case verb::unknown: - return ""; + return ""_sv; } BOOST_THROW_EXCEPTION(std::invalid_argument{"unknown verb"}); @@ -108,34 +109,20 @@ string_to_verb(string_view v) UNLOCK UNSUBSCRIBE */ + using beast::detail::operator "" _sv; if(v.size() < 3) return verb::unknown; - // s must be null terminated - auto const eq = - [](string_view sv, char const* s) - { - auto p = sv.data(); - for(;;) - { - if(*s != *p) - return false; - ++s; - ++p; - if(! *s) - return p == (sv.data() + sv.size()); - } - }; auto c = v[0]; v.remove_prefix(1); switch(c) { case 'A': - if(v == "CL") + if(v == "CL"_sv) return verb::acl; break; case 'B': - if(v == "IND") + if(v == "IND"_sv) return verb::bind; break; @@ -145,14 +132,14 @@ string_to_verb(string_view v) switch(c) { case 'H': - if(eq(v, "ECKOUT")) + if(v == "ECKOUT"_sv) return verb::checkout; break; case 'O': - if(eq(v, "NNECT")) + if(v == "NNECT"_sv) return verb::connect; - if(eq(v, "PY")) + if(v == "PY"_sv) return verb::copy; BOOST_FALLTHROUGH; @@ -162,24 +149,24 @@ string_to_verb(string_view v) break; case 'D': - if(eq(v, "ELETE")) + if(v == "ELETE"_sv) return verb::delete_; break; case 'G': - if(eq(v, "ET")) + if(v == "ET"_sv) return verb::get; break; case 'H': - if(eq(v, "EAD")) + if(v == "EAD"_sv) return verb::head; break; case 'L': - if(eq(v, "INK")) + if(v == "INK"_sv) return verb::link; - if(eq(v, "OCK")) + if(v == "OCK"_sv) return verb::lock; break; @@ -189,31 +176,31 @@ string_to_verb(string_view v) switch(c) { case '-': - if(eq(v, "SEARCH")) + if(v == "SEARCH"_sv) return verb::msearch; break; case 'E': - if(eq(v, "RGE")) + if(v == "RGE"_sv) return verb::merge; break; case 'K': - if(eq(v, "ACTIVITY")) + if(v == "ACTIVITY"_sv) return verb::mkactivity; if(v[0] == 'C') { v.remove_prefix(1); - if(eq(v, "ALENDAR")) + if(v == "ALENDAR"_sv) return verb::mkcalendar; - if(eq(v, "OL")) + if(v == "OL"_sv) return verb::mkcol; break; } break; - + case 'O': - if(eq(v, "VE")) + if(v == "VE"_sv) return verb::move; BOOST_FALLTHROUGH; @@ -223,12 +210,12 @@ string_to_verb(string_view v) break; case 'N': - if(eq(v, "OTIFY")) + if(v == "OTIFY"_sv) return verb::notify; break; case 'O': - if(eq(v, "PTIONS")) + if(v == "PTIONS"_sv) return verb::options; break; @@ -238,26 +225,26 @@ string_to_verb(string_view v) switch(c) { case 'A': - if(eq(v, "TCH")) + if(v == "TCH"_sv) return verb::patch; break; case 'O': - if(eq(v, "ST")) + if(v == "ST"_sv) return verb::post; break; case 'R': - if(eq(v, "OPFIND")) + if(v == "OPFIND"_sv) return verb::propfind; - if(eq(v, "OPPATCH")) + if(v == "OPPATCH"_sv) return verb::proppatch; break; case 'U': - if(eq(v, "RGE")) + if(v == "RGE"_sv) return verb::purge; - if(eq(v, "T")) + if(v == "T"_sv) return verb::put; BOOST_FALLTHROUGH; @@ -270,21 +257,21 @@ string_to_verb(string_view v) if(v[0] != 'E') break; v.remove_prefix(1); - if(eq(v, "BIND")) + if(v == "BIND"_sv) return verb::rebind; - if(eq(v, "PORT")) + if(v == "PORT"_sv) return verb::report; break; case 'S': - if(eq(v, "EARCH")) + if(v == "EARCH"_sv) return verb::search; - if(eq(v, "UBSCRIBE")) + if(v == "UBSCRIBE"_sv) return verb::subscribe; break; case 'T': - if(eq(v, "RACE")) + if(v == "RACE"_sv) return verb::trace; break; @@ -292,13 +279,13 @@ string_to_verb(string_view v) if(v[0] != 'N') break; v.remove_prefix(1); - if(eq(v, "BIND")) + if(v == "BIND"_sv) return verb::unbind; - if(eq(v, "LINK")) + if(v == "LINK"_sv) return verb::unlink; - if(eq(v, "LOCK")) + if(v == "LOCK"_sv) return verb::unlock; - if(eq(v, "SUBSCRIBE")) + if(v == "SUBSCRIBE"_sv) return verb::unsubscribe; break; diff --git a/include/boost/beast/src.hpp b/include/boost/beast/src.hpp index f5256f32..6debe246 100644 --- a/include/boost/beast/src.hpp +++ b/include/boost/beast/src.hpp @@ -39,6 +39,7 @@ the program, with the macro BOOST_BEAST_SEPARATE_COMPILATION defined. #include #include #include +#include #include #include diff --git a/include/boost/beast/websocket/detail/hybi13.ipp b/include/boost/beast/websocket/detail/hybi13.ipp index 8868b540..95d1086d 100644 --- a/include/boost/beast/websocket/detail/hybi13.ipp +++ b/include/boost/beast/websocket/detail/hybi13.ipp @@ -47,11 +47,12 @@ make_sec_ws_accept( string_view key) { BOOST_ASSERT(key.size() <= sec_ws_key_type::max_size_n); - auto const& guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + using beast::detail::operator "" _sv; + auto const guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"_sv; beast::detail::sha1_context ctx; beast::detail::init(ctx); beast::detail::update(ctx, key.data(), key.size()); - beast::detail::update(ctx, guid, sizeof(guid) - 1); + beast::detail::update(ctx, guid.data(), guid.size()); char digest[beast::detail::sha1_context::digest_size]; beast::detail::finish(ctx, &digest[0]); accept.resize(accept.max_size());