mirror of
https://github.com/boostorg/beast.git
synced 2025-08-02 14:24:31 +02:00
string_param optimizations
This commit is contained in:
@@ -1,3 +1,9 @@
|
|||||||
|
Version 66:
|
||||||
|
|
||||||
|
* string_param optimizations
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
Version 65:
|
Version 65:
|
||||||
|
|
||||||
* Enable narrowing warning on msvc cmake
|
* Enable narrowing warning on msvc cmake
|
||||||
|
@@ -27,17 +27,20 @@ class static_ostream_buffer
|
|||||||
using traits_type = typename
|
using traits_type = typename
|
||||||
std::basic_streambuf<CharT, Traits>::traits_type;
|
std::basic_streambuf<CharT, Traits>::traits_type;
|
||||||
|
|
||||||
char buf_[128];
|
char* data_;
|
||||||
std::string s_;
|
std::size_t size_;
|
||||||
std::size_t len_ = 0;
|
std::size_t len_ = 0;
|
||||||
|
std::string s_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static_ostream_buffer(static_ostream_buffer&&) = delete;
|
static_ostream_buffer(static_ostream_buffer&&) = delete;
|
||||||
static_ostream_buffer(static_ostream_buffer const&) = delete;
|
static_ostream_buffer(static_ostream_buffer const&) = delete;
|
||||||
|
|
||||||
static_ostream_buffer()
|
static_ostream_buffer(char* data, std::size_t size)
|
||||||
|
: data_(data)
|
||||||
|
, size_(size)
|
||||||
{
|
{
|
||||||
this->setp(buf_, buf_ + sizeof(buf_) - 1);
|
this->setp(data_, data_ + size - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
~static_ostream_buffer() noexcept
|
~static_ostream_buffer() noexcept
|
||||||
@@ -49,7 +52,7 @@ public:
|
|||||||
{
|
{
|
||||||
if(! s_.empty())
|
if(! s_.empty())
|
||||||
return {s_.data(), len_};
|
return {s_.data(), len_};
|
||||||
return {buf_, len_};
|
return {data_, len_};
|
||||||
}
|
}
|
||||||
|
|
||||||
int_type
|
int_type
|
||||||
@@ -81,17 +84,17 @@ private:
|
|||||||
{
|
{
|
||||||
static auto const growth_factor = 1.5;
|
static auto const growth_factor = 1.5;
|
||||||
|
|
||||||
if(len_ < sizeof(buf_) - 1)
|
if(len_ < size_ - 1)
|
||||||
{
|
{
|
||||||
this->setp(&buf_[len_],
|
this->setp(
|
||||||
&buf_[sizeof(buf_) - 2]);
|
data_ + len_, data_ + size_ - 2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(s_.empty())
|
if(s_.empty())
|
||||||
{
|
{
|
||||||
s_.resize(static_cast<std::size_t>(
|
s_.resize(static_cast<std::size_t>(
|
||||||
growth_factor * len_));
|
growth_factor * len_));
|
||||||
Traits::copy(&s_[0], buf_, len_);
|
Traits::copy(&s_[0], data_, len_);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -115,8 +118,9 @@ class static_ostream : public std::basic_ostream<char>
|
|||||||
static_ostream_buffer osb_;
|
static_ostream_buffer osb_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static_ostream()
|
static_ostream(char* data, std::size_t size)
|
||||||
: std::basic_ostream<char>(&this->osb_)
|
: std::basic_ostream<char>(&this->osb_)
|
||||||
|
, osb_(data, size)
|
||||||
{
|
{
|
||||||
imbue(std::locale::classic());
|
imbue(std::locale::classic());
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
#define BEAST_DETAIL_STATIC_STRING_HPP
|
#define BEAST_DETAIL_STATIC_STRING_HPP
|
||||||
|
|
||||||
#include <beast/core/string.hpp>
|
#include <beast/core/string.hpp>
|
||||||
|
#include <boost/assert.hpp>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
@@ -59,13 +60,68 @@ lexicographical_compare(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Maximum number of characters in the decimal
|
// Maximum number of characters in the decimal
|
||||||
// representation of a binary number
|
// representation of a binary number. This includes
|
||||||
|
// the potential minus sign.
|
||||||
|
//
|
||||||
inline
|
inline
|
||||||
std::size_t constexpr
|
std::size_t constexpr
|
||||||
max_digits(std::size_t bytes)
|
max_digits(std::size_t bytes)
|
||||||
{
|
{
|
||||||
return static_cast<std::size_t>(
|
return static_cast<std::size_t>(
|
||||||
bytes * 2.41) + 1;
|
bytes * 2.41) + 1 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class CharT, class Integer, class Traits>
|
||||||
|
CharT*
|
||||||
|
raw_to_string(
|
||||||
|
CharT* buf, Integer x, std::true_type)
|
||||||
|
{
|
||||||
|
if(x == 0)
|
||||||
|
{
|
||||||
|
Traits::assign(*--buf, '0');
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
if(x < 0)
|
||||||
|
{
|
||||||
|
x = -x;
|
||||||
|
for(;x > 0; x /= 10)
|
||||||
|
Traits::assign(*--buf ,
|
||||||
|
"0123456789"[x % 10]);
|
||||||
|
Traits::assign(*--buf, '-');
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
for(;x > 0; x /= 10)
|
||||||
|
Traits::assign(*--buf ,
|
||||||
|
"0123456789"[x % 10]);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class CharT, class Integer, class Traits>
|
||||||
|
CharT*
|
||||||
|
raw_to_string(
|
||||||
|
CharT* buf, Integer x, std::false_type)
|
||||||
|
{
|
||||||
|
if(x == 0)
|
||||||
|
{
|
||||||
|
*--buf = '0';
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
for(;x > 0; x /= 10)
|
||||||
|
Traits::assign(*--buf ,
|
||||||
|
"0123456789"[x % 10]);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
class CharT,
|
||||||
|
class Integer,
|
||||||
|
class Traits = std::char_traits<CharT>>
|
||||||
|
CharT*
|
||||||
|
raw_to_string(CharT* last, std::size_t size, Integer i)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(size >= max_digits(sizeof(Integer)));
|
||||||
|
return raw_to_string<CharT, Integer, Traits>(
|
||||||
|
last, i, std::is_signed<Integer>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#ifndef BEAST_IMPL_STATIC_STRING_IPP
|
#ifndef BEAST_IMPL_STATIC_STRING_IPP
|
||||||
#define BEAST_IMPL_STATIC_STRING_IPP
|
#define BEAST_IMPL_STATIC_STRING_IPP
|
||||||
|
|
||||||
|
#include <beast/core/detail/static_string.hpp>
|
||||||
#include <beast/core/detail/type_traits.hpp>
|
#include <beast/core/detail/type_traits.hpp>
|
||||||
#include <boost/throw_exception.hpp>
|
#include <boost/throw_exception.hpp>
|
||||||
|
|
||||||
@@ -534,12 +535,12 @@ assign_char(CharT, std::false_type) ->
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<class Integer>
|
template<class Integer>
|
||||||
static_string<1+max_digits(sizeof(Integer))>
|
static_string<max_digits(sizeof(Integer))>
|
||||||
to_static_string(Integer x, std::true_type)
|
to_static_string(Integer x, std::true_type)
|
||||||
{
|
{
|
||||||
if(x == 0)
|
if(x == 0)
|
||||||
return {'0'};
|
return {'0'};
|
||||||
static_string<1 + detail::max_digits(
|
static_string<detail::max_digits(
|
||||||
sizeof(Integer))> s;
|
sizeof(Integer))> s;
|
||||||
if(x < 0)
|
if(x < 0)
|
||||||
{
|
{
|
||||||
@@ -593,10 +594,19 @@ template<class Integer>
|
|||||||
static_string<detail::max_digits(sizeof(Integer))>
|
static_string<detail::max_digits(sizeof(Integer))>
|
||||||
to_static_string(Integer x)
|
to_static_string(Integer x)
|
||||||
{
|
{
|
||||||
|
using CharT = char;
|
||||||
|
using Traits = std::char_traits<CharT>;
|
||||||
BOOST_STATIC_ASSERT(std::is_integral<Integer>::value);
|
BOOST_STATIC_ASSERT(std::is_integral<Integer>::value);
|
||||||
return detail::to_static_string(
|
char buf[detail::max_digits(sizeof(Integer))];
|
||||||
x, std::integral_constant<bool,
|
auto last = buf + sizeof(buf);
|
||||||
std::is_signed<Integer>::value>{});
|
auto it = detail::raw_to_string<
|
||||||
|
CharT, Integer, Traits>(last, sizeof(buf), x);
|
||||||
|
static_string<detail::max_digits(sizeof(Integer))> s;
|
||||||
|
s.resize(static_cast<std::size_t>(last - it));
|
||||||
|
auto p = s.data();
|
||||||
|
while(it < last)
|
||||||
|
Traits::assign(*p++, *it++);
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // beast
|
} // beast
|
||||||
|
104
include/beast/core/impl/string_param.ipp
Normal file
104
include/beast/core/impl/string_param.ipp
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-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)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_IMPL_STRING_PARAM_IPP
|
||||||
|
#define BEAST_IMPL_STRING_PARAM_IPP
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_integral<T>::value>::type
|
||||||
|
string_param::
|
||||||
|
print(T const& t)
|
||||||
|
{
|
||||||
|
auto const last = buf_ + sizeof(buf_);
|
||||||
|
auto const it = detail::raw_to_string<
|
||||||
|
char, T, std::char_traits<char>>(
|
||||||
|
last, sizeof(buf_), t);
|
||||||
|
sv_ = {it, static_cast<std::size_t>(
|
||||||
|
last - it)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
typename std::enable_if<
|
||||||
|
! std::is_integral<T>::value &&
|
||||||
|
! std::is_convertible<T, string_view>::value
|
||||||
|
>::type
|
||||||
|
string_param::
|
||||||
|
print(T const& t)
|
||||||
|
{
|
||||||
|
os_.emplace(buf_, sizeof(buf_));
|
||||||
|
*os_ << t;
|
||||||
|
os_->flush();
|
||||||
|
sv_ = os_->str();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
string_param::
|
||||||
|
print(string_view const& sv)
|
||||||
|
{
|
||||||
|
sv_ = sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_integral<T>::value>::type
|
||||||
|
string_param::
|
||||||
|
print_1(T const& t)
|
||||||
|
{
|
||||||
|
char buf[detail::max_digits(sizeof(T))];
|
||||||
|
auto const last = buf + sizeof(buf);
|
||||||
|
auto const it = detail::raw_to_string<
|
||||||
|
char, T, std::char_traits<char>>(
|
||||||
|
last, sizeof(buf), t);
|
||||||
|
*os_ << string_view{it,
|
||||||
|
static_cast<std::size_t>(last - it)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
typename std::enable_if<
|
||||||
|
! std::is_integral<T>::value>::type
|
||||||
|
string_param::
|
||||||
|
print_1(T const& t)
|
||||||
|
{
|
||||||
|
*os_ << t;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T0, class... TN>
|
||||||
|
void
|
||||||
|
string_param::
|
||||||
|
print_n(T0 const& t0, TN const&... tn)
|
||||||
|
{
|
||||||
|
print_1(t0);
|
||||||
|
print_n(tn...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T0, class T1, class... TN>
|
||||||
|
void
|
||||||
|
string_param::
|
||||||
|
print(T0 const& t0, T1 const& t1, TN const&... tn)
|
||||||
|
{
|
||||||
|
os_.emplace(buf_, sizeof(buf_));
|
||||||
|
print_1(t0);
|
||||||
|
print_1(t1);
|
||||||
|
print_n(tn...);
|
||||||
|
os_->flush();
|
||||||
|
sv_ = os_->str();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
string_param::
|
||||||
|
string_param(Args const&... args)
|
||||||
|
{
|
||||||
|
print(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
@@ -10,8 +10,10 @@
|
|||||||
|
|
||||||
#include <beast/config.hpp>
|
#include <beast/config.hpp>
|
||||||
#include <beast/core/string.hpp>
|
#include <beast/core/string.hpp>
|
||||||
|
#include <beast/core/static_string.hpp>
|
||||||
#include <beast/core/detail/static_ostream.hpp>
|
#include <beast/core/detail/static_ostream.hpp>
|
||||||
#include <beast/core/detail/type_traits.hpp>
|
#include <beast/core/detail/type_traits.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
@@ -26,34 +28,47 @@ namespace beast {
|
|||||||
*/
|
*/
|
||||||
class string_param
|
class string_param
|
||||||
{
|
{
|
||||||
template<class T, class = void>
|
|
||||||
struct is_streamable : std::false_type{};
|
|
||||||
|
|
||||||
#if ! BEAST_DOXYGEN
|
|
||||||
template<class T>
|
|
||||||
struct is_streamable<T, detail::void_t<decltype(
|
|
||||||
std::declval<std::ostream&>() << std::declval<T const&>()
|
|
||||||
)>> : std::true_type {};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void
|
|
||||||
assign(T const& t, std::true_type)
|
|
||||||
{
|
|
||||||
sv_ = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void
|
|
||||||
assign(T const& t, std::false_type)
|
|
||||||
{
|
|
||||||
os_ << t;
|
|
||||||
os_.flush();
|
|
||||||
sv_ = os_.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
detail::static_ostream os_;
|
|
||||||
string_view sv_;
|
string_view sv_;
|
||||||
|
char buf_[128];
|
||||||
|
boost::optional<detail::static_ostream> os_;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_integral<T>::value>::type
|
||||||
|
print(T const&);
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
typename std::enable_if<
|
||||||
|
! std::is_integral<T>::value &&
|
||||||
|
! std::is_convertible<T, string_view>::value
|
||||||
|
>::type
|
||||||
|
print(T const&);
|
||||||
|
|
||||||
|
void
|
||||||
|
print(string_view const&);
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_integral<T>::value>::type
|
||||||
|
print_1(T const&);
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
typename std::enable_if<
|
||||||
|
! std::is_integral<T>::value>::type
|
||||||
|
print_1(T const&);
|
||||||
|
|
||||||
|
void
|
||||||
|
print_n()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T0, class... TN>
|
||||||
|
void
|
||||||
|
print_n(T0 const&, TN const&...);
|
||||||
|
|
||||||
|
template<class T0, class T1, class... TN>
|
||||||
|
void
|
||||||
|
print(T0 const&, T1 const&, TN const&...);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Copy constructor (disallowed)
|
/// Copy constructor (disallowed)
|
||||||
@@ -64,62 +79,31 @@ public:
|
|||||||
|
|
||||||
/** Constructor
|
/** Constructor
|
||||||
|
|
||||||
This function constructs a string from the specified
|
This function constructs a string as if by concatenating
|
||||||
parameter.
|
the result of streaming each argument in order into an
|
||||||
|
output stream. It is used as a notational convenience
|
||||||
|
at call sites which expect a parameter with the semantics
|
||||||
|
of a @ref string_view.
|
||||||
|
|
||||||
If @ref string_view is constructible from `T` then
|
The implementation uses a small, internal static buffer
|
||||||
the complexity is O(1), and no dynamic allocation
|
to avoid memory allocations especially for the case where
|
||||||
takes place.
|
the list of arguments to be converted consists of a single
|
||||||
|
integral type.
|
||||||
|
|
||||||
Otherwise, the argument is converted to a string
|
@param args One or more arguments to convert
|
||||||
as if by a call to `boost::lexical_cast<std::string>(t)`.
|
|
||||||
An attempt is made to store this string in a reasonably
|
|
||||||
sized buffer inside the class, to avoid dynamic allocation.
|
|
||||||
|
|
||||||
If the argument could not be converted given the space
|
|
||||||
of the class buffer, a final attempt is made to convert
|
|
||||||
the argument to a `std::string`. This attempt may cause
|
|
||||||
a memory allocation.
|
|
||||||
|
|
||||||
@param t The argument to convert to string.
|
|
||||||
|
|
||||||
@note This function participates in overload resolution
|
|
||||||
only if `T` is convertible to @ref string_view, or if
|
|
||||||
`operator<<(std::ostream&, T)` is defined.
|
|
||||||
*/
|
*/
|
||||||
#if BEAST_DOXYGEN
|
template<class... Args>
|
||||||
template<class T>
|
string_param(Args const&... args);
|
||||||
#else
|
|
||||||
template<class T, class = typename std::enable_if<
|
|
||||||
std::is_convertible<T, string_view>::value ||
|
|
||||||
is_streamable<T>::value
|
|
||||||
>::type>
|
|
||||||
#endif
|
|
||||||
string_param(T const& t);
|
|
||||||
|
|
||||||
/// Conversion to @ref string_view for the converted argument.
|
/// Implicit conversion to @ref string_view
|
||||||
string_view const
|
|
||||||
str() const
|
|
||||||
{
|
|
||||||
return sv_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Conversion to @ref string_view operator
|
|
||||||
operator string_view const() const
|
operator string_view const() const
|
||||||
{
|
{
|
||||||
return sv_;
|
return sv_;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#if ! BEAST_DOXYGEN
|
|
||||||
template<class T, class>
|
|
||||||
string_param::
|
|
||||||
string_param(T const& t)
|
|
||||||
{
|
|
||||||
assign(t, std::is_convertible<T, string_view>{});
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/core/impl/string_param.ipp>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -635,8 +635,8 @@ basic_fields<Allocator>::
|
|||||||
insert(field name,
|
insert(field name,
|
||||||
string_view sname, string_param const& value)
|
string_view sname, string_param const& value)
|
||||||
{
|
{
|
||||||
auto& e = new_element(
|
auto& e = new_element(name, sname,
|
||||||
name, sname, value.str());
|
static_cast<string_view>(value));
|
||||||
auto const before =
|
auto const before =
|
||||||
set_.upper_bound(sname, key_compare{});
|
set_.upper_bound(sname, key_compare{});
|
||||||
if(before == set_.begin())
|
if(before == set_.begin())
|
||||||
@@ -666,8 +666,8 @@ basic_fields<Allocator>::
|
|||||||
set(field name, string_param const& value)
|
set(field name, string_param const& value)
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(name != field::unknown);
|
BOOST_ASSERT(name != field::unknown);
|
||||||
set_element(new_element(name,
|
set_element(new_element(name, to_string(name),
|
||||||
to_string(name), value.str()));
|
static_cast<string_view>(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
@@ -676,8 +676,8 @@ basic_fields<Allocator>::
|
|||||||
set(string_view sname, string_param const& value)
|
set(string_view sname, string_param const& value)
|
||||||
{
|
{
|
||||||
set_element(new_element(
|
set_element(new_element(
|
||||||
string_to_field(sname),
|
string_to_field(sname), sname,
|
||||||
sname, value.str()));
|
static_cast<string_view>(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
|
@@ -18,12 +18,10 @@ class string_param_test : public unit_test::suite
|
|||||||
public:
|
public:
|
||||||
struct nop {};
|
struct nop {};
|
||||||
|
|
||||||
static_assert(! std::is_constructible<string_param, nop>::value, "");
|
|
||||||
|
|
||||||
void
|
void
|
||||||
check(string_param const& v, string_view s)
|
check(string_param const& v, string_view s)
|
||||||
{
|
{
|
||||||
BEAST_EXPECT(v.str() == s);
|
BEAST_EXPECT(static_cast<string_view>(v) == s);
|
||||||
}
|
}
|
||||||
|
|
||||||
class repeater
|
class repeater
|
||||||
@@ -56,6 +54,8 @@ public:
|
|||||||
check(123, "123");
|
check(123, "123");
|
||||||
check(1234, "1234");
|
check(1234, "1234");
|
||||||
check(12345, "12345");
|
check(12345, "12345");
|
||||||
|
check({"a", "b"}, "ab");
|
||||||
|
check({1, 2, 3}, "123");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
Reference in New Issue
Block a user