diff --git a/CHANGELOG.md b/CHANGELOG.md index faa735c2..ed253bc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Version 50 * parser is constructible from other body types * Add field enumeration +* Use allocator more in basic_fields API Changes: diff --git a/include/beast/http/detail/fields.hpp b/include/beast/http/detail/fields.hpp deleted file mode 100644 index a83e95a7..00000000 --- a/include/beast/http/detail/fields.hpp +++ /dev/null @@ -1,214 +0,0 @@ -// -// 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_HTTP_DETAIL_FIELDS_HPP -#define BEAST_HTTP_DETAIL_FIELDS_HPP - -#include -#include -#include -#include - -namespace beast { -namespace http { - -template -class basic_fields; - -namespace detail { - -class basic_fields_base -{ -public: - struct value_type - { - std::string first; - std::string second; - - value_type(string_view name_, - string_view value_) - : first(name_) - , second(value_) - { - } - - string_view - name() const - { - return first; - } - - string_view - value() const - { - return second; - } - }; - -protected: - template - friend class beast::http::basic_fields; - - struct element - : boost::intrusive::set_base_hook < - boost::intrusive::link_mode < - boost::intrusive::normal_link>> - , boost::intrusive::list_base_hook < - boost::intrusive::link_mode < - boost::intrusive::normal_link>> - { - value_type data; - - element(string_view name, - string_view value) - : data(name, value) - { - } - }; - - struct less : private beast::detail::ci_less - { - template - bool - operator()(String const& lhs, element const& rhs) const - { - return ci_less::operator()(lhs, rhs.data.first); - } - - template - bool - operator()(element const& lhs, String const& rhs) const - { - return ci_less::operator()(lhs.data.first, rhs); - } - - bool - operator()(element const& lhs, element const& rhs) const - { - return ci_less::operator()( - lhs.data.first, rhs.data.first); - } - }; - - using list_t = boost::intrusive::make_list>::type; - - using set_t = boost::intrusive::make_multiset, - boost::intrusive::compare>::type; - - // data - set_t set_; - list_t list_; - - basic_fields_base(set_t&& set, list_t&& list) - : set_(std::move(set)) - , list_(std::move(list)) - { - } - -public: - class const_iterator; - - using iterator = const_iterator; - - basic_fields_base() = default; -}; - -//------------------------------------------------------------------------------ - -class basic_fields_base::const_iterator -{ - using iter_type = list_t::const_iterator; - - iter_type it_; - - template - friend class beast::http::basic_fields; - - friend class basic_fields_base; - - const_iterator(iter_type it) - : it_(it) - { - } - -public: - using value_type = - typename basic_fields_base::value_type; - using pointer = value_type const*; - using reference = value_type const&; - using difference_type = std::ptrdiff_t; - using iterator_category = - std::bidirectional_iterator_tag; - - const_iterator() = default; - const_iterator(const_iterator&& other) = default; - const_iterator(const_iterator const& other) = default; - const_iterator& operator=(const_iterator&& other) = default; - const_iterator& operator=(const_iterator const& other) = default; - - bool - operator==(const_iterator const& other) const - { - return it_ == other.it_; - } - - bool - operator!=(const_iterator const& other) const - { - return !(*this == other); - } - - reference - operator*() const - { - return it_->data; - } - - pointer - operator->() const - { - return &**this; - } - - const_iterator& - operator++() - { - ++it_; - return *this; - } - - const_iterator - operator++(int) - { - auto temp = *this; - ++(*this); - return temp; - } - - const_iterator& - operator--() - { - --it_; - return *this; - } - - const_iterator - operator--(int) - { - auto temp = *this; - --(*this); - return temp; - } -}; - -} // detail -} // http -} // beast - -#endif diff --git a/include/beast/http/fields.hpp b/include/beast/http/fields.hpp index bf61a758..25116b92 100644 --- a/include/beast/http/fields.hpp +++ b/include/beast/http/fields.hpp @@ -10,11 +10,13 @@ #include #include -#include -#include +#include #include +#include +#include #include #include +#include #include #include #include @@ -39,70 +41,44 @@ namespace http { @note Meets the requirements of @b FieldSequence. */ template -class basic_fields : -#if ! BEAST_DOXYGEN - private beast::detail::empty_base_optimization< - typename std::allocator_traits:: - template rebind_alloc< - detail::basic_fields_base::element>>, -#endif - public detail::basic_fields_base +class basic_fields { - using alloc_type = typename - std::allocator_traits:: - template rebind_alloc< - detail::basic_fields_base::element>; +private: + using off_t = std::uint16_t; - using alloc_traits = - std::allocator_traits; +public: + /** The value type of the field sequence. - using size_type = - typename std::allocator_traits::size_type; - - void - delete_all(); - - void - move_assign(basic_fields&, std::false_type); - - void - move_assign(basic_fields&, std::true_type); - - void - copy_assign(basic_fields const&, std::false_type); - - void - copy_assign(basic_fields const&, std::true_type); - - template - void - copy_from(FieldSequence const& fs) + Meets the requirements of @b Field. + */ + struct value_type { - for(auto const& e : fs) - insert(e.first, e.second); - } + string_view + name() const + { + return {first, off - 2u}; + } + + string_view + value() const + { + return {first + off, len}; + } + + char const* first; + off_t off; + off_t len; + }; public: /// The type of allocator used. using allocator_type = Allocator; - /** The value type of the field sequence. + /// A constant iterator to the field sequence. + class const_iterator; - Meets the requirements of @b Field. - */ -#if BEAST_DOXYGEN - using value_type = implementation_defined; -#endif - - /// A const iterator to the field sequence -#if BEAST_DOXYGEN - using iterator = implementation_defined; -#endif - - /// A const iterator to the field sequence -#if BEAST_DOXYGEN - using const_iterator = implementation_defined; -#endif + /// A constant iterator to the field sequence. + using iterator = const_iterator; /// Default constructor. basic_fields() = default; @@ -147,42 +123,51 @@ public: template basic_fields& operator=(basic_fields const&); - /// Returns a const iterator to the beginning of the field sequence. + /// Return a copy of the allocator associated with the container. + allocator_type + get_allocator() const + { + return typename std::allocator_traits< + Allocator>::template rebind_alloc< + element>(alloc_); + } + + /// Return a const iterator to the beginning of the field sequence. const_iterator begin() const { return list_.cbegin(); } - /// Returns a const iterator to the end of the field sequence. + /// Return a const iterator to the end of the field sequence. const_iterator end() const { return list_.cend(); } - /// Returns a const iterator to the beginning of the field sequence. + /// Return a const iterator to the beginning of the field sequence. const_iterator cbegin() const { return list_.cbegin(); } - /// Returns a const iterator to the end of the field sequence. + /// Return a const iterator to the end of the field sequence. const_iterator cend() const { return list_.cend(); } - /// Returns `true` if the specified field exists. + /// Return `true` if the specified field exists. bool exists(string_view name) const { return set_.find(name, less{}) != set_.end(); } - /// Returns the number of values for the specified field. + /// Return the number of values for the specified field. std::size_t count(string_view name) const; @@ -247,6 +232,7 @@ public: ! std::is_constructible::value>::type insert(string_view name, T const& value) { + // VFALCO This should use a static buffer, see lexical_cast doc insert(name, boost::lexical_cast(value)); } @@ -277,6 +263,7 @@ public: ! std::is_constructible::value>::type replace(string_view name, T const& value) { + // VFALCO This should use a static buffer, see lexical_cast doc replace(name, boost::lexical_cast(value)); } @@ -329,6 +316,111 @@ private: else this->replace(":reason", s); } + +private: + struct element + : boost::intrusive::set_base_hook < + boost::intrusive::link_mode < + boost::intrusive::normal_link>> + , boost::intrusive::list_base_hook < + boost::intrusive::link_mode < + boost::intrusive::normal_link>> + { + element( + string_view name, string_view value) + { + char* p = reinterpret_cast(this + 1); + data.first = p; + data.off = + static_cast(name.size() + 2); + data.len = + static_cast(value.size()); + std::memcpy(p, name.data(), name.size()); + p[data.off-2] = ':'; + p[data.off-1] = ' '; + std::memcpy( + p + data.off, value.data(), value.size()); + p[data.off + data.len] = '\r'; + p[data.off + data.len + 1] = '\n'; + } + + value_type data; + }; + + struct less : private beast::detail::ci_less + { + template + bool + operator()(String const& lhs, element const& rhs) const + { + return ci_less::operator()(lhs, rhs.data.name()); + } + + template + bool + operator()(element const& lhs, String const& rhs) const + { + return ci_less::operator()(lhs.data.name(), rhs); + } + + bool + operator()(element const& lhs, element const& rhs) const + { + return ci_less::operator()( + lhs.data.name(), rhs.data.name()); + } + }; + + using alloc_type = typename + std::allocator_traits:: + template rebind_alloc; + + using alloc_traits = + std::allocator_traits; + + using size_type = + typename std::allocator_traits::size_type; + + using list_t = typename boost::intrusive::make_list< + element, boost::intrusive::constant_time_size< + false>>::type; + + using set_t = typename boost::intrusive::make_multiset< + element, boost::intrusive::constant_time_size< + true>, boost::intrusive::compare>::type; + + void + delete_all(); + + void + move_assign(basic_fields&, std::false_type); + + void + move_assign(basic_fields&, std::true_type); + + void + copy_assign(basic_fields const&, std::false_type); + + void + copy_assign(basic_fields const&, std::true_type); + + template + void + copy_from(FieldSequence const& fs) + { + for(auto const& e : fs) + insert(e.name(), e.value()); + } + + element& + new_element(string_view name, string_view value); + + void + delete_element(element& e); + + set_t set_; + list_t list_; + alloc_type alloc_; }; /// A typical HTTP header fields container diff --git a/include/beast/http/impl/fields.ipp b/include/beast/http/impl/fields.ipp index fae1a451..ea265692 100644 --- a/include/beast/http/impl/fields.ipp +++ b/include/beast/http/impl/fields.ipp @@ -9,23 +9,116 @@ #define BEAST_HTTP_IMPL_FIELDS_IPP #include +#include #include namespace beast { namespace http { +//------------------------------------------------------------------------------ + +template +basic_fields:: +~basic_fields() +{ + delete_all(); +} + +//------------------------------------------------------------------------------ + +template +class basic_fields:: + const_iterator +{ + using iter_type = typename list_t::const_iterator; + + iter_type it_; + + template + friend class beast::http::basic_fields; + + const_iterator(iter_type it) + : it_(it) + { + } + +public: + using value_type = typename + basic_fields::value_type; + using pointer = value_type const*; + using reference = value_type const&; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + const_iterator() = default; + const_iterator(const_iterator&& other) = default; + const_iterator(const_iterator const& other) = default; + const_iterator& operator=(const_iterator&& other) = default; + const_iterator& operator=(const_iterator const& other) = default; + + bool + operator==(const_iterator const& other) const + { + return it_ == other.it_; + } + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const + { + return it_->data; + } + + pointer + operator->() const + { + return &**this; + } + + const_iterator& + operator++() + { + ++it_; + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + + const_iterator& + operator--() + { + --it_; + return *this; + } + + const_iterator + operator--(int) + { + auto temp = *this; + --(*this); + return temp; + } +}; + template void basic_fields:: delete_all() { for(auto it = list_.begin(); it != list_.end();) - { - auto& e = *it++; - alloc_traits::destroy(this->member(), &e); - alloc_traits::deallocate( - this->member(), &e, 1); - } + delete_element(*it++); } template @@ -34,7 +127,7 @@ void basic_fields:: move_assign(basic_fields& other, std::false_type) { - if(this->member() != other.member()) + if(alloc_ != other.alloc_) { copy_from(other); other.clear(); @@ -52,7 +145,7 @@ void basic_fields:: move_assign(basic_fields& other, std::true_type) { - this->member() = std::move(other.member()); + alloc_ = std::move(other.alloc_); set_ = std::move(other.set_); list_ = std::move(other.list_); } @@ -72,34 +165,63 @@ void basic_fields:: copy_assign(basic_fields const& other, std::true_type) { - this->member() = other.member(); + alloc_ = other.alloc_; copy_from(other); } +template +auto +basic_fields:: +new_element(string_view name, string_view value) -> + element& +{ + if(name.size() + 2 > + (std::numeric_limits::max)()) + BOOST_THROW_EXCEPTION(std::length_error{ + "field name too large"}); + if(value.size() + 2 > + (std::numeric_limits::max)()) + BOOST_THROW_EXCEPTION(std::length_error{ + "field value too large"}); + value = detail::trim(value); + std::uint16_t const off = + static_cast(name.size() + 2); + std::uint16_t const len = + static_cast(value.size()); + auto const p = alloc_traits::allocate(alloc_, + 1 + (off + len + 2 + sizeof(element) - 1) / + sizeof(element)); + alloc_traits::construct(alloc_, p, name, value); + return *p; +} + +template +void +basic_fields:: +delete_element(element& e) +{ + auto const n = 1 + + (e.data.off + e.data.len + 2 + + sizeof(element) - 1) / sizeof(element); + alloc_traits::destroy(alloc_, &e); + alloc_traits::deallocate(alloc_, &e, n); +} + //------------------------------------------------------------------------------ -template -basic_fields:: -~basic_fields() -{ - delete_all(); -} - template basic_fields:: basic_fields(Allocator const& alloc) - : beast::detail::empty_base_optimization< - alloc_type>(alloc) + : alloc_(alloc) { } template basic_fields:: basic_fields(basic_fields&& other) - : beast::detail::empty_base_optimization( - std::move(other.member())) - , detail::basic_fields_base( - std::move(other.set_), std::move(other.list_)) + : set_(std::move(other.set_)) + , list_(std::move(other.list_)) + , alloc_(std::move(other.alloc_)) { } @@ -121,7 +243,7 @@ template basic_fields:: basic_fields(basic_fields const& other) : basic_fields(alloc_traits:: - select_on_container_copy_construction(other.member())) + select_on_container_copy_construction(other.alloc_)) { copy_from(other); } @@ -190,7 +312,7 @@ operator[](string_view name) const auto const it = find(name); if(it == end()) return {}; - return it->second; + return it->value(); } template @@ -218,8 +340,7 @@ erase(string_view name) auto& e = *it++; set_.erase(set_.iterator_to(e)); list_.erase(list_.iterator_to(e)); - alloc_traits::destroy(this->member(), &e); - alloc_traits::deallocate(this->member(), &e, 1); + delete_element(e); if(it == last) break; ++n; @@ -232,11 +353,9 @@ void basic_fields:: insert(string_view name, string_view value) { - value = detail::trim(value); - auto const p = alloc_traits::allocate(this->member(), 1); - alloc_traits::construct(this->member(), p, name, value); - set_.insert_before(set_.upper_bound(name, less{}), *p); - list_.push_back(*p); + auto& e = new_element(name, value); + set_.insert_before(set_.upper_bound(name, less{}), e); + list_.push_back(e); } template diff --git a/test/http/fields.cpp b/test/http/fields.cpp index 9607b91f..c34b1bf3 100644 --- a/test/http/fields.cpp +++ b/test/http/fields.cpp @@ -14,7 +14,7 @@ namespace beast { namespace http { -class basic_fields_test : public beast::unit_test::suite +class fields_test : public beast::unit_test::suite { public: template @@ -120,7 +120,7 @@ public: } }; -BEAST_DEFINE_TESTSUITE(basic_fields,http,beast); +BEAST_DEFINE_TESTSUITE(fields,http,beast); } // http } // beast