2605 lines
75 KiB
C++
2605 lines
75 KiB
C++
/*=============================================================================
|
|
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
|
|
//
|
|
// #HK130910:
|
|
// - Removed the getline implementation which was borrowed from the SGI
|
|
// STL as the license for this code is not compatible with Boost.
|
|
|
|
#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 <ios>
|
|
|
|
#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; BOOST_FALLTHROUGH;
|
|
case 7: *b = c; ++b; BOOST_FALLTHROUGH;
|
|
case 6: *b = c; ++b; BOOST_FALLTHROUGH;
|
|
case 5: *b = c; ++b; BOOST_FALLTHROUGH;
|
|
case 4: *b = c; ++b; BOOST_FALLTHROUGH;
|
|
case 3: *b = c; ++b; BOOST_FALLTHROUGH;
|
|
case 2: *b = c; ++b; BOOST_FALLTHROUGH;
|
|
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 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
|
|
BOOST_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);
|
|
|
|
BOOST_ASSERT(n2 >= 0);
|
|
using namespace flex_string_details;
|
|
BOOST_ASSERT(pos <= size());
|
|
|
|
const typename std::iterator_traits<FwdIterator>::difference_type maxn2 =
|
|
capacity() - size();
|
|
if (maxn2 < n2)
|
|
{
|
|
// Reallocate the string.
|
|
BOOST_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);
|
|
BOOST_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>)
|
|
{
|
|
BOOST_ASSERT(i1 <= i2);
|
|
BOOST_ASSERT(begin() <= i1 && i1 <= end());
|
|
BOOST_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;
|
|
BOOST_ASSERT(n1 >= 0);
|
|
const typename std::iterator_traits<FwdIterator>::difference_type n2 =
|
|
std::distance(s1, s2);
|
|
BOOST_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(); }
|
|
|
|
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_
|