- Add BOOST_CONTAINER_FORCEINLINE to trivial string internal functions

- Fixes #192 ("basic_string::clear() has poor codegen compared to STL implementations")
This commit is contained in:
Ion Gaztañaga
2021-09-13 14:19:38 +02:00
parent bfbab6ed7f
commit bcd41a1c64
2 changed files with 66 additions and 61 deletions

View File

@@ -1344,6 +1344,7 @@ use [*Boost.Container]? There are several reasons for that:
* [@https://github.com/boostorg/container/issues/186 GitHub #186: ['"Warnings out the wazoo"]]. * [@https://github.com/boostorg/container/issues/186 GitHub #186: ['"Warnings out the wazoo"]].
* [@https://github.com/boostorg/container/issues/187 GitHub #187: ['"flat_map::erase and unique keys"]]. * [@https://github.com/boostorg/container/issues/187 GitHub #187: ['"flat_map::erase and unique keys"]].
* [@https://github.com/boostorg/container/issues/188 GitHub #188: ['"Build fails when RTTI is disabled"]]. * [@https://github.com/boostorg/container/issues/188 GitHub #188: ['"Build fails when RTTI is disabled"]].
* [@https://github.com/boostorg/container/issues/192 GitHub #192: ['"basic_string::clear() has poor codegen compared to STL implementations"]].
[endsect] [endsect]

View File

@@ -95,31 +95,31 @@ class basic_string_base
typedef typename allocator_traits_type::size_type size_type; typedef typename allocator_traits_type::size_type size_type;
typedef ::boost::intrusive::pointer_traits<pointer> pointer_traits; typedef ::boost::intrusive::pointer_traits<pointer> pointer_traits;
basic_string_base() BOOST_CONTAINER_FORCEINLINE basic_string_base()
: members_() : members_()
{} {}
explicit basic_string_base(const allocator_type& a) BOOST_CONTAINER_FORCEINLINE explicit basic_string_base(const allocator_type& a)
: members_(a) : members_(a)
{} {}
explicit basic_string_base(BOOST_RV_REF(allocator_type) a) BOOST_CONTAINER_FORCEINLINE explicit basic_string_base(BOOST_RV_REF(allocator_type) a)
: members_(boost::move(a)) : members_(boost::move(a))
{} {}
basic_string_base(const allocator_type& a, size_type n) BOOST_CONTAINER_FORCEINLINE basic_string_base(const allocator_type& a, size_type n)
: members_(a) : members_(a)
{ {
this->allocate_initial_block(n); this->allocate_initial_block(n);
} }
explicit basic_string_base(size_type n) BOOST_CONTAINER_FORCEINLINE explicit basic_string_base(size_type n)
: members_() : members_()
{ {
this->allocate_initial_block(n); this->allocate_initial_block(n);
} }
~basic_string_base() BOOST_CONTAINER_FORCEINLINE ~basic_string_base()
{ {
if(!this->is_short()){ if(!this->is_short()){
this->deallocate(this->priv_long_addr(), this->priv_long_storage()); this->deallocate(this->priv_long_addr(), this->priv_long_storage());
@@ -136,15 +136,15 @@ class basic_string_base
size_type storage; size_type storage;
pointer start; pointer start;
long_t() BOOST_CONTAINER_FORCEINLINE long_t()
: is_short(0) : is_short(0)
{} {}
long_t(size_type len, size_type stor, pointer ptr) BOOST_CONTAINER_FORCEINLINE long_t(size_type len, size_type stor, pointer ptr)
: is_short(0), length(len), storage(stor), start(ptr) : is_short(0), length(len), storage(stor), start(ptr)
{} {}
long_t(const long_t &other) BOOST_CONTAINER_FORCEINLINE long_t(const long_t &other)
{ {
this->is_short = false; this->is_short = false;
length = other.length; length = other.length;
@@ -152,7 +152,7 @@ class basic_string_base
start = other.start; start = other.start;
} }
long_t &operator= (const long_t &other) BOOST_CONTAINER_FORCEINLINE long_t &operator= (const long_t &other)
{ {
length = other.length; length = other.length;
storage = other.storage; storage = other.storage;
@@ -208,41 +208,41 @@ class basic_string_base
struct members_holder struct members_holder
: public allocator_type : public allocator_type
{ {
void init() BOOST_CONTAINER_FORCEINLINE void init()
{ {
short_t &s = *::new(this->m_repr.data) short_t; short_t &s = *::new(this->m_repr.data) short_t;
s.h.is_short = 1; s.h.is_short = 1;
s.h.length = 0; s.h.length = 0;
} }
members_holder() BOOST_CONTAINER_FORCEINLINE members_holder()
: allocator_type() : allocator_type()
{ this->init(); } { this->init(); }
template<class AllocatorConvertible> template<class AllocatorConvertible>
explicit members_holder(BOOST_FWD_REF(AllocatorConvertible) a) BOOST_CONTAINER_FORCEINLINE explicit members_holder(BOOST_FWD_REF(AllocatorConvertible) a)
: allocator_type(boost::forward<AllocatorConvertible>(a)) : allocator_type(boost::forward<AllocatorConvertible>(a))
{ this->init(); } { this->init(); }
const short_t *pshort_repr() const BOOST_CONTAINER_FORCEINLINE const short_t *pshort_repr() const
{ return reinterpret_cast<const short_t*>(m_repr.data); } { return reinterpret_cast<const short_t*>(m_repr.data); }
const long_t *plong_repr() const BOOST_CONTAINER_FORCEINLINE const long_t *plong_repr() const
{ return reinterpret_cast<const long_t*>(m_repr.data); } { return reinterpret_cast<const long_t*>(m_repr.data); }
short_t *pshort_repr() BOOST_CONTAINER_FORCEINLINE short_t *pshort_repr()
{ return reinterpret_cast<short_t*>(m_repr.data); } { return reinterpret_cast<short_t*>(m_repr.data); }
long_t *plong_repr() BOOST_CONTAINER_FORCEINLINE long_t *plong_repr()
{ return reinterpret_cast<long_t*>(m_repr.data); } { return reinterpret_cast<long_t*>(m_repr.data); }
repr_t m_repr; repr_t m_repr;
} members_; } members_;
const allocator_type &alloc() const BOOST_CONTAINER_FORCEINLINE const allocator_type &alloc() const
{ return members_; } { return members_; }
allocator_type &alloc() BOOST_CONTAINER_FORCEINLINE allocator_type &alloc()
{ return members_; } { return members_; }
static const size_type InternalBufferChars = (sizeof(repr_t) - ShortDataOffset)/sizeof(value_type); static const size_type InternalBufferChars = (sizeof(repr_t) - ShortDataOffset)/sizeof(value_type);
@@ -252,7 +252,7 @@ class basic_string_base
static const size_type MinAllocation = InternalBufferChars*2; static const size_type MinAllocation = InternalBufferChars*2;
protected: protected:
bool is_short() const BOOST_CONTAINER_FORCEINLINE bool is_short() const
{ {
//Access and copy (to avoid UB) the first byte of the union to know if the //Access and copy (to avoid UB) the first byte of the union to know if the
//active representation is short or long //active representation is short or long
@@ -262,14 +262,14 @@ class basic_string_base
return hdr.is_short != 0; return hdr.is_short != 0;
} }
short_t *construct_short() BOOST_CONTAINER_FORCEINLINE short_t *construct_short()
{ {
short_t *ps = ::new(this->members_.m_repr.data) short_t; short_t *ps = ::new(this->members_.m_repr.data) short_t;
ps->h.is_short = 1; ps->h.is_short = 1;
return ps; return ps;
} }
void destroy_short() BOOST_CONTAINER_FORCEINLINE void destroy_short()
{ {
BOOST_ASSERT(this->is_short()); BOOST_ASSERT(this->is_short());
this->members_.pshort_repr()->~short_t(); this->members_.pshort_repr()->~short_t();
@@ -284,14 +284,14 @@ class basic_string_base
return this->members_.pshort_repr(); return this->members_.pshort_repr();
} }
long_t *construct_long() BOOST_CONTAINER_FORCEINLINE long_t *construct_long()
{ {
long_t *pl = ::new(this->members_.m_repr.data) long_t; long_t *pl = ::new(this->members_.m_repr.data) long_t;
//is_short flag is written in the constructor //is_short flag is written in the constructor
return pl; return pl;
} }
void destroy_long() BOOST_CONTAINER_FORCEINLINE void destroy_long()
{ {
BOOST_ASSERT(!this->is_short()); BOOST_ASSERT(!this->is_short());
this->members_.plong_repr()->~long_t(); this->members_.plong_repr()->~long_t();
@@ -337,7 +337,7 @@ class basic_string_base
this->alloc().deallocate(p, n); this->alloc().deallocate(p, n);
} }
void construct(pointer p, const value_type &value = value_type()) BOOST_CONTAINER_FORCEINLINE void construct(pointer p, const value_type &value = value_type())
{ {
allocator_traits_type::construct allocator_traits_type::construct
( this->alloc() ( this->alloc()
@@ -354,7 +354,7 @@ class basic_string_base
} }
} }
void destroy(pointer p) BOOST_CONTAINER_FORCEINLINE void destroy(pointer p)
{ {
allocator_traits_type::destroy allocator_traits_type::destroy
( this->alloc() ( this->alloc()
@@ -381,17 +381,17 @@ class basic_string_base
} }
} }
void deallocate_block() BOOST_CONTAINER_FORCEINLINE void deallocate_block()
{ this->deallocate(this->priv_addr(), this->priv_storage()); } { this->deallocate(this->priv_addr(), this->priv_storage()); }
size_type max_size() const BOOST_CONTAINER_FORCEINLINE size_type max_size() const
{ return allocator_traits_type::max_size(this->alloc()) - 1; } { return allocator_traits_type::max_size(this->alloc()) - 1; }
protected: protected:
size_type priv_capacity() const BOOST_CONTAINER_FORCEINLINE size_type priv_capacity() const
{ return this->priv_storage() - 1; } { return this->priv_storage() - 1; }
pointer priv_short_addr() const BOOST_CONTAINER_FORCEINLINE pointer priv_short_addr() const
{ return pointer_traits::pointer_to(const_cast<value_type&>(this->members_.pshort_repr()->data[0])); } { return pointer_traits::pointer_to(const_cast<value_type&>(this->members_.pshort_repr()->data[0])); }
//GCC seems a bit confused about uninitialized accesses //GCC seems a bit confused about uninitialized accesses
@@ -400,10 +400,10 @@ class basic_string_base
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif #endif
pointer priv_long_addr() const BOOST_CONTAINER_FORCEINLINE pointer priv_long_addr() const
{ return this->members_.plong_repr()->start; } { return this->members_.plong_repr()->start; }
pointer priv_addr() const BOOST_CONTAINER_FORCEINLINE pointer priv_addr() const
{ {
return this->is_short() return this->is_short()
? priv_short_addr() ? priv_short_addr()
@@ -411,7 +411,7 @@ class basic_string_base
; ;
} }
pointer priv_end_addr() const BOOST_CONTAINER_FORCEINLINE pointer priv_end_addr() const
{ {
return this->is_short() return this->is_short()
? this->priv_short_addr() + this->priv_short_size() ? this->priv_short_addr() + this->priv_short_size()
@@ -419,39 +419,39 @@ class basic_string_base
; ;
} }
void priv_long_addr(pointer addr) BOOST_CONTAINER_FORCEINLINE void priv_long_addr(pointer addr)
{ this->members_.plong_repr()->start = addr; } { this->members_.plong_repr()->start = addr; }
size_type priv_storage() const BOOST_CONTAINER_FORCEINLINE size_type priv_storage() const
{ return this->is_short() ? priv_short_storage() : priv_long_storage(); } { return this->is_short() ? priv_short_storage() : priv_long_storage(); }
size_type priv_short_storage() const BOOST_CONTAINER_FORCEINLINE size_type priv_short_storage() const
{ return InternalBufferChars; } { return InternalBufferChars; }
size_type priv_long_storage() const BOOST_CONTAINER_FORCEINLINE size_type priv_long_storage() const
{ return this->members_.plong_repr()->storage; } { return this->members_.plong_repr()->storage; }
void priv_storage(size_type storage) BOOST_CONTAINER_FORCEINLINE void priv_storage(size_type storage)
{ {
if(!this->is_short()) if(!this->is_short())
this->priv_long_storage(storage); this->priv_long_storage(storage);
} }
void priv_long_storage(size_type storage) BOOST_CONTAINER_FORCEINLINE void priv_long_storage(size_type storage)
{ {
this->members_.plong_repr()->storage = storage; this->members_.plong_repr()->storage = storage;
} }
size_type priv_size() const BOOST_CONTAINER_FORCEINLINE size_type priv_size() const
{ return this->is_short() ? this->priv_short_size() : this->priv_long_size(); } { return this->is_short() ? this->priv_short_size() : this->priv_long_size(); }
size_type priv_short_size() const BOOST_CONTAINER_FORCEINLINE size_type priv_short_size() const
{ return this->members_.pshort_repr()->h.length; } { return this->members_.pshort_repr()->h.length; }
size_type priv_long_size() const BOOST_CONTAINER_FORCEINLINE size_type priv_long_size() const
{ return this->members_.plong_repr()->length; } { return this->members_.plong_repr()->length; }
void priv_size(size_type sz) BOOST_CONTAINER_FORCEINLINE void priv_size(size_type sz)
{ {
if(this->is_short()) if(this->is_short())
this->priv_short_size(sz); this->priv_short_size(sz);
@@ -459,10 +459,10 @@ class basic_string_base
this->priv_long_size(sz); this->priv_long_size(sz);
} }
void priv_short_size(size_type sz) BOOST_CONTAINER_FORCEINLINE void priv_short_size(size_type sz)
{ this->members_.pshort_repr()->h.length = (unsigned char)sz; } { this->members_.pshort_repr()->h.length = (unsigned char)sz; }
void priv_long_size(size_type sz) BOOST_CONTAINER_FORCEINLINE void priv_long_size(size_type sz)
{ this->members_.plong_repr()->length = sz; } { this->members_.plong_repr()->length = sz; }
#if defined(BOOST_GCC) && (BOOST_GCC >= 40700) #if defined(BOOST_GCC) && (BOOST_GCC >= 40700)
@@ -1869,7 +1869,7 @@ class basic_string
//! //!
//! <b>Returns</b>: An iterator which refers to the copy of the first inserted //! <b>Returns</b>: An iterator which refers to the copy of the first inserted
//! character, or p if i1 is empty. //! character, or p if i1 is empty.
iterator insert(const_iterator p, std::initializer_list<CharT> il) BOOST_CONTAINER_FORCEINLINE iterator insert(const_iterator p, std::initializer_list<CharT> il)
{ {
return this->insert(p, il.begin(), il.end()); return this->insert(p, il.begin(), il.end());
} }
@@ -1955,10 +1955,14 @@ class basic_string
//! <b>Complexity</b>: Linear to the number of elements in the vector. //! <b>Complexity</b>: Linear to the number of elements in the vector.
void clear() BOOST_NOEXCEPT_OR_NOTHROW void clear() BOOST_NOEXCEPT_OR_NOTHROW
{ {
if (!this->empty()) { if(this->is_short()) {
Traits::assign(*this->priv_addr(), CharT(0)); Traits::assign(*this->priv_short_addr(), CharT(0));
this->priv_size(0); this->priv_short_size(0);
} }
else {
Traits::assign(*this->priv_long_addr(), CharT(0));
this->priv_long_size(0);
}
} }
//! <b>Requires</b>: pos1 <= size(). //! <b>Requires</b>: pos1 <= size().
@@ -1984,7 +1988,7 @@ class basic_string
//! <b>Effects</b>: Calls `return replace(pos1, n1, sv.data(), sv.size());`. //! <b>Effects</b>: Calls `return replace(pos1, n1, sv.data(), sv.size());`.
//! //!
template<template<class, class> class BasicStringView> template<template<class, class> class BasicStringView>
basic_string& replace(size_type pos1, size_type n1, BasicStringView<CharT, Traits> sv) BOOST_CONTAINER_FORCEINLINE basic_string& replace(size_type pos1, size_type n1, BasicStringView<CharT, Traits> sv)
{ {
return this->replace(pos1, n1, sv.data(), sv.size()); return this->replace(pos1, n1, sv.data(), sv.size());
} }
@@ -2061,7 +2065,7 @@ class basic_string
//! if the length of the resulting string would exceed max_size() //! if the length of the resulting string would exceed max_size()
//! //!
//! <b>Returns</b>: *this //! <b>Returns</b>: *this
basic_string& replace(size_type pos, size_type n1, const CharT* s) BOOST_CONTAINER_FORCEINLINE basic_string& replace(size_type pos, size_type n1, const CharT* s)
{ {
return this->replace(pos, n1, s, Traits::length(s)); return this->replace(pos, n1, s, Traits::length(s));
} }
@@ -2092,7 +2096,7 @@ class basic_string
//! <b>Throws</b>: if memory allocation throws //! <b>Throws</b>: if memory allocation throws
//! //!
//! <b>Returns</b>: *this //! <b>Returns</b>: *this
basic_string& replace(const_iterator i1, const_iterator i2, const basic_string& str) BOOST_CONTAINER_FORCEINLINE basic_string& replace(const_iterator i1, const_iterator i2, const basic_string& str)
{ return this->replace(i1, i2, str.data(), str.data()+str.size()); } { return this->replace(i1, i2, str.data(), str.data()+str.size()); }
//! <b>Requires</b>: [begin(),i1) and [i1,i2) are valid ranges and //! <b>Requires</b>: [begin(),i1) and [i1,i2) are valid ranges and
@@ -2103,7 +2107,7 @@ class basic_string
//! <b>Throws</b>: if memory allocation throws //! <b>Throws</b>: if memory allocation throws
//! //!
//! <b>Returns</b>: *this //! <b>Returns</b>: *this
basic_string& replace(const_iterator i1, const_iterator i2, const CharT* s, size_type n) BOOST_CONTAINER_FORCEINLINE basic_string& replace(const_iterator i1, const_iterator i2, const CharT* s, size_type n)
{ return this->replace(i1, i2, s, s + n); } { return this->replace(i1, i2, s, s + n); }
//! <b>Requires</b>: [begin(),i1) and [i1,i2) are valid ranges and s points to an //! <b>Requires</b>: [begin(),i1) and [i1,i2) are valid ranges and s points to an
@@ -2114,7 +2118,7 @@ class basic_string
//! <b>Throws</b>: if memory allocation throws //! <b>Throws</b>: if memory allocation throws
//! //!
//! <b>Returns</b>: *this //! <b>Returns</b>: *this
basic_string& replace(const_iterator i1, const_iterator i2, const CharT* s) BOOST_CONTAINER_FORCEINLINE basic_string& replace(const_iterator i1, const_iterator i2, const CharT* s)
{ return this->replace(i1, i2, s, s + Traits::length(s)); } { return this->replace(i1, i2, s, s + Traits::length(s)); }
//! <b>Requires</b>: [begin(),i1) and [i1,i2) are valid ranges. //! <b>Requires</b>: [begin(),i1) and [i1,i2) are valid ranges.
@@ -2199,7 +2203,7 @@ class basic_string
//! //!
//! <b>Returns</b>: *this. //! <b>Returns</b>: *this.
template<template <class, class> class BasicStringView> template<template <class, class> class BasicStringView>
basic_string& replace(const_iterator i1, const_iterator i2, BasicStringView<CharT, Traits> sv) BOOST_CONTAINER_FORCEINLINE basic_string& replace(const_iterator i1, const_iterator i2, BasicStringView<CharT, Traits> sv)
{ {
return this->replace( static_cast<size_type>(i1 - this->cbegin()) return this->replace( static_cast<size_type>(i1 - this->cbegin())
, static_cast<size_type>(i2 - i1), sv); , static_cast<size_type>(i2 - i1), sv);
@@ -2211,7 +2215,7 @@ class basic_string
//! <b>Effects</b>: Calls replace(i1 - begin(), i2 - i1, il.begin(), il.size()). //! <b>Effects</b>: Calls replace(i1 - begin(), i2 - i1, il.begin(), il.size()).
//! //!
//! <b>Returns</b>: *this. //! <b>Returns</b>: *this.
basic_string& replace(const_iterator i1, const_iterator i2, std::initializer_list<CharT> il) BOOST_CONTAINER_FORCEINLINE basic_string& replace(const_iterator i1, const_iterator i2, std::initializer_list<CharT> il)
{ {
return this->replace( static_cast<size_type>(i1 - this->cbegin()) return this->replace( static_cast<size_type>(i1 - this->cbegin())
, static_cast<size_type>(i2 - i1) , static_cast<size_type>(i2 - i1)
@@ -2288,7 +2292,7 @@ class basic_string
//! //!
//! <b>Complexity</b>: constant time. //! <b>Complexity</b>: constant time.
template<template <class, class> class BasicStringView> template<template <class, class> class BasicStringView>
operator BasicStringView<CharT, Traits>() const BOOST_NOEXCEPT_OR_NOTHROW BOOST_CONTAINER_FORCEINLINE operator BasicStringView<CharT, Traits>() const BOOST_NOEXCEPT_OR_NOTHROW
{ return this->to_view< BasicStringView<CharT, Traits> >(); } { return this->to_view< BasicStringView<CharT, Traits> >(); }
#endif #endif
@@ -3026,17 +3030,17 @@ class basic_string
Traits::assign(*result, *first); Traits::assign(*result, *first);
} }
void priv_copy(const CharT* first, const CharT* last, CharT* result) BOOST_CONTAINER_FORCEINLINE void priv_copy(const CharT* first, const CharT* last, CharT* result)
{ Traits::copy(result, first, last - first); } { Traits::copy(result, first, last - first); }
template <class Integer> template <class Integer>
basic_string& priv_replace_dispatch(const_iterator first, const_iterator last, BOOST_CONTAINER_FORCEINLINE basic_string& priv_replace_dispatch(const_iterator first, const_iterator last,
Integer n, Integer x, Integer n, Integer x,
dtl::true_) dtl::true_)
{ return this->replace(first, last, (size_type) n, (CharT) x); } { return this->replace(first, last, (size_type) n, (CharT) x); }
template <class InputIter> template <class InputIter>
basic_string& priv_replace_dispatch(const_iterator first, const_iterator last, BOOST_CONTAINER_FORCEINLINE basic_string& priv_replace_dispatch(const_iterator first, const_iterator last,
InputIter f, InputIter l, InputIter f, InputIter l,
dtl::false_) dtl::false_)
{ {