From 2f1ab4d056db6b2706a7f61419d2a5f12afcfe7c Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sat, 22 Jan 2022 13:47:20 -0800 Subject: [PATCH] refactor const_string --- include/boost/url.hpp | 2 +- include/boost/url/const_string.hpp | 175 +++++------ include/boost/url/detail/over_allocator.hpp | 2 +- include/boost/url/impl/const_string.hpp | 326 +++++++++----------- include/boost/url/impl/const_string.ipp | 171 ++++++++++ include/boost/url/impl/params.hpp | 4 +- include/boost/url/impl/params.ipp | 60 ++-- include/boost/url/impl/params_view.hpp | 6 +- include/boost/url/impl/params_view.ipp | 52 ++-- include/boost/url/impl/pct_encoding.hpp | 44 +-- include/boost/url/impl/segments.hpp | 4 +- include/boost/url/impl/segments.ipp | 32 +- include/boost/url/impl/segments_view.hpp | 6 +- include/boost/url/impl/segments_view.ipp | 23 +- include/boost/url/impl/static_pool.ipp | 3 +- include/boost/url/ipv4_address.hpp | 6 +- include/boost/url/ipv6_address.hpp | 6 +- include/boost/url/params.hpp | 4 +- include/boost/url/params_view.hpp | 4 +- include/boost/url/segments.hpp | 2 +- include/boost/url/segments_view.hpp | 2 +- include/boost/url/src.hpp | 1 + test/unit/const_string.cpp | 184 ++++++++++- test/unit/params.cpp | 46 +-- test/unit/segments_view.cpp | 26 +- test/unit/static_pool.cpp | 7 +- 26 files changed, 729 insertions(+), 469 deletions(-) create mode 100644 include/boost/url/impl/const_string.ipp diff --git a/include/boost/url.hpp b/include/boost/url.hpp index fbf3b9fe..901c12a7 100644 --- a/include/boost/url.hpp +++ b/include/boost/url.hpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -33,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/include/boost/url/const_string.hpp b/include/boost/url/const_string.hpp index 89fe6afb..87fdbc1c 100644 --- a/include/boost/url/const_string.hpp +++ b/include/boost/url/const_string.hpp @@ -12,8 +12,6 @@ #include #include -#include -#include namespace boost { namespace urls { @@ -45,120 +43,101 @@ namespace urls { class const_string : public string_view { struct base; + struct result; - base* p_ = nullptr; + base* p_; - template - base* - construct( - std::size_t n, - Allocator const& a, - char*& dest); - -public: -#ifdef BOOST_URL_DOCS - using allocator = __see_below__; -#else - class allocator; -#endif - - /** Destructor - - Upon destruction, ownership of the shared string is released. If - there are no more owners, the resources for the string are released. - - */ - inline - ~const_string(); - - /** Constructor - - Default-constructed strings are empty - - */ - const_string() = default; - - /** Constructor - - This function creates a const_string of the specified size at the - destination with the a custom allocator. - - This function sets the destination pointer to the new string address, - where the user can fill its contents while the const_string still - owns the buffer. - - The buffer is destroyed after the last const_string that refers to it - is destroyed. - - @tparam Allocator Allocator type - - @param n Number of chars in the string `dest` - - @param a Allocator to create a string in `dest` - - @param dest Destination - - */ - template - const_string( - std::size_t n, - Allocator const& a, - char*& dest); - - /** Constructor - - This function constructs a copy of a @ref const_string with the - specified allocator. - - This function allocates a @ref const_string the specified allocator - and copies its contents to this new string. - - @tparam Allocator Allocator type - - @param s The sting to copy - - @param a allocator used to create this const_string - - */ - template< class Allocator = - std::allocator > + BOOST_URL_DECL explicit const_string( - string_view s, - Allocator const& a = {}); + result const& r) noexcept; - /** Copy constructor +public: + class factory; - This function constructs a copy of a const_string. As const_string - is read-only, this function can only copy and increment the string - reference counter. + BOOST_URL_DECL + ~const_string(); - @param other The string to copy + BOOST_URL_DECL + const_string() noexcept; - */ - inline + BOOST_URL_DECL const_string( const_string const& other) noexcept; - /** Copy assignment - - This function copy assigns a @ref const_string. - - Objects of this type are cheap to copy. - - @param other The string to copy - - */ - inline + BOOST_URL_DECL const_string& - operator=(const_string const& other) & noexcept; + operator=( + const_string const& other) noexcept; + + template< + class Allocator, + class InitFn> + const_string( + std::size_t size, + Allocator const& a, + InitFn const& init); + + template + const_string( + string_view s, + Allocator const& a); }; +//------------------------------------------------ + +class const_string::factory +{ + friend class const_string; + + class base; + + base* p_; + + template + class impl; + +public: + BOOST_URL_DECL + ~factory(); + + BOOST_URL_DECL + factory( + factory const& other) noexcept; + + BOOST_URL_DECL + factory& + operator=( + factory const& other) noexcept; + + BOOST_URL_DECL + factory() noexcept; + + template + explicit + factory(std::allocator const&) + : factory() + { + } + + template + explicit + factory(Allocator const& alloc); + + template + const_string + operator()( + std::size_t n, + InitFn const& init) const; + + BOOST_URL_DECL + const_string + operator()(string_view s) const; +}; } // urls } // boost - #include #endif diff --git a/include/boost/url/detail/over_allocator.hpp b/include/boost/url/detail/over_allocator.hpp index f79353ee..3d144600 100644 --- a/include/boost/url/detail/over_allocator.hpp +++ b/include/boost/url/detail/over_allocator.hpp @@ -98,7 +98,7 @@ public: auto constexpr S = sizeof(U); using A = typename allocator_traits< Allocator>::template rebind_alloc; - A a{this->get()}; + A a(this->get()); return reinterpret_cast( std::allocator_traits::allocate(a, (n * sizeof(value_type) + extra_ + S - 1) / S)); diff --git a/include/boost/url/impl/const_string.hpp b/include/boost/url/impl/const_string.hpp index ea4155f9..56f554e6 100644 --- a/include/boost/url/impl/const_string.hpp +++ b/include/boost/url/impl/const_string.hpp @@ -12,239 +12,189 @@ #include #include +#include +#include namespace boost { namespace urls { -struct const_string::base +struct const_string::result { - std::atomic refs{1}; - virtual void destroy() noexcept = 0; + base* p; + char* data; + std::size_t size; }; -template -auto -const_string:: -construct( - std::size_t n, - Allocator const& a, - char*& dest) -> - base* +//------------------------------------------------ + +struct const_string::base { - class impl; + std::atomic< + std::uint32_t> refs{1}; - using allocator_type = - detail::over_allocator< - impl, Allocator>; - - class impl : public base + void + release(std::size_t size) noexcept { - allocator_type a_; + if(--refs > 0) + return; + destroy(size); + } - public: - ~impl() - { - } + virtual + void + destroy(std::size_t size) noexcept = 0; +}; - explicit - impl( - allocator_type const& a) +//------------------------------------------------ + +class const_string::factory::base +{ +public: + std::atomic< + std::uint32_t> refs{1}; + + void + release() noexcept + { + if(--refs > 0) + return; + destroy(); + } + + virtual + void + destroy() noexcept = 0; + + virtual + result + construct(std::size_t size) const = 0; +}; + +//------------------------------------------------ + +template +class const_string::factory::impl + : public base +{ + friend class const_string; + + Allocator a_; + + struct string final : const_string::base + { + Allocator a_; + + string(Allocator const& a) noexcept : a_(a) { } void - destroy() noexcept override + destroy(std::size_t size) noexcept override { - auto a(a_); + detail::over_allocator< + string, Allocator> a(size, a_); + this->~string(); a.deallocate(this, 1); } }; - if(n == 0) +public: + explicit + impl(Allocator const& a) noexcept + : a_(a) { - dest = nullptr; - return nullptr; } - allocator_type al(n, a); - auto p = ::new( - al.allocate(1)) impl(al); - dest = reinterpret_cast< - char*>(p + 1); - static_cast( - *this) = { dest, n }; - return p; -} -const_string:: -~const_string() -{ - if( p_ && - --p_->refs == 0) - p_->destroy(); -} + void + destroy() noexcept override + { + typename detail::allocator_traits< + Allocator>::template rebind_alloc< + impl> a(a_); + this->~impl(); + a.deallocate(this, 1); + } + + result + construct(std::size_t size) const override + { + detail::over_allocator< + string, Allocator> a(size, a_); + auto p = ::new(a.allocate(1)) string(a_); + return result{ p, reinterpret_cast< + char*>(p + 1), size }; + } +}; + +//------------------------------------------------ template const_string:: -const_string( - std::size_t n, - Allocator const& a, - char*& dest) - : p_(construct(n, a, dest)) +factory:: +factory(Allocator const& a) + : p_([&a] + { + using A = typename + detail::allocator_traits< + Allocator>::template rebind_alloc< + factory::impl>; + return ::new(A(a).allocate(1) + ) factory::impl(a); + }()) { } +template +const_string +const_string:: +factory:: +operator()( + std::size_t n, + InitFn const& init) const +{ + auto r = p_->construct(n); + const_string s(r); + init(n, r.data); + return s; +} + +//------------------------------------------------ + template const_string:: const_string( string_view s, Allocator const& a) { - char* dest; - p_ = construct( - s.size(), a, dest); - std::memcpy(dest, - s.data(), s.size()); + auto r = factory::impl< + Allocator>(a).construct( + s.size()); + static_cast( + *this) = { r.data, r.size }; + std::memcpy( + r.data, s.data(), s.size()); + p_ = r.p; } +template< + class Allocator, + class InitFn> const_string:: const_string( - const_string const& other) noexcept - : string_view(other) - , p_(other.p_) + std::size_t size, + Allocator const& a, + InitFn const& init) { - if(p_) - ++p_->refs; -} - -const_string& -const_string:: -operator=( - const_string const& other) & noexcept -{ - if( p_ && - --p_->refs == 0) - p_->destroy(); - p_ = other.p_; - if(p_) - ++p_->refs; + auto r = factory::impl< + Allocator>(a).construct( + size); static_cast( - *this) = other; - return *this; + *this) = { r.data, r.size }; + init(size, r.data); + p_ = r.p; } -//------------------------------------------------ - -class const_string::allocator -{ - struct base - { - std::size_t refs = 1; - - virtual - ~base() - { - } - - virtual - const_string - alloc( - std::size_t n, - char*& dest) = 0; - - virtual - void - destroy() noexcept = 0; - }; - - base* p_ = nullptr; - -public: - ~allocator() - { - if( p_ && - --p_->refs == 0) - p_->destroy(); - } - - allocator() = default; - - allocator( - allocator const& other) noexcept - : p_(other.p_) - { - if(p_) - ++p_->refs; - } - - allocator& - operator=( - allocator const& other) noexcept - { - if(other.p_) - ++other.p_->refs; - if( p_ && - --p_->refs == 0) - p_->destroy(); - p_ = other.p_; - return *this; - } - - template - explicit - allocator(Allocator const& a) - { - class impl; - - using allocator_type = typename - detail::allocator_traits< - Allocator>::template - rebind_alloc; - - class impl : public base - { - allocator_type a_; - - public: - ~impl() - { - } - - impl(allocator_type const& a) - : a_(a) - { - } - - const_string - alloc( - std::size_t n, - char*& dest) override - { - return const_string( - n, a_, dest); - } - - void - destroy() noexcept override - { - auto a(a_); - a.deallocate(this, 1); - } - }; - - allocator_type al(a); - p_ = ::new(al.allocate(1)) impl(al); - } - - const_string - make_const_string( - std::size_t n, - char*& dest) const - { - return p_->alloc(n, dest); - } -}; - } // urls } // boost diff --git a/include/boost/url/impl/const_string.ipp b/include/boost/url/impl/const_string.ipp new file mode 100644 index 00000000..238c6f8c --- /dev/null +++ b/include/boost/url/impl/const_string.ipp @@ -0,0 +1,171 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@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/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_CONST_STRING_IPP +#define BOOST_URL_IMPL_CONST_STRING_IPP + +#include +#include +#include +#include + +namespace boost { +namespace urls { + +const_string:: +factory:: +~factory() +{ + p_->release(); +} + +const_string:: +factory:: +factory( + factory const& other) noexcept + : p_(other.p_) +{ + ++p_->refs; +} + +auto +const_string:: +factory:: +operator=( + factory const& other) noexcept -> + factory& +{ + ++other.p_->refs; + p_->release(); + p_ = other.p_; + return *this; +} + +const_string:: +factory:: +factory() noexcept + : p_([] + { + using A = std::allocator; + + struct default_impl final + : public factory::base + { + void + destroy() noexcept override + { + } + + result + construct(std::size_t size) const override + { + struct string : const_string::base + { + void + destroy(std::size_t size) noexcept override + { + detail::over_allocator< + string, A> a(size, A{}); + this->~string(); + a.deallocate(this, 1); + } + }; + detail::over_allocator< + string, A> a(size, A{}); + auto p = ::new(a.allocate(1)) string; + return result{ p, reinterpret_cast< + char*>(p + 1), size }; + } + }; + static default_impl d{}; + ++d.refs; + return &d; + }()) +{ +} + +const_string +const_string:: +factory:: +operator()(string_view s) const +{ + return (*this)(s.size(), + [&s]( + std::size_t, + char* dest) + { + std::memcpy( + dest, + s.data(), + s.size()); + }); +} + +//------------------------------------------------ + +const_string:: +const_string( + result const& r) noexcept + : string_view( + r.data, r.size) + , p_(r.p) +{ +} + +const_string:: +~const_string() +{ + p_->release(size()); +} + +const_string:: +const_string() noexcept + : string_view() + , p_([] + { + struct empty_string final : base + { + void + destroy(std::size_t) noexcept override + { + } + }; + static empty_string cs{}; + ++cs.refs; + return &cs; + }()) +{ +} + +const_string:: +const_string( + const_string const& other) noexcept + : string_view(other) + , p_(other.p_) +{ + ++p_->refs; +} + +const_string& +const_string:: +operator=( + const_string const& other) noexcept +{ + ++other.p_->refs; + p_->release(size()); + p_ = other.p_; + static_cast( + *this) = other; + return *this; +} + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/params.hpp b/include/boost/url/impl/params.hpp index e6dda15d..418b805f 100644 --- a/include/boost/url/impl/params.hpp +++ b/include/boost/url/impl/params.hpp @@ -22,14 +22,14 @@ class params::iterator { url const* u_ = nullptr; std::size_t i_ = 0; - const_string::allocator a_; + const_string::factory a_; friend class params; iterator( url const& u, std::size_t i, - const_string::allocator a) noexcept + const_string::factory a) noexcept : u_(&u) , i_(i) , a_(a) diff --git a/include/boost/url/impl/params.ipp b/include/boost/url/impl/params.ipp index 523443d9..1f422fe1 100644 --- a/include/boost/url/impl/params.ipp +++ b/include/boost/url/impl/params.ipp @@ -24,26 +24,8 @@ reference( char const* const s, std::size_t const nk, std::size_t const nv, - const_string::allocator a) + const_string::factory const& a) { - if(nv > 0) - { - // value - BOOST_ASSERT(s[nk] == '='); - has_value = true; - string_view ev{ - s + nk + 1, nv - 1 }; - auto n = pct_decode_bytes_unchecked(ev); - char *dest; - value = a.make_const_string( - n, dest); - pct_decode_unchecked( - dest, dest + n, ev); - } - else - { - has_value = false; - } // key BOOST_ASSERT(nk > 0); BOOST_ASSERT( @@ -51,10 +33,31 @@ reference( string_view ek{s + 1, nk - 1}; auto n = pct_decode_bytes_unchecked(ek); - char* dest; - key = a.make_const_string(n, dest); - pct_decode_unchecked( - dest, dest + nk, ek); + key = a(n, [nk, ek] + (std::size_t, char* dest) + { + pct_decode_unchecked( + dest, dest + nk, ek); + }); + if(nv > 0) + { + // value + BOOST_ASSERT(s[nk] == '='); + has_value = true; + string_view ev{ + s + nk + 1, nv - 1 }; + n = pct_decode_bytes_unchecked(ev); + value = a(n, [ev] + (std::size_t n, char* dest) + { + pct_decode_unchecked( + dest, dest + n, ev); + }); + } + else + { + has_value = false; + } } auto @@ -121,11 +124,12 @@ at(string_view key) const -> r.nv - 1 }; auto n = pct_decode_bytes_unchecked(ev); - char *dest; - auto s = a_.make_const_string(n, dest); - pct_decode_unchecked( - dest, dest + n, ev); - return s; + return a_(n, [ev] + (std::size_t n, char* dest) + { + pct_decode_unchecked( + dest, dest + n, ev); + }); } //------------------------------------------------ diff --git a/include/boost/url/impl/params_view.hpp b/include/boost/url/impl/params_view.hpp index fb4ca19b..e782b9a7 100644 --- a/include/boost/url/impl/params_view.hpp +++ b/include/boost/url/impl/params_view.hpp @@ -24,7 +24,7 @@ class params_view::iterator char const* p_ = nullptr; std::size_t nk_ = 0; std::size_t nv_ = 0; - const_string::allocator a_; + const_string::factory a_; bool first_ = true; friend class params_view; @@ -33,13 +33,13 @@ class params_view::iterator iterator( string_view s, - const_string::allocator a) noexcept; + const_string::factory a) noexcept; // end iterator( string_view s, int, - const_string::allocator a) noexcept; + const_string::factory a) noexcept; string_view encoded_key() const noexcept; diff --git a/include/boost/url/impl/params_view.ipp b/include/boost/url/impl/params_view.ipp index b7ccaa9c..e9237a4b 100644 --- a/include/boost/url/impl/params_view.ipp +++ b/include/boost/url/impl/params_view.ipp @@ -44,13 +44,23 @@ value_type( char const* s, std::size_t nk, std::size_t const nv, - const_string::allocator a) + const_string::factory const& a) { if(nk + nv == 0) { has_value = false; return; } + // key + string_view ek{s, nk}; + auto n = + pct_decode_bytes_unchecked(ek); + key = a(n, [nk, ek] + (std::size_t, char* dest) + { + pct_decode_unchecked( + dest, dest + nk, ek); + }); if(nv > 0) { // value @@ -58,26 +68,18 @@ value_type( has_value = true; string_view ev{ s + nk + 1, nv - 1 }; - auto n = - pct_decode_bytes_unchecked(ev); - char *dest; - value = a.make_const_string( - n, dest); - pct_decode_unchecked( - dest, dest + n, ev); + n = pct_decode_bytes_unchecked(ev); + value = a(n, [ev] + (std::size_t n, char* dest) + { + pct_decode_unchecked( + dest, dest + n, ev); + }); } else { has_value = false; } - // key - string_view ek{s, nk}; - auto n = - pct_decode_bytes_unchecked(ek); - char* dest; - key = a.make_const_string(n, dest); - pct_decode_unchecked( - dest, dest + nk, ek); } //------------------------------------------------ @@ -119,7 +121,7 @@ params_view:: iterator:: iterator( string_view s, - const_string::allocator a) noexcept + const_string::factory a) noexcept : end_(s.data() + s.size()) , p_(s.data()) , a_(a) @@ -132,7 +134,7 @@ iterator:: iterator( string_view s, int, - const_string::allocator a) noexcept + const_string::factory a) noexcept : end_(s.data() + s.size()) , p_(nullptr) , a_(a) @@ -208,7 +210,8 @@ operator==( auto params_view:: at(string_view key) const -> - const_string { + const_string +{ auto it = find(key); for(;;) { @@ -225,11 +228,12 @@ at(string_view key) const -> it.nv_ - 1 }; auto n = pct_decode_bytes_unchecked(ev); - char *dest; - auto s = a_.make_const_string(n, dest); - pct_decode_unchecked( - dest, dest + n, ev); - return s; + return a_(n, [ev] + (std::size_t n, char* dest) + { + pct_decode_unchecked( + dest, dest + n, ev); + }); } //------------------------------------------------ diff --git a/include/boost/url/impl/pct_encoding.hpp b/include/boost/url/impl/pct_encoding.hpp index e193c4ea..6644ea7f 100644 --- a/include/boost/url/impl/pct_encoding.hpp +++ b/include/boost/url/impl/pct_encoding.hpp @@ -213,11 +213,13 @@ pct_decode_to_value( if(ec.failed()) detail::throw_invalid_argument( BOOST_CURRENT_LOCATION); - char* dest; - const_string r(n, a, dest); - pct_decode_unchecked( - dest, dest + n, s, opt); - return r; + return const_string(n, a, + [&opt, &s] + (std::size_t n, char* dest) + { + pct_decode_unchecked( + dest, dest + n, s, opt); + }); } //------------------------------------------------ @@ -233,13 +235,14 @@ pct_decode_unchecked( if(decoded_size == std::size_t(-1)) decoded_size = pct_decode_bytes_unchecked(s); - char* dest; - const_string r( - decoded_size, a, dest); - pct_decode_unchecked( - dest, dest + decoded_size, - s, opt); - return r; + return const_string( + decoded_size, a, + [&s, &opt]( + std::size_t n, char* dest) + { + pct_decode_unchecked( + dest, dest + n, s, opt); + }); } //------------------------------------------------ @@ -423,14 +426,15 @@ pct_encode_to_value( return const_string(); auto const n = pct_encode_bytes(s, opt, cs); - char* dest; - const_string r(n, a, dest); - char const* end = dest + n; - auto const n1 = pct_encode( - dest, end, s, opt, cs); - BOOST_ASSERT(n1 == n); - (void)n1; - return r; + return const_string(n, a, + [&s, &opt, &cs]( + std::size_t n, char* dest) + { + auto const n1 = pct_encode( + dest, dest+n, s, opt, cs); + BOOST_ASSERT(n1 == n); + (void)n1; + }); } } // urls diff --git a/include/boost/url/impl/segments.hpp b/include/boost/url/impl/segments.hpp index 8cdb792e..29a165fa 100644 --- a/include/boost/url/impl/segments.hpp +++ b/include/boost/url/impl/segments.hpp @@ -31,14 +31,14 @@ class segments::iterator { url const* u_ = nullptr; std::size_t i_ = 0; - const_string::allocator a_; + const_string::factory a_; friend class segments; iterator( url const& u, std::size_t i, - const_string::allocator const& a) noexcept + const_string::factory const& a) noexcept : u_(&u) , i_(i) , a_(a) diff --git a/include/boost/url/impl/segments.ipp b/include/boost/url/impl/segments.ipp index 9286e839..1c80f30d 100644 --- a/include/boost/url/impl/segments.ipp +++ b/include/boost/url/impl/segments.ipp @@ -39,14 +39,14 @@ operator*() const u_->cs_ + p0, p1 - p0); auto n = pct_decode_bytes_unchecked(s); - char* dest; - auto v = - a_.make_const_string(n, dest); - pct_decode_opts opt; - opt.plus_to_space = false; - pct_decode_unchecked( - dest, dest + n, s, opt); - return v; + return a_(n, [&s] + (std::size_t n, char* dest) + { + pct_decode_opts opt; + opt.plus_to_space = false; + pct_decode_unchecked( + dest, dest + n, s, opt); + }); } //------------------------------------------------ @@ -74,14 +74,14 @@ operator[]( u_->cs_ + p0, p1 - p0); auto n = pct_decode_bytes_unchecked(s); - char* dest; - auto v = - a_.make_const_string(n, dest); - pct_decode_opts opt; - opt.plus_to_space = false; - pct_decode_unchecked( - dest, dest + n, s, opt); - return v; + return a_(n, [&s] + (std::size_t n, char* dest) + { + pct_decode_opts opt; + opt.plus_to_space = false; + pct_decode_unchecked( + dest, dest + n, s, opt); + }); } //------------------------------------------------ diff --git a/include/boost/url/impl/segments_view.hpp b/include/boost/url/impl/segments_view.hpp index 8ee58ec0..b9e1b470 100644 --- a/include/boost/url/impl/segments_view.hpp +++ b/include/boost/url/impl/segments_view.hpp @@ -24,7 +24,7 @@ class segments_view:: char const* pos_ = nullptr; char const* next_ = nullptr; char const* end_ = nullptr; - const_string::allocator a_; + const_string::factory a_; pct_encoded_str t_; friend segments_view; @@ -33,14 +33,14 @@ class segments_view:: iterator( string_view s, std::size_t nseg, - const_string::allocator const& a) noexcept; + const_string::factory const& a) noexcept; // end ctor BOOST_URL_DECL iterator( string_view s, std::size_t nseg, - const_string::allocator const& a, + const_string::factory const& a, int) noexcept; public: diff --git a/include/boost/url/impl/segments_view.ipp b/include/boost/url/impl/segments_view.ipp index 73688da8..e8e6b4f5 100644 --- a/include/boost/url/impl/segments_view.ipp +++ b/include/boost/url/impl/segments_view.ipp @@ -25,7 +25,7 @@ iterator:: iterator( string_view s, std::size_t nseg, - const_string::allocator const& a) noexcept + const_string::factory const& a) noexcept : begin_(s.data()) , pos_(s.data()) , next_(s.data()) @@ -54,7 +54,7 @@ iterator:: iterator( string_view s, std::size_t nseg, - const_string::allocator const& a, + const_string::factory const& a, int) noexcept : i_(nseg) , begin_(s.data()) @@ -88,15 +88,16 @@ segments_view:: iterator:: operator*() const noexcept { - char* dest; - auto s = a_.make_const_string( - t_.decoded_size, dest); - pct_decode_opts opt; - opt.plus_to_space = false; - pct_decode_unchecked( - dest, dest + t_.decoded_size, - t_.str, opt); - return s; + return a_(t_.decoded_size, + [this]( + std::size_t, char* dest) + { + pct_decode_opts opt; + opt.plus_to_space = false; + pct_decode_unchecked( + dest, dest + t_.decoded_size, + t_.str, opt); + }); } auto diff --git a/include/boost/url/impl/static_pool.ipp b/include/boost/url/impl/static_pool.ipp index 59c6a6c2..40be0f3f 100644 --- a/include/boost/url/impl/static_pool.ipp +++ b/include/boost/url/impl/static_pool.ipp @@ -36,8 +36,7 @@ find(void* p) noexcept while(*t != p) { ++t; - BOOST_ASSERT( - t != end); + BOOST_ASSERT(t != end); } return t; } diff --git a/include/boost/url/ipv4_address.hpp b/include/boost/url/ipv4_address.hpp index 1bb755ce..d1ffe339 100644 --- a/include/boost/url/ipv4_address.hpp +++ b/include/boost/url/ipv4_address.hpp @@ -170,10 +170,8 @@ public: { char buf[max_str_len]; auto const n = print_impl(buf); - char* dest; - const_string s(n, a, dest); - std::memcpy(dest, buf, n); - return s; + return const_string( + string_view(buf, n), a); } /** Write a dotted decimal string representing the address to a buffer diff --git a/include/boost/url/ipv6_address.hpp b/include/boost/url/ipv6_address.hpp index e769c16b..03bc966f 100644 --- a/include/boost/url/ipv6_address.hpp +++ b/include/boost/url/ipv6_address.hpp @@ -199,10 +199,8 @@ public: { char buf[max_str_len]; auto const n = print_impl(buf); - char* dest; - const_string s(n, a, dest); - std::memcpy(dest, buf, n); - return s; + return const_string( + string_view(buf, n), a); } /** Write a dotted decimal string representing the address to a buffer diff --git a/include/boost/url/params.hpp b/include/boost/url/params.hpp index 85d0d16b..b0211176 100644 --- a/include/boost/url/params.hpp +++ b/include/boost/url/params.hpp @@ -48,7 +48,7 @@ class params This is the allocator used to create read-only strings when iterators are dereferenced. */ - const_string::allocator a_; + const_string::factory a_; /** Construct query params from a url @@ -110,7 +110,7 @@ public: char const* s, std::size_t nk, std::size_t nv, - const_string::allocator a); + const_string::factory const& a); public: /** The query key. diff --git a/include/boost/url/params_view.hpp b/include/boost/url/params_view.hpp index 138a5156..07567393 100644 --- a/include/boost/url/params_view.hpp +++ b/include/boost/url/params_view.hpp @@ -32,7 +32,7 @@ class params_view string_view s_; std::size_t n_; - const_string::allocator a_; + const_string::factory a_; template params_view( @@ -86,7 +86,7 @@ public: char const* s, std::size_t nk, std::size_t nv, - const_string::allocator a); + const_string::factory const& a); }; using reference = value_type; diff --git a/include/boost/url/segments.hpp b/include/boost/url/segments.hpp index bfa73832..fe60f1b3 100644 --- a/include/boost/url/segments.hpp +++ b/include/boost/url/segments.hpp @@ -67,7 +67,7 @@ class segments : private detail::parts_base { url* u_ = nullptr; - const_string::allocator a_; + const_string::factory a_; friend class url; diff --git a/include/boost/url/segments_view.hpp b/include/boost/url/segments_view.hpp index 6d833eb1..b271d0cc 100644 --- a/include/boost/url/segments_view.hpp +++ b/include/boost/url/segments_view.hpp @@ -32,7 +32,7 @@ class segments_view { string_view s_; std::size_t n_ = 0; - const_string::allocator a_; + const_string::factory a_; friend class url_view; friend class segments_encoded_view; diff --git a/include/boost/url/src.hpp b/include/boost/url/src.hpp index 650fafd2..cd259801 100644 --- a/include/boost/url/src.hpp +++ b/include/boost/url/src.hpp @@ -34,6 +34,7 @@ in a translation unit of the program. #include #include +#include #include #include #include diff --git a/test/unit/const_string.cpp b/test/unit/const_string.cpp index 47021d7b..11147398 100644 --- a/test/unit/const_string.cpp +++ b/test/unit/const_string.cpp @@ -10,33 +10,189 @@ // Test that header file is self-contained. #include +#include + #include "test_suite.hpp" namespace boost { namespace urls { -#ifdef BOOST_URL_HAS_STRING_VIEW -BOOST_STATIC_ASSERT(is_stringlike< - std::string_view>::value); -#endif - -class string_test +struct const_string_test { -public: + using A = std::allocator; + + void + testConstString() + { + // const_string() + // ~const_string() + { + const_string s; + BOOST_TEST(s == ""); + BOOST_TEST(s.empty()); + + // same buffer + BOOST_TEST( + const_string().data() == + const_string().data()); + } + + // const_string(string_view, InitFn) + // ~const_string() + { + const_string s(3, A{}, + [](std::size_t, char* dest) + { + dest[0] = 'a'; + dest[1] = 'b'; + dest[2] = 'c'; + }); + BOOST_TEST(s == "abc"); + BOOST_TEST(! s.empty()); + } + + // const_string(string_view, Allocator) + // ~const_string() + { + char const* const x = "x"; + const_string s(x, A{}); + BOOST_TEST(s == "x"); + BOOST_TEST(! s.empty()); + + // different buffer + BOOST_TEST(s.data() != x); + } + + // const_string(const_string const&) + { + const_string s1("abc", A{}); + const_string s2(s1); + BOOST_TEST(s1 == "abc"); + BOOST_TEST(s2 == "abc"); + BOOST_TEST(s2 == s1); + + // same buffer + BOOST_TEST(s1.data() == s2.data()); + } + + // operator=(const_string const&) + { + const_string s1("abc", A{}); + const_string s2; + BOOST_TEST(s1 == "abc"); + BOOST_TEST(s2.empty()); + s2 = s1; + BOOST_TEST(s1 == "abc"); + BOOST_TEST(s2 == "abc"); + BOOST_TEST(s2 == s1); + + // same buffer + BOOST_TEST(s1.data() == s2.data()); + } + } + + void + testFactory() + { + using factory = const_string::factory; + + // factory() + // ~factory() + { + factory a; + } + + // factory(std::allocator) + // ~factory() + { + factory a(A{}); + } + + // factory(Allocator) + // ~factory() + { + static_pool<100> sp; + factory a(sp.allocator()); + } + + // operator()(size_t, InitFn) + { + { + const_string s; + { + factory a; + s = a(3, + [](std::size_t, char* dest) + { + dest[0] = 'a'; + dest[1] = 'b'; + dest[2] = 'c'; + }); + } + BOOST_TEST(s == "abc"); + BOOST_TEST(! s.empty()); + } + { + static_pool<100> sp; + const_string s; + { + factory a(sp.allocator()); + s = a(3, + [](std::size_t, char* dest) + { + dest[0] = 'a'; + dest[1] = 'b'; + dest[2] = 'c'; + }); + } + BOOST_TEST(s == "abc"); + BOOST_TEST(! s.empty()); + } + } + + // operator()(string_view) + { + const char* const abc = "abc"; + + { + const_string s; + { + factory a; + s = a(abc); + } + BOOST_TEST(s == "abc"); + BOOST_TEST(! s.empty()); + + // different buffer + BOOST_TEST(s.data() != abc); + } + { + static_pool<100> sp; + const_string s; + { + factory a(sp.allocator()); + s = a(abc); + } + BOOST_TEST(s == "abc"); + BOOST_TEST(! s.empty()); + + // different buffer + BOOST_TEST(s.data() != abc); + } + } + } + void run() { - const_string sv("hello"); - auto sv2 = sv; - BOOST_TEST(sv2 == sv); - sv = {}; - BOOST_TEST(sv2 != sv); + testConstString(); + testFactory(); } }; TEST_SUITE( - string_test, - "boost.url.string"); + const_string_test, + "boost.url.const_string"); } // urls } // boost diff --git a/test/unit/params.cpp b/test/unit/params.cpp index cf415641..8ffefea2 100644 --- a/test/unit/params.cpp +++ b/test/unit/params.cpp @@ -21,7 +21,7 @@ class params_test { public: using pool_t = static_pool<4096>; - pool_t pa; + pool_t pa_; void testMembers() @@ -32,7 +32,7 @@ public: { url u = parse_uri_reference( "/?x#f").value(); - u.params(pa.allocator()) = { + u.params(pa_.allocator()) = { { "k1", "1", true }, { "k2", "2", true }, { "k3", "", true }, @@ -45,7 +45,7 @@ public: } { url u = parse_uri_reference("/?x#f").value(); - u.params(pa.allocator()) = {}; + u.params(pa_.allocator()) = {}; BOOST_TEST(u.encoded_query() == ""); BOOST_TEST(u.string() == "/?#f"); } @@ -61,7 +61,7 @@ public: { url u = parse_uri_reference( "?k0=0&k1=1&k2=&k3&k4=4444#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); BOOST_TEST(p.at(0).key == "k0"); BOOST_TEST(p.at(0).value == "0"); BOOST_TEST(p.at(0).has_value); @@ -87,7 +87,7 @@ public: { url u = parse_uri_reference( "?k0=0&k1=1&k2=&k3&k4=4444#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); BOOST_TEST(p.at("k0") == "0"); BOOST_TEST(p.at("k1") == "1"); BOOST_TEST(p.at("k2") == ""); @@ -107,13 +107,13 @@ public: { url u = parse_uri_reference( "?k0=0&k1=1&k2=&k3&k4=4444#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); BOOST_TEST(! p.empty()); BOOST_TEST(p.size() == 5); } { url u; - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); BOOST_TEST(p.empty()); BOOST_TEST(p.size() == 0); } @@ -126,7 +126,7 @@ public: { url u = parse_uri_reference( "/?k0=0&k1=1&k2=&k3&k4=4444#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); p.clear(); BOOST_TEST(u.encoded_query() == ""); BOOST_TEST(u.string() == "/?#f"); @@ -136,7 +136,7 @@ public: { url u = parse_uri_reference( "/?k0=0&k2=#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); auto it = p.insert(p.begin() + 1, {"k1", "1", true}); BOOST_TEST(it == p.begin() + 1); @@ -152,7 +152,7 @@ public: { url u = parse_uri_reference( "/?k0=0&k3#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); auto it = p.insert(p.begin() + 1,{ {"k1", "1", true}, {"k2", "", true}}); @@ -167,7 +167,7 @@ public: { url u = parse_uri_reference( "/?k0=0&k1=1&k3#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); auto it = p.replace( p.end() - 1, {"k2", "", true}); @@ -183,7 +183,7 @@ public: { url u = parse_uri_reference( "/?k0=0&k1=1&k2=&k3&k4=4444#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); auto it = p.replace( p.begin() + 1, p.begin() + 3, { {"a","aa",true}, @@ -200,7 +200,7 @@ public: { url u = parse_uri_reference( "/?k0=0&k%31=1&k2=#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); BOOST_TEST(p.at(1).key == "k1"); auto it = p.remove_value(p.begin() + 1); BOOST_TEST(u.encoded_query() == "k0=0&k%31&k2="); @@ -219,7 +219,7 @@ public: { url u = parse_uri_reference( "/?k0=0&k%31=1&k2=#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); auto it = p.emplace_at( p.begin() + 1, "k1", "1"); @@ -235,7 +235,7 @@ public: { url u = parse_uri_reference( "/?k0=0&k1=1&k2=&k3#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); auto it = p.emplace_at( p.begin() + 2, "hello_world"); @@ -251,7 +251,7 @@ public: { url u = parse_uri_reference( "/?k0=0&k2=&k3#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); auto it = p.emplace_before( p.begin() + 1, "k1", "1"); BOOST_TEST(it == p.begin() + 1); @@ -266,7 +266,7 @@ public: { url u = parse_uri_reference( "/?k0=0&k2=&k3#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); auto it = p.emplace_before( p.begin() + 1, "k1"); BOOST_TEST(it == p.begin() + 1); @@ -281,7 +281,7 @@ public: { url u = parse_uri_reference( "/?k0=0&k1=1&k2=&k3&k4=4444#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); p.erase(p.begin() + 2); BOOST_TEST(u.encoded_query() == "k0=0&k1=1&k3&k4=4444"); @@ -300,7 +300,7 @@ public: { url u = parse_uri_reference( "/?a=1&%62=2&c=3&c=4&c=5&d=6&e=7&d=8&f=9#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); BOOST_TEST(p.erase("c") == 3); BOOST_TEST(u.encoded_query() == "a=1&%62=2&d=6&e=7&d=8&f=9"); @@ -323,7 +323,7 @@ public: // emplace_back(Key) { url u = parse_uri_reference("/#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); p.emplace_back("k0", "0"); BOOST_TEST(u.encoded_query() == "k0=0"); BOOST_TEST(u.string() == "/?k0=0#f"); @@ -358,7 +358,7 @@ public: // pop_back() { url u = parse_uri_reference("/#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); p.push_back({"k0", "0", true}); BOOST_TEST(u.encoded_query() == "k0=0"); @@ -440,7 +440,7 @@ public: { url u = parse_uri_reference( "/?a=1&%62=2&c=3&c=4&c=5&d=6&e=7&d=8&f=9#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); BOOST_TEST(p.count("a") == 1); BOOST_TEST(p.count("b") == 1); BOOST_TEST(p.count("c") == 3); @@ -468,7 +468,7 @@ public: { url u = parse_uri_reference( "/?a=1&bb=22&ccc=333&dddd=4444#f").value(); - params p = u.params(pa.allocator()); + params p = u.params(pa_.allocator()); auto it = p.begin(); BOOST_TEST((*it).key == "a"); BOOST_TEST((*++it).key == "bb"); diff --git a/test/unit/segments_view.cpp b/test/unit/segments_view.cpp index e6ec517f..fb7b78b9 100644 --- a/test/unit/segments_view.cpp +++ b/test/unit/segments_view.cpp @@ -36,7 +36,7 @@ public: segments_view::iterator>); #endif - static_pool<4096> sp; + static_pool<4096> sp_; template static @@ -53,7 +53,7 @@ public: { segments_view sv; BOOST_TEST_THROWS( - sv = f(s).value().decoded(sp.allocator()), + sv = f(s).value().decoded(sp_.allocator()), std::exception); BOOST_TEST(sv.empty()); BOOST_TEST(sv.begin() == sv.end()); @@ -85,7 +85,7 @@ public: { segments_view sv; BOOST_TEST_NO_THROW( - sv = f(s).value().decoded(sp.allocator())); + sv = f(s).value().decoded(sp_.allocator())); // forward { std::vector v1; @@ -125,7 +125,7 @@ public: { segments_view sv = parse_path( "/%70%61%74%68/%74%6f/%66%69%6c%65%2e%74%78%74" - ).value().decoded(sp.allocator()); + ).value().decoded(sp_.allocator()); BOOST_TEST(sv.size() == 3); BOOST_TEST(sv.is_absolute()); } @@ -134,10 +134,10 @@ public: { segments_view sv; sv = parse_path("/path/to/file.txt" - ).value().decoded(sp.allocator()); + ).value().decoded(sp_.allocator()); BOOST_TEST(sv.is_absolute()); sv = parse_path("./my/downloads" - ).value().decoded(sp.allocator()); + ).value().decoded(sp_.allocator()); BOOST_TEST(! sv.is_absolute()); } } @@ -149,7 +149,7 @@ public: // back { segments_view sv = parse_path( - "/path/to/file.txt").value().decoded(sp.allocator()); + "/path/to/file.txt").value().decoded(sp_.allocator()); BOOST_TEST(sv.front() == "path"); BOOST_TEST(sv.back() == "file.txt"); } @@ -164,7 +164,7 @@ public: // iterator() { segments_view sv = parse_path( - "/path/to/file.txt").value().decoded(sp.allocator()); + "/path/to/file.txt").value().decoded(sp_.allocator()); iter_t it1; iter_t it2; BOOST_TEST(it1 == it2); @@ -175,7 +175,7 @@ public: // iterator(iterator const&) { segments_view sv = parse_path( - "/path/to/file.txt").value().decoded(sp.allocator()); + "/path/to/file.txt").value().decoded(sp_.allocator()); iter_t it1 = sv.begin(); iter_t it2(it1); BOOST_TEST(it2 == it1); @@ -188,7 +188,7 @@ public: { segments_view sv = parse_path( "/path/to/file.txt").value().decoded( - sp.allocator()); + sp_.allocator()); iter_t it1; it1 = sv.begin(); iter_t it2; @@ -207,7 +207,7 @@ public: { segments_view sv = parse_path( "/path/to/file.txt").value().decoded( - sp.allocator()); + sp_.allocator()); iter_t it = sv.begin(); BOOST_TEST(*it == "path"); BOOST_TEST(*++it == "to"); @@ -222,7 +222,7 @@ public: { segments_view sv = parse_path( "/path/to/file.txt").value().decoded( - sp.allocator()); + sp_.allocator()); iter_t it = sv.end(); BOOST_TEST(*--it == "file.txt"); BOOST_TEST(*it-- == "file.txt"); @@ -236,7 +236,7 @@ public: { segments_view sv = parse_path( "/path/to/file.txt").value().decoded( - sp.allocator()); + sp_.allocator()); iter_t it = sv.begin(); BOOST_TEST(it == sv.begin()); BOOST_TEST(it != sv.end()); diff --git a/test/unit/static_pool.cpp b/test/unit/static_pool.cpp index 98f3bef8..0aa7c8a5 100644 --- a/test/unit/static_pool.cpp +++ b/test/unit/static_pool.cpp @@ -29,12 +29,7 @@ public: string_view s, Allocator const& a = {}) { - char* dest; - const_string sv( - s.size(), a, dest); - std::memcpy(dest, - s.data(), s.size()); - return sv; + return const_string(s, a); } void