string_param optimizations

This commit is contained in:
Vinnie Falco
2017-06-22 16:49:46 -07:00
parent 90a88a9361
commit 518ae23383
8 changed files with 263 additions and 99 deletions

View File

@@ -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

View File

@@ -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());
} }

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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>

View File

@@ -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