diff --git a/CHANGELOG.md b/CHANGELOG.md index 561a0d94..9b7d14cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Version 259: * Reduce the number of instantiations of filter_token_list * Remove the use of `static_string` from `http::fields` * Add gcc-9 to AzP CI test matrix +* Enable split compilation in http::basic_fields -------------------------------------------------------------------------------- diff --git a/include/boost/beast/core/detail/impl/temporary_buffer.ipp b/include/boost/beast/core/detail/impl/temporary_buffer.ipp new file mode 100644 index 00000000..f3dd3843 --- /dev/null +++ b/include/boost/beast/core/detail/impl/temporary_buffer.ipp @@ -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 +#include +#include + +#include +#include + +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 diff --git a/include/boost/beast/core/detail/temporary_buffer.hpp b/include/boost/beast/core/detail/temporary_buffer.hpp new file mode 100644 index 00000000..2f9a16c9 --- /dev/null +++ b/include/boost/beast/core/detail/temporary_buffer.hpp @@ -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 +#include + +#include + +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 +#endif + +#endif diff --git a/include/boost/beast/http/impl/fields.hpp b/include/boost/beast/http/impl/fields.hpp index 28aac84d..3cc4a4cb 100644 --- a/include/boost/beast/http/impl/fields.hpp +++ b/include/boost/beast/http/impl/fields.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -23,13 +24,6 @@ #include #include -#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 beast { namespace http { @@ -750,160 +744,10 @@ equal_range(string_view name) const -> 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::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 -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 -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 { - bool operator()(string_view s) + bool + operator()(string_view s) const { return beast::iequals(s, sv1) || beast::iequals(s, sv2); } @@ -912,51 +756,19 @@ struct iequals_predicate 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 keep_alive_impl( - 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"); - } - } -} + beast::detail::temporary_buffer& s, string_view value, + unsigned version, bool keep_alive); } // detail @@ -1073,7 +885,7 @@ void basic_fields:: set_chunked_impl(bool value) { - detail::temporary_buffer buf; + beast::detail::temporary_buffer buf; auto it = find(field::transfer_encoding); if(value) { @@ -1104,8 +916,7 @@ set_chunked_impl(bool value) if(it == end()) return; - detail::filter_token_list_last(buf, it->value(), - detail::iequals_predicate{"chunked"}); + detail::filter_token_list_last(buf, it->value(), {"chunked"}); if(! buf.empty()) set(field::transfer_encoding, buf.view()); else @@ -1132,7 +943,7 @@ set_keep_alive_impl( { // VFALCO What about Proxy-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); if(buf.empty()) erase(field::connection); @@ -1394,4 +1205,8 @@ swap(basic_fields& other, std::false_type) } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + #endif diff --git a/include/boost/beast/http/impl/fields.ipp b/include/boost/beast/http/impl/fields.ipp new file mode 100644 index 00000000..9a41d977 --- /dev/null +++ b/include/boost/beast/http/impl/fields.ipp @@ -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 + +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::max)() >= + (std::numeric_limits::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 diff --git a/include/boost/beast/src.hpp b/include/boost/beast/src.hpp index 87b4ab30..f5256f32 100644 --- a/include/boost/beast/src.hpp +++ b/include/boost/beast/src.hpp @@ -31,6 +31,7 @@ the program, with the macro BOOST_BEAST_SEPARATE_COMPILATION defined. #include #include +#include #include #include #include @@ -44,6 +45,7 @@ the program, with the macro BOOST_BEAST_SEPARATE_COMPILATION defined. #include #include #include +#include #include #include #include diff --git a/test/beast/http/fields.cpp b/test/beast/http/fields.cpp index c6f45468..92c1d5e5 100644 --- a/test/beast/http/fields.cpp +++ b/test/beast/http/fields.cpp @@ -25,7 +25,7 @@ class fields_test : public beast::unit_test::suite { public: static constexpr std::size_t max_static_buffer = - sizeof(http::detail::temporary_buffer); + sizeof(beast::detail::temporary_buffer); template class test_allocator