forked from boostorg/beast
* Optimize hash calculation and equality for field strings Experiments show the digest and equality functions taking the most time. Optimizing L1 cache usage did not show significant improvements.
557 lines
14 KiB
C++
557 lines
14 KiB
C++
//
|
|
// Copyright (c) 2016-2017 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_HTTP_IMPL_FIELD_IPP
|
|
#define BOOST_BEAST_HTTP_IMPL_FIELD_IPP
|
|
|
|
#include <boost/beast/core/string.hpp>
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
#include <cstring>
|
|
#include <boost/assert.hpp>
|
|
|
|
namespace boost {
|
|
namespace beast {
|
|
namespace http {
|
|
|
|
namespace detail {
|
|
|
|
struct field_table
|
|
{
|
|
using array_type =
|
|
std::array<string_view, 353>;
|
|
|
|
// Strings are converted to lowercase
|
|
static
|
|
std::uint32_t
|
|
digest(string_view s)
|
|
{
|
|
std::uint32_t r = 0;
|
|
std::size_t n = s.size();
|
|
unsigned char const* p =reinterpret_cast<
|
|
unsigned char const*>(s.data());
|
|
while(n >= 4)
|
|
{
|
|
std::uint32_t v;
|
|
std::memcpy(&v, p, 4);
|
|
r = r * 5 + ( v | 0x20202020 );
|
|
p += 4;
|
|
n -= 4;
|
|
}
|
|
while( n > 0 )
|
|
{
|
|
r = r * 5 + ( *p | 0x20 );
|
|
++p;
|
|
--n;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
// This comparison is case-insensitive, and the
|
|
// strings must contain only valid http field characters.
|
|
static
|
|
bool
|
|
equals(string_view lhs, string_view rhs)
|
|
{
|
|
using Int = std::uint32_t; // or std::size_t
|
|
auto n = lhs.size();
|
|
if(n != rhs.size())
|
|
return false;
|
|
auto p1 = lhs.data();
|
|
auto p2 = rhs.data();
|
|
auto constexpr S = sizeof(Int);
|
|
auto constexpr Mask = static_cast<Int>(
|
|
0xDFDFDFDFDFDFDFDF & ~Int{0});
|
|
for(; n >= S; p1 += S, p2 += S, n -= S)
|
|
{
|
|
Int v1, v2;
|
|
std::memcpy( &v1, p1, S );
|
|
std::memcpy( &v2, p2, S );
|
|
if((v1 ^ v2) & Mask)
|
|
return false;
|
|
}
|
|
for(; n; ++p1, ++p2, --n)
|
|
if(( *p1 ^ *p2) & 0xDF)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
array_type by_name_;
|
|
|
|
enum { N = 5155 };
|
|
unsigned char map_[ N ][ 2 ] = {};
|
|
|
|
/*
|
|
From:
|
|
|
|
https://www.iana.org/assignments/message-headers/message-headers.xhtml
|
|
*/
|
|
field_table()
|
|
: by_name_({{
|
|
"<unknown-field>",
|
|
"A-IM",
|
|
"Accept",
|
|
"Accept-Additions",
|
|
"Accept-Charset",
|
|
"Accept-Datetime",
|
|
"Accept-Encoding",
|
|
"Accept-Features",
|
|
"Accept-Language",
|
|
"Accept-Patch",
|
|
"Accept-Post",
|
|
"Accept-Ranges",
|
|
"Access-Control",
|
|
"Access-Control-Allow-Credentials",
|
|
"Access-Control-Allow-Headers",
|
|
"Access-Control-Allow-Methods",
|
|
"Access-Control-Allow-Origin",
|
|
"Access-Control-Expose-Headers",
|
|
"Access-Control-Max-Age",
|
|
"Access-Control-Request-Headers",
|
|
"Access-Control-Request-Method",
|
|
"Age",
|
|
"Allow",
|
|
"ALPN",
|
|
"Also-Control",
|
|
"Alt-Svc",
|
|
"Alt-Used",
|
|
"Alternate-Recipient",
|
|
"Alternates",
|
|
"Apparently-To",
|
|
"Apply-To-Redirect-Ref",
|
|
"Approved",
|
|
"Archive",
|
|
"Archived-At",
|
|
"Article-Names",
|
|
"Article-Updates",
|
|
"Authentication-Control",
|
|
"Authentication-Info",
|
|
"Authentication-Results",
|
|
"Authorization",
|
|
"Auto-Submitted",
|
|
"Autoforwarded",
|
|
"Autosubmitted",
|
|
"Base",
|
|
"Bcc",
|
|
"Body",
|
|
"C-Ext",
|
|
"C-Man",
|
|
"C-Opt",
|
|
"C-PEP",
|
|
"C-PEP-Info",
|
|
"Cache-Control",
|
|
"CalDAV-Timezones",
|
|
"Cancel-Key",
|
|
"Cancel-Lock",
|
|
"Cc",
|
|
"Close",
|
|
"Comments",
|
|
"Compliance",
|
|
"Connection",
|
|
"Content-Alternative",
|
|
"Content-Base",
|
|
"Content-Description",
|
|
"Content-Disposition",
|
|
"Content-Duration",
|
|
"Content-Encoding",
|
|
"Content-features",
|
|
"Content-ID",
|
|
"Content-Identifier",
|
|
"Content-Language",
|
|
"Content-Length",
|
|
"Content-Location",
|
|
"Content-MD5",
|
|
"Content-Range",
|
|
"Content-Return",
|
|
"Content-Script-Type",
|
|
"Content-Style-Type",
|
|
"Content-Transfer-Encoding",
|
|
"Content-Type",
|
|
"Content-Version",
|
|
"Control",
|
|
"Conversion",
|
|
"Conversion-With-Loss",
|
|
"Cookie",
|
|
"Cookie2",
|
|
"Cost",
|
|
"DASL",
|
|
"Date",
|
|
"Date-Received",
|
|
"DAV",
|
|
"Default-Style",
|
|
"Deferred-Delivery",
|
|
"Delivery-Date",
|
|
"Delta-Base",
|
|
"Depth",
|
|
"Derived-From",
|
|
"Destination",
|
|
"Differential-ID",
|
|
"Digest",
|
|
"Discarded-X400-IPMS-Extensions",
|
|
"Discarded-X400-MTS-Extensions",
|
|
"Disclose-Recipients",
|
|
"Disposition-Notification-Options",
|
|
"Disposition-Notification-To",
|
|
"Distribution",
|
|
"DKIM-Signature",
|
|
"DL-Expansion-History",
|
|
"Downgraded-Bcc",
|
|
"Downgraded-Cc",
|
|
"Downgraded-Disposition-Notification-To",
|
|
"Downgraded-Final-Recipient",
|
|
"Downgraded-From",
|
|
"Downgraded-In-Reply-To",
|
|
"Downgraded-Mail-From",
|
|
"Downgraded-Message-Id",
|
|
"Downgraded-Original-Recipient",
|
|
"Downgraded-Rcpt-To",
|
|
"Downgraded-References",
|
|
"Downgraded-Reply-To",
|
|
"Downgraded-Resent-Bcc",
|
|
"Downgraded-Resent-Cc",
|
|
"Downgraded-Resent-From",
|
|
"Downgraded-Resent-Reply-To",
|
|
"Downgraded-Resent-Sender",
|
|
"Downgraded-Resent-To",
|
|
"Downgraded-Return-Path",
|
|
"Downgraded-Sender",
|
|
"Downgraded-To",
|
|
"EDIINT-Features",
|
|
"Eesst-Version",
|
|
"Encoding",
|
|
"Encrypted",
|
|
"Errors-To",
|
|
"ETag",
|
|
"Expect",
|
|
"Expires",
|
|
"Expiry-Date",
|
|
"Ext",
|
|
"Followup-To",
|
|
"Forwarded",
|
|
"From",
|
|
"Generate-Delivery-Report",
|
|
"GetProfile",
|
|
"Hobareg",
|
|
"Host",
|
|
"HTTP2-Settings",
|
|
"If",
|
|
"If-Match",
|
|
"If-Modified-Since",
|
|
"If-None-Match",
|
|
"If-Range",
|
|
"If-Schedule-Tag-Match",
|
|
"If-Unmodified-Since",
|
|
"IM",
|
|
"Importance",
|
|
"In-Reply-To",
|
|
"Incomplete-Copy",
|
|
"Injection-Date",
|
|
"Injection-Info",
|
|
"Jabber-ID",
|
|
"Keep-Alive",
|
|
"Keywords",
|
|
"Label",
|
|
"Language",
|
|
"Last-Modified",
|
|
"Latest-Delivery-Time",
|
|
"Lines",
|
|
"Link",
|
|
"List-Archive",
|
|
"List-Help",
|
|
"List-ID",
|
|
"List-Owner",
|
|
"List-Post",
|
|
"List-Subscribe",
|
|
"List-Unsubscribe",
|
|
"List-Unsubscribe-Post",
|
|
"Location",
|
|
"Lock-Token",
|
|
"Man",
|
|
"Max-Forwards",
|
|
"Memento-Datetime",
|
|
"Message-Context",
|
|
"Message-ID",
|
|
"Message-Type",
|
|
"Meter",
|
|
"Method-Check",
|
|
"Method-Check-Expires",
|
|
"MIME-Version",
|
|
"MMHS-Acp127-Message-Identifier",
|
|
"MMHS-Authorizing-Users",
|
|
"MMHS-Codress-Message-Indicator",
|
|
"MMHS-Copy-Precedence",
|
|
"MMHS-Exempted-Address",
|
|
"MMHS-Extended-Authorisation-Info",
|
|
"MMHS-Handling-Instructions",
|
|
"MMHS-Message-Instructions",
|
|
"MMHS-Message-Type",
|
|
"MMHS-Originator-PLAD",
|
|
"MMHS-Originator-Reference",
|
|
"MMHS-Other-Recipients-Indicator-CC",
|
|
"MMHS-Other-Recipients-Indicator-To",
|
|
"MMHS-Primary-Precedence",
|
|
"MMHS-Subject-Indicator-Codes",
|
|
"MT-Priority",
|
|
"Negotiate",
|
|
"Newsgroups",
|
|
"NNTP-Posting-Date",
|
|
"NNTP-Posting-Host",
|
|
"Non-Compliance",
|
|
"Obsoletes",
|
|
"Opt",
|
|
"Optional",
|
|
"Optional-WWW-Authenticate",
|
|
"Ordering-Type",
|
|
"Organization",
|
|
"Origin",
|
|
"Original-Encoded-Information-Types",
|
|
"Original-From",
|
|
"Original-Message-ID",
|
|
"Original-Recipient",
|
|
"Original-Sender",
|
|
"Original-Subject",
|
|
"Originator-Return-Address",
|
|
"Overwrite",
|
|
"P3P",
|
|
"Path",
|
|
"PEP",
|
|
"Pep-Info",
|
|
"PICS-Label",
|
|
"Position",
|
|
"Posting-Version",
|
|
"Pragma",
|
|
"Prefer",
|
|
"Preference-Applied",
|
|
"Prevent-NonDelivery-Report",
|
|
"Priority",
|
|
"Privicon",
|
|
"ProfileObject",
|
|
"Protocol",
|
|
"Protocol-Info",
|
|
"Protocol-Query",
|
|
"Protocol-Request",
|
|
"Proxy-Authenticate",
|
|
"Proxy-Authentication-Info",
|
|
"Proxy-Authorization",
|
|
"Proxy-Connection",
|
|
"Proxy-Features",
|
|
"Proxy-Instruction",
|
|
"Public",
|
|
"Public-Key-Pins",
|
|
"Public-Key-Pins-Report-Only",
|
|
"Range",
|
|
"Received",
|
|
"Received-SPF",
|
|
"Redirect-Ref",
|
|
"References",
|
|
"Referer",
|
|
"Referer-Root",
|
|
"Relay-Version",
|
|
"Reply-By",
|
|
"Reply-To",
|
|
"Require-Recipient-Valid-Since",
|
|
"Resent-Bcc",
|
|
"Resent-Cc",
|
|
"Resent-Date",
|
|
"Resent-From",
|
|
"Resent-Message-ID",
|
|
"Resent-Reply-To",
|
|
"Resent-Sender",
|
|
"Resent-To",
|
|
"Resolution-Hint",
|
|
"Resolver-Location",
|
|
"Retry-After",
|
|
"Return-Path",
|
|
"Safe",
|
|
"Schedule-Reply",
|
|
"Schedule-Tag",
|
|
"Sec-WebSocket-Accept",
|
|
"Sec-WebSocket-Extensions",
|
|
"Sec-WebSocket-Key",
|
|
"Sec-WebSocket-Protocol",
|
|
"Sec-WebSocket-Version",
|
|
"Security-Scheme",
|
|
"See-Also",
|
|
"Sender",
|
|
"Sensitivity",
|
|
"Server",
|
|
"Set-Cookie",
|
|
"Set-Cookie2",
|
|
"SetProfile",
|
|
"SIO-Label",
|
|
"SIO-Label-History",
|
|
"SLUG",
|
|
"SoapAction",
|
|
"Solicitation",
|
|
"Status-URI",
|
|
"Strict-Transport-Security",
|
|
"Subject",
|
|
"SubOK",
|
|
"Subst",
|
|
"Summary",
|
|
"Supersedes",
|
|
"Surrogate-Capability",
|
|
"Surrogate-Control",
|
|
"TCN",
|
|
"TE",
|
|
"Timeout",
|
|
"Title",
|
|
"To",
|
|
"Topic",
|
|
"Trailer",
|
|
"Transfer-Encoding",
|
|
"TTL",
|
|
"UA-Color",
|
|
"UA-Media",
|
|
"UA-Pixels",
|
|
"UA-Resolution",
|
|
"UA-Windowpixels",
|
|
"Upgrade",
|
|
"Urgency",
|
|
"URI",
|
|
"User-Agent",
|
|
"Variant-Vary",
|
|
"Vary",
|
|
"VBR-Info",
|
|
"Version",
|
|
"Via",
|
|
"Want-Digest",
|
|
"Warning",
|
|
"WWW-Authenticate",
|
|
"X-Archived-At",
|
|
"X-Device-Accept",
|
|
"X-Device-Accept-Charset",
|
|
"X-Device-Accept-Encoding",
|
|
"X-Device-Accept-Language",
|
|
"X-Device-User-Agent",
|
|
"X-Frame-Options",
|
|
"X-Mittente",
|
|
"X-PGP-Sig",
|
|
"X-Ricevuta",
|
|
"X-Riferimento-Message-ID",
|
|
"X-TipoRicevuta",
|
|
"X-Trasporto",
|
|
"X-VerificaSicurezza",
|
|
"X400-Content-Identifier",
|
|
"X400-Content-Return",
|
|
"X400-Content-Type",
|
|
"X400-MTS-Identifier",
|
|
"X400-Originator",
|
|
"X400-Received",
|
|
"X400-Recipients",
|
|
"X400-Trace",
|
|
"Xref"
|
|
}})
|
|
{
|
|
for(std::size_t i = 1, n = 256; i < n; ++i)
|
|
{
|
|
auto sv = by_name_[ i ];
|
|
auto h = digest(sv);
|
|
auto j = h % N;
|
|
BOOST_ASSERT(map_[j][0] == 0);
|
|
map_[j][0] = static_cast<unsigned char>(i);
|
|
}
|
|
|
|
for(std::size_t i = 256, n = by_name_.size(); i < n; ++i)
|
|
{
|
|
auto sv = by_name_[i];
|
|
auto h = digest(sv);
|
|
auto j = h % N;
|
|
BOOST_ASSERT(map_[j][1] == 0);
|
|
map_[j][1] = static_cast<unsigned char>(i - 255);
|
|
}
|
|
}
|
|
|
|
field
|
|
string_to_field(string_view s) const
|
|
{
|
|
auto h = digest(s);
|
|
auto j = h % N;
|
|
int i = map_[j][0];
|
|
string_view s2 = by_name_[i];
|
|
if(i != 0 && equals(s, s2))
|
|
return static_cast<field>(i);
|
|
i = map_[j][1];
|
|
if(i == 0)
|
|
return field::unknown;
|
|
i += 255;
|
|
s2 = by_name_[i];
|
|
|
|
if(equals(s, s2))
|
|
return static_cast<field>(i);
|
|
return field::unknown;
|
|
}
|
|
|
|
//
|
|
// Deprecated
|
|
//
|
|
|
|
using const_iterator =
|
|
array_type::const_iterator;
|
|
|
|
std::size_t
|
|
size() const
|
|
{
|
|
return by_name_.size();
|
|
}
|
|
|
|
const_iterator
|
|
begin() const
|
|
{
|
|
return by_name_.begin();
|
|
}
|
|
|
|
const_iterator
|
|
end() const
|
|
{
|
|
return by_name_.end();
|
|
}
|
|
};
|
|
|
|
inline
|
|
field_table const&
|
|
get_field_table()
|
|
{
|
|
static field_table const tab;
|
|
return tab;
|
|
}
|
|
|
|
template<class = void>
|
|
string_view
|
|
to_string(field f)
|
|
{
|
|
auto const& v = get_field_table();
|
|
BOOST_ASSERT(static_cast<unsigned>(f) < v.size());
|
|
return v.begin()[static_cast<unsigned>(f)];
|
|
}
|
|
|
|
} // detail
|
|
|
|
inline
|
|
string_view
|
|
to_string(field f)
|
|
{
|
|
return detail::to_string(f);
|
|
}
|
|
|
|
inline
|
|
field
|
|
string_to_field(string_view s)
|
|
{
|
|
return detail::get_field_table().string_to_field(s);
|
|
}
|
|
|
|
} // http
|
|
} // beast
|
|
} // boost
|
|
|
|
#endif
|