Improve performance of http::string_to_verb

`operator==` performs better than the hand-written comparison routine.

fix #1636

Signed-off-by: Damian Jarek <damian.jarek93@gmail.com>
This commit is contained in:
Damian Jarek
2019-06-12 03:55:30 +02:00
parent ab55c4d21e
commit a0d5baf22a
7 changed files with 218 additions and 174 deletions

View File

@ -9,6 +9,7 @@ Version 259:
* Remove unused includes from `test::stream` * Remove unused includes from `test::stream`
* Move `char_buffer` into a separate file * Move `char_buffer` into a separate file
* Fix coverage collection in AzP CI * Fix coverage collection in AzP CI
* Improve performance of `http::string_to_verb`
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -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 <boost/beast/core/detail/config.hpp>
#include <boost/version.hpp>
#if defined(BOOST_BEAST_USE_STD_STRING_VIEW)
#include <string_view>
#else
#include <boost/utility/string_view.hpp>
#endif
namespace boost {
namespace beast {
namespace detail {
#if defined(BOOST_BEAST_USE_STD_STRING_VIEW)
using string_view = std::string_view;
template<class CharT, class Traits>
using basic_string_view =
std::basic_string_view<CharT, Traits>;
#else
using string_view = boost::string_view;
template<class CharT, class Traits>
using basic_string_view =
boost::basic_string_view<CharT, Traits>;
#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<unsigned>(c) - 65U) < 26) ?
c + 'a' - 'A' : c;
}
} // detail
} // beast
} // boost
#endif

View File

@ -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 <boost/beast/core/string.hpp>
#include <algorithm>
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

View File

@ -10,82 +10,17 @@
#ifndef BOOST_BEAST_STRING_HPP #ifndef BOOST_BEAST_STRING_HPP
#define BOOST_BEAST_STRING_HPP #define BOOST_BEAST_STRING_HPP
#include <boost/beast/core/detail/config.hpp> #include <boost/beast/core/detail/string.hpp>
#include <boost/version.hpp>
#if defined(BOOST_BEAST_USE_STD_STRING_VIEW)
#include <string_view>
#else
#include <boost/utility/string_view.hpp>
#endif
#include <algorithm>
namespace boost { namespace boost {
namespace beast { namespace beast {
#if defined(BOOST_BEAST_USE_STD_STRING_VIEW) /// The type of string view used by the library
/// The type of string view used by the library using detail::string_view;
using string_view = std::string_view;
/// The type of basic string view used by the library /// The type of basic string view used by the library
template<class CharT, class Traits> template<class CharT, class Traits>
using basic_string_view = using basic_string_view = detail::basic_string_view<CharT, Traits>;
std::basic_string_view<CharT, Traits>;
#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<class CharT, class Traits>
using basic_string_view =
boost::basic_string_view<CharT, Traits>;
#endif
namespace detail {
inline
char
ascii_tolower(char c)
{
return ((static_cast<unsigned>(c) - 65U) < 26) ?
c + 'a' - 'A' : c;
}
template<class = void>
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
/** Returns `true` if two strings are equal, using a case-insensitive comparison. /** 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 @param rhs The string on the right side of the equality
*/ */
inline BOOST_BEAST_DECL
bool bool
iequals( iequals(
beast::string_view lhs, beast::string_view lhs,
beast::string_view rhs) beast::string_view rhs);
{
return detail::iequals(lhs, rhs);
}
/** A case-insensitive less predicate for strings. /** A case-insensitive less predicate for strings.
@ -110,21 +42,11 @@ iequals(
*/ */
struct iless struct iless
{ {
BOOST_BEAST_DECL
bool bool
operator()( operator()(
string_view lhs, string_view lhs,
string_view rhs) const 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);
}
);
}
}; };
/** A case-insensitive equality predicate for strings. /** A case-insensitive equality predicate for strings.
@ -145,4 +67,8 @@ struct iequal
} // beast } // beast
} // boost } // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/impl/string.ipp>
#endif
#endif #endif

View File

@ -21,50 +21,51 @@ namespace http {
string_view string_view
to_string(verb v) to_string(verb v)
{ {
using beast::detail::operator "" _sv;
switch(v) switch(v)
{ {
case verb::delete_: return "DELETE"; case verb::delete_: return "DELETE"_sv;
case verb::get: return "GET"; case verb::get: return "GET"_sv;
case verb::head: return "HEAD"; case verb::head: return "HEAD"_sv;
case verb::post: return "POST"; case verb::post: return "POST"_sv;
case verb::put: return "PUT"; case verb::put: return "PUT"_sv;
case verb::connect: return "CONNECT"; case verb::connect: return "CONNECT"_sv;
case verb::options: return "OPTIONS"; case verb::options: return "OPTIONS"_sv;
case verb::trace: return "TRACE"; case verb::trace: return "TRACE"_sv;
case verb::copy: return "COPY"; case verb::copy: return "COPY"_sv;
case verb::lock: return "LOCK"; case verb::lock: return "LOCK"_sv;
case verb::mkcol: return "MKCOL"; case verb::mkcol: return "MKCOL"_sv;
case verb::move: return "MOVE"; case verb::move: return "MOVE"_sv;
case verb::propfind: return "PROPFIND"; case verb::propfind: return "PROPFIND"_sv;
case verb::proppatch: return "PROPPATCH"; case verb::proppatch: return "PROPPATCH"_sv;
case verb::search: return "SEARCH"; case verb::search: return "SEARCH"_sv;
case verb::unlock: return "UNLOCK"; case verb::unlock: return "UNLOCK"_sv;
case verb::bind: return "BIND"; case verb::bind: return "BIND"_sv;
case verb::rebind: return "REBIND"; case verb::rebind: return "REBIND"_sv;
case verb::unbind: return "UNBIND"; case verb::unbind: return "UNBIND"_sv;
case verb::acl: return "ACL"; case verb::acl: return "ACL"_sv;
case verb::report: return "REPORT"; case verb::report: return "REPORT"_sv;
case verb::mkactivity: return "MKACTIVITY"; case verb::mkactivity: return "MKACTIVITY"_sv;
case verb::checkout: return "CHECKOUT"; case verb::checkout: return "CHECKOUT"_sv;
case verb::merge: return "MERGE"; case verb::merge: return "MERGE"_sv;
case verb::msearch: return "M-SEARCH"; case verb::msearch: return "M-SEARCH"_sv;
case verb::notify: return "NOTIFY"; case verb::notify: return "NOTIFY"_sv;
case verb::subscribe: return "SUBSCRIBE"; case verb::subscribe: return "SUBSCRIBE"_sv;
case verb::unsubscribe: return "UNSUBSCRIBE"; case verb::unsubscribe: return "UNSUBSCRIBE"_sv;
case verb::patch: return "PATCH"; case verb::patch: return "PATCH"_sv;
case verb::purge: return "PURGE"; 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: case verb::unknown:
return "<unknown>"; return "<unknown>"_sv;
} }
BOOST_THROW_EXCEPTION(std::invalid_argument{"unknown verb"}); BOOST_THROW_EXCEPTION(std::invalid_argument{"unknown verb"});
@ -108,34 +109,20 @@ string_to_verb(string_view v)
UNLOCK UNLOCK
UNSUBSCRIBE UNSUBSCRIBE
*/ */
using beast::detail::operator "" _sv;
if(v.size() < 3) if(v.size() < 3)
return verb::unknown; 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]; auto c = v[0];
v.remove_prefix(1); v.remove_prefix(1);
switch(c) switch(c)
{ {
case 'A': case 'A':
if(v == "CL") if(v == "CL"_sv)
return verb::acl; return verb::acl;
break; break;
case 'B': case 'B':
if(v == "IND") if(v == "IND"_sv)
return verb::bind; return verb::bind;
break; break;
@ -145,14 +132,14 @@ string_to_verb(string_view v)
switch(c) switch(c)
{ {
case 'H': case 'H':
if(eq(v, "ECKOUT")) if(v == "ECKOUT"_sv)
return verb::checkout; return verb::checkout;
break; break;
case 'O': case 'O':
if(eq(v, "NNECT")) if(v == "NNECT"_sv)
return verb::connect; return verb::connect;
if(eq(v, "PY")) if(v == "PY"_sv)
return verb::copy; return verb::copy;
BOOST_FALLTHROUGH; BOOST_FALLTHROUGH;
@ -162,24 +149,24 @@ string_to_verb(string_view v)
break; break;
case 'D': case 'D':
if(eq(v, "ELETE")) if(v == "ELETE"_sv)
return verb::delete_; return verb::delete_;
break; break;
case 'G': case 'G':
if(eq(v, "ET")) if(v == "ET"_sv)
return verb::get; return verb::get;
break; break;
case 'H': case 'H':
if(eq(v, "EAD")) if(v == "EAD"_sv)
return verb::head; return verb::head;
break; break;
case 'L': case 'L':
if(eq(v, "INK")) if(v == "INK"_sv)
return verb::link; return verb::link;
if(eq(v, "OCK")) if(v == "OCK"_sv)
return verb::lock; return verb::lock;
break; break;
@ -189,31 +176,31 @@ string_to_verb(string_view v)
switch(c) switch(c)
{ {
case '-': case '-':
if(eq(v, "SEARCH")) if(v == "SEARCH"_sv)
return verb::msearch; return verb::msearch;
break; break;
case 'E': case 'E':
if(eq(v, "RGE")) if(v == "RGE"_sv)
return verb::merge; return verb::merge;
break; break;
case 'K': case 'K':
if(eq(v, "ACTIVITY")) if(v == "ACTIVITY"_sv)
return verb::mkactivity; return verb::mkactivity;
if(v[0] == 'C') if(v[0] == 'C')
{ {
v.remove_prefix(1); v.remove_prefix(1);
if(eq(v, "ALENDAR")) if(v == "ALENDAR"_sv)
return verb::mkcalendar; return verb::mkcalendar;
if(eq(v, "OL")) if(v == "OL"_sv)
return verb::mkcol; return verb::mkcol;
break; break;
} }
break; break;
case 'O': case 'O':
if(eq(v, "VE")) if(v == "VE"_sv)
return verb::move; return verb::move;
BOOST_FALLTHROUGH; BOOST_FALLTHROUGH;
@ -223,12 +210,12 @@ string_to_verb(string_view v)
break; break;
case 'N': case 'N':
if(eq(v, "OTIFY")) if(v == "OTIFY"_sv)
return verb::notify; return verb::notify;
break; break;
case 'O': case 'O':
if(eq(v, "PTIONS")) if(v == "PTIONS"_sv)
return verb::options; return verb::options;
break; break;
@ -238,26 +225,26 @@ string_to_verb(string_view v)
switch(c) switch(c)
{ {
case 'A': case 'A':
if(eq(v, "TCH")) if(v == "TCH"_sv)
return verb::patch; return verb::patch;
break; break;
case 'O': case 'O':
if(eq(v, "ST")) if(v == "ST"_sv)
return verb::post; return verb::post;
break; break;
case 'R': case 'R':
if(eq(v, "OPFIND")) if(v == "OPFIND"_sv)
return verb::propfind; return verb::propfind;
if(eq(v, "OPPATCH")) if(v == "OPPATCH"_sv)
return verb::proppatch; return verb::proppatch;
break; break;
case 'U': case 'U':
if(eq(v, "RGE")) if(v == "RGE"_sv)
return verb::purge; return verb::purge;
if(eq(v, "T")) if(v == "T"_sv)
return verb::put; return verb::put;
BOOST_FALLTHROUGH; BOOST_FALLTHROUGH;
@ -270,21 +257,21 @@ string_to_verb(string_view v)
if(v[0] != 'E') if(v[0] != 'E')
break; break;
v.remove_prefix(1); v.remove_prefix(1);
if(eq(v, "BIND")) if(v == "BIND"_sv)
return verb::rebind; return verb::rebind;
if(eq(v, "PORT")) if(v == "PORT"_sv)
return verb::report; return verb::report;
break; break;
case 'S': case 'S':
if(eq(v, "EARCH")) if(v == "EARCH"_sv)
return verb::search; return verb::search;
if(eq(v, "UBSCRIBE")) if(v == "UBSCRIBE"_sv)
return verb::subscribe; return verb::subscribe;
break; break;
case 'T': case 'T':
if(eq(v, "RACE")) if(v == "RACE"_sv)
return verb::trace; return verb::trace;
break; break;
@ -292,13 +279,13 @@ string_to_verb(string_view v)
if(v[0] != 'N') if(v[0] != 'N')
break; break;
v.remove_prefix(1); v.remove_prefix(1);
if(eq(v, "BIND")) if(v == "BIND"_sv)
return verb::unbind; return verb::unbind;
if(eq(v, "LINK")) if(v == "LINK"_sv)
return verb::unlink; return verb::unlink;
if(eq(v, "LOCK")) if(v == "LOCK"_sv)
return verb::unlock; return verb::unlock;
if(eq(v, "SUBSCRIBE")) if(v == "SUBSCRIBE"_sv)
return verb::unsubscribe; return verb::unsubscribe;
break; break;

View File

@ -39,6 +39,7 @@ the program, with the macro BOOST_BEAST_SEPARATE_COMPILATION defined.
#include <boost/beast/core/impl/flat_static_buffer.ipp> #include <boost/beast/core/impl/flat_static_buffer.ipp>
#include <boost/beast/core/impl/saved_handler.ipp> #include <boost/beast/core/impl/saved_handler.ipp>
#include <boost/beast/core/impl/static_buffer.ipp> #include <boost/beast/core/impl/static_buffer.ipp>
#include <boost/beast/core/impl/string.ipp>
#include <boost/beast/http/detail/basic_parser.ipp> #include <boost/beast/http/detail/basic_parser.ipp>
#include <boost/beast/http/detail/rfc7230.ipp> #include <boost/beast/http/detail/rfc7230.ipp>

View File

@ -47,11 +47,12 @@ make_sec_ws_accept(
string_view key) string_view key)
{ {
BOOST_ASSERT(key.size() <= sec_ws_key_type::max_size_n); 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::sha1_context ctx;
beast::detail::init(ctx); beast::detail::init(ctx);
beast::detail::update(ctx, key.data(), key.size()); 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]; char digest[beast::detail::sha1_context::digest_size];
beast::detail::finish(ctx, &digest[0]); beast::detail::finish(ctx, &digest[0]);
accept.resize(accept.max_size()); accept.resize(accept.max_size());