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:
* Enable narrowing warning on msvc cmake

View File

@@ -27,17 +27,20 @@ class static_ostream_buffer
using traits_type = typename
std::basic_streambuf<CharT, Traits>::traits_type;
char buf_[128];
std::string s_;
char* data_;
std::size_t size_;
std::size_t len_ = 0;
std::string s_;
public:
static_ostream_buffer(static_ostream_buffer&&) = 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
@@ -49,7 +52,7 @@ public:
{
if(! s_.empty())
return {s_.data(), len_};
return {buf_, len_};
return {data_, len_};
}
int_type
@@ -81,17 +84,17 @@ private:
{
static auto const growth_factor = 1.5;
if(len_ < sizeof(buf_) - 1)
if(len_ < size_ - 1)
{
this->setp(&buf_[len_],
&buf_[sizeof(buf_) - 2]);
this->setp(
data_ + len_, data_ + size_ - 2);
return;
}
if(s_.empty())
{
s_.resize(static_cast<std::size_t>(
growth_factor * len_));
Traits::copy(&s_[0], buf_, len_);
Traits::copy(&s_[0], data_, len_);
}
else
{
@@ -115,8 +118,9 @@ class static_ostream : public std::basic_ostream<char>
static_ostream_buffer osb_;
public:
static_ostream()
static_ostream(char* data, std::size_t size)
: std::basic_ostream<char>(&this->osb_)
, osb_(data, size)
{
imbue(std::locale::classic());
}

View File

@@ -9,6 +9,7 @@
#define BEAST_DETAIL_STATIC_STRING_HPP
#include <beast/core/string.hpp>
#include <boost/assert.hpp>
#include <iterator>
#include <type_traits>
@@ -59,13 +60,68 @@ lexicographical_compare(
}
// Maximum number of characters in the decimal
// representation of a binary number
// representation of a binary number. This includes
// the potential minus sign.
//
inline
std::size_t constexpr
max_digits(std::size_t bytes)
{
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

View File

@@ -8,6 +8,7 @@
#ifndef 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 <boost/throw_exception.hpp>
@@ -534,12 +535,12 @@ assign_char(CharT, std::false_type) ->
namespace detail {
template<class Integer>
static_string<1+max_digits(sizeof(Integer))>
static_string<max_digits(sizeof(Integer))>
to_static_string(Integer x, std::true_type)
{
if(x == 0)
return {'0'};
static_string<1 + detail::max_digits(
static_string<detail::max_digits(
sizeof(Integer))> s;
if(x < 0)
{
@@ -593,10 +594,19 @@ template<class Integer>
static_string<detail::max_digits(sizeof(Integer))>
to_static_string(Integer x)
{
using CharT = char;
using Traits = std::char_traits<CharT>;
BOOST_STATIC_ASSERT(std::is_integral<Integer>::value);
return detail::to_static_string(
x, std::integral_constant<bool,
std::is_signed<Integer>::value>{});
char buf[detail::max_digits(sizeof(Integer))];
auto last = buf + sizeof(buf);
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

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/core/string.hpp>
#include <beast/core/static_string.hpp>
#include <beast/core/detail/static_ostream.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/optional.hpp>
namespace beast {
@@ -26,34 +28,47 @@ namespace beast {
*/
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_;
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:
/// Copy constructor (disallowed)
@@ -64,62 +79,31 @@ public:
/** Constructor
This function constructs a string from the specified
parameter.
If @ref string_view is constructible from `T` then
the complexity is O(1), and no dynamic allocation
takes place.
This function constructs a string as if by concatenating
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.
Otherwise, the argument is converted to a string
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.
The implementation uses a small, internal static buffer
to avoid memory allocations especially for the case where
the list of arguments to be converted consists of a single
integral type.
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.
@param args One or more arguments to convert
*/
#if BEAST_DOXYGEN
template<class T>
#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);
template<class... Args>
string_param(Args const&... args);
/// Conversion to @ref string_view for the converted argument.
string_view const
str() const
{
return sv_;
}
/// Conversion to @ref string_view operator
/// Implicit conversion to @ref string_view
operator string_view const() const
{
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
#include <beast/core/impl/string_param.ipp>
#endif

View File

@@ -635,8 +635,8 @@ basic_fields<Allocator>::
insert(field name,
string_view sname, string_param const& value)
{
auto& e = new_element(
name, sname, value.str());
auto& e = new_element(name, sname,
static_cast<string_view>(value));
auto const before =
set_.upper_bound(sname, key_compare{});
if(before == set_.begin())
@@ -666,8 +666,8 @@ basic_fields<Allocator>::
set(field name, string_param const& value)
{
BOOST_ASSERT(name != field::unknown);
set_element(new_element(name,
to_string(name), value.str()));
set_element(new_element(name, to_string(name),
static_cast<string_view>(value)));
}
template<class Allocator>
@@ -676,8 +676,8 @@ basic_fields<Allocator>::
set(string_view sname, string_param const& value)
{
set_element(new_element(
string_to_field(sname),
sname, value.str()));
string_to_field(sname), sname,
static_cast<string_view>(value)));
}
template<class Allocator>

View File

@@ -18,12 +18,10 @@ class string_param_test : public unit_test::suite
public:
struct nop {};
static_assert(! std::is_constructible<string_param, nop>::value, "");
void
check(string_param const& v, string_view s)
{
BEAST_EXPECT(v.str() == s);
BEAST_EXPECT(static_cast<string_view>(v) == s);
}
class repeater
@@ -56,6 +54,8 @@ public:
check(123, "123");
check(1234, "1234");
check(12345, "12345");
check({"a", "b"}, "ab");
check({1, 2, 3}, "123");
}
void