Enable split compilation in http::basic_fields

Moved functions,that are independent of template argument, into
an *.ipp file.

Signed-off-by: Damian Jarek <damian.jarek93@gmail.com>
This commit is contained in:
Damian Jarek
2019-06-08 16:16:58 +02:00
parent 2bc521d6ab
commit d5f5f1467f
7 changed files with 317 additions and 207 deletions

View File

@ -3,6 +3,7 @@ Version 259:
* Reduce the number of instantiations of filter_token_list * Reduce the number of instantiations of filter_token_list
* Remove the use of `static_string` from `http::fields` * Remove the use of `static_string` from `http::fields`
* Add gcc-9 to AzP CI test matrix * Add gcc-9 to AzP CI test matrix
* Enable split compilation in http::basic_fields
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -0,0 +1,67 @@
//
// Copyright (c) 2019 Damian Jarek(damian.jarek93@gmail.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_IMPL_TEMPORARY_BUFFER_IPP
#define BOOST_BEAST_DETAIL_IMPL_TEMPORARY_BUFFER_IPP
#include <boost/beast/core/detail/temporary_buffer.hpp>
#include <boost/beast/core/detail/clamp.hpp>
#include <boost/core/exchange.hpp>
#include <memory>
#include <cstring>
namespace boost {
namespace beast {
namespace detail {
void
temporary_buffer::append(string_view sv)
{
grow(sv.size());
unchecked_append(sv);
}
void
temporary_buffer::append(string_view sv1, string_view sv2)
{
grow(sv1.size() + sv2.size());
unchecked_append(sv1);
unchecked_append(sv2);
}
void
temporary_buffer::unchecked_append(string_view sv)
{
auto n = sv.size();
std::memcpy(&data_[size_], sv.data(), n);
size_ += n;
}
void
temporary_buffer::grow(std::size_t sv_size)
{
if (capacity_ - size_ >= sv_size)
{
return;
}
auto const new_cap = (sv_size + size_) * 2u;
BOOST_ASSERT(!detail::sum_exceeds(sv_size, size_, new_cap));
char* const p = new char[new_cap];
std::memcpy(p, data_, size_);
deallocate(boost::exchange(data_, p));
capacity_ = new_cap;
}
} // detail
} // beast
} // boost
#endif

View File

@ -0,0 +1,87 @@
//
// Copyright (c) 2019 Damian Jarek(damian.jarek93@gmail.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_TEMPORARY_BUFFER_HPP
#define BOOST_BEAST_DETAIL_TEMPORARY_BUFFER_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/string.hpp>
#include <memory>
namespace boost {
namespace beast {
namespace detail {
struct temporary_buffer
{
temporary_buffer() = default;
temporary_buffer(temporary_buffer const&) = delete;
temporary_buffer(temporary_buffer&&) = delete;
temporary_buffer& operator=(temporary_buffer const&) = delete;
temporary_buffer& operator=(temporary_buffer&&) = delete;
~temporary_buffer() noexcept
{
deallocate(data_);
}
BOOST_BEAST_DECL
void
append(string_view sv);
BOOST_BEAST_DECL
void
append(string_view sv1, string_view sv2);
string_view
view() const noexcept
{
return {data_, size_};
}
bool
empty() const noexcept
{
return size_ == 0;
}
private:
BOOST_BEAST_DECL
void
unchecked_append(string_view sv);
BOOST_BEAST_DECL
void
grow(std::size_t sv_size);
void
deallocate(char* data) noexcept
{
if (data != buffer_)
delete[] data;
}
char buffer_[4096];
char* data_ = buffer_;
std::size_t capacity_ = sizeof(buffer_);
std::size_t size_ = 0;
};
} // detail
} // beast
} // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/detail/impl/temporary_buffer.ipp>
#endif
#endif

View File

@ -14,6 +14,7 @@
#include <boost/beast/core/string.hpp> #include <boost/beast/core/string.hpp>
#include <boost/beast/core/detail/buffers_ref.hpp> #include <boost/beast/core/detail/buffers_ref.hpp>
#include <boost/beast/core/detail/clamp.hpp> #include <boost/beast/core/detail/clamp.hpp>
#include <boost/beast/core/detail/temporary_buffer.hpp>
#include <boost/beast/http/verb.hpp> #include <boost/beast/http/verb.hpp>
#include <boost/beast/http/rfc7230.hpp> #include <boost/beast/http/rfc7230.hpp>
#include <boost/beast/http/status.hpp> #include <boost/beast/http/status.hpp>
@ -23,13 +24,6 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000
// Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
#ifndef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
#define BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
#endif
#endif
namespace boost { namespace boost {
namespace beast { namespace beast {
namespace http { namespace http {
@ -750,160 +744,10 @@ equal_range(string_view name) const ->
namespace detail { namespace detail {
struct temporary_buffer
{
temporary_buffer() = default;
temporary_buffer(temporary_buffer const&) = delete;
temporary_buffer(temporary_buffer&&) = delete;
temporary_buffer& operator=(temporary_buffer const&) = delete;
temporary_buffer& operator=(temporary_buffer&&) = delete;
~temporary_buffer() noexcept
{
deallocate(data_);
}
void append(string_view sv)
{
reserve(sv.size());
unsafe_append(sv);
}
void append(string_view sv1, string_view sv2)
{
reserve(sv1.size() + sv2.size());
unsafe_append(sv1);
unsafe_append(sv2);
}
string_view view() const noexcept
{
return {data_, size_};
}
std::size_t size() const noexcept
{
return size_;
}
std::size_t capacity() const noexcept
{
return capacity_;
}
bool empty() const noexcept
{
return size_ == 0;
}
private:
void unsafe_append(string_view sv)
{
auto n = sv.size();
std::memcpy(&data_[size_], sv.data(), n);
size_ += n;
}
void reserve(std::size_t sv_size)
{
if (capacity_ - size_ < sv_size)
{
auto constexpr limit = (std::numeric_limits<std::size_t>::max)();
if (limit/2 < capacity_ || limit - size_ < sv_size)
{
BOOST_THROW_EXCEPTION(
std::length_error{"temporary_buffer::append"});
}
auto const new_cap = (std::max)(size_ + sv_size, capacity_*2);
char* const p = new char[new_cap];
std::memcpy(p, data_, size_);
deallocate(boost::exchange(data_, p));
capacity_ = new_cap;
}
}
void deallocate(char* data) noexcept
{
if (data != buffer_)
delete[] data;
}
char buffer_[4096];
char* data_ = buffer_;
std::size_t capacity_ = sizeof(buffer_);
std::size_t size_ = 0;
};
// Filter a token list
//
template<class Pred>
void
filter_token_list(
detail::temporary_buffer& s,
string_view value,
Pred&& pred)
{
token_list te{value};
auto it = te.begin();
auto last = te.end();
if(it == last)
return;
while(pred(*it))
if(++it == last)
return;
s.append(*it);
while(++it != last)
{
if(! pred(*it))
{
s.append(", ", *it);
}
}
}
// Filter the last item in a token list
template<class Pred>
void
filter_token_list_last(
detail::temporary_buffer& s,
string_view value,
Pred&& pred)
{
token_list te{value};
if(te.begin() != te.end())
{
auto it = te.begin();
auto next = std::next(it);
if(next == te.end())
{
if(! pred(*it))
s.append(*it);
return;
}
s.append(*it);
for(;;)
{
it = next;
next = std::next(it);
if(next == te.end())
{
if(! pred(*it))
{
s.append(", ", *it);
}
return;
}
s.append(", ", *it);
}
}
}
struct iequals_predicate struct iequals_predicate
{ {
bool operator()(string_view s) bool
operator()(string_view s) const
{ {
return beast::iequals(s, sv1) || beast::iequals(s, sv2); return beast::iequals(s, sv1) || beast::iequals(s, sv2);
} }
@ -912,51 +756,19 @@ struct iequals_predicate
string_view sv2; string_view sv2;
}; };
inline // Filter the last item in a token list
BOOST_BEAST_DECL
void
filter_token_list_last(
beast::detail::temporary_buffer& s,
string_view value,
iequals_predicate const& pred);
BOOST_BEAST_DECL
void void
keep_alive_impl( keep_alive_impl(
detail::temporary_buffer& s, string_view value, beast::detail::temporary_buffer& s, string_view value,
unsigned version, bool keep_alive) unsigned version, bool keep_alive);
{
if(version < 11)
{
if(keep_alive)
{
// remove close
filter_token_list(s, value, iequals_predicate{"close"});
// add keep-alive
if(s.empty())
s.append("keep-alive");
else if(! token_list{value}.exists("keep-alive"))
s.append(", keep-alive");
}
else
{
// remove close and keep-alive
filter_token_list(s, value,
iequals_predicate{"close", "keep-alive"});
}
}
else
{
if(keep_alive)
{
// remove close and keep-alive
filter_token_list(s, value,
iequals_predicate{"close", "keep-alive"});
}
else
{
// remove keep-alive
filter_token_list(s, value, iequals_predicate{"keep-alive"});
// add close
if(s.empty())
s.append("close");
else if(! token_list{value}.exists("close"))
s.append(", close");
}
}
}
} // detail } // detail
@ -1073,7 +885,7 @@ void
basic_fields<Allocator>:: basic_fields<Allocator>::
set_chunked_impl(bool value) set_chunked_impl(bool value)
{ {
detail::temporary_buffer buf; beast::detail::temporary_buffer buf;
auto it = find(field::transfer_encoding); auto it = find(field::transfer_encoding);
if(value) if(value)
{ {
@ -1104,8 +916,7 @@ set_chunked_impl(bool value)
if(it == end()) if(it == end())
return; return;
detail::filter_token_list_last(buf, it->value(), detail::filter_token_list_last(buf, it->value(), {"chunked"});
detail::iequals_predicate{"chunked"});
if(! buf.empty()) if(! buf.empty())
set(field::transfer_encoding, buf.view()); set(field::transfer_encoding, buf.view());
else else
@ -1132,7 +943,7 @@ set_keep_alive_impl(
{ {
// VFALCO What about Proxy-Connection ? // VFALCO What about Proxy-Connection ?
auto const value = (*this)[field::connection]; auto const value = (*this)[field::connection];
detail::temporary_buffer buf; beast::detail::temporary_buffer buf;
detail::keep_alive_impl(buf, value, version, keep_alive); detail::keep_alive_impl(buf, value, version, keep_alive);
if(buf.empty()) if(buf.empty())
erase(field::connection); erase(field::connection);
@ -1394,4 +1205,8 @@ swap(basic_fields& other, std::false_type)
} // beast } // beast
} // boost } // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/http/impl/fields.ipp>
#endif
#endif #endif

View File

@ -0,0 +1,138 @@
//
// 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_HTTP_IMPL_FIELDS_IPP
#define BOOST_BEAST_HTTP_IMPL_FIELDS_IPP
#include <boost/beast/http/fields.hpp>
namespace boost {
namespace beast {
namespace http {
namespace detail {
// `basic_fields` assumes that `std::size_t` is larger than `uint16_t`, so we
// verify it explicitly here, so that users that use split compilation don't
// need to pay the (fairly small) price for this sanity check
BOOST_STATIC_ASSERT((std::numeric_limits<std::size_t>::max)() >=
(std::numeric_limits<std::uint32_t>::max)());
// Filter a token list
//
inline
void
filter_token_list(
beast::detail::temporary_buffer& s,
string_view value,
iequals_predicate const& pred)
{
token_list te{value};
auto it = te.begin();
auto last = te.end();
if(it == last)
return;
while(pred(*it))
if(++it == last)
return;
s.append(*it);
while(++it != last)
{
if(! pred(*it))
{
s.append(", ", *it);
}
}
}
void
filter_token_list_last(
beast::detail::temporary_buffer& s,
string_view value,
iequals_predicate const& pred)
{
token_list te{value};
if(te.begin() != te.end())
{
auto it = te.begin();
auto next = std::next(it);
if(next == te.end())
{
if(! pred(*it))
s.append(*it);
return;
}
s.append(*it);
for(;;)
{
it = next;
next = std::next(it);
if(next == te.end())
{
if(! pred(*it))
{
s.append(", ", *it);
}
return;
}
s.append(", ", *it);
}
}
}
void
keep_alive_impl(
beast::detail::temporary_buffer& s, string_view value,
unsigned version, bool keep_alive)
{
if(version < 11)
{
if(keep_alive)
{
// remove close
filter_token_list(s, value, iequals_predicate{"close"});
// add keep-alive
if(s.empty())
s.append("keep-alive");
else if(! token_list{value}.exists("keep-alive"))
s.append(", keep-alive");
}
else
{
// remove close and keep-alive
filter_token_list(s, value,
iequals_predicate{"close", "keep-alive"});
}
}
else
{
if(keep_alive)
{
// remove close and keep-alive
filter_token_list(s, value,
iequals_predicate{"close", "keep-alive"});
}
else
{
// remove keep-alive
filter_token_list(s, value, iequals_predicate{"keep-alive"});
// add close
if(s.empty())
s.append("close");
else if(! token_list{value}.exists("close"))
s.append(", close");
}
}
}
} // detail
} // http
} // beast
} // boost
#endif // BOOST_BEAST_HTTP_IMPL_FIELDS_IPP

View File

@ -31,6 +31,7 @@ the program, with the macro BOOST_BEAST_SEPARATE_COMPILATION defined.
#include <boost/beast/core/detail/base64.ipp> #include <boost/beast/core/detail/base64.ipp>
#include <boost/beast/core/detail/sha1.ipp> #include <boost/beast/core/detail/sha1.ipp>
#include <boost/beast/core/detail/impl/temporary_buffer.ipp>
#include <boost/beast/core/impl/error.ipp> #include <boost/beast/core/impl/error.ipp>
#include <boost/beast/core/impl/file_posix.ipp> #include <boost/beast/core/impl/file_posix.ipp>
#include <boost/beast/core/impl/file_stdio.ipp> #include <boost/beast/core/impl/file_stdio.ipp>
@ -44,6 +45,7 @@ the program, with the macro BOOST_BEAST_SEPARATE_COMPILATION defined.
#include <boost/beast/http/impl/basic_parser.ipp> #include <boost/beast/http/impl/basic_parser.ipp>
#include <boost/beast/http/impl/error.ipp> #include <boost/beast/http/impl/error.ipp>
#include <boost/beast/http/impl/field.ipp> #include <boost/beast/http/impl/field.ipp>
#include <boost/beast/http/impl/fields.ipp>
#include <boost/beast/http/impl/rfc7230.ipp> #include <boost/beast/http/impl/rfc7230.ipp>
#include <boost/beast/http/impl/status.ipp> #include <boost/beast/http/impl/status.ipp>
#include <boost/beast/http/impl/verb.ipp> #include <boost/beast/http/impl/verb.ipp>

View File

@ -25,7 +25,7 @@ class fields_test : public beast::unit_test::suite
{ {
public: public:
static constexpr std::size_t max_static_buffer = static constexpr std::size_t max_static_buffer =
sizeof(http::detail::temporary_buffer); sizeof(beast::detail::temporary_buffer);
template<class T> template<class T>
class test_allocator class test_allocator