| /*============================================================================= |
| Boost.Wave: A Standard compliant C++ preprocessor library |
| http://www.boost.org/ |
| |
| Copyright (c) 2001 by Andrei Alexandrescu. 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) |
| =============================================================================*/ |
| |
| // This code is taken from: |
| // Andrei Alexandrescu, Generic<Programming>: A Policy-Based basic_string |
| // Implementation. http://www.cuj.com/documents/s=7994/cujcexp1906alexandr/ |
| // |
| // #HK030306: |
| // - Moved into the namespace boost::wave::util |
| // - Added a bunch of missing typename(s) |
| // - Integrated with boost config |
| // - Added a missing header include |
| // - Added special constructors and operator= to allow CowString to be |
| // a real COW-string (removed unnecessary data copying) |
| // - Fixed a string terminating bug in append |
| // |
| // #HK040109: |
| // - Incorporated the changes from Andrei's latest version of this class |
| // |
| // #HK070307: |
| // - Once again incorporated the changes from Andrei's latest version of |
| // this class |
| // |
| // #HK090523: |
| // - Incorporated the changes from latest version of flex_string as |
| // maintained in Loki |
| |
| #ifndef FLEX_STRING_INC_ |
| #define FLEX_STRING_INC_ |
| |
| /* |
| //////////////////////////////////////////////////////////////////////////////// |
| template <typename E, class A = @> |
| class StoragePolicy |
| { |
| typedef E value_type; |
| typedef @ iterator; |
| typedef @ const_iterator; |
| typedef A allocator_type; |
| typedef @ size_type; |
| |
| StoragePolicy(const StoragePolicy& s); |
| StoragePolicy(const A&); |
| StoragePolicy(const E* s, size_type len, const A&); |
| StoragePolicy(size_type len, E c, const A&); |
| ~StoragePolicy(); |
| |
| iterator begin(); |
| const_iterator begin() const; |
| iterator end(); |
| const_iterator end() const; |
| |
| size_type size() const; |
| size_type max_size() const; |
| size_type capacity() const; |
| |
| void reserve(size_type res_arg); |
| |
| void append(const E* s, size_type sz); |
| |
| template <class InputIterator> |
| void append(InputIterator b, InputIterator e); |
| |
| void resize(size_type newSize, E fill); |
| |
| void swap(StoragePolicy& rhs); |
| |
| const E* c_str() const; |
| const E* data() const; |
| |
| A get_allocator() const; |
| }; |
| //////////////////////////////////////////////////////////////////////////////// |
| */ |
| |
| #include <boost/config.hpp> |
| #include <boost/assert.hpp> |
| #include <boost/throw_exception.hpp> |
| |
| #include <boost/iterator/reverse_iterator.hpp> |
| |
| #include <boost/wave/wave_config.hpp> |
| #if BOOST_WAVE_SERIALIZATION != 0 |
| #include <boost/serialization/serialization.hpp> |
| #include <boost/serialization/split_free.hpp> |
| #include <boost/serialization/collections_save_imp.hpp> |
| #include <boost/serialization/collections_load_imp.hpp> |
| #define BOOST_WAVE_FLEX_STRING_SERIALIZATION_HACK 1 |
| #endif |
| |
| #include <memory> |
| #include <new> |
| #include <string> |
| #include <vector> |
| #include <algorithm> |
| #include <functional> |
| #include <limits> |
| #include <stdexcept> |
| #include <iosfwd> |
| #include <cstddef> |
| #include <cstring> |
| #include <cstdlib> |
| |
| // this must occur after all of the includes and before any code appears |
| #ifdef BOOST_HAS_ABI_HEADERS |
| #include BOOST_ABI_PREFIX |
| #endif |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| namespace boost { |
| namespace wave { |
| namespace util { |
| |
| namespace flex_string_details |
| { |
| template <class InIt, class OutIt> |
| OutIt copy_n(InIt b, |
| typename std::iterator_traits<InIt>::difference_type n, OutIt d) |
| { |
| for (/**/; n != 0; --n, ++b, ++d) |
| { |
| *d = *b; |
| } |
| return d; |
| } |
| |
| template <class Pod, class T> |
| inline void pod_fill(Pod* b, Pod* e, T c) |
| { |
| switch ((e - b) & 7) |
| { |
| case 0: |
| while (b != e) |
| { |
| *b = c; ++b; |
| case 7: *b = c; ++b; |
| case 6: *b = c; ++b; |
| case 5: *b = c; ++b; |
| case 4: *b = c; ++b; |
| case 3: *b = c; ++b; |
| case 2: *b = c; ++b; |
| case 1: *b = c; ++b; |
| } |
| } |
| } |
| |
| template <class Pod> |
| inline void pod_move(const Pod* b, const Pod* e, Pod* d) |
| { |
| using namespace std; |
| memmove(d, b, (e - b) * sizeof(*b)); |
| } |
| |
| template <class Pod> |
| inline Pod* pod_copy(const Pod* b, const Pod* e, Pod* d) |
| { |
| const std::size_t s = e - b; |
| using namespace std; |
| memcpy(d, b, s * sizeof(*b)); |
| return d + s; |
| } |
| |
| template <typename T> struct get_unsigned |
| { |
| typedef T result; |
| }; |
| |
| template <> struct get_unsigned<char> |
| { |
| typedef unsigned char result; |
| }; |
| |
| template <> struct get_unsigned<signed char> |
| { |
| typedef unsigned char result; |
| }; |
| |
| template <> struct get_unsigned<short int> |
| { |
| typedef unsigned short int result; |
| }; |
| |
| template <> struct get_unsigned<int> |
| { |
| typedef unsigned int result; |
| }; |
| |
| template <> struct get_unsigned<long int> |
| { |
| typedef unsigned long int result; |
| }; |
| |
| enum Shallow {}; |
| } |
| |
| template <class T> class mallocator |
| { |
| public: |
| typedef T value_type; |
| typedef value_type* pointer; |
| typedef const value_type* const_pointer; |
| typedef value_type& reference; |
| typedef const value_type& const_reference; |
| typedef std::size_t size_type; |
| //typedef unsigned int size_type; |
| //typedef std::ptrdiff_t difference_type; |
| typedef int difference_type; |
| |
| template <class U> |
| struct rebind { typedef mallocator<U> other; }; |
| |
| mallocator() {} |
| mallocator(const mallocator&) {} |
| //template <class U> |
| //mallocator(const mallocator<U>&) {} |
| ~mallocator() {} |
| |
| pointer address(reference x) const { return &x; } |
| const_pointer address(const_reference x) const |
| { |
| return x; |
| } |
| |
| pointer allocate(size_type n, const_pointer = 0) |
| { |
| using namespace std; |
| void* p = malloc(n * sizeof(T)); |
| if (!p) boost::throw_exception(std::bad_alloc()); |
| return static_cast<pointer>(p); |
| } |
| |
| void deallocate(pointer p, size_type) |
| { |
| using namespace std; |
| free(p); |
| } |
| |
| size_type max_size() const |
| { |
| return static_cast<size_type>(-1) / sizeof(T); |
| } |
| |
| void construct(pointer p, const value_type& x) |
| { |
| new(p) value_type(x); |
| } |
| |
| void destroy(pointer p) |
| { |
| p->~value_type(); |
| } |
| |
| private: |
| void operator=(const mallocator&); |
| }; |
| |
| template<> class mallocator<void> |
| { |
| typedef void value_type; |
| typedef void* pointer; |
| typedef const void* const_pointer; |
| |
| template <class U> |
| struct rebind { typedef mallocator<U> other; }; |
| }; |
| |
| template <class T> |
| inline bool operator==(const mallocator<T>&, |
| const mallocator<T>&) { |
| return true; |
| } |
| |
| template <class T> |
| inline bool operator!=(const mallocator<T>&, |
| const mallocator<T>&) { |
| return false; |
| } |
| |
| template <class Allocator> |
| typename Allocator::pointer Reallocate( |
| Allocator& alloc, |
| typename Allocator::pointer p, |
| typename Allocator::size_type oldObjCount, |
| typename Allocator::size_type newObjCount, |
| void*) |
| { |
| // @@@ not implemented |
| return NULL; |
| } |
| |
| template <class Allocator> |
| typename Allocator::pointer Reallocate( |
| Allocator& alloc, |
| typename Allocator::pointer p, |
| typename Allocator::size_type oldObjCount, |
| typename Allocator::size_type newObjCount, |
| mallocator<void>*) |
| { |
| // @@@ not implemented |
| return NULL; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // class template SimpleStringStorage |
| // Allocates memory with malloc |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| template <typename E, class A = std::allocator<E> > |
| class SimpleStringStorage |
| { |
| // The "public" below exists because MSVC can't do template typedefs |
| public: |
| struct Data |
| { |
| Data() : pEnd_(buffer_), pEndOfMem_(buffer_) { buffer_[0] = E(0); } |
| |
| E* pEnd_; |
| E* pEndOfMem_; |
| E buffer_[1]; |
| }; |
| static const Data emptyString_; |
| |
| typedef typename A::size_type size_type; |
| |
| private: |
| Data* pData_; |
| |
| void Init(size_type size, size_type capacity) |
| { |
| BOOST_ASSERT(size <= capacity); |
| if (capacity == 0) |
| { |
| pData_ = const_cast<Data*>(&emptyString_); |
| } |
| else |
| { |
| // 11-17-2000: comment added: |
| // No need to allocate (capacity + 1) to |
| // accommodate the terminating 0, because Data already |
| // has one one character in there |
| pData_ = static_cast<Data*>( |
| malloc(sizeof(Data) + capacity * sizeof(E))); |
| if (!pData_) boost::throw_exception(std::bad_alloc()); |
| pData_->pEnd_ = pData_->buffer_ + size; |
| pData_->pEndOfMem_ = pData_->buffer_ + capacity; |
| } |
| } |
| |
| private: |
| // Warning - this doesn't initialize pData_. Used in reserve() |
| SimpleStringStorage() |
| { } |
| |
| public: |
| typedef E value_type; |
| typedef E* iterator; |
| typedef const E* const_iterator; |
| typedef A allocator_type; |
| |
| SimpleStringStorage(const SimpleStringStorage& rhs) |
| { |
| const size_type sz = rhs.size(); |
| Init(sz, sz); |
| if (sz) flex_string_details::pod_copy(rhs.begin(), rhs.end(), begin()); |
| } |
| |
| SimpleStringStorage(const SimpleStringStorage& s, |
| flex_string_details::Shallow) |
| : pData_(s.pData_) |
| { |
| } |
| |
| SimpleStringStorage(const A&) |
| { pData_ = const_cast<Data*>(&emptyString_); } |
| |
| SimpleStringStorage(const E* s, size_type len, const A&) |
| { |
| Init(len, len); |
| flex_string_details::pod_copy(s, s + len, begin()); |
| } |
| |
| SimpleStringStorage(size_type len, E c, const A&) |
| { |
| Init(len, len); |
| flex_string_details::pod_fill(begin(), end(), c); |
| } |
| |
| SimpleStringStorage& operator=(const SimpleStringStorage& rhs) |
| { |
| const size_type sz = rhs.size(); |
| reserve(sz); |
| flex_string_details::pod_copy(&*rhs.begin(), &*rhs.end(), begin()); |
| pData_->pEnd_ = &*begin() + sz; |
| return *this; |
| } |
| |
| ~SimpleStringStorage() |
| { |
| BOOST_ASSERT(begin() <= end()); |
| if (pData_ != &emptyString_) free(pData_); |
| } |
| |
| iterator begin() |
| { return pData_->buffer_; } |
| |
| const_iterator begin() const |
| { return pData_->buffer_; } |
| |
| iterator end() |
| { return pData_->pEnd_; } |
| |
| const_iterator end() const |
| { return pData_->pEnd_; } |
| |
| size_type size() const |
| { return pData_->pEnd_ - pData_->buffer_; } |
| |
| size_type max_size() const |
| { return std::size_t(-1) / sizeof(E) - sizeof(Data) - 1; } |
| |
| size_type capacity() const |
| { return pData_->pEndOfMem_ - pData_->buffer_; } |
| |
| void reserve(size_type res_arg) |
| { |
| if (res_arg <= capacity()) |
| { |
| // @@@ insert shrinkage here if you wish |
| return; |
| } |
| |
| if (pData_ == &emptyString_) |
| { |
| Init(0, res_arg); |
| } |
| else |
| { |
| const size_type sz = size(); |
| |
| void* p = realloc(pData_, |
| sizeof(Data) + res_arg * sizeof(E)); |
| if (!p) boost::throw_exception(std::bad_alloc()); |
| |
| if (p != pData_) |
| { |
| pData_ = static_cast<Data*>(p); |
| pData_->pEnd_ = pData_->buffer_ + sz; |
| } |
| pData_->pEndOfMem_ = pData_->buffer_ + res_arg; |
| } |
| } |
| |
| void append(const E* s, size_type sz) |
| { |
| const size_type neededCapacity = size() + sz; |
| |
| if (capacity() < neededCapacity) |
| { |
| const iterator b = begin(); |
| static std::less_equal<const E*> le; |
| if (le(b, s) && le(s, end())) |
| { |
| // aliased |
| const size_type offset = s - b; |
| reserve(neededCapacity); |
| s = begin() + offset; |
| } |
| else |
| { |
| reserve(neededCapacity); |
| } |
| } |
| flex_string_details::pod_copy(s, s + sz, end()); |
| pData_->pEnd_ += sz; |
| } |
| |
| template <class InputIterator> |
| void append(InputIterator b, InputIterator e) |
| { |
| // @@@ todo: optimize this depending on iterator type |
| for (; b != e; ++b) |
| { |
| *this += *b; |
| } |
| } |
| |
| void resize(size_type newSize, E fill) |
| { |
| const int delta = int(newSize - size()); |
| if (delta == 0) return; |
| |
| if (delta > 0) |
| { |
| if (newSize > capacity()) |
| { |
| reserve(newSize); |
| } |
| E* e = &*end(); |
| flex_string_details::pod_fill(e, e + delta, fill); |
| } |
| pData_->pEnd_ = pData_->buffer_ + newSize; |
| } |
| |
| void swap(SimpleStringStorage& rhs) |
| { |
| std::swap(pData_, rhs.pData_); |
| } |
| |
| const E* c_str() const |
| { |
| if (pData_ != &emptyString_) *pData_->pEnd_ = E(); |
| return pData_->buffer_; |
| } |
| |
| const E* data() const |
| { return pData_->buffer_; } |
| |
| A get_allocator() const |
| { return A(); } |
| }; |
| |
| template <typename E, class A> |
| const typename SimpleStringStorage<E, A>::Data |
| SimpleStringStorage<E, A>::emptyString_ = |
| typename SimpleStringStorage<E, A>::Data(); |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // class template AllocatorStringStorage |
| // Allocates with your allocator |
| // Takes advantage of the Empty Base Optimization if available |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| template <typename E, class A = std::allocator<E> > |
| class AllocatorStringStorage : public A |
| { |
| typedef typename A::size_type size_type; |
| typedef typename SimpleStringStorage<E, A>::Data Data; |
| |
| void* Alloc(size_type sz, const void* p = 0) |
| { |
| return A::allocate(1 + (sz - 1) / sizeof(E), |
| static_cast<const char*>(p)); |
| } |
| |
| void* Realloc(void* p, size_type oldSz, size_type newSz) |
| { |
| void* r = Alloc(newSz); |
| flex_string_details::pod_copy(p, p + Min(oldSz, newSz), r); |
| Free(p, oldSz); |
| return r; |
| } |
| |
| void Free(void* p, size_type sz) |
| { |
| A::deallocate(static_cast<E*>(p), sz); |
| } |
| |
| Data* pData_; |
| |
| void Init(size_type size, size_type cap) |
| { |
| BOOST_ASSERT(size <= cap); |
| |
| if (cap == 0) |
| { |
| pData_ = const_cast<Data*>( |
| &SimpleStringStorage<E, A>::emptyString_); |
| } |
| else |
| { |
| pData_ = static_cast<Data*>(Alloc( |
| cap * sizeof(E) + sizeof(Data))); |
| pData_->pEnd_ = pData_->buffer_ + size; |
| pData_->pEndOfMem_ = pData_->buffer_ + cap; |
| } |
| } |
| |
| public: |
| typedef E value_type; |
| typedef E* iterator; |
| typedef const E* const_iterator; |
| typedef A allocator_type; |
| |
| AllocatorStringStorage() |
| : A(), pData_(0) |
| { |
| } |
| |
| AllocatorStringStorage(const AllocatorStringStorage& rhs) |
| : A(rhs.get_allocator()) |
| { |
| const size_type sz = rhs.size(); |
| Init(sz, sz); |
| if (sz) flex_string_details::pod_copy(rhs.begin(), rhs.end(), begin()); |
| } |
| |
| AllocatorStringStorage(const AllocatorStringStorage& s, |
| flex_string_details::Shallow) |
| : A(s.get_allocator()) |
| { |
| pData_ = s.pData_; |
| } |
| |
| AllocatorStringStorage(const A& a) : A(a) |
| { |
| pData_ = const_cast<Data*>( |
| &SimpleStringStorage<E, A>::emptyString_); |
| } |
| |
| AllocatorStringStorage(const E* s, size_type len, const A& a) |
| : A(a) |
| { |
| Init(len, len); |
| flex_string_details::pod_copy(s, s + len, begin()); |
| } |
| |
| AllocatorStringStorage(size_type len, E c, const A& a) |
| : A(a) |
| { |
| Init(len, len); |
| flex_string_details::pod_fill(&*begin(), &*end(), c); |
| } |
| |
| AllocatorStringStorage& operator=(const AllocatorStringStorage& rhs) |
| { |
| const size_type sz = rhs.size(); |
| reserve(sz); |
| flex_string_details::pod_copy(&*rhs.begin(), &*rhs.end(), begin()); |
| pData_->pEnd_ = &*begin() + rhs.size(); |
| return *this; |
| } |
| |
| ~AllocatorStringStorage() |
| { |
| if (capacity()) |
| { |
| Free(pData_, |
| sizeof(Data) + capacity() * sizeof(E)); |
| } |
| } |
| |
| iterator begin() |
| { return pData_->buffer_; } |
| |
| const_iterator begin() const |
| { return pData_->buffer_; } |
| |
| iterator end() |
| { return pData_->pEnd_; } |
| |
| const_iterator end() const |
| { return pData_->pEnd_; } |
| |
| size_type size() const |
| { return size_type(end() - begin()); } |
| |
| size_type max_size() const |
| { return A::max_size(); } |
| |
| size_type capacity() const |
| { return size_type(pData_->pEndOfMem_ - pData_->buffer_); } |
| |
| void resize(size_type n, E c) |
| { |
| reserve(n); |
| iterator newEnd = begin() + n; |
| iterator oldEnd = end(); |
| if (newEnd > oldEnd) |
| { |
| // Copy the characters |
| flex_string_details::pod_fill(oldEnd, newEnd, c); |
| } |
| if (capacity()) pData_->pEnd_ = newEnd; |
| } |
| |
| void reserve(size_type res_arg) |
| { |
| if (res_arg <= capacity()) |
| { |
| // @@@ shrink to fit here |
| return; |
| } |
| |
| A& myAlloc = *this; |
| AllocatorStringStorage newStr(myAlloc); |
| newStr.Init(size(), res_arg); |
| |
| flex_string_details::pod_copy(begin(), end(), newStr.begin()); |
| |
| swap(newStr); |
| } |
| |
| template <class ForwardIterator> |
| void append(ForwardIterator b, ForwardIterator e) |
| { |
| const size_type |
| sz = std::distance(b, e), |
| neededCapacity = size() + sz; |
| |
| if (capacity() < neededCapacity) |
| { |
| // typedef std::less_equal<const E*> le_type; |
| // BOOST_ASSERT(!(le_type()(begin(), &*b) && le_type()(&*b, end()))); |
| reserve(neededCapacity); |
| } |
| std::copy(b, e, end()); |
| pData_->pEnd_ += sz; |
| } |
| |
| void swap(AllocatorStringStorage& rhs) |
| { |
| // @@@ The following line is commented due to a bug in MSVC |
| //std::swap(lhsAlloc, rhsAlloc); |
| std::swap(pData_, rhs.pData_); |
| } |
| |
| const E* c_str() const |
| { |
| if (pData_ != &SimpleStringStorage<E, A>::emptyString_) |
| { |
| *pData_->pEnd_ = E(); |
| } |
| return &*begin(); |
| } |
| |
| const E* data() const |
| { return &*begin(); } |
| |
| A get_allocator() const |
| { return *this; } |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // class template VectorStringStorage |
| // Uses std::vector |
| // Takes advantage of the Empty Base Optimization if available |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| template <typename E, class A = std::allocator<E> > |
| class VectorStringStorage : protected std::vector<E, A> |
| { |
| typedef std::vector<E, A> base; |
| |
| public: // protected: |
| typedef E value_type; |
| typedef typename base::iterator iterator; |
| typedef typename base::const_iterator const_iterator; |
| typedef A allocator_type; |
| typedef typename A::size_type size_type; |
| |
| VectorStringStorage(const VectorStringStorage& s) : base(s) |
| { } |
| |
| VectorStringStorage(const A& a) : base(1, E(), a) |
| { } |
| |
| VectorStringStorage(const E* s, size_type len, const A& a) |
| : base(a) |
| { |
| base::reserve(len + 1); |
| base::insert(base::end(), s, s + len); |
| // Terminating zero |
| base::insert(base::end(), E()); |
| } |
| |
| VectorStringStorage(size_type len, E c, const A& a) |
| : base(len + 1, c, a) |
| { |
| // Terminating zero |
| base::back() = E(); |
| } |
| |
| VectorStringStorage& operator=(const VectorStringStorage& rhs) |
| { |
| base& v = *this; |
| v = rhs; |
| return *this; |
| } |
| |
| iterator begin() |
| { return base::begin(); } |
| |
| const_iterator begin() const |
| { return base::begin(); } |
| |
| iterator end() |
| { return base::end() - 1; } |
| |
| const_iterator end() const |
| { return base::end() - 1; } |
| |
| size_type size() const |
| { return base::size() - 1; } |
| |
| size_type max_size() const |
| { return base::max_size() - 1; } |
| |
| size_type capacity() const |
| { return base::capacity() - 1; } |
| |
| void reserve(size_type res_arg) |
| { |
| BOOST_ASSERT(res_arg < max_size()); |
| base::reserve(res_arg + 1); |
| } |
| |
| void append(const E* s, size_type sz) |
| { |
| // Check for aliasing because std::vector doesn't do it. |
| static std::less_equal<const E*> le; |
| if (!base::empty()) |
| { |
| const E* start = &base::front(); |
| if (le(start, s) && le(s, start + size())) |
| { |
| // aliased |
| const size_type offset = s - start; |
| reserve(size() + sz); |
| s = &base::front() + offset; |
| } |
| } |
| base::insert(end(), s, s + sz); |
| } |
| |
| template <class InputIterator> |
| void append(InputIterator b, InputIterator e) |
| { |
| base::insert(end(), b, e); |
| } |
| |
| void resize(size_type n, E c) |
| { |
| base::reserve(n + 1); |
| base::back() = c; |
| base::resize(n + 1, c); |
| base::back() = E(); |
| } |
| |
| void swap(VectorStringStorage& rhs) |
| { base::swap(rhs); } |
| |
| const E* c_str() const |
| { return &*begin(); } |
| |
| const E* data() const |
| { return &*begin(); } |
| |
| A get_allocator() const |
| { return base::get_allocator(); } |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // class template SmallStringOpt |
| // Builds the small string optimization over any other storage |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| template <class Storage, unsigned int threshold, |
| typename Align = typename Storage::value_type*> |
| class SmallStringOpt |
| { |
| public: |
| typedef typename Storage::value_type value_type; |
| typedef value_type* iterator; |
| typedef const value_type* const_iterator; |
| typedef typename Storage::allocator_type allocator_type; |
| typedef typename allocator_type::size_type size_type; |
| |
| private: |
| enum { temp1 = threshold * sizeof(value_type) > sizeof(Storage) |
| ? threshold * sizeof(value_type) |
| : sizeof(Storage) }; |
| |
| enum { temp2 = temp1 > sizeof(Align) ? temp1 : sizeof(Align) }; |
| |
| public: |
| enum { maxSmallString = |
| (temp2 + sizeof(value_type) - 1) / sizeof(value_type) }; |
| |
| private: |
| enum { magic = maxSmallString + 1 }; |
| |
| union |
| { |
| mutable value_type buf_[maxSmallString + 1]; |
| Align align_; |
| }; |
| |
| Storage& GetStorage() |
| { |
| BOOST_ASSERT(buf_[maxSmallString] == magic); |
| Storage* p = reinterpret_cast<Storage*>(&buf_[0]); |
| return *p; |
| } |
| |
| const Storage& GetStorage() const |
| { |
| BOOST_ASSERT(buf_[maxSmallString] == magic); |
| const Storage *p = reinterpret_cast<const Storage*>(&buf_[0]); |
| return *p; |
| } |
| |
| bool Small() const |
| { |
| return buf_[maxSmallString] != magic; |
| } |
| |
| public: |
| SmallStringOpt(const SmallStringOpt& s) |
| { |
| if (s.Small()) |
| { |
| flex_string_details::pod_copy( |
| s.buf_, |
| s.buf_ + s.size(), |
| buf_); |
| } |
| else |
| { |
| new(buf_) Storage(s.GetStorage()); |
| } |
| buf_[maxSmallString] = s.buf_[maxSmallString]; |
| } |
| |
| SmallStringOpt(const allocator_type&) |
| { |
| buf_[maxSmallString] = maxSmallString; |
| } |
| |
| SmallStringOpt(const value_type* s, size_type len, const allocator_type& a) |
| { |
| if (len <= maxSmallString) |
| { |
| flex_string_details::pod_copy(s, s + len, buf_); |
| buf_[maxSmallString] = value_type(maxSmallString - len); |
| } |
| else |
| { |
| new(buf_) Storage(s, len, a); |
| buf_[maxSmallString] = magic; |
| } |
| } |
| |
| SmallStringOpt(size_type len, value_type c, const allocator_type& a) |
| { |
| if (len <= maxSmallString) |
| { |
| flex_string_details::pod_fill(buf_, buf_ + len, c); |
| buf_[maxSmallString] = value_type(maxSmallString - len); |
| } |
| else |
| { |
| new(buf_) Storage(len, c, a); |
| buf_[maxSmallString] = magic; |
| } |
| } |
| |
| SmallStringOpt& operator=(const SmallStringOpt& rhs) |
| { |
| reserve(rhs.size()); |
| resize(0, 0); |
| append(rhs.data(), rhs.size()); |
| return *this; |
| } |
| |
| ~SmallStringOpt() |
| { |
| if (!Small()) GetStorage().~Storage(); |
| } |
| |
| iterator begin() |
| { |
| if (Small()) return buf_; |
| return &*GetStorage().begin(); |
| } |
| |
| const_iterator begin() const |
| { |
| if (Small()) return buf_; |
| return &*GetStorage().begin(); |
| } |
| |
| iterator end() |
| { |
| if (Small()) return buf_ + maxSmallString - buf_[maxSmallString]; |
| return &*GetStorage().end(); |
| } |
| |
| const_iterator end() const |
| { |
| if (Small()) return buf_ + maxSmallString - buf_[maxSmallString]; |
| return &*GetStorage().end(); |
| } |
| |
| size_type size() const |
| { |
| BOOST_ASSERT(!Small() || maxSmallString >= buf_[maxSmallString]); |
| return Small() |
| ? maxSmallString - buf_[maxSmallString] |
| : GetStorage().size(); |
| } |
| |
| size_type max_size() const |
| { return get_allocator().max_size(); } |
| |
| size_type capacity() const |
| { return Small() ? maxSmallString : GetStorage().capacity(); } |
| |
| void reserve(size_type res_arg) |
| { |
| if (Small()) |
| { |
| if (res_arg <= maxSmallString) return; |
| SmallStringOpt temp(*this); |
| this->~SmallStringOpt(); |
| new(buf_) Storage(temp.data(), temp.size(), |
| temp.get_allocator()); |
| buf_[maxSmallString] = magic; |
| GetStorage().reserve(res_arg); |
| } |
| else |
| { |
| GetStorage().reserve(res_arg); |
| } |
| BOOST_ASSERT(capacity() >= res_arg); |
| } |
| |
| void append(const value_type* s, size_type sz) |
| { |
| if (!Small()) |
| { |
| GetStorage().append(s, sz); |
| } |
| else |
| { |
| // append to a small string |
| const size_type neededCapacity = |
| maxSmallString - buf_[maxSmallString] + sz; |
| |
| if (maxSmallString < neededCapacity) |
| { |
| // need to change storage strategy |
| allocator_type alloc; |
| Storage temp(alloc); |
| temp.reserve(neededCapacity); |
| temp.append(buf_, maxSmallString - buf_[maxSmallString]); |
| temp.append(s, sz); |
| buf_[maxSmallString] = magic; |
| new(buf_) Storage(temp.get_allocator()); |
| GetStorage().swap(temp); |
| } |
| else |
| { |
| flex_string_details::pod_move(s, s + sz, |
| buf_ + maxSmallString - buf_[maxSmallString]); |
| buf_[maxSmallString] -= value_type(sz); |
| } |
| } |
| } |
| |
| template <class InputIterator> |
| void append(InputIterator b, InputIterator e) |
| { |
| // @@@ todo: optimize this depending on iterator type |
| for (; b != e; ++b) |
| { |
| *this += *b; |
| } |
| } |
| |
| void resize(size_type n, value_type c) |
| { |
| if (Small()) |
| { |
| if (n > maxSmallString) |
| { |
| // Small string resized to big string |
| SmallStringOpt temp(*this); // can't throw |
| // 11-17-2001: correct exception safety bug |
| Storage newString(temp.data(), temp.size(), |
| temp.get_allocator()); |
| newString.resize(n, c); |
| // We make the reasonable assumption that an empty Storage |
| // constructor won't throw |
| this->~SmallStringOpt(); |
| new(&buf_[0]) Storage(temp.get_allocator()); |
| buf_[maxSmallString] = value_type(magic); |
| GetStorage().swap(newString); |
| } |
| else |
| { |
| // Small string resized to small string |
| // 11-17-2001: bug fix: terminating zero not copied |
| size_type toFill = n > size() ? n - size() : 0; |
| flex_string_details::pod_fill(end(), end() + toFill, c); |
| buf_[maxSmallString] = value_type(maxSmallString - n); |
| } |
| } |
| else |
| { |
| if (n > maxSmallString) |
| { |
| // Big string resized to big string |
| GetStorage().resize(n, c); |
| } |
| else |
| { |
| // Big string resized to small string |
| // 11-17=2001: bug fix in the BOOST_ASSERTion below |
| BOOST_ASSERT(capacity() > n); |
| SmallStringOpt newObj(data(), n, get_allocator()); |
| newObj.swap(*this); |
| } |
| } |
| } |
| |
| void swap(SmallStringOpt& rhs) |
| { |
| if (Small()) |
| { |
| if (rhs.Small()) |
| { |
| // Small swapped with small |
| std::swap_ranges(buf_, buf_ + maxSmallString + 1, |
| rhs.buf_); |
| } |
| else |
| { |
| // Small swapped with big |
| // Make a copy of myself - can't throw |
| SmallStringOpt temp(*this); |
| // Nuke myself |
| this->~SmallStringOpt(); |
| // Make an empty storage for myself (likely won't throw) |
| new(buf_) Storage(0, value_type(), rhs.get_allocator()); |
| buf_[maxSmallString] = magic; |
| // Recurse to this same function |
| swap(rhs); |
| // Nuke rhs |
| rhs.~SmallStringOpt(); |
| // Build the new small string into rhs |
| new(&rhs) SmallStringOpt(temp); |
| } |
| } |
| else |
| { |
| if (rhs.Small()) |
| { |
| // Big swapped with small |
| // Already implemented, recurse with reversed args |
| rhs.swap(*this); |
| } |
| else |
| { |
| // Big swapped with big |
| GetStorage().swap(rhs.GetStorage()); |
| } |
| } |
| } |
| |
| const value_type* c_str() const |
| { |
| if (!Small()) return GetStorage().c_str(); |
| buf_[maxSmallString - buf_[maxSmallString]] = value_type(); |
| return buf_; |
| } |
| |
| const value_type* data() const |
| { return Small() ? buf_ : GetStorage().data(); } |
| |
| allocator_type get_allocator() const |
| { return allocator_type(); } |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // class template CowString |
| // Implements Copy on Write over any storage |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| template < |
| typename Storage, |
| typename Align = BOOST_DEDUCED_TYPENAME Storage::value_type* |
| > |
| class CowString |
| { |
| typedef typename Storage::value_type E; |
| typedef typename flex_string_details::get_unsigned<E>::result RefCountType; |
| |
| public: |
| typedef E value_type; |
| typedef typename Storage::iterator iterator; |
| typedef typename Storage::const_iterator const_iterator; |
| typedef typename Storage::allocator_type allocator_type; |
| typedef typename allocator_type::size_type size_type; |
| typedef typename Storage::reference reference; |
| |
| private: |
| union |
| { |
| mutable char buf_[sizeof(Storage)]; |
| Align align_; |
| }; |
| |
| Storage& Data() const |
| { |
| Storage* p = reinterpret_cast<Storage*>(&buf_[0]); |
| return *p; |
| } |
| |
| RefCountType GetRefs() const |
| { |
| const Storage& d = Data(); |
| BOOST_ASSERT(d.size() > 0); |
| BOOST_ASSERT(static_cast<RefCountType>(*d.begin()) != 0); |
| return *d.begin(); |
| } |
| |
| RefCountType& Refs() |
| { |
| Storage& d = Data(); |
| BOOST_ASSERT(d.size() > 0); |
| return reinterpret_cast<RefCountType&>(*d.begin()); |
| } |
| |
| void MakeUnique() const |
| { |
| BOOST_ASSERT(GetRefs() >= 1); |
| if (GetRefs() == 1) return; |
| |
| union |
| { |
| char buf_[sizeof(Storage)]; |
| Align align_; |
| } temp; |
| |
| --(*Data().begin()); // decrement the use count of the remaining object |
| |
| Storage* p = reinterpret_cast<Storage*>(&temp.buf_[0]); |
| new(buf_) Storage( |
| *new(p) Storage(Data()), |
| flex_string_details::Shallow()); |
| *Data().begin() = 1; |
| } |
| |
| public: |
| CowString(const CowString& s) |
| { |
| if (s.GetRefs() == (std::numeric_limits<RefCountType>::max)()) |
| { |
| // must make a brand new copy |
| new(buf_) Storage(s.Data()); // non shallow |
| Refs() = 1; |
| } |
| else |
| { |
| new(buf_) Storage(s.Data(), flex_string_details::Shallow()); |
| ++Refs(); |
| } |
| BOOST_ASSERT(Data().size() > 0); |
| } |
| |
| CowString(const allocator_type& a) |
| { |
| new(buf_) Storage(1, 1, a); |
| } |
| |
| CowString(const E* s, size_type len, const allocator_type& a) |
| { |
| // Warning - MSVC's debugger has trouble tracing through the code below. |
| // It seems to be a const-correctness issue |
| // |
| new(buf_) Storage(a); |
| Data().reserve(len + 1); |
| Data().resize(1, 1); |
| Data().append(s, s + len); |
| } |
| |
| CowString(size_type len, E c, const allocator_type& a) |
| { |
| new(buf_) Storage(len + 1, c, a); |
| Refs() = 1; |
| } |
| |
| CowString& operator=(const CowString& rhs) |
| { |
| // CowString(rhs).swap(*this); |
| if (--Refs() == 0) |
| Data().~Storage(); |
| if (rhs.GetRefs() == (std::numeric_limits<RefCountType>::max)()) |
| { |
| // must make a brand new copy |
| new(buf_) Storage(rhs.Data()); // non shallow |
| Refs() = 1; |
| } |
| else |
| { |
| new(buf_) Storage(rhs.Data(), flex_string_details::Shallow()); |
| ++Refs(); |
| } |
| BOOST_ASSERT(Data().size() > 0); |
| return *this; |
| } |
| |
| ~CowString() |
| { |
| BOOST_ASSERT(Data().size() > 0); |
| if (--Refs() == 0) |
| Data().~Storage(); |
| } |
| |
| iterator begin() |
| { |
| BOOST_ASSERT(Data().size() > 0); |
| MakeUnique(); |
| return Data().begin() + 1; |
| } |
| |
| const_iterator begin() const |
| { |
| BOOST_ASSERT(Data().size() > 0); |
| return Data().begin() + 1; |
| } |
| |
| iterator end() |
| { |
| MakeUnique(); |
| return Data().end(); |
| } |
| |
| const_iterator end() const |
| { |
| return Data().end(); |
| } |
| |
| size_type size() const |
| { |
| BOOST_ASSERT(Data().size() > 0); |
| return Data().size() - 1; |
| } |
| |
| size_type max_size() const |
| { |
| BOOST_ASSERT(Data().max_size() > 0); |
| return Data().max_size() - 1; |
| } |
| |
| size_type capacity() const |
| { |
| BOOST_ASSERT(Data().capacity() > 0); |
| return Data().capacity() - 1; |
| } |
| |
| void resize(size_type n, E c) |
| { |
| BOOST_ASSERT(Data().size() > 0); |
| MakeUnique(); |
| Data().resize(n + 1, c); |
| } |
| |
| template <class FwdIterator> |
| void append(FwdIterator b, FwdIterator e) |
| { |
| MakeUnique(); |
| Data().append(b, e); |
| } |
| |
| void reserve(size_type res_arg) |
| { |
| if (capacity() > res_arg) return; |
| MakeUnique(); |
| Data().reserve(res_arg + 1); |
| } |
| |
| void swap(CowString& rhs) |
| { |
| Data().swap(rhs.Data()); |
| } |
| |
| const E* c_str() const |
| { |
| BOOST_ASSERT(Data().size() > 0); |
| return Data().c_str() + 1; |
| } |
| |
| const E* data() const |
| { |
| BOOST_ASSERT(Data().size() > 0); |
| return Data().data() + 1; |
| } |
| |
| allocator_type get_allocator() const |
| { |
| return Data().get_allocator(); |
| } |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // class template flex_string |
| // a std::basic_string compatible implementation |
| // Uses a Storage policy |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| template <typename E, |
| class T = std::char_traits<E>, |
| class A = std::allocator<E>, |
| class Storage = AllocatorStringStorage<E, A> > |
| class flex_string : private Storage |
| { |
| #if defined(BOOST_WAVE_FLEXSTRING_THROW_ON_ENFORCE) |
| template <typename Exception> |
| static void Enforce(bool condition, Exception*, const char* msg) |
| { if (!condition) boost::throw_exception(Exception(msg)); } |
| #else |
| template <typename Exception> |
| static inline void Enforce(bool condition, Exception*, const char* msg) |
| { BOOST_ASSERT(condition && msg); } |
| #endif // defined(BOOST_WAVE_FLEXSTRING_THROW_ON_ENFORCE) |
| |
| #ifndef NDEBUG |
| bool Sane() const |
| { |
| return |
| begin() <= end() && |
| empty() == (size() == 0) && |
| empty() == (begin() == end()) && |
| size() <= max_size() && |
| capacity() <= max_size() && |
| size() <= capacity(); |
| } |
| |
| struct Invariant; |
| friend struct Invariant; |
| struct Invariant |
| { |
| Invariant(const flex_string& s) : s_(s) |
| { |
| BOOST_ASSERT(s_.Sane()); |
| } |
| ~Invariant() |
| { |
| BOOST_ASSERT(s_.Sane()); |
| } |
| private: |
| const flex_string& s_; |
| Invariant& operator=(const Invariant&); |
| }; |
| #endif |
| |
| public: |
| // types |
| typedef T traits_type; |
| typedef typename traits_type::char_type value_type; |
| typedef A allocator_type; |
| typedef typename A::size_type size_type; |
| typedef typename A::difference_type difference_type; |
| |
| typedef typename A::reference reference; |
| typedef typename A::const_reference const_reference; |
| typedef typename A::pointer pointer; |
| typedef typename A::const_pointer const_pointer; |
| |
| typedef typename Storage::iterator iterator; |
| typedef typename Storage::const_iterator const_iterator; |
| |
| typedef boost::reverse_iterator<iterator> reverse_iterator; |
| typedef boost::reverse_iterator<const_iterator> const_reverse_iterator; |
| |
| static const size_type npos; // = size_type(-1) |
| |
| private: |
| static size_type Min(size_type lhs, size_type rhs) |
| { return lhs < rhs ? lhs : rhs; } |
| static void Procust(size_type& n, size_type nmax) |
| { if (n > nmax) n = nmax; } |
| |
| public: |
| // 21.3.1 construct/copy/destroy |
| explicit flex_string(const A& a = A()) |
| : Storage(a) |
| {} |
| |
| flex_string(const flex_string& str) |
| : Storage(str) |
| { |
| } |
| |
| flex_string(const flex_string& str, size_type pos, |
| size_type n = npos, const A& a = A()) |
| : Storage(a) |
| { |
| Enforce(pos <= str.size(), (std::out_of_range*)0, ""); |
| assign(str, pos, n); |
| } |
| |
| flex_string(const value_type* s, const A& a = A()) |
| : Storage(s, traits_type::length(s), a) |
| {} |
| |
| flex_string(const value_type* s, size_type n, const A& a = A()) |
| : Storage(s, n, a) |
| {} |
| |
| flex_string(size_type n, value_type c, const A& a = A()) |
| : Storage(n, c, a) |
| {} |
| |
| template <class InputIterator> |
| flex_string(InputIterator begin, InputIterator end, const A& a = A()) |
| : Storage(a) |
| { |
| assign(begin, end); |
| } |
| |
| ~flex_string() |
| {} |
| |
| flex_string& operator=(const flex_string& str) |
| { |
| if (this != &str) { |
| Storage& s = *this; |
| s = str; |
| } |
| return *this; |
| } |
| |
| flex_string& operator=(const value_type* s) |
| { |
| assign(s); |
| return *this; |
| } |
| |
| flex_string& operator=(value_type c) |
| { |
| assign(1, c); |
| return *this; |
| } |
| |
| // 21.3.2 iterators: |
| iterator begin() |
| { return Storage::begin(); } |
| |
| const_iterator begin() const |
| { return Storage::begin(); } |
| |
| iterator end() |
| { return Storage::end(); } |
| |
| const_iterator end() const |
| { return Storage::end(); } |
| |
| reverse_iterator rbegin() |
| { return reverse_iterator(end()); } |
| |
| const_reverse_iterator rbegin() const |
| { return const_reverse_iterator(end()); } |
| |
| reverse_iterator rend() |
| { return reverse_iterator(begin()); } |
| |
| const_reverse_iterator rend() const |
| { return const_reverse_iterator(begin()); } |
| |
| #if BOOST_WAVE_FLEX_STRING_SERIALIZATION_HACK != 0 |
| // temporary hack to make it easier to serialize flex_string's using |
| // the Boost.Serialization library |
| value_type & back() { return *(begin()+size()-1); } |
| value_type const& back() const { return *(begin()+size()-1); } |
| #endif |
| |
| // 21.3.3 capacity: |
| size_type size() const |
| { return Storage::size(); } |
| |
| size_type length() const |
| { return size(); } |
| |
| size_type max_size() const |
| { return Storage::max_size(); } |
| |
| void resize(size_type n, value_type c) |
| { Storage::resize(n, c); } |
| |
| void resize(size_type n) |
| { resize(n, value_type()); } |
| |
| size_type capacity() const |
| { return Storage::capacity(); } |
| |
| void reserve(size_type res_arg = 0) |
| { |
| Enforce(res_arg <= max_size(), (std::length_error*)0, ""); |
| Storage::reserve(res_arg); |
| } |
| |
| void clear() |
| { resize(0); } |
| |
| bool empty() const |
| { return size() == 0; } |
| |
| // 21.3.4 element access: |
| const_reference operator[](size_type pos) const |
| { return *(begin() + pos); } |
| |
| reference operator[](size_type pos) |
| { return *(begin() + pos); } |
| |
| const_reference at(size_type n) const |
| { |
| Enforce(n < size(), (std::out_of_range*)0, ""); |
| return (*this)[n]; |
| } |
| |
| reference at(size_type n) |
| { |
| Enforce(n < size(), (std::out_of_range*)0, ""); |
| return (*this)[n]; |
| } |
| |
| // 21.3.5 modifiers: |
| flex_string& operator+=(const flex_string& str) |
| { return append(str); } |
| |
| flex_string& operator+=(const value_type* s) |
| { return append(s); } |
| |
| flex_string& operator+=(value_type c) |
| { |
| push_back(c); |
| return *this; |
| } |
| |
| flex_string& append(const flex_string& str) |
| { return append(str, 0, npos); } |
| |
| flex_string& append(const flex_string& str, const size_type pos, |
| size_type n) |
| { |
| const size_type sz = str.size(); |
| Enforce(pos <= sz, (std::out_of_range*)0, ""); |
| Procust(n, sz - pos); |
| return append(str.c_str() + pos, n); |
| } |
| |
| flex_string& append(const value_type* s, const size_type n) |
| { |
| #ifndef NDEBUG |
| Invariant checker(*this); |
| #endif |
| if (IsAliasedRange(s, s + n)) |
| { |
| const size_type offset = s - &*begin(); |
| Storage::reserve(size() + n); |
| s = &*begin() + offset; |
| } |
| Storage::append(s, s+ n); |
| return *this; |
| } |
| |
| flex_string& append(const value_type* s) |
| { return append(s, traits_type::length(s)); } |
| |
| flex_string& append(size_type n, value_type c) |
| { |
| resize(size() + n, c); |
| return *this; |
| } |
| |
| template<class InputIterator> |
| flex_string& append(InputIterator first, InputIterator last) |
| { |
| insert(end(), first, last); |
| return *this; |
| } |
| |
| void push_back(value_type c) |
| { |
| const size_type cap = capacity(); |
| if (size() == cap) |
| { |
| reserve(cap << 1u); |
| } |
| Storage::append(&c, &c + 1); |
| } |
| |
| flex_string& assign(const flex_string& str) |
| { |
| if (&str == this) return *this; |
| return assign(str.data(), str.size()); |
| } |
| |
| flex_string& assign(const flex_string& str, size_type pos, |
| size_type n) |
| { |
| const size_type sz = str.size(); |
| Enforce(pos <= str.size(), (std::out_of_range*)0, ""); |
| Procust(n, sz - pos); |
| return assign(str.data() + pos, n); |
| } |
| |
| flex_string& assign(const value_type* s, size_type n) |
| { |
| #ifndef NDEBUG |
| Invariant checker(*this); |
| #endif |
| if (size() >= n) |
| { |
| std::copy(s, s + n, begin()); |
| resize(n); |
| } |
| else |
| { |
| const value_type *const s2 = s + size(); |
| std::copy(s, s2, begin()); |
| append(s2, n - size()); |
| } |
| return *this; |
| } |
| |
| flex_string& assign(const value_type* s) |
| { return assign(s, traits_type::length(s)); } |
| |
| template <class ItOrLength, class ItOrChar> |
| flex_string& assign(ItOrLength first_or_n, ItOrChar last_or_c) |
| { return replace(begin(), end(), first_or_n, last_or_c); } |
| |
| flex_string& insert(size_type pos1, const flex_string& str) |
| { return insert(pos1, str.data(), str.size()); } |
| |
| flex_string& insert(size_type pos1, const flex_string& str, |
| size_type pos2, size_type n) |
| { |
| Enforce(pos2 <= str.length(), (std::out_of_range*)0, ""); |
| Procust(n, str.length() - pos2); |
| return insert(pos1, str.data() + pos2, n); |
| } |
| |
| flex_string& insert(size_type pos, const value_type* s, size_type n) |
| { |
| Enforce(pos <= length(), (std::out_of_range*)0, ""); |
| insert(begin() + pos, s, s + n); |
| return *this; |
| } |
| |
| flex_string& insert(size_type pos, const value_type* s) |
| { return insert(pos, s, traits_type::length(s)); } |
| |
| flex_string& insert(size_type pos, size_type n, value_type c) |
| { |
| Enforce(pos <= length(), (std::out_of_range*)0, ""); |
| insert(begin() + pos, n, c); |
| return *this; |
| } |
| |
| iterator insert(iterator p, value_type c = value_type()) |
| { |
| const size_type pos = p - begin(); |
| insert(pos, &c, 1); |
| return begin() + pos; |
| } |
| |
| private: |
| // Care must be taken when dereferencing some iterator types. |
| // |
| // Users can implement this function in their namespace if their storage |
| // uses a special iterator type, the function will be found through ADL. |
| template<class Iterator> |
| const typename std::iterator_traits<Iterator>::value_type* |
| DereferenceValidIterator(Iterator it) const |
| { |
| return &*it; |
| } |
| |
| // Care must be taken when dereferencing a reverse iterators, hence this |
| // special case. This isn't in the std namespace so as not to pollute it or |
| // create name clashes. |
| template<typename Iterator> |
| const typename std::iterator_traits<Iterator>::value_type* |
| DereferenceValidIterator(std::reverse_iterator<Iterator> it) const |
| { |
| return &*--it; |
| } |
| |
| // Determine if the range aliases the current string. |
| // |
| // This method cannot be const because calling begin/end on copy-on-write |
| // implementations must have side effects. |
| // A const version wouldn't make the string unique through this call. |
| template<class Iterator> |
| bool IsAliasedRange(Iterator beginIterator, Iterator endIterator) |
| { |
| if(!empty() && beginIterator != endIterator) |
| { |
| typedef const typename std::iterator_traits<Iterator>::value_type * |
| pointer; |
| |
| pointer myBegin(&*begin()); |
| pointer myEnd(&*begin() + size()); |
| pointer rangeBegin(DereferenceValidIterator(beginIterator)); |
| |
| const std::less_equal<pointer> less_equal = std::less_equal<pointer>(); |
| if(less_equal(myBegin, rangeBegin) && less_equal(rangeBegin, myEnd)) |
| return true; |
| } |
| return false; |
| } |
| |
| template <int i> class Selector {}; |
| |
| flex_string& InsertImplDiscr(iterator p, |
| size_type n, value_type c, Selector<1>) |
| { |
| #ifndef NDEBUG |
| Invariant checker(*this); |
| #endif |
| assert(begin() <= p && p <= end()); |
| const size_type insertOffset(p - begin()); |
| const size_type originalSize(size()); |
| if(n < originalSize - insertOffset) |
| { |
| // The new characters fit within the original string. |
| // The characters that are pushed back need to be moved because |
| // they're aliased. |
| // The appended characters will all be overwritten by the move. |
| append(n, value_type(0)); |
| value_type* begin(&*begin()); |
| flex_string_details::pod_move(begin + insertOffset, |
| begin + originalSize, begin + insertOffset + n); |
| std::fill(begin + insertOffset, begin + insertOffset + n, c); |
| } |
| else |
| { |
| // The new characters exceed the original string. |
| // The characters that are pushed back can simply be copied since |
| // they aren't aliased. |
| // The appended characters will partly be overwritten by the copy. |
| append(n, c); |
| value_type* begin(&*begin()); |
| flex_string_details::pod_copy(begin + insertOffset, |
| begin + originalSize, begin + insertOffset + n); |
| std::fill(begin + insertOffset, begin + originalSize, c); |
| } |
| return *this; |
| } |
| |
| template<class InputIterator> |
| flex_string& InsertImplDiscr(iterator i, |
| InputIterator b, InputIterator e, Selector<0>) |
| { |
| InsertImpl(i, b, e, |
| typename std::iterator_traits<InputIterator>::iterator_category()); |
| return *this; |
| } |
| |
| template <class FwdIterator> |
| void InsertImpl(iterator i, |
| FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) |
| { |
| if(s1 == s2) |
| { |
| // Insert an empty range. |
| return; |
| } |
| |
| if(IsAliasedRange(s1, s2)) |
| { |
| // The source range is contained in the current string, copy it |
| // and recurse. |
| const flex_string temporary(s1, s2); |
| InsertImpl(i, temporary.begin(), temporary.end(), |
| typename std::iterator_traits<FwdIterator>::iterator_category()); |
| return; |
| } |
| |
| #ifndef NDEBUG |
| Invariant checker(*this); |
| #endif |
| const size_type pos = i - begin(); |
| const typename std::iterator_traits<FwdIterator>::difference_type n2 = |
| std::distance(s1, s2); |
| |
| assert(n2 >= 0); |
| using namespace flex_string_details; |
| assert(pos <= size()); |
| |
| const typename std::iterator_traits<FwdIterator>::difference_type maxn2 = |
| capacity() - size(); |
| if (maxn2 < n2) |
| { |
| // Reallocate the string. |
| assert(!IsAliasedRange(s1, s2)); |
| reserve(size() + n2); |
| i = begin() + pos; |
| } |
| if (pos + n2 <= size()) |
| { |
| const iterator tailBegin = end() - n2; |
| Storage::append(tailBegin, tailBegin + n2); |
| std::copy(reverse_iterator(tailBegin), reverse_iterator(i), |
| reverse_iterator(tailBegin + n2)); |
| std::copy(s1, s2, i); |
| } |
| else |
| { |
| FwdIterator t = s1; |
| const size_type old_size = size(); |
| std::advance(t, old_size - pos); |
| assert(std::distance(t, s2) >= 0); |
| Storage::append(t, s2); |
| Storage::append(data() + pos, data() + old_size); |
| std::copy(s1, t, i); |
| } |
| } |
| |
| template <class InputIterator> |
| void InsertImpl(iterator insertPosition, |
| InputIterator inputBegin, InputIterator inputEnd, |
| std::input_iterator_tag) |
| { |
| flex_string temporary(begin(), insertPosition); |
| for (; inputBegin != inputEnd; ++inputBegin) |
| { |
| temporary.push_back(*inputBegin); |
| } |
| temporary.append(insertPosition, end()); |
| swap(temporary); |
| } |
| |
| public: |
| template <class ItOrLength, class ItOrChar> |
| void insert(iterator p, ItOrLength first_or_n, ItOrChar last_or_c) |
| { |
| Selector<std::numeric_limits<ItOrLength>::is_specialized> sel; |
| InsertImplDiscr(p, first_or_n, last_or_c, sel); |
| } |
| |
| flex_string& erase(size_type pos = 0, size_type n = npos) |
| { |
| #ifndef NDEBUG |
| Invariant checker(*this); |
| #endif |
| Enforce(pos <= length(), (std::out_of_range*)0, ""); |
| Procust(n, length() - pos); |
| std::copy(begin() + pos + n, end(), begin() + pos); |
| resize(length() - n); |
| return *this; |
| } |
| |
| iterator erase(iterator position) |
| { |
| const size_type pos(position - begin()); |
| erase(pos, 1); |
| return begin() + pos; |
| } |
| |
| iterator erase(iterator first, iterator last) |
| { |
| const size_type pos(first - begin()); |
| erase(pos, last - first); |
| return begin() + pos; |
| } |
| |
| // Replaces at most n1 chars of *this, starting with pos1 with the content of str |
| flex_string& replace(size_type pos1, size_type n1, const flex_string& str) |
| { return replace(pos1, n1, str, 0, npos); } |
| |
| // Replaces at most n1 chars of *this, starting with pos1, |
| // with at most n2 chars of str starting with pos2 |
| flex_string& replace(size_type pos1, size_type n1, const flex_string& str, |
| size_type pos2, size_type n2) |
| { |
| Enforce(pos2 <= str.length(), (std::out_of_range*)0, ""); |
| return replace(pos1, n1, str.data() + pos2, |
| Min(n2, str.size() - pos2)); |
| } |
| |
| // Replaces at most n1 chars of *this, starting with pos, with chars from s |
| flex_string& replace(size_type pos, size_type n1, const value_type* s) |
| { return replace(pos, n1, s, traits_type::length(s)); } |
| |
| // Replaces at most n1 chars of *this, starting with pos, with n2 occurences of c |
| // consolidated with |
| // Replaces at most n1 chars of *this, starting with pos, |
| // with at most n2 chars of str. |
| // str must have at least n2 chars. |
| template <class StrOrLength, class NumOrChar> |
| flex_string& replace(size_type pos, size_type n1, |
| StrOrLength s_or_n2, NumOrChar n_or_c) |
| { |
| #ifndef NDEBUG |
| Invariant checker(*this); |
| #endif |
| Enforce(pos <= size(), (std::out_of_range*)0, ""); |
| Procust(n1, length() - pos); |
| const iterator b = begin() + pos; |
| return replace(b, b + n1, s_or_n2, n_or_c); |
| } |
| |
| flex_string& replace(iterator i1, iterator i2, const flex_string& str) |
| { return replace(i1, i2, str.c_str(), str.length()); } |
| |
| flex_string& replace(iterator i1, iterator i2, const value_type* s) |
| { return replace(i1, i2, s, traits_type::length(s)); } |
| |
| private: |
| flex_string& ReplaceImplDiscr(iterator i1, iterator i2, |
| const value_type* s, size_type n, Selector<2>) |
| { |
| assert(i1 <= i2); |
| assert(begin() <= i1 && i1 <= end()); |
| assert(begin() <= i2 && i2 <= end()); |
| return replace(i1, i2, s, s + n); |
| } |
| |
| flex_string& ReplaceImplDiscr(iterator i1, iterator i2, |
| size_type n2, value_type c, Selector<1>) |
| { |
| const size_type n1 = i2 - i1; |
| if (n1 > n2) |
| { |
| std::fill(i1, i1 + n2, c); |
| erase(i1 + n2, i2); |
| } |
| else |
| { |
| std::fill(i1, i2, c); |
| insert(i2, n2 - n1, c); |
| } |
| return *this; |
| } |
| |
| template <class InputIterator> |
| flex_string& ReplaceImplDiscr(iterator i1, iterator i2, |
| InputIterator b, InputIterator e, Selector<0>) |
| { |
| ReplaceImpl(i1, i2, b, e, |
| typename std::iterator_traits<InputIterator>::iterator_category()); |
| return *this; |
| } |
| |
| template <class FwdIterator> |
| void ReplaceImpl(iterator i1, iterator i2, |
| FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) |
| { |
| #ifndef NDEBUG |
| Invariant checker(*this); |
| #endif |
| const typename std::iterator_traits<iterator>::difference_type n1 = |
| i2 - i1; |
| assert(n1 >= 0); |
| const typename std::iterator_traits<FwdIterator>::difference_type n2 = |
| std::distance(s1, s2); |
| assert(n2 >= 0); |
| |
| if (IsAliasedRange(s1, s2)) |
| { |
| // Aliased replace, copy to new string. |
| flex_string temporary; |
| temporary.reserve(size() - n1 + n2); |
| temporary.append(begin(), i1).append(s1, s2).append(i2, end()); |
| swap(temporary); |
| return; |
| } |
| |
| if (n1 > n2) |
| { |
| // Shrinks |
| std::copy(s1, s2, i1); |
| erase(i1 + n2, i2); |
| } |
| else |
| { |
| // Grows |
| flex_string_details::copy_n(s1, n1, i1); |
| std::advance(s1, n1); |
| insert(i2, s1, s2); |
| } |
| } |
| |
| template <class InputIterator> |
| void ReplaceImpl(iterator i1, iterator i2, |
| InputIterator b, InputIterator e, std::input_iterator_tag) |
| { |
| flex_string temp(begin(), i1); |
| temp.append(b, e).append(i2, end()); |
| swap(temp); |
| } |
| |
| public: |
| template <class T1, class T2> |
| flex_string& replace(iterator i1, iterator i2, |
| T1 first_or_n_or_s, T2 last_or_c_or_n) |
| { |
| const bool |
| num1 = std::numeric_limits<T1>::is_specialized, |
| num2 = std::numeric_limits<T2>::is_specialized; |
| return ReplaceImplDiscr(i1, i2, first_or_n_or_s, last_or_c_or_n, |
| Selector<num1 ? (num2 ? 1 : -1) : (num2 ? 2 : 0)>()); |
| } |
| |
| size_type copy(value_type* s, size_type n, size_type pos = 0) const |
| { |
| Enforce(pos <= size(), (std::out_of_range*)0, ""); |
| n = Min(n, size() - pos); |
| |
| flex_string_details::pod_copy( |
| &*begin() + pos, |
| &*begin() + pos + n, |
| s); |
| return n; |
| } |
| |
| void swap(flex_string& rhs) |
| { |
| Storage& srhs = rhs; |
| this->Storage::swap(srhs); |
| } |
| |
| // 21.3.6 string operations: |
| const value_type* c_str() const |
| { return Storage::c_str(); } |
| |
| const value_type* data() const |
| { return Storage::data(); } |
| |
| allocator_type get_allocator() const |
| { return Storage::get_allocator(); } |
| |
| size_type find(const flex_string& str, size_type pos = 0) const |
| { return find(str.data(), pos, str.length()); } |
| |
| size_type find (const value_type* s, size_type pos, size_type n) const |
| { |
| const size_type size_(size()); |
| if (n + pos > size_) |
| return npos; |
| for (; pos < size_; ++pos) |
| { |
| if (traits_type::compare(&*begin() + pos, s, n) == 0) |
| { |
| return pos; |
| } |
| } |
| return npos; |
| } |
| |
| size_type find (const value_type* s, size_type pos = 0) const |
| { return find(s, pos, traits_type::length(s)); } |
| |
| size_type find (value_type c, size_type pos = 0) const |
| { return find(&c, pos, 1); } |
| |
| size_type rfind(const flex_string& str, size_type pos = npos) const |
| { return rfind(str.c_str(), pos, str.length()); } |
| |
| size_type rfind(const value_type* s, size_type pos, size_type n) const |
| { |
| if (n > length()) return npos; |
| pos = Min(pos, length() - n); |
| if (n == 0) return pos; |
| |
| const_iterator i(begin() + pos); |
| for (; ; --i) |
| { |
| if (traits_type::eq(*i, *s) |
| && traits_type::compare(&*i, s, n) == 0) |
| { |
| return i - begin(); |
| } |
| if (i == begin()) break; |
| } |
| return npos; |
| } |
| |
| size_type rfind(const value_type* s, size_type pos = npos) const |
| { return rfind(s, pos, traits_type::length(s)); } |
| |
| size_type rfind(value_type c, size_type pos = npos) const |
| { return rfind(&c, pos, 1); } |
| |
| size_type find_first_of(const flex_string& str, size_type pos = 0) const |
| { return find_first_of(str.c_str(), pos, str.length()); } |
| |
| size_type find_first_of(const value_type* s, |
| size_type pos, size_type n) const |
| { |
| if (pos > length() || n == 0) return npos; |
| const_iterator i(begin() + pos), |
| finish(end()); |
| for (; i != finish; ++i) |
| { |
| if (traits_type::find(s, n, *i) != 0) |
| { |
| return i - begin(); |
| } |
| } |
| return npos; |
| } |
| |
| size_type find_first_of(const value_type* s, size_type pos = 0) const |
| { return find_first_of(s, pos, traits_type::length(s)); } |
| |
| size_type find_first_of(value_type c, size_type pos = 0) const |
| { return find_first_of(&c, pos, 1); } |
| |
| size_type find_last_of (const flex_string& str, |
| size_type pos = npos) const |
| { return find_last_of(str.c_str(), pos, str.length()); } |
| |
| size_type find_last_of (const value_type* s, size_type pos, |
| size_type n) const |
| { |
| if (!empty() && n > 0) |
| { |
| pos = Min(pos, length() - 1); |
| const_iterator i(begin() + pos); |
| for (;; --i) |
| { |
| if (traits_type::find(s, n, *i) != 0) |
| { |
| return i - begin(); |
| } |
| if (i == begin()) break; |
| } |
| } |
| return npos; |
| } |
| |
| size_type find_last_of (const value_type* s, |
| size_type pos = npos) const |
| { return find_last_of(s, pos, traits_type::length(s)); } |
| |
| size_type find_last_of (value_type c, size_type pos = npos) const |
| { return find_last_of(&c, pos, 1); } |
| |
| size_type find_first_not_of(const flex_string& str, |
| size_type pos = 0) const |
| { return find_first_not_of(str.data(), pos, str.size()); } |
| |
| size_type find_first_not_of(const value_type* s, size_type pos, |
| size_type n) const |
| { |
| if (pos < length()) |
| { |
| const_iterator |
| i(begin() + pos), |
| finish(end()); |
| for (; i != finish; ++i) |
| { |
| if (traits_type::find(s, n, *i) == 0) |
| { |
| return i - begin(); |
| } |
| } |
| } |
| return npos; |
| } |
| |
| size_type find_first_not_of(const value_type* s, |
| size_type pos = 0) const |
| { return find_first_not_of(s, pos, traits_type::length(s)); } |
| |
| size_type find_first_not_of(value_type c, size_type pos = 0) const |
| { return find_first_not_of(&c, pos, 1); } |
| |
| size_type find_last_not_of(const flex_string& str, |
| size_type pos = npos) const |
| { return find_last_not_of(str.c_str(), pos, str.length()); } |
| |
| size_type find_last_not_of(const value_type* s, size_type pos, |
| size_type n) const |
| { |
| if (!empty()) |
| { |
| pos = Min(pos, size() - 1); |
| const_iterator i(begin() + pos); |
| for (;; --i) |
| { |
| if (traits_type::find(s, n, *i) == 0) |
| { |
| return i - begin(); |
| } |
| if (i == begin()) break; |
| } |
| } |
| return npos; |
| } |
| |
| size_type find_last_not_of(const value_type* s, |
| size_type pos = npos) const |
| { return find_last_not_of(s, pos, traits_type::length(s)); } |
| |
| size_type find_last_not_of (value_type c, size_type pos = npos) const |
| { return find_last_not_of(&c, pos, 1); } |
| |
| flex_string substr(size_type pos = 0, size_type n = npos) const |
| { |
| Enforce(pos <= size(), (std::out_of_range*)0, ""); |
| return flex_string(data() + pos, Min(n, size() - pos)); |
| } |
| |
| std::ptrdiff_t compare(const flex_string& str) const |
| { |
| // FIX due to Goncalo N M de Carvalho July 18, 2005 |
| return compare(0, size(), str); |
| } |
| |
| std::ptrdiff_t compare(size_type pos1, size_type n1, |
| const flex_string& str) const |
| { return compare(pos1, n1, str.data(), str.size()); } |
| |
| // FIX to compare: added the TC |
| // (http://www.comeaucomputing.com/iso/lwg-defects.html number 5) |
| // Thanks to Caleb Epstein for the fix |
| std::ptrdiff_t compare(size_type pos1, size_type n1, |
| const value_type* s) const |
| { |
| return compare(pos1, n1, s, traits_type::length(s)); |
| } |
| |
| std::ptrdiff_t compare(size_type pos1, size_type n1, |
| const value_type* s, size_type n2) const |
| { |
| Enforce(pos1 <= size(), (std::out_of_range*)0, ""); |
| Procust(n1, size() - pos1); |
| const int r = traits_type::compare(pos1 + data(), s, Min(n1, n2)); |
| return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0; |
| } |
| |
| std::ptrdiff_t compare(size_type pos1, size_type n1, |
| const flex_string& str, |
| size_type pos2, size_type n2) const |
| { |
| Enforce(pos2 <= str.size(), (std::out_of_range*)0, ""); |
| return compare(pos1, n1, str.data() + pos2, Min(n2, str.size() - pos2)); |
| } |
| |
| std::ptrdiff_t compare(const value_type* s) const |
| { |
| // Could forward to compare(0, size(), s, traits_type::length(s)) |
| // but that does two extra checks |
| const size_type n1(size()), n2(traits_type::length(s)); |
| const int r = traits_type::compare(data(), s, Min(n1, n2)); |
| return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0; |
| } |
| }; |
| |
| // non-member functions |
| template <typename E, class T, class A, class S> |
| flex_string<E, T, A, S> operator+(const flex_string<E, T, A, S>& lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { |
| flex_string<E, T, A, S> result; |
| result.reserve(lhs.size() + rhs.size()); |
| result.append(lhs).append(rhs); |
| return result; |
| } |
| |
| template <typename E, class T, class A, class S> |
| flex_string<E, T, A, S> operator+(const typename flex_string<E, T, A, S>::value_type* lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { |
| flex_string<E, T, A, S> result; |
| const typename flex_string<E, T, A, S>::size_type len = |
| flex_string<E, T, A, S>::traits_type::length(lhs); |
| result.reserve(len + rhs.size()); |
| result.append(lhs, len).append(rhs); |
| return result; |
| } |
| |
| template <typename E, class T, class A, class S> |
| flex_string<E, T, A, S> operator+( |
| typename flex_string<E, T, A, S>::value_type lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { |
| flex_string<E, T, A, S> result; |
| result.reserve(1 + rhs.size()); |
| result.push_back(lhs); |
| result.append(rhs); |
| return result; |
| } |
| |
| template <typename E, class T, class A, class S> |
| flex_string<E, T, A, S> operator+(const flex_string<E, T, A, S>& lhs, |
| const typename flex_string<E, T, A, S>::value_type* rhs) |
| { |
| typedef typename flex_string<E, T, A, S>::size_type size_type; |
| typedef typename flex_string<E, T, A, S>::traits_type traits_type; |
| |
| flex_string<E, T, A, S> result; |
| const size_type len = traits_type::length(rhs); |
| result.reserve(lhs.size() + len); |
| result.append(lhs).append(rhs, len); |
| return result; |
| } |
| |
| template <typename E, class T, class A, class S> |
| flex_string<E, T, A, S> operator+(const flex_string<E, T, A, S>& lhs, |
| typename flex_string<E, T, A, S>::value_type rhs) |
| { |
| flex_string<E, T, A, S> result; |
| result.reserve(lhs.size() + 1); |
| result.append(lhs); |
| result.push_back(rhs); |
| return result; |
| } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator==(const flex_string<E, T, A, S>& lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { return lhs.compare(rhs) == 0; } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator==(const typename flex_string<E, T, A, S>::value_type* lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { return rhs == lhs; } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator==(const flex_string<E, T, A, S>& lhs, |
| const typename flex_string<E, T, A, S>::value_type* rhs) |
| { return lhs.compare(rhs) == 0; } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator!=(const flex_string<E, T, A, S>& lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { return !(lhs == rhs); } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator!=(const typename flex_string<E, T, A, S>::value_type* lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { return !(lhs == rhs); } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator!=(const flex_string<E, T, A, S>& lhs, |
| const typename flex_string<E, T, A, S>::value_type* rhs) |
| { return !(lhs == rhs); } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator<(const flex_string<E, T, A, S>& lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { return lhs.compare(rhs) < 0; } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator<(const flex_string<E, T, A, S>& lhs, |
| const typename flex_string<E, T, A, S>::value_type* rhs) |
| { return lhs.compare(rhs) < 0; } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator<(const typename flex_string<E, T, A, S>::value_type* lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { return rhs.compare(lhs) > 0; } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator>(const flex_string<E, T, A, S>& lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { return rhs < lhs; } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator>(const flex_string<E, T, A, S>& lhs, |
| const typename flex_string<E, T, A, S>::value_type* rhs) |
| { return rhs < lhs; } |
| |
| template <typename E, class T, class A, class S> |
| bool operator>(const typename flex_string<E, T, A, S>::value_type* lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { return rhs < lhs; } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator<=(const flex_string<E, T, A, S>& lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { return !(rhs < lhs); } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator<=(const flex_string<E, T, A, S>& lhs, |
| const typename flex_string<E, T, A, S>::value_type* rhs) |
| { return !(rhs < lhs); } |
| |
| template <typename E, class T, class A, class S> |
| bool operator<=(const typename flex_string<E, T, A, S>::value_type* lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { return !(rhs < lhs); } |
| |
| template <typename E, class T, class A, class S> |
| bool operator>=(const flex_string<E, T, A, S>& lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { return !(lhs < rhs); } |
| |
| template <typename E, class T, class A, class S> |
| bool operator>=(const flex_string<E, T, A, S>& lhs, |
| const typename flex_string<E, T, A, S>::value_type* rhs) |
| { return !(lhs < rhs); } |
| |
| template <typename E, class T, class A, class S> |
| inline bool operator>=(const typename flex_string<E, T, A, S>::value_type* lhs, |
| const flex_string<E, T, A, S>& rhs) |
| { return !(lhs < rhs); } |
| |
| template <typename E, class T, class A, class S> |
| void swap(flex_string<E, T, A, S>& lhs, flex_string<E, T, A, S>& rhs) |
| { |
| // subclause 21.3.7.8: |
| lhs.swap(rhs); |
| } |
| |
| template <typename E, class T, class A, class S> |
| inline std::basic_istream<typename flex_string<E, T, A, S>::value_type, |
| typename flex_string<E, T, A, S>::traits_type>& |
| operator>>( |
| std::basic_istream<typename flex_string<E, T, A, S>::value_type, |
| typename flex_string<E, T, A, S>::traits_type>& is, |
| flex_string<E, T, A, S>& str); |
| |
| template <typename E, class T, class A, class S> |
| std::basic_ostream<typename flex_string<E, T, A, S>::value_type, |
| typename flex_string<E, T, A, S>::traits_type>& |
| operator<<( |
| std::basic_ostream<typename flex_string<E, T, A, S>::value_type, |
| typename flex_string<E, T, A, S>::traits_type>& os, |
| const flex_string<E, T, A, S>& str) |
| { return os << str.c_str(); } |
| |
| |
| // The getline below implementations are from the SGI STL (http://www.sgi.com/tech/stl/) |
| // and come with the following copyright: |
| // |
| // Permission to use, copy, modify, distribute and sell this software and its |
| // documentation for any purpose is hereby granted without fee, provided that |
| // the below copyright notice appears in all copies and that both the copyright |
| // notice and this permission notice appear in supporting documentation. Silicon |
| // Graphics makes no representations about the suitability of this software for |
| // any purpose. It is provided "as is" without express or implied warranty. |
| // |
| // Copyright (c) 1997-1999 |
| // Silicon Graphics Computer Systems, Inc. |
| // |
| // Copyright (c) 1994 |
| // Hewlett-Packard Company |
| |
| template <typename E, class T, class A, class S> |
| std::basic_istream<typename flex_string<E, T, A, S>::value_type, |
| typename flex_string<E, T, A, S>::traits_type>& |
| getline( |
| std::basic_istream<typename flex_string<E, T, A, S>::value_type, |
| typename flex_string<E, T, A, S>::traits_type>& is, |
| flex_string<E, T, A, S>& str, |
| typename flex_string<E, T, A, S>::value_type delim) |
| { |
| size_t nread = 0; |
| typename std::basic_istream<typename flex_string<E, T, A, S>::value_type, |
| typename flex_string<E, T, A, S>::traits_type>::sentry sentry(is, true); |
| |
| if (sentry) { |
| std::basic_streambuf<typename flex_string<E, T, A, S>::value_type, |
| typename flex_string<E, T, A, S>::traits_type>* buf = is.rdbuf(); |
| str.clear(); |
| |
| while (nread < str.max_size()) { |
| int c1 = buf->sbumpc(); |
| if (flex_string<E, T, A, S>::traits_type::eq_int_type(c1, |
| flex_string<E, T, A, S>::traits_type::eof())) |
| { |
| is.setstate(std::ios_base::eofbit); |
| break; |
| } |
| else { |
| ++nread; |
| typename flex_string<E, T, A, S>::value_type c = |
| flex_string<E, T, A, S>::traits_type::to_char_type(c1); |
| |
| if (!flex_string<E, T, A, S>::traits_type::eq(c, delim)) |
| str.push_back(c); |
| else |
| break; // Character is extracted but not appended. |
| } |
| } |
| } |
| if (nread == 0 || nread >= str.max_size()) |
| is.setstate(std::ios_base::failbit); |
| |
| return is; |
| } |
| |
| template <typename E, class T, class A, class S> |
| std::basic_istream<typename flex_string<E, T, A, S>::value_type, |
| typename flex_string<E, T, A, S>::traits_type>& |
| getline( |
| std::basic_istream<typename flex_string<E, T, A, S>::value_type, |
| typename flex_string<E, T, A, S>::traits_type>& is, |
| flex_string<E, T, A, S>& str) |
| { |
| return getline(is, str, is.widen('\n')); |
| } |
| |
| template <typename E1, class T, class A, class S> |
| const typename flex_string<E1, T, A, S>::size_type |
| flex_string<E1, T, A, S>::npos = (typename flex_string<E1, T, A, S>::size_type)(-1); |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| } // namespace util |
| } // namespace wave |
| } // namespace boost |
| |
| #if BOOST_WAVE_SERIALIZATION != 0 |
| /////////////////////////////////////////////////////////////////////////////// |
| namespace boost { namespace serialization { |
| |
| #if !defined(BOOST_WAVE_FLEX_STRING_SERIALIZATION_HACK) |
| |
| // FIXME: This doesn't work because of the missing flex_string::operator>>() |
| template <typename E, class T, class A, class S> |
| struct implementation_level<boost::wave::util::flex_string<E, T, A, S> > |
| { |
| typedef mpl::integral_c_tag tag; |
| typedef mpl::int_<boost::serialization::primitive_type> type; |
| BOOST_STATIC_CONSTANT( |
| int, |
| value = implementation_level::type::value |
| ); |
| }; |
| |
| #else |
| |
| // We serialize flex_strings as vectors of char's for now |
| template<class Archive, typename E, class T, class A, class S> |
| inline void save(Archive & ar, |
| boost::wave::util::flex_string<E, T, A, S> const &t, |
| const unsigned int file_version) |
| { |
| boost::serialization::stl::save_collection< |
| Archive, wave::util::flex_string<E, T, A, S> >(ar, t); |
| } |
| |
| template<class Archive, typename E, class T, class A, class S> |
| inline void load(Archive & ar, boost::wave::util::flex_string<E, T, A, S> &t, |
| const unsigned int file_version) |
| { |
| boost::serialization::stl::load_collection< |
| Archive, boost::wave::util::flex_string<E, T, A, S>, |
| boost::serialization::stl::archive_input_seq< |
| Archive, boost::wave::util::flex_string<E, T, A, S> >, |
| boost::serialization::stl::reserve_imp< |
| boost::wave::util::flex_string<E, T, A, S> > |
| >(ar, t); |
| } |
| |
| // split non-intrusive serialization function member into separate |
| // non intrusive save/load member functions |
| template<class Archive, typename E, class T, class A, class S> |
| inline void serialize(Archive & ar, boost::wave::util::flex_string<E, T, A, S> &t, |
| const unsigned int file_version) |
| { |
| boost::serialization::split_free(ar, t, file_version); |
| } |
| |
| #endif |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| }} // boost::serialization |
| #endif |
| |
| // the suffix header occurs after all of the code |
| #ifdef BOOST_HAS_ABI_HEADERS |
| #include BOOST_ABI_SUFFIX |
| #endif |
| |
| #endif // FLEX_STRING_INC_ |