mirror of
https://github.com/boostorg/beast.git
synced 2025-07-29 20:37:31 +02:00
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:
@ -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`
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
57
include/boost/beast/core/detail/string.hpp
Normal file
57
include/boost/beast/core/detail/string.hpp
Normal 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
|
71
include/boost/beast/core/impl/string.ipp
Normal file
71
include/boost/beast/core/impl/string.ipp
Normal 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
|
@ -10,82 +10,17 @@
|
||||
#ifndef BOOST_BEAST_STRING_HPP
|
||||
#define BOOST_BEAST_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
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/beast/core/detail/string.hpp>
|
||||
|
||||
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<class CharT, class Traits>
|
||||
using basic_string_view =
|
||||
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
|
||||
/// The type of basic string view used by the library
|
||||
template<class CharT, class Traits>
|
||||
using basic_string_view = detail::basic_string_view<CharT, Traits>;
|
||||
|
||||
/** 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 <boost/beast/core/impl/string.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -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 "<unknown>";
|
||||
return "<unknown>"_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;
|
||||
|
||||
|
@ -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/saved_handler.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/rfc7230.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());
|
||||
|
Reference in New Issue
Block a user