////////////////////////////////////////////////////////////////////////////// // // (C) Copyright Ion Gaztanaga 2005-2015. 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) // // See http://www.boost.org/libs/container for documentation. // ////////////////////////////////////////////////////////////////////////////// #ifndef BOOST_CONTAINER_STRING_HPP #define BOOST_CONTAINER_STRING_HPP #ifndef BOOST_CONFIG_HPP # include #endif #if defined(BOOST_HAS_PRAGMA_ONCE) # pragma once #endif #include #include #include // container #include #include //new_allocator #include // container/detail #include #include #include #include #include #include #include #include #include #include #include #include #include // for char_traits //intrusive #include #include #include //move #include #include #include #include #include // #include #include #include #include #include //std #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) #include //for std::initializer_list #endif //GCC 12 has a regression for array-bounds warnings #if defined(BOOST_GCC) && (BOOST_GCC >= 120000) && (BOOST_GCC < 130000) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Warray-bounds" #endif namespace boost { namespace container { #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED namespace dtl { // ------------------------------------------------------------ // Class basic_string_base. // basic_string_base is a helper class that makes it it easier to write // an exception-safe version of basic_string. The constructor allocates, // but does not initialize, a block of memory. The destructor // deallocates, but does not destroy elements within, a block of // memory. The destructor assumes that the memory either is the internal buffer, // or else points to a block of memory that was allocated using string_base's // allocator and whose size is this->m_storage. template class basic_string_base { basic_string_base & operator=(const basic_string_base &); basic_string_base(const basic_string_base &); typedef Allocator allocator_type; public: typedef allocator_traits allocator_traits_type; typedef allocator_type stored_allocator_type; typedef typename allocator_traits_type::pointer pointer; typedef typename allocator_traits_type::value_type value_type; typedef typename allocator_traits_type::size_type size_type; typedef typename allocator_traits_type::difference_type difference_type; typedef ::boost::intrusive::pointer_traits pointer_traits; inline basic_string_base() : members_() {} inline explicit basic_string_base(const allocator_type& a) : members_(a) {} inline explicit basic_string_base(BOOST_RV_REF(allocator_type) a) : members_(boost::move(a)) {} inline basic_string_base(const allocator_type& a, size_type n) : members_(a) { this->allocate_initial_block(n); } inline explicit basic_string_base(size_type n) : members_() { this->allocate_initial_block(n); } inline ~basic_string_base() { if(!this->is_short()){ this->deallocate(this->priv_long_addr(), this->priv_long_storage()); } } private: #if defined(BOOST_GCC) && (BOOST_GCC >= 40600) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif //This is the structure controlling a long string struct long_t { size_type is_short : 1; size_type length : (sizeof(size_type)*CHAR_BIT - 1); size_type storage; pointer start; inline long_t() : is_short(0) {} inline long_t(size_type len, size_type stor, pointer ptr) : is_short(0), length(len), storage(stor), start(ptr) {} inline long_t(const long_t &other) { this->is_short = false; length = other.length; storage = other.storage; start = other.start; } inline long_t &operator= (const long_t &other) { length = other.length; storage = other.storage; start = other.start; return *this; } }; #if defined(BOOST_GCC) && (BOOST_GCC >= 40600) #pragma GCC diagnostic pop #endif //This type is the first part of the structure controlling a short string //The "data" member stores struct short_header { unsigned char is_short : 1; unsigned char length : (CHAR_BIT - 1); }; //This type has the same alignment and size as long_t but it's POD //so, unlike long_t, it can be placed in a union typedef typename dtl::aligned_storage ::value>::type long_raw_t; protected: static const size_type MinInternalBufferChars = 8; static const size_type AlignmentOfValueType = alignment_of::value; static const size_type ShortDataOffset = ((sizeof(short_header)-1)/AlignmentOfValueType+1)*AlignmentOfValueType; static const size_type ZeroCostInternalBufferChars = (sizeof(long_t) - ShortDataOffset)/sizeof(value_type); static const size_type UnalignedFinalInternalBufferChars = (ZeroCostInternalBufferChars > MinInternalBufferChars) ? ZeroCostInternalBufferChars : MinInternalBufferChars; struct short_t { short_header h; value_type data[UnalignedFinalInternalBufferChars]; }; union repr_t_size_t { long_raw_t r; short_t s; }; union repr_t { long_raw_t r_aligner; short_t s_aligner; unsigned char data[sizeof(repr_t_size_t)]; }; struct members_holder : public allocator_type { inline void init() { short_t &s = *::new(this->m_repr.data) short_t; s.h.is_short = 1; s.h.length = 0; } inline members_holder() : allocator_type() { this->init(); } template inline explicit members_holder(BOOST_FWD_REF(AllocatorConvertible) a) : allocator_type(boost::forward(a)) { this->init(); } inline const short_t *pshort_repr() const { return move_detail::force_ptr(m_repr.data); } inline const long_t *plong_repr() const { return move_detail::force_ptr(m_repr.data); } inline short_t *pshort_repr() { return move_detail::force_ptr(m_repr.data); } inline long_t *plong_repr() { return move_detail::force_ptr(m_repr.data); } repr_t m_repr; } members_; inline const allocator_type &alloc() const { return members_; } inline allocator_type &alloc() { return members_; } static const size_type InternalBufferChars = (sizeof(repr_t) - ShortDataOffset)/sizeof(value_type); private: static const size_type MinAllocation = InternalBufferChars*2; protected: inline bool is_short() const { //Access and copy (to avoid UB) the first byte of the union to know if the //active representation is short or long short_header hdr; BOOST_CONTAINER_STATIC_ASSERT((sizeof(short_header) == 1)); *(unsigned char*)&hdr = *(unsigned char*)&this->members_.m_repr; return hdr.is_short != 0; } inline short_t *construct_short() { short_t *ps = ::new(this->members_.m_repr.data) short_t; ps->h.is_short = 1; return ps; } inline void destroy_short() { BOOST_ASSERT(this->is_short()); this->members_.pshort_repr()->~short_t(); } short_t *assure_short() { if (!this->is_short()){ this->destroy_long(); return construct_short(); } return this->members_.pshort_repr(); } inline long_t *construct_long() { long_t *pl = ::new(this->members_.m_repr.data) long_t; //is_short flag is written in the constructor return pl; } inline void destroy_long() { BOOST_ASSERT(!this->is_short()); this->members_.plong_repr()->~long_t(); } long_t *assure_long() { if (this->is_short()){ this->destroy_short(); return this->construct_long(); } return this->members_.plong_repr(); } protected: typedef dtl::integral_constant::value> alloc_version; pointer allocation_command(allocation_type command, size_type limit_size, size_type &prefer_in_recvd_out_size, pointer &reuse) { if(this->is_short() && (command & (expand_fwd | expand_bwd)) ){ reuse = 0; command &= ~(expand_fwd | expand_bwd); } return dtl::allocator_version_traits::allocation_command (this->alloc(), command, limit_size, prefer_in_recvd_out_size, reuse); } size_type next_capacity(size_type additional_objects) const { return growth_factor_100() ( this->priv_storage(), additional_objects, allocator_traits_type::max_size(this->alloc())); } void deallocate(pointer p, size_type n) { if (p && (n > InternalBufferChars)) this->alloc().deallocate(p, n); } void destroy(pointer p, size_type n) { value_type *raw_p = boost::movelib::to_raw_pointer(p); for(; n--; ++raw_p){ allocator_traits_type::destroy( this->alloc(), raw_p); } } inline void destroy(pointer p) { allocator_traits_type::destroy ( this->alloc() , boost::movelib::to_raw_pointer(p) ); } void allocate_initial_block(size_type n) { if (n <= this->max_size()) { if(n > InternalBufferChars){ size_type new_cap = this->next_capacity(n); pointer reuse = 0; pointer p = this->allocation_command(allocate_new, n, new_cap, reuse); BOOST_ASSERT(this->is_short()); this->construct_long(); this->priv_long_addr(p); this->priv_long_size(0); this->priv_storage(new_cap); } } else{ throw_length_error("basic_string::allocate_initial_block max_size() exceeded"); } } inline void deallocate_block() { this->deallocate(this->priv_addr(), this->priv_storage()); } inline size_type max_size() const { return allocator_traits_type::max_size(this->alloc()) - 1; } protected: inline size_type priv_capacity() const { return this->priv_storage() - 1; } inline pointer priv_short_addr() const { return pointer_traits::pointer_to(const_cast(this->members_.pshort_repr()->data[0])); } //GCC seems a bit confused about uninitialized accesses #if defined(BOOST_GCC) && (BOOST_GCC >= 40700) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif inline pointer priv_long_addr() const { return this->members_.plong_repr()->start; } inline pointer priv_addr() const { return this->is_short() ? priv_short_addr() : priv_long_addr() ; } inline pointer priv_end_addr() const { return this->is_short() ? this->priv_short_addr() + difference_type(this->priv_short_size()) : this->priv_long_addr() + difference_type(this->priv_long_size()) ; } inline void priv_long_addr(pointer addr) { this->members_.plong_repr()->start = addr; } inline size_type priv_storage() const { return this->is_short() ? priv_short_storage() : priv_long_storage(); } inline size_type priv_short_storage() const { return InternalBufferChars; } inline size_type priv_long_storage() const { return this->members_.plong_repr()->storage; } inline void priv_storage(size_type storage) { if(!this->is_short()) this->priv_long_storage(storage); } inline void priv_long_storage(size_type storage) { this->members_.plong_repr()->storage = storage; } inline size_type priv_size() const { return this->is_short() ? this->priv_short_size() : this->priv_long_size(); } inline size_type priv_short_size() const { return this->members_.pshort_repr()->h.length; } inline size_type priv_long_size() const { return this->members_.plong_repr()->length; } inline void priv_size(size_type sz) { if(this->is_short()) this->priv_short_size(sz); else this->priv_long_size(sz); } inline void priv_short_size(size_type sz) { typedef unsigned char uchar_type; static const uchar_type mask = uchar_type(uchar_type(-1) >> 1U); BOOST_ASSERT( sz <= mask ); //Make -Wconversion happy this->members_.pshort_repr()->h.length = uchar_type(uchar_type(sz) & mask); } inline void priv_long_size(size_type sz) { static const size_type mask = size_type(-1) >> 1U; BOOST_ASSERT( sz <= mask ); //Make -Wconversion happy this->members_.plong_repr()->length = sz & mask; } #if defined(BOOST_GCC) && (BOOST_GCC >= 40700) #pragma GCC diagnostic pop #endif void swap_data(basic_string_base& other) { if(this->is_short()){ if(other.is_short()){ repr_t tmp(this->members_.m_repr); this->members_.m_repr = other.members_.m_repr; other.members_.m_repr = tmp; } else{ short_t short_backup(*this->members_.pshort_repr()); this->members_.pshort_repr()->~short_t(); ::new(this->members_.plong_repr()) long_t(*other.members_.plong_repr()); other.members_.plong_repr()->~long_t(); ::new(other.members_.pshort_repr()) short_t(short_backup); } } else{ if(other.is_short()){ short_t short_backup(*other.members_.pshort_repr()); other.members_.pshort_repr()->~short_t(); ::new(other.members_.plong_repr()) long_t(*this->members_.plong_repr()); this->members_.plong_repr()->~long_t(); ::new(this->members_.pshort_repr()) short_t(short_backup); } else{ boost::adl_move_swap(*this->members_.plong_repr(), *other.members_.plong_repr()); } } } }; } //namespace dtl { #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED //! The basic_string class represents a Sequence of characters. It contains all the //! usual operations of a Sequence, and, additionally, it contains standard string //! operations such as search and concatenation. //! //! The basic_string class is parameterized by character type, and by that type's //! Character Traits. //! //! This class has performance characteristics very much like vector<>, meaning, //! for example, that it does not perform reference-count or copy-on-write, and that //! concatenation of two strings is an O(N) operation. //! //! Some of basic_string's member functions use an unusual method of specifying positions //! and ranges. In addition to the conventional method using iterators, many of //! basic_string's member functions use a single value pos of type size_type to represent a //! position (in which case the position is begin() + pos, and many of basic_string's //! member functions use two values, pos and n, to represent a range. In that case pos is //! the beginning of the range and n is its size. That is, the range is //! [begin() + pos, begin() + pos + n). //! //! Note that the C++ standard does not specify the complexity of basic_string operations. //! In this implementation, basic_string has performance characteristics very similar to //! those of vector: access to a single character is O(1), while copy and concatenation //! are O(N). //! //! In this implementation, begin(), //! end(), rbegin(), rend(), operator[], c_str(), and data() do not invalidate iterators. //! In this implementation, iterators are only invalidated by member functions that //! explicitly change the string's contents. //! //! \tparam CharT The type of character it contains. //! \tparam Traits The Character Traits type, which encapsulates basic character operations //! \tparam Allocator The allocator, used for internal memory management. #ifdef BOOST_CONTAINER_DOXYGEN_INVOKED template , class Allocator = void > #else template #endif class basic_string : private dtl::basic_string_base::type> { #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED private: BOOST_COPYABLE_AND_MOVABLE(basic_string) typedef dtl::basic_string_base::type> base_t; typedef typename base_t::allocator_traits_type allocator_traits_type; static const typename base_t::size_type InternalBufferChars = base_t::InternalBufferChars; protected: // Allocator helper class to use a char_traits as a function object. template struct Eq_traits { //Compatibility with std::binary_function typedef typename Tr::char_type first_argument_type; typedef typename Tr::char_type second_argument_type; typedef bool result_type; bool operator()(const first_argument_type& x, const second_argument_type& y) const { return Tr::eq(x, y); } }; template struct Not_within_traits { typedef typename Tr::char_type argument_type; typedef bool result_type; typedef const typename Tr::char_type* Pointer; const Pointer m_first; const Pointer m_last; Not_within_traits(Pointer f, Pointer l) : m_first(f), m_last(l) {} bool operator()(const typename Tr::char_type& x) const { return boost::container::find_if(m_first, m_last, boost::container::bind1st(Eq_traits(), x)) == m_last; } }; #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED public: ////////////////////////////////////////////// // // types // ////////////////////////////////////////////// typedef Traits traits_type; typedef CharT value_type; typedef typename real_allocator::type allocator_type; typedef typename ::boost::container::allocator_traits::pointer pointer; typedef typename ::boost::container::allocator_traits::const_pointer const_pointer; typedef typename ::boost::container::allocator_traits::reference reference; typedef typename ::boost::container::allocator_traits::const_reference const_reference; typedef typename ::boost::container::allocator_traits::size_type size_type; typedef typename ::boost::container::allocator_traits::difference_type difference_type; typedef BOOST_CONTAINER_IMPDEF(allocator_type) stored_allocator_type; typedef BOOST_CONTAINER_IMPDEF(pointer) iterator; typedef BOOST_CONTAINER_IMPDEF(const_pointer) const_iterator; typedef BOOST_CONTAINER_IMPDEF(boost::container::reverse_iterator) reverse_iterator; typedef BOOST_CONTAINER_IMPDEF(boost::container::reverse_iterator) const_reverse_iterator; static const size_type npos = size_type(-1); #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED private: typedef constant_iterator cvalue_iterator; typedef typename base_t::alloc_version alloc_version; typedef ::boost::intrusive::pointer_traits pointer_traits; #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED public: // Constructor, destructor, assignment. ////////////////////////////////////////////// // // construct/copy/destroy // ////////////////////////////////////////////// #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED struct reserve_t {}; basic_string(reserve_t, size_type n, const allocator_type& a = allocator_type()) //Select allocator as in copy constructor as reserve_t-based constructors //are two step copies optimized for capacity : base_t( allocator_traits_type::select_on_container_copy_construction(a) , n + 1) { this->priv_terminate_string(); } #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED //! Effects: Default constructs a basic_string. //! //! Throws: If allocator_type's default constructor throws. basic_string() BOOST_NOEXCEPT_IF(dtl::is_nothrow_default_constructible::value) : base_t() { this->priv_terminate_string(); } //! Effects: Constructs a basic_string taking the allocator as parameter. //! //! Throws: Nothing explicit basic_string(const allocator_type& a) BOOST_NOEXCEPT_OR_NOTHROW : base_t(a) { this->priv_terminate_string(); } //! Effects: Copy constructs a basic_string. //! //! Postcondition: x == *this. //! //! Throws: If allocator_type's default constructor or allocation throws. basic_string(const basic_string& s) : base_t(allocator_traits_type::select_on_container_copy_construction(s.alloc())) { this->priv_terminate_string(); this->assign(s.begin(), s.end()); } //! Effects: Same as basic_string(sv.data(), sv.size(), a). //! //! Throws: If allocator_type's default constructor or allocation throws. template