#include "wx/stringops.h"
#include "wx/unichar.h"
+// by default we cache the mapping of the positions in UTF-8 string to the byte
+// offset as this results in noticeable performance improvements for loops over
+// strings using indices; comment out this line to disable this
+//
+// notice that this optimization is well worth using even in debug builds as it
+// changes asymptotic complexity of algorithms using indices to iterate over
+// wxString back to expected linear from quadratic
+//
+// also notice that wxTLS_TYPE() (__declspec(thread) in this case) is unsafe to
+// use in DLL build under pre-Vista Windows so we disable this code for now, if
+// anybody really needs to use UTF-8 build under Windows with this optimization
+// it would have to be re-tested and probably corrected
+#if wxUSE_UNICODE_UTF8 && !defined(__WXMSW__)
+ #define wxUSE_STRING_POS_CACHE 1
+#else
+ #define wxUSE_STRING_POS_CACHE 0
+#endif
+
+#if wxUSE_STRING_POS_CACHE
+ #include "wx/tls.h"
+
+ // change this 0 to 1 to enable additional (very expensive) asserts
+ // verifying that string caching logic works as expected
+ #if 0
+ #define wxSTRING_CACHE_ASSERT(cond) wxASSERT(cond)
+ #else
+ #define wxSTRING_CACHE_ASSERT(cond)
+ #endif
+#endif // wxUSE_STRING_POS_CACHE
+
class WXDLLIMPEXP_FWD_BASE wxString;
// unless this symbol is predefined to disable the compatibility functions, do
static size_t LenToImpl(size_t len) { return len; }
static size_t PosFromImpl(size_t pos) { return pos; }
- // we don't want to define this as an empty inline function as it could
+ // we don't want to define these as empty inline functions as it could
// result in noticeable (and quite unnecessary in non-UTF-8 build) slowdown
// in debug build where the inline functions are not effectively inlined
- #define wxSTRING_INVALIDATE_INDEX_CACHE()
+ #define wxSTRING_INVALIDATE_CACHE()
+ #define wxSTRING_INVALIDATE_CACHED_LENGTH()
+ #define wxSTRING_UPDATE_CACHED_LENGTH(n)
+ #define wxSTRING_SET_CACHED_LENGTH(n)
#else // wxUSE_UNICODE_UTF8
static SubstrBufFromWC ImplStr(const wchar_t* str, size_t n)
{ return ConvertStr(str, n, wxMBConvUTF8()); }
- // this is an extremely simple cache used by PosToImpl()
- struct PosToImplCache
+#if wxUSE_STRING_POS_CACHE
+ // this is an extremely simple cache used by PosToImpl(): each cache element
+ // contains the string it applies to and the index corresponding to the last
+ // used position in this wxString in its m_impl string
+ //
+ // NB: notice that this struct (and nested Element one) must be a POD or we
+ // wouldn't be able to use a thread-local variable of this type, in
+ // particular it should have no ctor -- we rely on statics being
+ // initialized to 0 instead
+ struct Cache
{
- const wxString *str;
- size_t pos,
- impl;
+ enum { SIZE = 8 };
+
+ struct Element
+ {
+ const wxString *str; // the string to which this element applies
+ size_t pos, // the cached index in this string
+ impl, // the corresponding position in its m_impl
+ len; // cached length or npos if unknown
+
+ // reset cached index to 0
+ void ResetPos() { pos = impl = 0; }
+
+ // reset position and length
+ void Reset() { ResetPos(); len = npos; }
+ };
+
+ // cache the indices mapping for the last few string used
+ Element cached[SIZE];
+
+ // the last used index
+ unsigned lastUsed;
};
- static PosToImplCache ms_cache;
+#ifndef wxHAS_COMPILER_TLS
+ // we must use an accessor function and not a static variable when the TLS
+ // variables support is implemented in the library (and not by the compiler)
+ // because the global s_cache variable could be not yet initialized when a
+ // ctor of another global object is executed and if that ctor uses any
+ // wxString methods, bad things happen
+ //
+ // however notice that this approach does not work when compiler TLS is used,
+ // at least not with g++ 4.1.2 under amd64 as it apparently compiles code
+ // using this accessor incorrectly when optimizations are enabled (-O2 is
+ // enough) -- luckily we don't need it then neither as static __thread
+ // variables are initialized by 0 anyhow then and so we can use the variable
+ // directly
+ WXEXPORT static Cache& GetCache()
+ {
+ static wxTLS_TYPE(Cache) s_cache;
+
+ return wxTLS_VALUE(s_cache);
+ }
+
+ // this helper struct is used to ensure that GetCache() is called during
+ // static initialization time, i.e. before any threads creation, as otherwise
+ // the static s_cache construction inside GetCache() wouldn't be MT-safe
+ friend struct wxStrCacheInitializer;
+#else // wxHAS_COMPILER_TLS
+ static wxTLS_TYPE(Cache) ms_cache;
+ static Cache& GetCache() { return wxTLS_VALUE(ms_cache); }
+#endif // !wxHAS_COMPILER_TLS/wxHAS_COMPILER_TLS
+
+ static Cache::Element *GetCacheBegin() { return GetCache().cached; }
+ static Cache::Element *GetCacheEnd() { return GetCacheBegin() + Cache::SIZE; }
+ static unsigned& LastUsedCacheElement() { return GetCache().lastUsed; }
+
+ // this is used in debug builds only to provide a convenient function,
+ // callable from a debugger, to show the cache contents
+ friend struct wxStrCacheDumper;
+
+ // uncomment this to have access to some profiling statistics on program
+ // termination
+ //#define wxPROFILE_STRING_CACHE
+
+#ifdef wxPROFILE_STRING_CACHE
+ static struct PosToImplCacheStats
+ {
+ unsigned postot, // total non-trivial calls to PosToImpl
+ poshits, // cache hits from PosToImpl()
+ mishits, // cached position beyond the needed one
+ sumpos, // sum of all positions, used to compute the
+ // average position after dividing by postot
+ sumofs, // sum of all offsets after using the cache, used to
+ // compute the average after dividing by hits
+ lentot, // number of total calls to length()
+ lenhits; // number of cache hits in length()
+ } ms_cacheStats;
+
+ friend struct wxStrCacheStatsDumper;
+
+ #define wxCACHE_PROFILE_FIELD_INC(field) ms_cacheStats.field++
+ #define wxCACHE_PROFILE_FIELD_ADD(field, val) ms_cacheStats.field += (val)
+#else // !wxPROFILE_STRING_CACHE
+ #define wxCACHE_PROFILE_FIELD_INC(field)
+ #define wxCACHE_PROFILE_FIELD_ADD(field, val)
+#endif // wxPROFILE_STRING_CACHE/!wxPROFILE_STRING_CACHE
+
+ // note: it could seem that the functions below shouldn't be inline because
+ // they are big, contain loops and so the compiler shouldn't be able to
+ // inline them anyhow, however moving them into string.cpp does decrease the
+ // code performance by ~5%, at least when using g++ 4.1 so do keep them here
+ // unless tests show that it's not advantageous any more
+
+ // return the pointer to the cache element for this string or NULL if not
+ // cached
+ Cache::Element *FindCacheElement() const
+ {
+ // profiling seems to show a small but consistent gain if we use this
+ // simple loop instead of starting from the last used element (there are
+ // a lot of misses in this function...)
+ Cache::Element * const cacheBegin = GetCacheBegin();
+#ifndef wxHAS_COMPILER_TLS
+ // during destruction tls calls may return NULL, in this case return NULL
+ // immediately without accessing anything else
+ if ( cacheBegin == NULL )
+ return NULL;
+#endif
+ Cache::Element * const cacheEnd = GetCacheEnd();
+ for ( Cache::Element *c = cacheBegin; c != cacheEnd; c++ )
+ {
+ if ( c->str == this )
+ return c;
+ }
+
+ return NULL;
+ }
- size_t PosToImpl(size_t pos) const
+ // unlike FindCacheElement(), this one always returns a valid pointer to the
+ // cache element for this string, it may have valid last cached position and
+ // its corresponding index in the byte string or not
+ Cache::Element *GetCacheElement() const
{
- if ( pos == 0 || pos == npos )
- return pos;
+ Cache::Element * const cacheBegin = GetCacheBegin();
+ Cache::Element * const cacheEnd = GetCacheEnd();
+ Cache::Element * const cacheStart = cacheBegin + LastUsedCacheElement();
+
+ // check the last used first, this does no (measurable) harm for a miss
+ // but does help for simple loops addressing the same string all the time
+ if ( cacheStart->str == this )
+ return cacheStart;
- PosToImplCache& cache = ms_cache;
- if ( this == cache.str )
+ // notice that we're going to check cacheStart again inside this call but
+ // profiling shows that it's still faster to use a simple loop like
+ // inside FindCacheElement() than manually looping with wrapping starting
+ // from the cache entry after the start one
+ Cache::Element *c = FindCacheElement();
+ if ( !c )
{
- if ( pos == cache.pos )
- return cache.impl;
+ // claim the next cache entry for this string
+ c = cacheStart;
+ if ( ++c == cacheEnd )
+ c = cacheBegin;
- // TODO: is it worth complicating this function even further by going
- // backwards from the last position? it might be if we're just
- // before it...
- if ( cache.pos > pos )
- {
- cache.pos =
- cache.impl = 0;
- }
+ c->str = this;
+ c->Reset();
+
+ // and remember the last used element
+ LastUsedCacheElement() = c - cacheBegin;
+ }
+
+ return c;
+ }
+
+ size_t DoPosToImpl(size_t pos) const
+ {
+ wxCACHE_PROFILE_FIELD_INC(postot);
+
+ // NB: although the case of pos == 1 (and offset from cached position
+ // equal to 1) are common, nothing is gained by writing special code
+ // for handling them, the compiler (at least g++ 4.1 used) seems to
+ // optimize the code well enough on its own
+
+ wxCACHE_PROFILE_FIELD_ADD(sumpos, pos);
+
+ Cache::Element * const cache = GetCacheElement();
+
+ // cached position can't be 0 so if it is, it means that this entry was
+ // used for length caching only so far, i.e. it doesn't count as a hit
+ // from our point of view
+ if ( cache->pos )
+ {
+ wxCACHE_PROFILE_FIELD_INC(poshits);
}
- else // data for this string not cached
+
+ if ( pos == cache->pos )
+ return cache->impl;
+
+ // this seems to happen only rarely so just reset the cache in this case
+ // instead of complicating code even further by seeking backwards in this
+ // case
+ if ( cache->pos > pos )
{
- cache.str = this;
- cache.pos =
- cache.impl = 0;
+ wxCACHE_PROFILE_FIELD_INC(mishits);
+
+ cache->ResetPos();
}
- wxStringImpl::const_iterator i(m_impl.begin() + cache.impl);
- for ( size_t n = cache.pos; n < pos; n++ )
+ wxCACHE_PROFILE_FIELD_ADD(sumofs, pos - cache->pos);
+
+
+ wxStringImpl::const_iterator i(m_impl.begin() + cache->impl);
+ for ( size_t n = cache->pos; n < pos; n++ )
wxStringOperations::IncIter(i);
- cache.pos = pos;
- return cache.impl = i - m_impl.begin();
+ cache->pos = pos;
+ cache->impl = i - m_impl.begin();
+
+ wxSTRING_CACHE_ASSERT(
+ (int)cache->impl == (begin() + pos).impl() - m_impl.begin() );
+
+ return cache->impl;
+ }
+
+ void InvalidateCache()
+ {
+ Cache::Element * const cache = FindCacheElement();
+ if ( cache )
+ cache->Reset();
+ }
+
+ void InvalidateCachedLength()
+ {
+ Cache::Element * const cache = FindCacheElement();
+ if ( cache )
+ cache->len = npos;
+ }
+
+ void SetCachedLength(size_t len)
+ {
+ // we optimistically cache the length here even if the string wasn't
+ // present in the cache before, this seems to do no harm and the
+ // potential for avoiding length recomputation for long strings looks
+ // interesting
+ GetCacheElement()->len = len;
}
- void InvalidatePosToImplCache()
+ void UpdateCachedLength(ptrdiff_t delta)
{
- PosToImplCache& cache = ms_cache;
- if ( cache.str == this )
+ Cache::Element * const cache = FindCacheElement();
+ if ( cache && cache->len != npos )
{
- cache.pos =
- cache.impl = 0;
+ wxSTRING_CACHE_ASSERT( (ptrdiff_t)cache->len + delta >= 0 );
+
+ cache->len += delta;
}
}
- #define wxSTRING_INVALIDATE_INDEX_CACHE() InvalidatePosToImplCache()
+ #define wxSTRING_INVALIDATE_CACHE() InvalidateCache()
+ #define wxSTRING_INVALIDATE_CACHED_LENGTH() InvalidateCachedLength()
+ #define wxSTRING_UPDATE_CACHED_LENGTH(n) UpdateCachedLength(n)
+ #define wxSTRING_SET_CACHED_LENGTH(n) SetCachedLength(n)
+#else // !wxUSE_STRING_POS_CACHE
+ size_t DoPosToImpl(size_t pos) const
+ {
+ return (begin() + pos).impl() - m_impl.begin();
+ }
+
+ #define wxSTRING_INVALIDATE_CACHE()
+ #define wxSTRING_INVALIDATE_CACHED_LENGTH()
+ #define wxSTRING_UPDATE_CACHED_LENGTH(n)
+ #define wxSTRING_SET_CACHED_LENGTH(n)
+#endif // wxUSE_STRING_POS_CACHE/!wxUSE_STRING_POS_CACHE
+
+ size_t PosToImpl(size_t pos) const
+ {
+ return pos == 0 || pos == npos ? pos : DoPosToImpl(pos);
+ }
void PosLenToImpl(size_t pos, size_t len, size_t *implPos, size_t *implLen) const;
typedef size_t size_type;
typedef wxUniChar const_reference;
-#if wxUSE_STL
+#if wxUSE_STD_STRING
#if wxUSE_UNICODE_UTF8
// random access is not O(1), as required by Random Access Iterator
#define WX_STR_ITERATOR_TAG std::bidirectional_iterator_tag
#else
#define WX_STR_ITERATOR_TAG std::random_access_iterator_tag
#endif
+ #define WX_DEFINE_ITERATOR_CATEGORY(cat) typedef cat iterator_category;
#else
- #define WX_STR_ITERATOR_TAG void /* dummy type */
+ // not defining iterator_category at all in this case is better than defining
+ // it as some dummy type -- at least it results in more intelligible error
+ // messages
+ #define WX_DEFINE_ITERATOR_CATEGORY(cat)
#endif
#define WX_STR_ITERATOR_IMPL(iterator_name, pointer_type, reference_type) \
private: \
typedef wxStringImpl::iterator_name underlying_iterator; \
public: \
- typedef WX_STR_ITERATOR_TAG iterator_category; \
+ WX_DEFINE_ITERATOR_CATEGORY(WX_STR_ITERATOR_TAG) \
typedef wxUniChar value_type; \
typedef int difference_type; \
typedef reference_type reference; \
}
reference operator*()
- { return wxUniCharRef::CreateForString(m_node, m_cur); }
+ { return wxUniCharRef::CreateForString(*str(), m_cur); }
iterator operator+(ptrdiff_t n) const
{ return iterator(str(), wxStringOperations::AddToIter(m_cur, n)); }
size_t IterToImplPos(wxString::iterator i) const
{ return wxStringImpl::const_iterator(i.impl()) - m_impl.begin(); }
- iterator GetNthIter(size_t n)
+ iterator GetIterForNthChar(size_t n)
{ return iterator(this, m_impl.begin() + PosToImpl(n)); }
- const_iterator GetNthIter(size_t n) const
+ const_iterator GetIterForNthChar(size_t n) const
{ return const_iterator(this, m_impl.begin() + PosToImpl(n)); }
#else // !wxUSE_UNICODE_UTF8
: m_cur(ptr) {}
};
- iterator GetNthIter(size_t n) { return begin() + n; }
- const_iterator GetNthIter(size_t n) const { return begin() + n; }
+ iterator GetIterForNthChar(size_t n) { return begin() + n; }
+ const_iterator GetIterForNthChar(size_t n) const { return begin() + n; }
#endif // wxUSE_UNICODE_UTF8/!wxUSE_UNICODE_UTF8
#undef WX_STR_ITERATOR_TAG
public:
typedef T iterator_type;
- typedef typename T::iterator_category iterator_category;
+ WX_DEFINE_ITERATOR_CATEGORY(typename T::iterator_category)
typedef typename T::value_type value_type;
typedef typename T::difference_type difference_type;
typedef typename T::reference reference;
wxString(const wxString& str, size_t nLength)
{ assign(str, nLength); }
+
+#if wxUSE_STRING_POS_CACHE
+ ~wxString()
+ {
+ // we need to invalidate our cache entry as another string could be
+ // recreated at the same address (unlikely, but still possible, with the
+ // heap-allocated strings but perfectly common with stack-allocated ones)
+ InvalidateCache();
+ }
+#endif // wxUSE_STRING_POS_CACHE
+
// even if we're not built with wxUSE_STL == 1 it is very convenient to allow
// implicit conversions from std::string to wxString and vice verse as this
// allows to use the same strings in non-GUI and GUI code, however we don't
// std::string methods:
#if wxUSE_UNICODE_UTF8
- size_t length() const { return end() - begin(); } // FIXME-UTF8: optimize!
+ size_t length() const
+ {
+#if wxUSE_STRING_POS_CACHE
+ wxCACHE_PROFILE_FIELD_INC(lentot);
+
+ Cache::Element * const cache = GetCacheElement();
+
+ if ( cache->len == npos )
+ {
+ // it's probably not worth trying to be clever and using cache->pos
+ // here as it's probably 0 anyhow -- you usually call length() before
+ // starting to index the string
+ cache->len = end() - begin();
+ }
+ else
+ {
+ wxCACHE_PROFILE_FIELD_INC(lenhits);
+
+ wxSTRING_CACHE_ASSERT( (int)cache->len == end() - begin() );
+ }
+
+ return cache->len;
+#else // !wxUSE_STRING_POS_CACHE
+ return end() - begin();
+#endif // wxUSE_STRING_POS_CACHE/!wxUSE_STRING_POS_CACHE
+ }
#else
size_t length() const { return m_impl.length(); }
#endif
bool empty() const { return m_impl.empty(); }
- size_type capacity() const { return m_impl.capacity(); } // FIXME-UTF8
- void reserve(size_t sz) { m_impl.reserve(sz); } // FIXME-UTF8
+ // NB: these methods don't have a well-defined meaning in UTF-8 case
+ size_type capacity() const { return m_impl.capacity(); }
+ void reserve(size_t sz) { m_impl.reserve(sz); }
void resize(size_t nSize, wxUniChar ch = wxT('\0'))
{
#if wxUSE_UNICODE_UTF8
if ( nSize < len )
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
// we can't use wxStringImpl::resize() for truncating the string as it
// counts in bytes, not characters
// we also can't use (presumably more efficient) resize() if we have to
// append characters taking more than one byte
if ( !ch.IsAscii() )
+ {
append(nSize - len, ch);
- else
+ }
+ else // can use (presumably faster) resize() version
#endif // wxUSE_UNICODE_UTF8
+ {
+ wxSTRING_INVALIDATE_CACHED_LENGTH();
+
m_impl.resize(nSize, (wxStringCharType)ch);
+ }
}
wxString substr(size_t nStart = 0, size_t nLen = npos) const
// truncate the string to given length
wxString& Truncate(size_t uiLen);
// empty string contents
- void Empty()
- {
- Truncate(0);
-
- wxASSERT_MSG( empty(), _T("string not empty after call to Empty()?") );
- }
+ void Empty() { clear(); }
// empty the string and free memory
void Clear() { clear(); }
{ return at(n); }
// read/write access
wxUniCharRef at(size_t n)
- { return *GetNthIter(n); }
+ { return *GetIterForNthChar(n); }
wxUniCharRef GetWritableChar(size_t n)
{ return at(n); }
// write access
void SetChar(size_t n, wxUniChar ch)
- {
- wxUniCharRef ref(at(n));
-
-#if wxUSE_UNICODE_UTF8
- // if the new character takes the same number of bytes as the old one,
- // we can avoid invalidating the index cache and it's an important case
- // as it is exercised by a simple for loop using indices
- if ( wxStringOperations::GetUtf8CharLength(ref)
- != wxStringOperations::GetUtf8CharLength(ch) )
- {
- wxSTRING_INVALIDATE_INDEX_CACHE();
- }
-#endif // wxUSE_UNICODE_UTF8
-
- ref = ch;
- }
+ { at(n) = ch; }
// get last character
wxUniChar Last() const
{
if ( this != &stringSrc )
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl = stringSrc.m_impl;
}
// from a character
wxString& operator=(wxUniChar ch)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
#if wxUSE_UNICODE_UTF8
if ( !ch.IsAscii() )
#if wxUSE_STL_BASED_WXSTRING
wxString& operator=(const char *psz)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
if ( psz )
m_impl = ImplStr(psz);
wxString& operator=(const wchar_t *pwz)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
if ( pwz )
m_impl = ImplStr(pwz);
#else // !wxUSE_STL_BASED_WXSTRING
wxString& operator=(const char *psz)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl = ImplStr(psz);
wxString& operator=(const wchar_t *pwz)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl = ImplStr(pwz);
// append elements str[pos], ..., str[pos+n]
wxString& append(const wxString& str, size_t pos, size_t n)
{
- size_t from, len;
- str.PosLenToImpl(pos, n, &from, &len);
- m_impl.append(str.m_impl, from, len);
- return *this;
+ wxSTRING_UPDATE_CACHED_LENGTH(n);
+
+ size_t from, len;
+ str.PosLenToImpl(pos, n, &from, &len);
+ m_impl.append(str.m_impl, from, len);
+ return *this;
}
// append a string
wxString& append(const wxString& str)
- { m_impl.append(str.m_impl); return *this; }
+ {
+ wxSTRING_UPDATE_CACHED_LENGTH(str.length());
+
+ m_impl.append(str.m_impl);
+ return *this;
+ }
+
// append first n (or all if n == npos) characters of sz
wxString& append(const char *sz)
- { m_impl.append(ImplStr(sz)); return *this; }
+ {
+ wxSTRING_INVALIDATE_CACHED_LENGTH();
+
+ m_impl.append(ImplStr(sz));
+ return *this;
+ }
+
wxString& append(const wchar_t *sz)
- { m_impl.append(ImplStr(sz)); return *this; }
+ {
+ wxSTRING_INVALIDATE_CACHED_LENGTH();
+
+ m_impl.append(ImplStr(sz));
+ return *this;
+ }
+
wxString& append(const char *sz, size_t n)
{
- SubstrBufFromMB str(ImplStr(sz, n));
- m_impl.append(str.data, str.len);
- return *this;
+ wxSTRING_INVALIDATE_CACHED_LENGTH();
+
+ SubstrBufFromMB str(ImplStr(sz, n));
+ m_impl.append(str.data, str.len);
+ return *this;
}
wxString& append(const wchar_t *sz, size_t n)
{
- SubstrBufFromWC str(ImplStr(sz, n));
- m_impl.append(str.data, str.len);
- return *this;
+ wxSTRING_UPDATE_CACHED_LENGTH(n);
+
+ SubstrBufFromWC str(ImplStr(sz, n));
+ m_impl.append(str.data, str.len);
+ return *this;
}
wxString& append(const wxCStrData& str)
wxString& append(size_t n, wxUniChar ch)
{
#if wxUSE_UNICODE_UTF8
- if ( !ch.IsAscii() )
- m_impl.append(wxStringOperations::EncodeNChars(n, ch));
- else
+ if ( !ch.IsAscii() )
+ {
+ wxSTRING_INVALIDATE_CACHED_LENGTH();
+
+ m_impl.append(wxStringOperations::EncodeNChars(n, ch));
+ }
+ else // ASCII
#endif
- m_impl.append(n, (wxStringCharType)ch);
- return *this;
+ {
+ wxSTRING_UPDATE_CACHED_LENGTH(n);
+
+ m_impl.append(n, (wxStringCharType)ch);
+ }
+
+ return *this;
}
+
wxString& append(size_t n, wxUniCharRef ch)
{ return append(n, wxUniChar(ch)); }
wxString& append(size_t n, char ch)
// append from first to last
wxString& append(const_iterator first, const_iterator last)
- { m_impl.append(first.impl(), last.impl()); return *this; }
+ {
+ wxSTRING_INVALIDATE_CACHED_LENGTH();
+
+ m_impl.append(first.impl(), last.impl());
+ return *this;
+ }
#if WXWIN_COMPATIBILITY_STRING_PTR_AS_ITER
wxString& append(const char *first, const char *last)
{ return append(first, last - first); }
// same as `this_string = str'
wxString& assign(const wxString& str)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_SET_CACHED_LENGTH(str.length());
m_impl = str.m_impl;
wxString& assign(const wxString& str, size_t len)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_SET_CACHED_LENGTH(len);
m_impl.assign(str.m_impl, 0, str.LenToImpl(len));
// same as ` = str[pos..pos + n]
wxString& assign(const wxString& str, size_t pos, size_t n)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
-
size_t from, len;
str.PosLenToImpl(pos, n, &from, &len);
m_impl.assign(str.m_impl, from, len);
+
+ // it's important to call this after PosLenToImpl() above in case str is
+ // the same string as this one
+ wxSTRING_SET_CACHED_LENGTH(n);
+
return *this;
}
// same as `= first n (or all if n == npos) characters of sz'
wxString& assign(const char *sz)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl.assign(ImplStr(sz));
wxString& assign(const wchar_t *sz)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl.assign(ImplStr(sz));
wxString& assign(const char *sz, size_t n)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_SET_CACHED_LENGTH(n);
SubstrBufFromMB str(ImplStr(sz, n));
m_impl.assign(str.data, str.len);
wxString& assign(const wchar_t *sz, size_t n)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_SET_CACHED_LENGTH(n);
SubstrBufFromWC str(ImplStr(sz, n));
m_impl.assign(str.data, str.len);
// same as `= n copies of ch'
wxString& assign(size_t n, wxUniChar ch)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_SET_CACHED_LENGTH(n);
#if wxUSE_UNICODE_UTF8
- if ( !ch.IsAscii() )
- m_impl.assign(wxStringOperations::EncodeNChars(n, ch));
- else
+ if ( !ch.IsAscii() )
+ m_impl.assign(wxStringOperations::EncodeNChars(n, ch));
+ else
#endif
- m_impl.assign(n, (wxStringCharType)ch);
- return *this;
+ m_impl.assign(n, (wxStringCharType)ch);
+
+ return *this;
}
wxString& assign(size_t n, wxUniCharRef ch)
// assign from first to last
wxString& assign(const_iterator first, const_iterator last)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl.assign(first.impl(), last.impl());
// insert another string
wxString& insert(size_t nPos, const wxString& str)
- { insert(GetNthIter(nPos), str.begin(), str.end()); return *this; }
+ { insert(GetIterForNthChar(nPos), str.begin(), str.end()); return *this; }
// insert n chars of str starting at nStart (in str)
wxString& insert(size_t nPos, const wxString& str, size_t nStart, size_t n)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_UPDATE_CACHED_LENGTH(n);
size_t from, len;
str.PosLenToImpl(nStart, n, &from, &len);
// insert first n (or all if n == npos) characters of sz
wxString& insert(size_t nPos, const char *sz)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl.insert(PosToImpl(nPos), ImplStr(sz));
wxString& insert(size_t nPos, const wchar_t *sz)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl.insert(PosToImpl(nPos), ImplStr(sz)); return *this;
}
wxString& insert(size_t nPos, const char *sz, size_t n)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_UPDATE_CACHED_LENGTH(n);
SubstrBufFromMB str(ImplStr(sz, n));
m_impl.insert(PosToImpl(nPos), str.data, str.len);
wxString& insert(size_t nPos, const wchar_t *sz, size_t n)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_UPDATE_CACHED_LENGTH(n);
SubstrBufFromWC str(ImplStr(sz, n));
m_impl.insert(PosToImpl(nPos), str.data, str.len);
// insert n copies of ch
wxString& insert(size_t nPos, size_t n, wxUniChar ch)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_UPDATE_CACHED_LENGTH(n);
#if wxUSE_UNICODE_UTF8
- if ( !ch.IsAscii() )
- m_impl.insert(PosToImpl(nPos), wxStringOperations::EncodeNChars(n, ch));
- else
+ if ( !ch.IsAscii() )
+ m_impl.insert(PosToImpl(nPos), wxStringOperations::EncodeNChars(n, ch));
+ else
#endif
- m_impl.insert(PosToImpl(nPos), n, (wxStringCharType)ch);
- return *this;
+ m_impl.insert(PosToImpl(nPos), n, (wxStringCharType)ch);
+ return *this;
}
iterator insert(iterator it, wxUniChar ch)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_UPDATE_CACHED_LENGTH(1);
#if wxUSE_UNICODE_UTF8
- if ( !ch.IsAscii() )
- {
- size_t pos = IterToImplPos(it);
- m_impl.insert(pos, wxStringOperations::EncodeChar(ch));
- return iterator(this, m_impl.begin() + pos);
- }
- else
+ if ( !ch.IsAscii() )
+ {
+ size_t pos = IterToImplPos(it);
+ m_impl.insert(pos, wxStringOperations::EncodeChar(ch));
+ return iterator(this, m_impl.begin() + pos);
+ }
+ else
#endif
- return iterator(this, m_impl.insert(it.impl(), (wxStringCharType)ch));
+ return iterator(this, m_impl.insert(it.impl(), (wxStringCharType)ch));
}
void insert(iterator it, const_iterator first, const_iterator last)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl.insert(it.impl(), first.impl(), last.impl());
}
void insert(iterator it, size_type n, wxUniChar ch)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_UPDATE_CACHED_LENGTH(n);
#if wxUSE_UNICODE_UTF8
- if ( !ch.IsAscii() )
- m_impl.insert(IterToImplPos(it), wxStringOperations::EncodeNChars(n, ch));
- else
+ if ( !ch.IsAscii() )
+ m_impl.insert(IterToImplPos(it), wxStringOperations::EncodeNChars(n, ch));
+ else
#endif
- m_impl.insert(it.impl(), n, (wxStringCharType)ch);
+ m_impl.insert(it.impl(), n, (wxStringCharType)ch);
}
// delete characters from nStart to nStart + nLen
wxString& erase(size_type pos = 0, size_type n = npos)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
size_t from, len;
PosLenToImpl(pos, n, &from, &len);
// delete characters from first up to last
iterator erase(iterator first, iterator last)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
return iterator(this, m_impl.erase(first.impl(), last.impl()));
}
iterator erase(iterator first)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_UPDATE_CACHED_LENGTH(-1);
return iterator(this, m_impl.erase(first.impl()));
}
#else
void clear()
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_SET_CACHED_LENGTH(0);
m_impl.clear();
}
// replaces the substring of length nLen starting at nStart
wxString& replace(size_t nStart, size_t nLen, const char* sz)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
size_t from, len;
PosLenToImpl(nStart, nLen, &from, &len);
wxString& replace(size_t nStart, size_t nLen, const wchar_t* sz)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
size_t from, len;
PosLenToImpl(nStart, nLen, &from, &len);
// replaces the substring of length nLen starting at nStart
wxString& replace(size_t nStart, size_t nLen, const wxString& str)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
size_t from, len;
PosLenToImpl(nStart, nLen, &from, &len);
// replaces the substring with nCount copies of ch
wxString& replace(size_t nStart, size_t nLen, size_t nCount, wxUniChar ch)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
size_t from, len;
PosLenToImpl(nStart, nLen, &from, &len);
wxString& replace(size_t nStart, size_t nLen,
const wxString& str, size_t nStart2, size_t nLen2)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
size_t from, len;
PosLenToImpl(nStart, nLen, &from, &len);
wxString& replace(size_t nStart, size_t nLen,
const char* sz, size_t nCount)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
size_t from, len;
PosLenToImpl(nStart, nLen, &from, &len);
wxString& replace(size_t nStart, size_t nLen,
const wchar_t* sz, size_t nCount)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
size_t from, len;
PosLenToImpl(nStart, nLen, &from, &len);
wxString& replace(size_t nStart, size_t nLen,
const wxString& s, size_t nCount)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
size_t from, len;
PosLenToImpl(nStart, nLen, &from, &len);
wxString& replace(iterator first, iterator last, const char* s)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl.replace(first.impl(), last.impl(), ImplStr(s));
wxString& replace(iterator first, iterator last, const wchar_t* s)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl.replace(first.impl(), last.impl(), ImplStr(s));
wxString& replace(iterator first, iterator last, const char* s, size_type n)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
SubstrBufFromMB str(ImplStr(s, n));
m_impl.replace(first.impl(), last.impl(), str.data, str.len);
wxString& replace(iterator first, iterator last, const wchar_t* s, size_type n)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
SubstrBufFromWC str(ImplStr(s, n));
m_impl.replace(first.impl(), last.impl(), str.data, str.len);
wxString& replace(iterator first, iterator last, const wxString& s)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl.replace(first.impl(), last.impl(), s.m_impl);
wxString& replace(iterator first, iterator last, size_type n, wxUniChar ch)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
#if wxUSE_UNICODE_UTF8
if ( !ch.IsAscii() )
wxString& replace(iterator first, iterator last,
const_iterator first1, const_iterator last1)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl.replace(first.impl(), last.impl(), first1.impl(), last1.impl());
// swap two strings
void swap(wxString& str)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+#if wxUSE_STRING_POS_CACHE
+ // we modify not only this string but also the other one directly so we
+ // need to invalidate cache for both of them (we could also try to
+ // exchange their cache entries but it seems unlikely to be worth it)
+ InvalidateCache();
+ str.InvalidateCache();
+#endif // wxUSE_STRING_POS_CACHE
m_impl.swap(str.m_impl);
}
// string += string
wxString& operator+=(const wxString& s)
- { m_impl += s.m_impl; return *this; }
+ {
+ wxSTRING_INVALIDATE_CACHED_LENGTH();
+
+ m_impl += s.m_impl;
+ return *this;
+ }
// string += C string
wxString& operator+=(const char *psz)
- { m_impl += ImplStr(psz); return *this; }
+ {
+ wxSTRING_INVALIDATE_CACHED_LENGTH();
+
+ m_impl += ImplStr(psz);
+ return *this;
+ }
wxString& operator+=(const wchar_t *pwz)
- { m_impl += ImplStr(pwz); return *this; }
+ {
+ wxSTRING_INVALIDATE_CACHED_LENGTH();
+
+ m_impl += ImplStr(pwz);
+ return *this;
+ }
wxString& operator+=(const wxCStrData& s)
- { m_impl += s.AsString().m_impl; return *this; }
+ {
+ wxSTRING_INVALIDATE_CACHED_LENGTH();
+
+ m_impl += s.AsString().m_impl;
+ return *this;
+ }
wxString& operator+=(const wxCharBuffer& s)
{ return operator+=(s.data()); }
wxString& operator+=(const wxWCharBuffer& s)
// string += char
wxString& operator+=(wxUniChar ch)
{
+ wxSTRING_UPDATE_CACHED_LENGTH(1);
+
#if wxUSE_UNICODE_UTF8
- if ( !ch.IsAscii() )
- m_impl += wxStringOperations::EncodeChar(ch);
- else
+ if ( !ch.IsAscii() )
+ m_impl += wxStringOperations::EncodeChar(ch);
+ else
#endif
- m_impl += (wxStringCharType)ch;
- return *this;
+ m_impl += (wxStringCharType)ch;
+ return *this;
}
wxString& operator+=(wxUniCharRef ch) { return *this += wxUniChar(ch); }
wxString& operator+=(int ch) { return *this += wxUniChar(ch); }
void DoUngetWriteBuf()
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_INVALIDATE_CACHE();
m_impl.DoUngetWriteBuf();
}
void DoUngetWriteBuf(size_t nLen)
{
- wxSTRING_INVALIDATE_INDEX_CACHE();
+ wxSTRING_SET_CACHED_LENGTH(nLen);
m_impl.DoUngetWriteBuf(nLen);
}