/////////////////////////////////////////////////////////////////////////////
-// Name: string.cpp
+// Name: src/common/string.cpp
// Purpose: wxString class
-// Author: Vadim Zeitlin
+// Author: Vadim Zeitlin, Ryan Norton
// Modified by:
// Created: 29/01/98
// RCS-ID: $Id$
// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
-// Licence: wxWindows license
+// (c) 2004 Ryan Norton <wxprojects@comcast.net>
+// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
-#ifdef __GNUG__
-#pragma implementation "string.h"
-#endif
-
/*
* About ref counting:
* 1) all empty strings use g_strEmpty, nRefs = -1 (set in Init())
#include "wx/wxprec.h"
#ifdef __BORLANDC__
-#pragma hdrstop
+ #pragma hdrstop
#endif
#ifndef WX_PRECOMP
-#include "wx/defs.h"
-#include "wx/string.h"
+ #include "wx/string.h"
+ #include "wx/intl.h"
+ #include "wx/thread.h"
#endif
#include <ctype.h>
+#include <errno.h>
#include <string.h>
#include <stdlib.h>
-#ifdef WXSTRING_IS_WXOBJECT
- IMPLEMENT_DYNAMIC_CLASS(wxString, wxObject)
-#endif //WXSTRING_IS_WXOBJECT
+#ifdef __SALFORDC__
+ #include <clib.h>
+#endif
+
+// allocating extra space for each string consumes more memory but speeds up
+// the concatenation operations (nLen is the current string's length)
+// NB: EXTRA_ALLOC must be >= 0!
+#define EXTRA_ALLOC (19 - nLen % 16)
// ---------------------------------------------------------------------------
// static class variables definition
// ---------------------------------------------------------------------------
-#ifdef STD_STRING_COMPATIBILITY
- const size_t wxString::npos = STRING_MAXLEN;
+#if !wxUSE_STL
+ //According to STL _must_ be a -1 size_t
+ const size_t wxStringBase::npos = (size_t) -1;
#endif
-// ===========================================================================
-// static class data, special inlines
-// ===========================================================================
-
-// for an empty string, GetStringData() will return this address
-static int g_strEmpty[] = { -1, // ref count (locked)
- 0, // current length
- 0, // allocated memory
- 0 }; // string data
-// empty string shares memory with g_strEmpty
-static wxStringData *g_strNul = (wxStringData*)&g_strEmpty;
-// empty C style string: points to 'string data' byte of g_strEmpty
-extern const char *g_szNul = (const char *)(&g_strEmpty[3]);
+// ----------------------------------------------------------------------------
+// static data
+// ----------------------------------------------------------------------------
-// ===========================================================================
-// global functions
-// ===========================================================================
+#if wxUSE_STL
-#ifdef STD_STRING_COMPATIBILITY
+extern const wxChar WXDLLIMPEXP_BASE *wxEmptyString = _T("");
-// MS Visual C++ version 5.0 provides the new STL headers as well as the old
-// iostream ones.
-//
-// ATTN: you can _not_ use both of these in the same program!
-#if 0 // def _MSC_VER
- #include <iostream>
- #define NAMESPACE std::
#else
- #include <iostream.h>
- #define NAMESPACE
-#endif //Visual C++
-
-NAMESPACE istream& operator>>(NAMESPACE istream& is, wxString& WXUNUSED(str))
-{
-#if 0
- int w = is.width(0);
- if ( is.ipfx(0) ) {
- NAMESPACE streambuf *sb = is.rdbuf();
- str.erase();
- while ( true ) {
- int ch = sb->sbumpc ();
- if ( ch == EOF ) {
- is.setstate(NAMESPACE ios::eofbit);
- break;
- }
- else if ( isspace(ch) ) {
- sb->sungetc();
- break;
- }
-
- str += ch;
- if ( --w == 1 )
- break;
- }
- }
- is.isfx();
- if ( str.length() == 0 )
- is.setstate(NAMESPACE ios::failbit);
+// for an empty string, GetStringData() will return this address: this
+// structure has the same layout as wxStringData and it's data() method will
+// return the empty string (dummy pointer)
+static const struct
+{
+ wxStringData data;
+ wxChar dummy;
+} g_strEmpty = { {-1, 0, 0}, wxT('\0') };
+
+// empty C style string: points to 'string data' byte of g_strEmpty
+extern const wxChar WXDLLIMPEXP_BASE *wxEmptyString = &g_strEmpty.dummy;
+
#endif
- return is;
-}
-#endif //std::string compatibility
+// ----------------------------------------------------------------------------
+// global functions
+// ----------------------------------------------------------------------------
-// ===========================================================================
-// wxString class core
-// ===========================================================================
+#if wxUSE_STD_IOSTREAM
-// ---------------------------------------------------------------------------
-// construction
-// ---------------------------------------------------------------------------
+#include <iostream>
-// construct an empty string
-wxString::wxString()
+wxSTD ostream& operator<<(wxSTD ostream& os, const wxString& str)
{
- Init();
+#ifdef __BORLANDC__
+ os << str.mb_str();
+#else
+ os << str.c_str();
+#endif
+ return os;
}
-// copy constructor
-wxString::wxString(const wxString& stringSrc)
-{
- wxASSERT( stringSrc.GetStringData()->IsValid() );
+#endif // wxUSE_STD_IOSTREAM
- if ( stringSrc.IsEmpty() ) {
- // nothing to do for an empty string
- Init();
- }
- else {
- m_pchData = stringSrc.m_pchData; // share same data
- GetStringData()->Lock(); // => one more copy
- }
-}
+// ----------------------------------------------------------------------------
+// private classes
+// ----------------------------------------------------------------------------
-// constructs string of <nLength> copies of character <ch>
-wxString::wxString(char ch, size_t nLength)
-{
- Init();
+// this small class is used to gather statistics for performance tuning
+//#define WXSTRING_STATISTICS
+#ifdef WXSTRING_STATISTICS
+ class Averager
+ {
+ public:
+ Averager(const wxChar *sz) { m_sz = sz; m_nTotal = m_nCount = 0; }
+ ~Averager()
+ { wxPrintf("wxString: average %s = %f\n", m_sz, ((float)m_nTotal)/m_nCount); }
+
+ void Add(size_t n) { m_nTotal += n; m_nCount++; }
+
+ private:
+ size_t m_nCount, m_nTotal;
+ const wxChar *m_sz;
+ } g_averageLength("allocation size"),
+ g_averageSummandLength("summand length"),
+ g_averageConcatHit("hit probability in concat"),
+ g_averageInitialLength("initial string length");
+
+ #define STATISTICS_ADD(av, val) g_average##av.Add(val)
+#else
+ #define STATISTICS_ADD(av, val)
+#endif // WXSTRING_STATISTICS
- if ( nLength > 0 ) {
- AllocBuffer(nLength);
-
- wxASSERT( sizeof(char) == 1 ); // can't use memset if not
+#if !wxUSE_STL
- memset(m_pchData, ch, nLength);
- }
+// ===========================================================================
+// wxStringData class deallocation
+// ===========================================================================
+
+#if defined(__VISUALC__) && defined(_MT) && !defined(_DLL)
+# pragma message (__FILE__ ": building with Multithreaded non DLL runtime has a performance impact on wxString!")
+void wxStringData::Free()
+{
+ free(this);
}
+#endif
+
+// ===========================================================================
+// wxStringBase
+// ===========================================================================
// takes nLength elements of psz starting at nPos
-void wxString::InitWith(const char *psz, size_t nPos, size_t nLength)
+void wxStringBase::InitWith(const wxChar *psz, size_t nPos, size_t nLength)
{
Init();
- wxASSERT( nPos <= Strlen(psz) );
+ // if the length is not given, assume the string to be NUL terminated
+ if ( nLength == npos ) {
+ wxASSERT_MSG( nPos <= wxStrlen(psz), _T("index out of bounds") );
+
+ nLength = wxStrlen(psz + nPos);
+ }
- if ( nLength == STRING_MAXLEN )
- nLength = Strlen(psz + nPos);
+ STATISTICS_ADD(InitialLength, nLength);
if ( nLength > 0 ) {
// trailing '\0' is written in AllocBuffer()
- AllocBuffer(nLength);
- memcpy(m_pchData, psz + nPos, nLength*sizeof(char));
+ if ( !AllocBuffer(nLength) ) {
+ wxFAIL_MSG( _T("out of memory in wxStringBase::InitWith") );
+ return;
+ }
+ wxTmemcpy(m_pchData, psz + nPos, nLength);
}
}
-
-// take first nLength characters of C string psz
-// (default value of STRING_MAXLEN means take all the string)
-wxString::wxString(const char *psz, size_t nLength)
-{
- InitWith(psz, 0, nLength);
-}
-
-// the same as previous constructor, but for compilers using unsigned char
-wxString::wxString(const unsigned char* psz, size_t nLength)
-{
- InitWith((const char *)psz, 0, nLength);
-}
-
-#ifdef STD_STRING_COMPATIBILITY
-
-// ctor from a substring
-wxString::wxString(const wxString& s, size_t nPos, size_t nLen)
-{
- InitWith(s.c_str(), nPos, nLen == npos ? 0 : nLen);
-}
// poor man's iterators are "void *" pointers
-wxString::wxString(const void *pStart, const void *pEnd)
+wxStringBase::wxStringBase(const void *pStart, const void *pEnd)
{
- InitWith((const char *)pStart, 0,
- (const char *)pEnd - (const char *)pStart);
-}
-
-#endif //std::string compatibility
-
-// from wide string
-wxString::wxString(const wchar_t *pwz)
-{
- // first get necessary size
- size_t nLen = wcstombs(NULL, pwz, 0);
-
- // empty?
- if ( nLen != 0 ) {
- AllocBuffer(nLen);
- wcstombs(m_pchData, pwz, nLen);
+ if ( pEnd >= pStart )
+ {
+ InitWith((const wxChar *)pStart, 0,
+ (const wxChar *)pEnd - (const wxChar *)pStart);
}
- else {
+ else
+ {
+ wxFAIL_MSG( _T("pStart is not before pEnd") );
Init();
}
}
+wxStringBase::wxStringBase(size_type n, wxChar ch)
+{
+ Init();
+ append(n, ch);
+}
+
// ---------------------------------------------------------------------------
// memory allocation
// ---------------------------------------------------------------------------
// allocates memory needed to store a C string of length nLen
-void wxString::AllocBuffer(size_t nLen)
+bool wxStringBase::AllocBuffer(size_t nLen)
{
- wxASSERT( nLen > 0 ); //
- wxASSERT( nLen <= INT_MAX-1 ); // max size (enough room for 1 extra)
+ // allocating 0 sized buffer doesn't make sense, all empty strings should
+ // reuse g_strEmpty
+ wxASSERT( nLen > 0 );
+
+ // make sure that we don't overflow
+ wxASSERT( nLen < (INT_MAX / sizeof(wxChar)) -
+ (sizeof(wxStringData) + EXTRA_ALLOC + 1) );
+
+ STATISTICS_ADD(Length, nLen);
// allocate memory:
// 1) one extra character for '\0' termination
// 2) sizeof(wxStringData) for housekeeping info
- wxStringData* pData = (wxStringData*)new char[sizeof(wxStringData) +
- (nLen + 1)*sizeof(char)];
+ wxStringData* pData = (wxStringData*)
+ malloc(sizeof(wxStringData) + (nLen + EXTRA_ALLOC + 1)*sizeof(wxChar));
+
+ if ( pData == NULL ) {
+ // allocation failures are handled by the caller
+ return false;
+ }
+
pData->nRefs = 1;
- pData->data()[nLen] = '\0';
pData->nDataLength = nLen;
- pData->nAllocLength = nLen;
+ pData->nAllocLength = nLen + EXTRA_ALLOC;
m_pchData = pData->data(); // data starts after wxStringData
-}
-
-// releases the string memory and reinits it
-void wxString::Reinit()
-{
- GetStringData()->Unlock();
- Init();
-}
-
-// wrapper around wxString::Reinit
-void wxString::Empty()
-{
- if ( GetStringData()->nDataLength != 0 )
- Reinit();
-
- wxASSERT( GetStringData()->nDataLength == 0 );
- wxASSERT( GetStringData()->nAllocLength == 0 );
+ m_pchData[nLen] = wxT('\0');
+ return true;
}
// must be called before changing this string
-void wxString::CopyBeforeWrite()
+bool wxStringBase::CopyBeforeWrite()
{
wxStringData* pData = GetStringData();
if ( pData->IsShared() ) {
pData->Unlock(); // memory not freed because shared
- AllocBuffer(pData->nDataLength);
- memcpy(m_pchData, pData->data(), (pData->nDataLength + 1)*sizeof(char));
+ size_t nLen = pData->nDataLength;
+ if ( !AllocBuffer(nLen) ) {
+ // allocation failures are handled by the caller
+ return false;
+ }
+ wxTmemcpy(m_pchData, pData->data(), nLen);
}
- wxASSERT( !pData->IsShared() ); // we must be the only owner
+ wxASSERT( !GetStringData()->IsShared() ); // we must be the only owner
+
+ return true;
}
// must be called before replacing contents of this string
-void wxString::AllocBeforeWrite(size_t nLen)
+bool wxStringBase::AllocBeforeWrite(size_t nLen)
{
wxASSERT( nLen != 0 ); // doesn't make any sense
// must not share string and must have enough space
- register wxStringData* pData = GetStringData();
- if ( pData->IsShared() || (nLen > pData->nAllocLength) ) {
+ wxStringData* pData = GetStringData();
+ if ( pData->IsShared() || pData->IsEmpty() ) {
// can't work with old buffer, get new one
pData->Unlock();
- AllocBuffer(nLen);
+ if ( !AllocBuffer(nLen) ) {
+ // allocation failures are handled by the caller
+ return false;
+ }
+ }
+ else {
+ if ( nLen > pData->nAllocLength ) {
+ // realloc the buffer instead of calling malloc() again, this is more
+ // efficient
+ STATISTICS_ADD(Length, nLen);
+
+ nLen += EXTRA_ALLOC;
+
+ pData = (wxStringData*)
+ realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar));
+
+ if ( pData == NULL ) {
+ // allocation failures are handled by the caller
+ // keep previous data since reallocation failed
+ return false;
+ }
+
+ pData->nAllocLength = nLen;
+ m_pchData = pData->data();
+ }
}
- wxASSERT( !pData->IsShared() ); // we must be the only owner
+ wxASSERT( !GetStringData()->IsShared() ); // we must be the only owner
+
+ // it doesn't really matter what the string length is as it's going to be
+ // overwritten later but, for extra safety, set it to 0 for now as we may
+ // have some junk in m_pchData
+ GetStringData()->nDataLength = 0;
+
+ return true;
}
-// get the pointer to writable buffer of (at least) nLen bytes
-char *wxString::GetWriteBuf(size_t nLen)
+wxStringBase& wxStringBase::append(size_t n, wxChar ch)
{
- AllocBeforeWrite(nLen);
- return m_pchData;
+ size_type len = length();
+
+ if ( !Alloc(len + n) || !CopyBeforeWrite() ) {
+ wxFAIL_MSG( _T("out of memory in wxStringBase::append") );
+ }
+ GetStringData()->nDataLength = len + n;
+ m_pchData[len + n] = '\0';
+ for ( size_t i = 0; i < n; ++i )
+ m_pchData[len + i] = ch;
+ return *this;
}
-// dtor frees memory if no other strings use it
-wxString::~wxString()
+void wxStringBase::resize(size_t nSize, wxChar ch)
{
- GetStringData()->Unlock();
+ size_t len = length();
+
+ if ( nSize < len )
+ {
+ erase(begin() + nSize, end());
+ }
+ else if ( nSize > len )
+ {
+ append(nSize - len, ch);
+ }
+ //else: we have exactly the specified length, nothing to do
}
-// ---------------------------------------------------------------------------
-// data access
-// ---------------------------------------------------------------------------
+// allocate enough memory for nLen characters
+bool wxStringBase::Alloc(size_t nLen)
+{
+ wxStringData *pData = GetStringData();
+ if ( pData->nAllocLength <= nLen ) {
+ if ( pData->IsEmpty() ) {
+ nLen += EXTRA_ALLOC;
-// all functions are inline in string.h
+ pData = (wxStringData *)
+ malloc(sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar));
-// ---------------------------------------------------------------------------
-// assignment operators
-// ---------------------------------------------------------------------------
+ if ( pData == NULL ) {
+ // allocation failure handled by caller
+ return false;
+ }
-// helper function: does real copy
-void wxString::AssignCopy(size_t nSrcLen, const char *pszSrcData)
-{
- if ( nSrcLen == 0 ) {
- Reinit();
- }
- else {
- AllocBeforeWrite(nSrcLen);
- memcpy(m_pchData, pszSrcData, nSrcLen*sizeof(char));
- GetStringData()->nDataLength = nSrcLen;
- m_pchData[nSrcLen] = '\0';
+ pData->nRefs = 1;
+ pData->nDataLength = 0;
+ pData->nAllocLength = nLen;
+ m_pchData = pData->data(); // data starts after wxStringData
+ m_pchData[0u] = wxT('\0');
+ }
+ else if ( pData->IsShared() ) {
+ pData->Unlock(); // memory not freed because shared
+ size_t nOldLen = pData->nDataLength;
+ if ( !AllocBuffer(nLen) ) {
+ // allocation failure handled by caller
+ return false;
+ }
+ // +1 to copy the terminator, too
+ memcpy(m_pchData, pData->data(), (nOldLen+1)*sizeof(wxChar));
+ GetStringData()->nDataLength = nOldLen;
+ }
+ else {
+ nLen += EXTRA_ALLOC;
+
+ pData = (wxStringData *)
+ realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar));
+
+ if ( pData == NULL ) {
+ // allocation failure handled by caller
+ // keep previous data since reallocation failed
+ return false;
+ }
+
+ // it's not important if the pointer changed or not (the check for this
+ // is not faster than assigning to m_pchData in all cases)
+ pData->nAllocLength = nLen;
+ m_pchData = pData->data();
+ }
}
+ //else: we've already got enough
+ return true;
}
-// assigns one string to another
-wxString& wxString::operator=(const wxString& stringSrc)
+wxStringBase::iterator wxStringBase::begin()
{
- // don't copy string over itself
- if ( m_pchData != stringSrc.m_pchData ) {
- if ( stringSrc.GetStringData()->IsEmpty() ) {
- Reinit();
- }
- else {
- // adjust references
- GetStringData()->Unlock();
- m_pchData = stringSrc.m_pchData;
- GetStringData()->Lock();
+ if (length() > 0)
+ CopyBeforeWrite();
+ return m_pchData;
+}
+
+wxStringBase::iterator wxStringBase::end()
+{
+ if (length() > 0)
+ CopyBeforeWrite();
+ return m_pchData + length();
+}
+
+wxStringBase::iterator wxStringBase::erase(iterator it)
+{
+ size_type idx = it - begin();
+ erase(idx, 1);
+ return begin() + idx;
+}
+
+wxStringBase& wxStringBase::erase(size_t nStart, size_t nLen)
+{
+ wxASSERT(nStart <= length());
+ size_t strLen = length() - nStart;
+ // delete nLen or up to the end of the string characters
+ nLen = strLen < nLen ? strLen : nLen;
+ wxString strTmp(c_str(), nStart);
+ strTmp.append(c_str() + nStart + nLen, length() - nStart - nLen);
+
+ swap(strTmp);
+ return *this;
+}
+
+wxStringBase& wxStringBase::insert(size_t nPos, const wxChar *sz, size_t n)
+{
+ wxASSERT( nPos <= length() );
+
+ if ( n == npos ) n = wxStrlen(sz);
+ if ( n == 0 ) return *this;
+
+ if ( !Alloc(length() + n) || !CopyBeforeWrite() ) {
+ wxFAIL_MSG( _T("out of memory in wxStringBase::insert") );
}
- }
- return *this;
+ memmove(m_pchData + nPos + n, m_pchData + nPos,
+ (length() - nPos) * sizeof(wxChar));
+ memcpy(m_pchData + nPos, sz, n * sizeof(wxChar));
+ GetStringData()->nDataLength = length() + n;
+ m_pchData[length()] = '\0';
+
+ return *this;
}
-// assigns a single character
-wxString& wxString::operator=(char ch)
+void wxStringBase::swap(wxStringBase& str)
{
- AssignCopy(1, &ch);
- return *this;
+ wxChar* tmp = str.m_pchData;
+ str.m_pchData = m_pchData;
+ m_pchData = tmp;
}
-// assigns C string
-wxString& wxString::operator=(const char *psz)
+size_t wxStringBase::find(const wxStringBase& str, size_t nStart) const
{
- AssignCopy(Strlen(psz), psz);
- return *this;
+ // deal with the special case of empty string first
+ const size_t nLen = length();
+ const size_t nLenOther = str.length();
+
+ if ( !nLenOther )
+ {
+ // empty string is a substring of anything
+ return 0;
+ }
+
+ if ( !nLen )
+ {
+ // the other string is non empty so can't be our substring
+ return npos;
+ }
+
+ wxASSERT( str.GetStringData()->IsValid() );
+ wxASSERT( nStart <= nLen );
+
+ const wxChar * const other = str.c_str();
+
+ // anchor
+ const wxChar* p = (const wxChar*)wxTmemchr(c_str() + nStart,
+ *other,
+ nLen - nStart);
+
+ if ( !p )
+ return npos;
+
+ while ( p - c_str() + nLenOther <= nLen && wxTmemcmp(p, other, nLenOther) )
+ {
+ p++;
+
+ // anchor again
+ p = (const wxChar*)wxTmemchr(p, *other, nLen - (p - c_str()));
+
+ if ( !p )
+ return npos;
+ }
+
+ return p - c_str() + nLenOther <= nLen ? p - c_str() : npos;
}
-// same as 'signed char' variant
-wxString& wxString::operator=(const unsigned char* psz)
+size_t wxStringBase::find(const wxChar* sz, size_t nStart, size_t n) const
{
- *this = (const char *)psz;
- return *this;
+ return find(wxStringBase(sz, n), nStart);
}
-wxString& wxString::operator=(const wchar_t *pwz)
+size_t wxStringBase::find(wxChar ch, size_t nStart) const
{
- wxString str(pwz);
- *this = str;
- return *this;
+ wxASSERT( nStart <= length() );
+
+ const wxChar *p = (const wxChar*)wxTmemchr(c_str() + nStart, ch, length() - nStart);
+
+ return p == NULL ? npos : p - c_str();
}
-// ---------------------------------------------------------------------------
-// string concatenation
-// ---------------------------------------------------------------------------
+size_t wxStringBase::rfind(const wxStringBase& str, size_t nStart) const
+{
+ wxASSERT( str.GetStringData()->IsValid() );
+ wxASSERT( nStart == npos || nStart <= length() );
+
+ if ( length() >= str.length() )
+ {
+ // avoids a corner case later
+ if ( length() == 0 && str.length() == 0 )
+ return 0;
+
+ // "top" is the point where search starts from
+ size_t top = length() - str.length();
+
+ if ( nStart == npos )
+ nStart = length() - 1;
+ if ( nStart < top )
+ top = nStart;
-// concatenate two sources
-// NB: assume that 'this' is a new wxString object
-void wxString::ConcatCopy(int nSrc1Len, const char *pszSrc1Data,
- int nSrc2Len, const char *pszSrc2Data)
+ const wxChar *cursor = c_str() + top;
+ do
+ {
+ if ( wxTmemcmp(cursor, str.c_str(),
+ str.length()) == 0 )
+ {
+ return cursor - c_str();
+ }
+ } while ( cursor-- > c_str() );
+ }
+
+ return npos;
+}
+
+size_t wxStringBase::rfind(const wxChar* sz, size_t nStart, size_t n) const
{
- int nNewLen = nSrc1Len + nSrc2Len;
- if ( nNewLen != 0 )
- {
- AllocBuffer(nNewLen);
- memcpy(m_pchData, pszSrc1Data, nSrc1Len*sizeof(char));
- memcpy(m_pchData + nSrc1Len, pszSrc2Data, nSrc2Len*sizeof(char));
- }
+ return rfind(wxStringBase(sz, n), nStart);
}
-// add something to this string
-void wxString::ConcatSelf(int nSrcLen, const char *pszSrcData)
+size_t wxStringBase::rfind(wxChar ch, size_t nStart) const
{
- // concatenating an empty string is a NOP
- if ( nSrcLen != 0 ) {
- register wxStringData *pData = GetStringData();
+ if ( nStart == npos )
+ {
+ nStart = length();
+ }
+ else
+ {
+ wxASSERT( nStart <= length() );
+ }
- // alloc new buffer if current is too small
- if ( pData->IsShared() ||
- pData->nDataLength + nSrcLen > pData->nAllocLength ) {
- // we have to grow the buffer, use the ConcatCopy routine
- // (which will allocate memory)
- wxStringData* pOldData = GetStringData();
- ConcatCopy(pOldData->nDataLength, m_pchData, nSrcLen, pszSrcData);
- pOldData->Unlock();
+ const wxChar *actual;
+ for ( actual = c_str() + ( nStart == npos ? length() : nStart + 1 );
+ actual > c_str(); --actual )
+ {
+ if ( *(actual - 1) == ch )
+ return (actual - 1) - c_str();
}
- else {
- // fast concatenation when buffer big enough
- memcpy(m_pchData + pData->nDataLength, pszSrcData, nSrcLen*sizeof(char));
- pData->nDataLength += nSrcLen;
- // should be enough space
- wxASSERT( pData->nDataLength <= pData->nAllocLength );
+ return npos;
+}
+
+size_t wxStringBase::find_first_of(const wxChar* sz, size_t nStart) const
+{
+ wxASSERT(nStart <= length());
+
+ size_t len = wxStrlen(sz);
- m_pchData[pData->nDataLength] = '\0'; // put terminating '\0'
+ size_t i;
+ for(i = nStart; i < this->length(); ++i)
+ {
+ if (wxTmemchr(sz, *(c_str() + i), len))
+ break;
}
- }
+
+ if(i == this->length())
+ return npos;
+ else
+ return i;
}
-/*
- * string may be concatenated with other string, C string or a character
- */
+size_t wxStringBase::find_first_of(const wxChar* sz, size_t nStart,
+ size_t n) const
+{
+ return find_first_of(wxStringBase(sz, n), nStart);
+}
-void wxString::operator+=(const wxString& string)
+size_t wxStringBase::find_last_of(const wxChar* sz, size_t nStart) const
{
- ConcatSelf(string.Len(), string);
+ if ( nStart == npos )
+ {
+ nStart = length() - 1;
+ }
+ else
+ {
+ wxASSERT_MSG( nStart <= length(),
+ _T("invalid index in find_last_of()") );
+ }
+
+ size_t len = wxStrlen(sz);
+
+ for ( const wxChar *p = c_str() + nStart; p >= c_str(); --p )
+ {
+ if ( wxTmemchr(sz, *p, len) )
+ return p - c_str();
+ }
+
+ return npos;
}
-void wxString::operator+=(const char *psz)
+size_t wxStringBase::find_last_of(const wxChar* sz, size_t nStart,
+ size_t n) const
{
- ConcatSelf(Strlen(psz), psz);
+ return find_last_of(wxStringBase(sz, n), nStart);
}
-void wxString::operator+=(char ch)
+size_t wxStringBase::find_first_not_of(const wxChar* sz, size_t nStart) const
{
- ConcatSelf(1, &ch);
+ if ( nStart == npos )
+ {
+ nStart = length();
+ }
+ else
+ {
+ wxASSERT( nStart <= length() );
+ }
+
+ size_t len = wxStrlen(sz);
+
+ size_t i;
+ for(i = nStart; i < this->length(); ++i)
+ {
+ if (!wxTmemchr(sz, *(c_str() + i), len))
+ break;
+ }
+
+ if(i == this->length())
+ return npos;
+ else
+ return i;
}
-/*
- * Same as above but return the result
- */
+size_t wxStringBase::find_first_not_of(const wxChar* sz, size_t nStart,
+ size_t n) const
+{
+ return find_first_not_of(wxStringBase(sz, n), nStart);
+}
-wxString& wxString::operator<<(const wxString& string)
+size_t wxStringBase::find_first_not_of(wxChar ch, size_t nStart) const
{
- ConcatSelf(string.Len(), string);
- return *this;
+ wxASSERT( nStart <= length() );
+
+ for ( const wxChar *p = c_str() + nStart; *p; p++ )
+ {
+ if ( *p != ch )
+ return p - c_str();
+ }
+
+ return npos;
}
-wxString& wxString::operator<<(const char *psz)
+size_t wxStringBase::find_last_not_of(const wxChar* sz, size_t nStart) const
{
- ConcatSelf(Strlen(psz), psz);
- return *this;
+ if ( nStart == npos )
+ {
+ nStart = length() - 1;
+ }
+ else
+ {
+ wxASSERT( nStart <= length() );
+ }
+
+ size_t len = wxStrlen(sz);
+
+ for ( const wxChar *p = c_str() + nStart; p >= c_str(); --p )
+ {
+ if ( !wxTmemchr(sz, *p,len) )
+ return p - c_str();
+ }
+
+ return npos;
}
-wxString& wxString::operator<<(char ch)
+size_t wxStringBase::find_last_not_of(const wxChar* sz, size_t nStart,
+ size_t n) const
{
- ConcatSelf(1, &ch);
- return *this;
+ return find_last_not_of(wxStringBase(sz, n), nStart);
}
-/*
- * concatenation functions come in 5 flavours:
- * string + string
- * char + string and string + char
- * C str + string and string + C str
- */
+size_t wxStringBase::find_last_not_of(wxChar ch, size_t nStart) const
+{
+ if ( nStart == npos )
+ {
+ nStart = length() - 1;
+ }
+ else
+ {
+ wxASSERT( nStart <= length() );
+ }
+
+ for ( const wxChar *p = c_str() + nStart; p >= c_str(); --p )
+ {
+ if ( *p != ch )
+ return p - c_str();
+ }
+
+ return npos;
+}
+
+wxStringBase& wxStringBase::replace(size_t nStart, size_t nLen,
+ const wxChar *sz)
+{
+ wxASSERT_MSG( nStart <= length(),
+ _T("index out of bounds in wxStringBase::replace") );
+ size_t strLen = length() - nStart;
+ nLen = strLen < nLen ? strLen : nLen;
+
+ wxStringBase strTmp;
+ strTmp.reserve(length()); // micro optimisation to avoid multiple mem allocs
+
+ //This is kind of inefficient, but its pretty good considering...
+ //we don't want to use character access operators here because on STL
+ //it will freeze the reference count of strTmp, which means a deep copy
+ //at the end when swap is called
+ //
+ //Also, we can't use append with the full character pointer and must
+ //do it manually because this string can contain null characters
+ for(size_t i1 = 0; i1 < nStart; ++i1)
+ strTmp.append(1, this->c_str()[i1]);
+
+ //its safe to do the full version here because
+ //sz must be a normal c string
+ strTmp.append(sz);
+
+ for(size_t i2 = nStart + nLen; i2 < length(); ++i2)
+ strTmp.append(1, this->c_str()[i2]);
+
+ swap(strTmp);
+ return *this;
+}
+
+wxStringBase& wxStringBase::replace(size_t nStart, size_t nLen,
+ size_t nCount, wxChar ch)
+{
+ return replace(nStart, nLen, wxStringBase(nCount, ch).c_str());
+}
+
+wxStringBase& wxStringBase::replace(size_t nStart, size_t nLen,
+ const wxStringBase& str,
+ size_t nStart2, size_t nLen2)
+{
+ return replace(nStart, nLen, str.substr(nStart2, nLen2));
+}
+
+wxStringBase& wxStringBase::replace(size_t nStart, size_t nLen,
+ const wxChar* sz, size_t nCount)
+{
+ return replace(nStart, nLen, wxStringBase(sz, nCount).c_str());
+}
+
+wxStringBase wxStringBase::substr(size_t nStart, size_t nLen) const
+{
+ if ( nLen == npos )
+ nLen = length() - nStart;
+ return wxStringBase(*this, nStart, nLen);
+}
+
+// assigns one string to another
+wxStringBase& wxStringBase::operator=(const wxStringBase& stringSrc)
+{
+ wxASSERT( stringSrc.GetStringData()->IsValid() );
+
+ // don't copy string over itself
+ if ( m_pchData != stringSrc.m_pchData ) {
+ if ( stringSrc.GetStringData()->IsEmpty() ) {
+ Reinit();
+ }
+ else {
+ // adjust references
+ GetStringData()->Unlock();
+ m_pchData = stringSrc.m_pchData;
+ GetStringData()->Lock();
+ }
+ }
+
+ return *this;
+}
+
+// assigns a single character
+wxStringBase& wxStringBase::operator=(wxChar ch)
+{
+ if ( !AssignCopy(1, &ch) ) {
+ wxFAIL_MSG( _T("out of memory in wxStringBase::operator=(wxChar)") );
+ }
+ return *this;
+}
+
+// assigns C string
+wxStringBase& wxStringBase::operator=(const wxChar *psz)
+{
+ if ( !AssignCopy(wxStrlen(psz), psz) ) {
+ wxFAIL_MSG( _T("out of memory in wxStringBase::operator=(const wxChar *)") );
+ }
+ return *this;
+}
+
+// helper function: does real copy
+bool wxStringBase::AssignCopy(size_t nSrcLen, const wxChar *pszSrcData)
+{
+ if ( nSrcLen == 0 ) {
+ Reinit();
+ }
+ else {
+ if ( !AllocBeforeWrite(nSrcLen) ) {
+ // allocation failure handled by caller
+ return false;
+ }
+ memcpy(m_pchData, pszSrcData, nSrcLen*sizeof(wxChar));
+ GetStringData()->nDataLength = nSrcLen;
+ m_pchData[nSrcLen] = wxT('\0');
+ }
+ return true;
+}
+
+// ---------------------------------------------------------------------------
+// string concatenation
+// ---------------------------------------------------------------------------
+
+// add something to this string
+bool wxStringBase::ConcatSelf(size_t nSrcLen, const wxChar *pszSrcData,
+ size_t nMaxLen)
+{
+ STATISTICS_ADD(SummandLength, nSrcLen);
+
+ nSrcLen = nSrcLen < nMaxLen ? nSrcLen : nMaxLen;
+
+ // concatenating an empty string is a NOP
+ if ( nSrcLen > 0 ) {
+ wxStringData *pData = GetStringData();
+ size_t nLen = pData->nDataLength;
+ size_t nNewLen = nLen + nSrcLen;
+
+ // alloc new buffer if current is too small
+ if ( pData->IsShared() ) {
+ STATISTICS_ADD(ConcatHit, 0);
+
+ // we have to allocate another buffer
+ wxStringData* pOldData = GetStringData();
+ if ( !AllocBuffer(nNewLen) ) {
+ // allocation failure handled by caller
+ return false;
+ }
+ memcpy(m_pchData, pOldData->data(), nLen*sizeof(wxChar));
+ pOldData->Unlock();
+ }
+ else if ( nNewLen > pData->nAllocLength ) {
+ STATISTICS_ADD(ConcatHit, 0);
+
+ reserve(nNewLen);
+ // we have to grow the buffer
+ if ( capacity() < nNewLen ) {
+ // allocation failure handled by caller
+ return false;
+ }
+ }
+ else {
+ STATISTICS_ADD(ConcatHit, 1);
+
+ // the buffer is already big enough
+ }
+
+ // should be enough space
+ wxASSERT( nNewLen <= GetStringData()->nAllocLength );
+
+ // fast concatenation - all is done in our buffer
+ memcpy(m_pchData + nLen, pszSrcData, nSrcLen*sizeof(wxChar));
+
+ m_pchData[nNewLen] = wxT('\0'); // put terminating '\0'
+ GetStringData()->nDataLength = nNewLen; // and fix the length
+ }
+ //else: the string to append was empty
+ return true;
+}
+
+// ---------------------------------------------------------------------------
+// simple sub-string extraction
+// ---------------------------------------------------------------------------
+
+// helper function: clone the data attached to this string
+bool wxStringBase::AllocCopy(wxString& dest, int nCopyLen, int nCopyIndex) const
+{
+ if ( nCopyLen == 0 ) {
+ dest.Init();
+ }
+ else {
+ if ( !dest.AllocBuffer(nCopyLen) ) {
+ // allocation failure handled by caller
+ return false;
+ }
+ memcpy(dest.m_pchData, m_pchData + nCopyIndex, nCopyLen*sizeof(wxChar));
+ }
+ return true;
+}
+
+#endif // !wxUSE_STL
+
+#if !wxUSE_STL || !defined(HAVE_STD_STRING_COMPARE)
+
+#if !wxUSE_STL
+ #define STRINGCLASS wxStringBase
+#else
+ #define STRINGCLASS wxString
+#endif
+
+static inline int wxDoCmp(const wxChar* s1, size_t l1,
+ const wxChar* s2, size_t l2)
+{
+ if( l1 == l2 )
+ return wxTmemcmp(s1, s2, l1);
+ else if( l1 < l2 )
+ {
+ int ret = wxTmemcmp(s1, s2, l1);
+ return ret == 0 ? -1 : ret;
+ }
+ else
+ {
+ int ret = wxTmemcmp(s1, s2, l2);
+ return ret == 0 ? +1 : ret;
+ }
+}
+
+int STRINGCLASS::compare(const wxStringBase& str) const
+{
+ return ::wxDoCmp(data(), length(), str.data(), str.length());
+}
+
+int STRINGCLASS::compare(size_t nStart, size_t nLen,
+ const wxStringBase& str) const
+{
+ wxASSERT(nStart <= length());
+ size_type strLen = length() - nStart;
+ nLen = strLen < nLen ? strLen : nLen;
+ return ::wxDoCmp(data() + nStart, nLen, str.data(), str.length());
+}
+
+int STRINGCLASS::compare(size_t nStart, size_t nLen,
+ const wxStringBase& str,
+ size_t nStart2, size_t nLen2) const
+{
+ wxASSERT(nStart <= length());
+ wxASSERT(nStart2 <= str.length());
+ size_type strLen = length() - nStart,
+ strLen2 = str.length() - nStart2;
+ nLen = strLen < nLen ? strLen : nLen;
+ nLen2 = strLen2 < nLen2 ? strLen2 : nLen2;
+ return ::wxDoCmp(data() + nStart, nLen, str.data() + nStart2, nLen2);
+}
+
+int STRINGCLASS::compare(const wxChar* sz) const
+{
+ size_t nLen = wxStrlen(sz);
+ return ::wxDoCmp(data(), length(), sz, nLen);
+}
+
+int STRINGCLASS::compare(size_t nStart, size_t nLen,
+ const wxChar* sz, size_t nCount) const
+{
+ wxASSERT(nStart <= length());
+ size_type strLen = length() - nStart;
+ nLen = strLen < nLen ? strLen : nLen;
+ if( nCount == npos )
+ nCount = wxStrlen(sz);
+
+ return ::wxDoCmp(data() + nStart, nLen, sz, nCount);
+}
+
+#undef STRINGCLASS
+
+#endif // !wxUSE_STL || !defined(HAVE_STD_STRING_COMPARE)
+
+// ===========================================================================
+// wxString class core
+// ===========================================================================
+
+// ---------------------------------------------------------------------------
+// construction and conversion
+// ---------------------------------------------------------------------------
+
+#if wxUSE_UNICODE
+
+// from multibyte string
+wxString::wxString(const char *psz, const wxMBConv& conv, size_t nLength)
+{
+ // anything to do?
+ if ( psz && nLength != 0 )
+ {
+ if ( nLength == npos )
+ {
+ nLength = wxNO_LEN;
+ }
+
+ size_t nLenWide;
+ wxWCharBuffer wbuf = conv.cMB2WC(psz, nLength, &nLenWide);
+
+ if ( nLenWide )
+ assign(wbuf, nLenWide);
+ }
+}
+
+//Convert wxString in Unicode mode to a multi-byte string
+const wxCharBuffer wxString::mb_str(const wxMBConv& conv) const
+{
+ return conv.cWC2MB(c_str(), length() + 1 /* size, not length */, NULL);
+}
+
+#else // ANSI
+
+#if wxUSE_WCHAR_T
+
+// from wide string
+wxString::wxString(const wchar_t *pwz, const wxMBConv& conv, size_t nLength)
+{
+ // anything to do?
+ if ( pwz && nLength != 0 )
+ {
+ if ( nLength == npos )
+ {
+ nLength = wxNO_LEN;
+ }
+
+ size_t nLenMB;
+ wxCharBuffer buf = conv.cWC2MB(pwz, nLength, &nLenMB);
+
+ if ( nLenMB )
+ assign(buf, nLenMB);
+ }
+}
+
+//Converts this string to a wide character string if unicode
+//mode is not enabled and wxUSE_WCHAR_T is enabled
+const wxWCharBuffer wxString::wc_str(const wxMBConv& conv) const
+{
+ return conv.cMB2WC(c_str(), length() + 1 /* size, not length */, NULL);
+}
+
+#endif // wxUSE_WCHAR_T
+
+#endif // Unicode/ANSI
+
+// shrink to minimal size (releasing extra memory)
+bool wxString::Shrink()
+{
+ wxString tmp(begin(), end());
+ swap(tmp);
+ return tmp.length() == length();
+}
+
+#if !wxUSE_STL
+// get the pointer to writable buffer of (at least) nLen bytes
+wxChar *wxString::GetWriteBuf(size_t nLen)
+{
+ if ( !AllocBeforeWrite(nLen) ) {
+ // allocation failure handled by caller
+ return NULL;
+ }
+
+ wxASSERT( GetStringData()->nRefs == 1 );
+ GetStringData()->Validate(false);
+
+ return m_pchData;
+}
+
+// put string back in a reasonable state after GetWriteBuf
+void wxString::UngetWriteBuf()
+{
+ GetStringData()->nDataLength = wxStrlen(m_pchData);
+ GetStringData()->Validate(true);
+}
+
+void wxString::UngetWriteBuf(size_t nLen)
+{
+ GetStringData()->nDataLength = nLen;
+ GetStringData()->Validate(true);
+}
+#endif
+
+// ---------------------------------------------------------------------------
+// data access
+// ---------------------------------------------------------------------------
+
+// all functions are inline in string.h
+
+// ---------------------------------------------------------------------------
+// assignment operators
+// ---------------------------------------------------------------------------
+
+#if !wxUSE_UNICODE
+
+// same as 'signed char' variant
+wxString& wxString::operator=(const unsigned char* psz)
+{
+ *this = (const char *)psz;
+ return *this;
+}
+
+#if wxUSE_WCHAR_T
+wxString& wxString::operator=(const wchar_t *pwz)
+{
+ wxString str(pwz);
+ swap(str);
+ return *this;
+}
+#endif
+
+#endif
+
+/*
+ * concatenation functions come in 5 flavours:
+ * string + string
+ * char + string and string + char
+ * C str + string and string + C str
+ */
+
+wxString operator+(const wxString& str1, const wxString& str2)
+{
+#if !wxUSE_STL
+ wxASSERT( str1.GetStringData()->IsValid() );
+ wxASSERT( str2.GetStringData()->IsValid() );
+#endif
+
+ wxString s = str1;
+ s += str2;
+
+ return s;
+}
+
+wxString operator+(const wxString& str, wxChar ch)
+{
+#if !wxUSE_STL
+ wxASSERT( str.GetStringData()->IsValid() );
+#endif
+
+ wxString s = str;
+ s += ch;
+
+ return s;
+}
+
+wxString operator+(wxChar ch, const wxString& str)
+{
+#if !wxUSE_STL
+ wxASSERT( str.GetStringData()->IsValid() );
+#endif
+
+ wxString s = ch;
+ s += str;
+
+ return s;
+}
+
+wxString operator+(const wxString& str, const wxChar *psz)
+{
+#if !wxUSE_STL
+ wxASSERT( str.GetStringData()->IsValid() );
+#endif
+
+ wxString s;
+ if ( !s.Alloc(wxStrlen(psz) + str.length()) ) {
+ wxFAIL_MSG( _T("out of memory in wxString::operator+") );
+ }
+ s += str;
+ s += psz;
+
+ return s;
+}
+
+wxString operator+(const wxChar *psz, const wxString& str)
+{
+#if !wxUSE_STL
+ wxASSERT( str.GetStringData()->IsValid() );
+#endif
+
+ wxString s;
+ if ( !s.Alloc(wxStrlen(psz) + str.length()) ) {
+ wxFAIL_MSG( _T("out of memory in wxString::operator+") );
+ }
+ s = psz;
+ s += str;
+
+ return s;
+}
+
+// ===========================================================================
+// other common string functions
+// ===========================================================================
+
+int wxString::Cmp(const wxString& s) const
+{
+ return compare(s);
+}
+
+int wxString::Cmp(const wxChar* psz) const
+{
+ return compare(psz);
+}
+
+static inline int wxDoCmpNoCase(const wxChar* s1, size_t l1,
+ const wxChar* s2, size_t l2)
+{
+ size_t i;
+
+ if( l1 == l2 )
+ {
+ for(i = 0; i < l1; ++i)
+ {
+ if(wxTolower(s1[i]) != wxTolower(s2[i]))
+ break;
+ }
+ return i == l1 ? 0 : wxTolower(s1[i]) < wxTolower(s2[i]) ? -1 : 1;
+ }
+ else if( l1 < l2 )
+ {
+ for(i = 0; i < l1; ++i)
+ {
+ if(wxTolower(s1[i]) != wxTolower(s2[i]))
+ break;
+ }
+ return i == l1 ? -1 : wxTolower(s1[i]) < wxTolower(s2[i]) ? -1 : 1;
+ }
+ else
+ {
+ for(i = 0; i < l2; ++i)
+ {
+ if(wxTolower(s1[i]) != wxTolower(s2[i]))
+ break;
+ }
+ return i == l2 ? 1 : wxTolower(s1[i]) < wxTolower(s2[i]) ? -1 : 1;
+ }
+}
+
+int wxString::CmpNoCase(const wxString& s) const
+{
+ return wxDoCmpNoCase(data(), length(), s.data(), s.length());
+}
+
+int wxString::CmpNoCase(const wxChar* psz) const
+{
+ int nLen = wxStrlen(psz);
+
+ return wxDoCmpNoCase(data(), length(), psz, nLen);
+}
+
+
+#if wxUSE_UNICODE
+
+#ifdef __MWERKS__
+#ifndef __SCHAR_MAX__
+#define __SCHAR_MAX__ 127
+#endif
+#endif
+
+wxString wxString::FromAscii(const char *ascii)
+{
+ if (!ascii)
+ return wxEmptyString;
+
+ size_t len = strlen( ascii );
+ wxString res;
+
+ if ( len )
+ {
+ wxStringBuffer buf(res, len);
+
+ wchar_t *dest = buf;
+
+ for ( ;; )
+ {
+ if ( (*dest++ = (wchar_t)(unsigned char)*ascii++) == L'\0' )
+ break;
+ }
+ }
+
+ return res;
+}
+
+wxString wxString::FromAscii(const char ascii)
+{
+ // What do we do with '\0' ?
+
+ wxString res;
+ res += (wchar_t)(unsigned char) ascii;
+
+ return res;
+}
+
+const wxCharBuffer wxString::ToAscii() const
+{
+ // this will allocate enough space for the terminating NUL too
+ wxCharBuffer buffer(length());
+
+
+ char *dest = buffer.data();
+
+ const wchar_t *pwc = c_str();
+ for ( ;; )
+ {
+ *dest++ = (char)(*pwc > SCHAR_MAX ? wxT('_') : *pwc);
+
+ // the output string can't have embedded NULs anyhow, so we can safely
+ // stop at first of them even if we do have any
+ if ( !*pwc++ )
+ break;
+ }
+
+ return buffer;
+}
+
+#endif // Unicode
+
+// extract string of length nCount starting at nFirst
+wxString wxString::Mid(size_t nFirst, size_t nCount) const
+{
+ size_t nLen = length();
+
+ // default value of nCount is npos and means "till the end"
+ if ( nCount == npos )
+ {
+ nCount = nLen - nFirst;
+ }
-wxString operator+(const wxString& string1, const wxString& string2)
-{
- wxString s;
- s.ConcatCopy(string1.GetStringData()->nDataLength, string1.m_pchData,
- string2.GetStringData()->nDataLength, string2.m_pchData);
- return s;
-}
+ // out-of-bounds requests return sensible things
+ if ( nFirst + nCount > nLen )
+ {
+ nCount = nLen - nFirst;
+ }
-wxString operator+(const wxString& string1, char ch)
-{
- wxString s;
- s.ConcatCopy(string1.GetStringData()->nDataLength, string1.m_pchData, 1, &ch);
- return s;
-}
+ if ( nFirst > nLen )
+ {
+ // AllocCopy() will return empty string
+ return wxEmptyString;
+ }
-wxString operator+(char ch, const wxString& string)
-{
- wxString s;
- s.ConcatCopy(1, &ch, string.GetStringData()->nDataLength, string.m_pchData);
- return s;
-}
+ wxString dest(*this, nFirst, nCount);
+ if ( dest.length() != nCount )
+ {
+ wxFAIL_MSG( _T("out of memory in wxString::Mid") );
+ }
-wxString operator+(const wxString& string, const char *psz)
-{
- wxString s;
- s.ConcatCopy(string.GetStringData()->nDataLength, string.m_pchData,
- Strlen(psz), psz);
- return s;
+ return dest;
}
-wxString operator+(const char *psz, const wxString& string)
+// check that the string starts with prefix and return the rest of the string
+// in the provided pointer if it is not NULL, otherwise return false
+bool wxString::StartsWith(const wxChar *prefix, wxString *rest) const
{
- wxString s;
- s.ConcatCopy(Strlen(psz), psz,
- string.GetStringData()->nDataLength, string.m_pchData);
- return s;
-}
+ wxASSERT_MSG( prefix, _T("invalid parameter in wxString::StartsWith") );
-// ===========================================================================
-// other common string functions
-// ===========================================================================
+ // first check if the beginning of the string matches the prefix: note
+ // that we don't have to check that we don't run out of this string as
+ // when we reach the terminating NUL, either prefix string ends too (and
+ // then it's ok) or we break out of the loop because there is no match
+ const wxChar *p = c_str();
+ while ( *prefix )
+ {
+ if ( *prefix++ != *p++ )
+ {
+ // no match
+ return false;
+ }
+ }
-// ---------------------------------------------------------------------------
-// simple sub-string extraction
-// ---------------------------------------------------------------------------
+ if ( rest )
+ {
+ // put the rest of the string into provided pointer
+ *rest = p;
+ }
-// helper function: clone the data attached to this string
-void wxString::AllocCopy(wxString& dest, int nCopyLen, int nCopyIndex) const
-{
- if ( nCopyLen == 0 )
- {
- dest.Init();
- }
- else
- {
- dest.AllocBuffer(nCopyLen);
- memcpy(dest.m_pchData, m_pchData + nCopyIndex, nCopyLen*sizeof(char));
- }
+ return true;
}
-// extract string of length nCount starting at nFirst
-// default value of nCount is 0 and means "till the end"
-wxString wxString::Mid(size_t nFirst, size_t nCount) const
+
+// check that the string ends with suffix and return the rest of it in the
+// provided pointer if it is not NULL, otherwise return false
+bool wxString::EndsWith(const wxChar *suffix, wxString *rest) const
{
- // out-of-bounds requests return sensible things
- if ( nCount == 0 )
- nCount = GetStringData()->nDataLength - nFirst;
+ wxASSERT_MSG( suffix, _T("invalid parameter in wxString::EndssWith") );
- if ( nFirst + nCount > (size_t)GetStringData()->nDataLength )
- nCount = GetStringData()->nDataLength - nFirst;
- if ( nFirst > (size_t)GetStringData()->nDataLength )
- nCount = 0;
+ int start = length() - wxStrlen(suffix);
+ if ( start < 0 || wxStrcmp(c_str() + start, suffix) != 0 )
+ return false;
- wxString dest;
- AllocCopy(dest, nCount, nFirst);
- return dest;
+ if ( rest )
+ {
+ // put the rest of the string into provided pointer
+ rest->assign(*this, 0, start);
+ }
+
+ return true;
}
+
// extract nCount last (rightmost) characters
wxString wxString::Right(size_t nCount) const
{
- if ( nCount > (size_t)GetStringData()->nDataLength )
- nCount = GetStringData()->nDataLength;
+ if ( nCount > length() )
+ nCount = length();
- wxString dest;
- AllocCopy(dest, nCount, GetStringData()->nDataLength - nCount);
+ wxString dest(*this, length() - nCount, nCount);
+ if ( dest.length() != nCount ) {
+ wxFAIL_MSG( _T("out of memory in wxString::Right") );
+ }
return dest;
}
// get all characters after the last occurence of ch
// (returns the whole string if ch not found)
-wxString wxString::Right(char ch) const
+wxString wxString::AfterLast(wxChar ch) const
{
wxString str;
- int iPos = Find(ch, TRUE);
- if ( iPos == NOT_FOUND )
+ int iPos = Find(ch, true);
+ if ( iPos == wxNOT_FOUND )
str = *this;
else
str = c_str() + iPos + 1;
// extract nCount first (leftmost) characters
wxString wxString::Left(size_t nCount) const
{
- if ( nCount > (size_t)GetStringData()->nDataLength )
- nCount = GetStringData()->nDataLength;
+ if ( nCount > length() )
+ nCount = length();
- wxString dest;
- AllocCopy(dest, nCount, 0);
+ wxString dest(*this, 0, nCount);
+ if ( dest.length() != nCount ) {
+ wxFAIL_MSG( _T("out of memory in wxString::Left") );
+ }
return dest;
}
// get all characters before the first occurence of ch
// (returns the whole string if ch not found)
-wxString wxString::Left(char ch) const
+wxString wxString::BeforeFirst(wxChar ch) const
{
- wxString str;
- for ( const char *pc = m_pchData; *pc != '\0' && *pc != ch; pc++ )
- str += *pc;
-
- return str;
+ int iPos = Find(ch);
+ if ( iPos == wxNOT_FOUND ) iPos = length();
+ return wxString(*this, 0, iPos);
}
/// get all characters before the last occurence of ch
/// (returns empty string if ch not found)
-wxString wxString::Before(char ch) const
+wxString wxString::BeforeLast(wxChar ch) const
{
wxString str;
- int iPos = Find(ch, TRUE);
- if ( iPos != NOT_FOUND && iPos != 0 )
+ int iPos = Find(ch, true);
+ if ( iPos != wxNOT_FOUND && iPos != 0 )
str = wxString(c_str(), iPos);
return str;
/// get all characters after the first occurence of ch
/// (returns empty string if ch not found)
-wxString wxString::After(char ch) const
+wxString wxString::AfterFirst(wxChar ch) const
{
wxString str;
int iPos = Find(ch);
- if ( iPos != NOT_FOUND )
+ if ( iPos != wxNOT_FOUND )
str = c_str() + iPos + 1;
return str;
}
// replace first (or all) occurences of some substring with another one
-uint wxString::Replace(const char *szOld, const char *szNew, bool bReplaceAll)
-{
- uint uiCount = 0; // count of replacements made
-
- uint uiOldLen = Strlen(szOld);
-
- wxString strTemp;
- const char *pCurrent = m_pchData;
- const char *pSubstr;
- while ( *pCurrent != '\0' ) {
- pSubstr = strstr(pCurrent, szOld);
- if ( pSubstr == NULL ) {
- // strTemp is unused if no replacements were made, so avoid the copy
- if ( uiCount == 0 )
- return 0;
-
- strTemp += pCurrent; // copy the rest
- break; // exit the loop
- }
- else {
- // take chars before match
- strTemp.ConcatSelf(pSubstr - pCurrent, pCurrent);
- strTemp += szNew;
- pCurrent = pSubstr + uiOldLen; // restart after match
-
- uiCount++;
-
- // stop now?
- if ( !bReplaceAll ) {
- strTemp += pCurrent; // copy the rest
- break; // exit the loop
- }
+size_t wxString::Replace(const wxChar *szOld,
+ const wxChar *szNew, bool bReplaceAll)
+{
+ // if we tried to replace an empty string we'd enter an infinite loop below
+ wxCHECK_MSG( szOld && *szOld && szNew, 0,
+ _T("wxString::Replace(): invalid parameter") );
+
+ size_t uiCount = 0; // count of replacements made
+
+ size_t uiOldLen = wxStrlen(szOld);
+ size_t uiNewLen = wxStrlen(szNew);
+
+ size_t dwPos = 0;
+
+ while ( this->c_str()[dwPos] != wxT('\0') )
+ {
+ //DO NOT USE STRSTR HERE
+ //this string can contain embedded null characters,
+ //so strstr will function incorrectly
+ dwPos = find(szOld, dwPos);
+ if ( dwPos == npos )
+ break; // exit the loop
+ else
+ {
+ //replace this occurance of the old string with the new one
+ replace(dwPos, uiOldLen, szNew, uiNewLen);
+
+ //move up pos past the string that was replaced
+ dwPos += uiNewLen;
+
+ //increase replace count
+ ++uiCount;
+
+ // stop now?
+ if ( !bReplaceAll )
+ break; // exit the loop
+ }
}
- }
-
- // only done if there were replacements, otherwise would have returned above
- *this = strTemp;
- return uiCount;
+ return uiCount;
}
bool wxString::IsAscii() const
{
- const char *s = (const char*) *this;
+ const wxChar *s = (const wxChar*) *this;
while(*s){
- if(!isascii(*s)) return(FALSE);
+ if(!isascii(*s)) return(false);
s++;
}
- return(TRUE);
+ return(true);
}
-
+
bool wxString::IsWord() const
{
- const char *s = (const char*) *this;
+ const wxChar *s = (const wxChar*) *this;
while(*s){
- if(!isalpha(*s)) return(FALSE);
+ if(!wxIsalpha(*s)) return(false);
s++;
}
- return(TRUE);
+ return(true);
}
-
+
bool wxString::IsNumber() const
{
- const char *s = (const char*) *this;
+ const wxChar *s = (const wxChar*) *this;
+ if (wxStrlen(s))
+ if ((s[0] == wxT('-')) || (s[0] == wxT('+'))) s++;
while(*s){
- if(!isdigit(*s)) return(FALSE);
+ if(!wxIsdigit(*s)) return(false);
s++;
}
- return(TRUE);
+ return(true);
}
-// kludge: we don't have declaraton of wxStringData here, so we add offsets
-// manually to get to the "length" field of wxStringData structure
-bool wxString::IsEmpty() const { return Len() == 0; }
-
wxString wxString::Strip(stripType w) const
{
wxString s = *this;
- if ( w & leading ) s.Trim(FALSE);
- if ( w & trailing ) s.Trim(TRUE);
+ if ( w & leading ) s.Trim(false);
+ if ( w & trailing ) s.Trim(true);
return s;
}
-/// case-insensitive strcmp() (platform independent)
-int Stricmp(const char *psz1, const char *psz2)
-{
-#if defined(_MSC_VER)
- return _stricmp(psz1, psz2);
-#elif defined(__BORLANDC__)
- return stricmp(psz1, psz2);
-#elif defined(__UNIX__) || defined(__GNUWIN32__)
- return strcasecmp(psz1, psz2);
-#else
- // almost all compilers/libraries provide this function (unfortunately under
- // different names), that's why we don't implement our own which will surely
- // be more efficient than this code (uncomment to use):
- /*
- register char c1, c2;
- do {
- c1 = tolower(*psz1++);
- c2 = tolower(*psz2++);
- } while ( c1 && (c1 == c2) );
-
- return c1 - c2;
- */
-
- #error "Please define string case-insensitive compare for your OS/compiler"
-#endif // OS/compiler
-}
-
// ---------------------------------------------------------------------------
// case conversion
// ---------------------------------------------------------------------------
wxString& wxString::MakeUpper()
{
- CopyBeforeWrite();
-
- for ( char *p = m_pchData; *p; p++ )
- *p = (char)toupper(*p);
+ for ( iterator it = begin(), en = end(); it != en; ++it )
+ *it = (wxChar)wxToupper(*it);
return *this;
}
wxString& wxString::MakeLower()
{
- CopyBeforeWrite();
-
- for ( char *p = m_pchData; *p; p++ )
- *p = (char)tolower(*p);
+ for ( iterator it = begin(), en = end(); it != en; ++it )
+ *it = (wxChar)wxTolower(*it);
return *this;
}
// trimming and padding
// ---------------------------------------------------------------------------
+// some compilers (VC++ 6.0 not to name them) return true for a call to
+// isspace('ê') in the C locale which seems to be broken to me, but we have to
+// live with this by checking that the character is a 7 bit one - even if this
+// may fail to detect some spaces (I don't know if Unicode doesn't have
+// space-like symbols somewhere except in the first 128 chars), it is arguably
+// still better than trimming away accented letters
+inline int wxSafeIsspace(wxChar ch) { return (ch < 127) && wxIsspace(ch); }
+
// trims spaces (in the sense of isspace) from left or right side
wxString& wxString::Trim(bool bFromRight)
{
- CopyBeforeWrite();
-
- if ( bFromRight )
- {
- // find last non-space character
- char *psz = m_pchData + GetStringData()->nDataLength - 1;
- while ( isspace(*psz) && (psz >= m_pchData) )
- psz--;
-
- // truncate at trailing space start
- *++psz = '\0';
- GetStringData()->nDataLength = psz - m_pchData;
- }
- else
- {
- // find first non-space character
- const char *psz = m_pchData;
- while ( isspace(*psz) )
- psz++;
-
- // fix up data and length
- int nDataLength = GetStringData()->nDataLength - (psz - m_pchData);
- memmove(m_pchData, psz, (nDataLength + 1)*sizeof(char));
- GetStringData()->nDataLength = nDataLength;
- }
+ // first check if we're going to modify the string at all
+ if ( !empty() &&
+ (
+ (bFromRight && wxSafeIsspace(GetChar(length() - 1))) ||
+ (!bFromRight && wxSafeIsspace(GetChar(0u)))
+ )
+ )
+ {
+ if ( bFromRight )
+ {
+ // find last non-space character
+ reverse_iterator psz = rbegin();
+ while ( (psz != rend()) && wxSafeIsspace(*psz) )
+ psz++;
+
+ // truncate at trailing space start
+ erase(psz.base(), end());
+ }
+ else
+ {
+ // find first non-space character
+ iterator psz = begin();
+ while ( (psz != end()) && wxSafeIsspace(*psz) )
+ psz++;
+
+ // fix up data and length
+ erase(begin(), psz);
+ }
+ }
- return *this;
+ return *this;
}
// adds nCount characters chPad to the string from either side
-wxString& wxString::Pad(size_t nCount, char chPad, bool bFromRight)
+wxString& wxString::Pad(size_t nCount, wxChar chPad, bool bFromRight)
{
- wxString s(chPad, nCount);
+ wxString s(chPad, nCount);
- if ( bFromRight )
- *this += s;
- else
- {
- s += *this;
- *this = s;
- }
+ if ( bFromRight )
+ *this += s;
+ else
+ {
+ s += *this;
+ swap(s);
+ }
- return *this;
+ return *this;
}
// truncate the string
wxString& wxString::Truncate(size_t uiLen)
{
- *(m_pchData + uiLen) = '\0';
- GetStringData()->nDataLength = uiLen;
+ if ( uiLen < length() )
+ {
+ erase(begin() + uiLen, end());
+ }
+ //else: nothing to do, string is already short enough
- return *this;
+ return *this;
}
// ---------------------------------------------------------------------------
-// finding (return NOT_FOUND if not found and index otherwise)
+// finding (return wxNOT_FOUND if not found and index otherwise)
// ---------------------------------------------------------------------------
// find a character
-int wxString::Find(char ch, bool bFromEnd) const
+int wxString::Find(wxChar ch, bool bFromEnd) const
{
- const char *psz = bFromEnd ? strrchr(m_pchData, ch) : strchr(m_pchData, ch);
+ size_type idx = bFromEnd ? find_last_of(ch) : find_first_of(ch);
- return (psz == NULL) ? NOT_FOUND : psz - m_pchData;
+ return (idx == npos) ? wxNOT_FOUND : (int)idx;
}
// find a sub-string (like strstr)
-int wxString::Find(const char *pszSub) const
+int wxString::Find(const wxChar *pszSub) const
{
- const char *psz = strstr(m_pchData, pszSub);
+ size_type idx = find(pszSub);
- return (psz == NULL) ? NOT_FOUND : psz - m_pchData;
+ return (idx == npos) ? wxNOT_FOUND : (int)idx;
}
-// ---------------------------------------------------------------------------
-// formatted output
-// ---------------------------------------------------------------------------
-int wxString::Printf(const char *pszFormat, ...)
+// ----------------------------------------------------------------------------
+// conversion to numbers
+// ----------------------------------------------------------------------------
+
+// the implementation of all the functions below is exactly the same so factor
+// it out
+#ifndef __WATCOMC__
+
+template <typename T>
+bool wxStringToIntType(const wxChar *start,
+ T *val,
+ int base,
+ T (*func)(const wxChar *, wxChar **, int))
{
- va_list argptr;
- va_start(argptr, pszFormat);
+ wxCHECK_MSG( val, false, _T("NULL output pointer") );
+ wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") );
- int iLen = PrintfV(pszFormat, argptr);
+ errno = 0;
- va_end(argptr);
+ wxChar *end;
+ *val = (*func)(start, &end, base);
- return iLen;
+ // return true only if scan was stopped by the terminating NUL and if the
+ // string was not empty to start with and no under/overflow occurred
+ return !*end && (end != start) && (errno != ERANGE);
}
-int wxString::PrintfV(const char* pszFormat, va_list argptr)
-{
- static char s_szScratch[1024];
+#define wxSTR2INT(val, b, func) return wxStringToIntType(c_str(), val, b, func)
- int iLen = vsprintf(s_szScratch, pszFormat, argptr);
- AllocBeforeWrite(iLen);
- strcpy(m_pchData, s_szScratch);
+#else // __WATCOMC__
- return iLen;
-}
+// FIXME, TODO, ASAP !!! - ugly trick to make release for Open Watcom possible
+// without changing code flow for other compilers
-// ---------------------------------------------------------------------------
-// standard C++ library string functions
-// ---------------------------------------------------------------------------
-#ifdef STD_STRING_COMPATIBILITY
+#define wxSTR2INT(val, base, func) \
+ wxCHECK_MSG( val, false, _T("NULL output pointer") ); \
+ wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); \
+ \
+ errno = 0; \
+ \
+ wxChar *end; \
+ *val = (*func)(c_str(), &end, base); \
+ \
+ return !*end && (end != c_str()) && (errno != ERANGE)
-wxString& wxString::insert(size_t nPos, const wxString& str)
-{
- wxASSERT( nPos <= Len() );
+#endif // !__WATCOMC__/__WATCOMC__
- wxString strTmp;
- char *pc = strTmp.GetWriteBuf(Len() + str.Len() + 1);
- strncpy(pc, c_str(), nPos);
- strcpy(pc + nPos, str);
- strcpy(pc + nPos + str.Len(), c_str() + nPos);
- *this = strTmp;
-
- return *this;
+bool wxString::ToLong(long *val, int base) const
+{
+ wxSTR2INT(val, base, wxStrtol);
}
-size_t wxString::find(const wxString& str, size_t nStart) const
+bool wxString::ToULong(unsigned long *val, int base) const
{
- wxASSERT( nStart <= Len() );
-
- const char *p = strstr(c_str() + nStart, str);
-
- return p == NULL ? npos : p - c_str();
+ wxSTR2INT(val, base, wxStrtoul);
}
-// VC++ 1.5 can't cope with the default argument in the header.
-#if ! (defined(_MSC_VER) && !defined(__WIN32__))
-size_t wxString::find(const char* sz, size_t nStart, size_t n) const
+bool wxString::ToLongLong(wxLongLong_t *val, int base) const
{
- return find(wxString(sz, n == npos ? 0 : n), nStart);
+#ifdef wxHAS_STRTOLL
+ wxSTR2INT(val, base, wxStrtoll);
+#else
+ // TODO: implement this ourselves
+ wxUnusedVar(val);
+ wxUnusedVar(base);
+ return false;
+#endif // wxHAS_STRTOLL
}
+
+bool wxString::ToULongLong(wxULongLong_t *val, int base) const
+{
+#ifdef wxHAS_STRTOLL
+ wxSTR2INT(val, base, wxStrtoull);
+#else
+ // TODO: implement this ourselves
+ wxUnusedVar(val);
+ wxUnusedVar(base);
+ return false;
#endif
-
-size_t wxString::find(char ch, size_t nStart) const
+}
+
+bool wxString::ToDouble(double *val) const
{
- wxASSERT( nStart <= Len() );
+ wxCHECK_MSG( val, false, _T("NULL pointer in wxString::ToDouble") );
+
+ errno = 0;
+
+ const wxChar *start = c_str();
+ wxChar *end;
+ *val = wxStrtod(start, &end);
- const char *p = strchr(c_str() + nStart, ch);
-
- return p == NULL ? npos : p - c_str();
+ // return true only if scan was stopped by the terminating NUL and if the
+ // string was not empty to start with and no under/overflow occurred
+ return !*end && (end != start) && (errno != ERANGE);
}
-size_t wxString::rfind(const wxString& str, size_t nStart) const
+// ---------------------------------------------------------------------------
+// formatted output
+// ---------------------------------------------------------------------------
+
+/* static */
+wxString wxString::Format(const wxChar *pszFormat, ...)
{
- wxASSERT( nStart <= Len() );
+ va_list argptr;
+ va_start(argptr, pszFormat);
- // # could be quicker than that
- const char *p = c_str() + (nStart == npos ? Len() : nStart);
- while ( p >= c_str() + str.Len() ) {
- if ( strncmp(p - str.Len(), str, str.Len()) == 0 )
- return p - str.Len() - c_str();
- p--;
- }
-
- return npos;
+ wxString s;
+ s.PrintfV(pszFormat, argptr);
+
+ va_end(argptr);
+
+ return s;
}
-
-// VC++ 1.5 can't cope with the default argument in the header.
-#if ! (defined(_MSC_VER) && !defined(__WIN32__))
-size_t wxString::rfind(const char* sz, size_t nStart, size_t n) const
+
+/* static */
+wxString wxString::FormatV(const wxChar *pszFormat, va_list argptr)
{
- return rfind(wxString(sz, n == npos ? 0 : n), nStart);
+ wxString s;
+ s.PrintfV(pszFormat, argptr);
+ return s;
}
-size_t wxString::rfind(char ch, size_t nStart) const
-{
- wxASSERT( nStart <= Len() );
+int wxString::Printf(const wxChar *pszFormat, ...)
+{
+ va_list argptr;
+ va_start(argptr, pszFormat);
+
+ int iLen = PrintfV(pszFormat, argptr);
+
+ va_end(argptr);
+
+ return iLen;
+}
+
+int wxString::PrintfV(const wxChar* pszFormat, va_list argptr)
+{
+ int size = 1024;
+
+ for ( ;; )
+ {
+ wxStringBuffer tmp(*this, size + 1);
+ wxChar *buf = tmp;
+
+ if ( !buf )
+ {
+ // out of memory
+ return -1;
+ }
+
+ // wxVsnprintf() may modify the original arg pointer, so pass it
+ // only a copy
+ va_list argptrcopy;
+ wxVaCopy(argptrcopy, argptr);
+ int len = wxVsnprintf(buf, size, pszFormat, argptrcopy);
+ va_end(argptrcopy);
+
+ // some implementations of vsnprintf() don't NUL terminate
+ // the string if there is not enough space for it so
+ // always do it manually
+ buf[size] = _T('\0');
+
+ // vsnprintf() may return either -1 (traditional Unix behaviour) or the
+ // total number of characters which would have been written if the
+ // buffer were large enough (newer standards such as Unix98)
+ if ( len < 0 )
+ {
+ // still not enough, as we don't know how much we need, double the
+ // current size of the buffer
+ size *= 2;
+ }
+ else if ( len >= size )
+ {
+ // some vsnprintf() implementations NUL-terminate the buffer and
+ // some don't in len == size case, to be safe always add 1
+ size = len + 1;
+ }
+ else // ok, there was enough space
+ {
+ break;
+ }
+ }
- const char *p = strrchr(c_str() + nStart, ch);
-
- return p == NULL ? npos : p - c_str();
-}
-#endif
+ // we could have overshot
+ Shrink();
+
+ return length();
+}
+
+// ----------------------------------------------------------------------------
+// misc other operations
+// ----------------------------------------------------------------------------
+
+// returns true if the string matches the pattern which may contain '*' and
+// '?' metacharacters (as usual, '?' matches any character and '*' any number
+// of them)
+bool wxString::Matches(const wxChar *pszMask) const
+{
+ // I disable this code as it doesn't seem to be faster (in fact, it seems
+ // to be much slower) than the old, hand-written code below and using it
+ // here requires always linking with libregex even if the user code doesn't
+ // use it
+#if 0 // wxUSE_REGEX
+ // first translate the shell-like mask into a regex
+ wxString pattern;
+ pattern.reserve(wxStrlen(pszMask));
+
+ pattern += _T('^');
+ while ( *pszMask )
+ {
+ switch ( *pszMask )
+ {
+ case _T('?'):
+ pattern += _T('.');
+ break;
+
+ case _T('*'):
+ pattern += _T(".*");
+ break;
+
+ case _T('^'):
+ case _T('.'):
+ case _T('$'):
+ case _T('('):
+ case _T(')'):
+ case _T('|'):
+ case _T('+'):
+ case _T('\\'):
+ // these characters are special in a RE, quote them
+ // (however note that we don't quote '[' and ']' to allow
+ // using them for Unix shell like matching)
+ pattern += _T('\\');
+ // fall through
+
+ default:
+ pattern += *pszMask;
+ }
+
+ pszMask++;
+ }
+ pattern += _T('$');
-wxString wxString::substr(size_t nStart, size_t nLen) const
-{
- // npos means 'take all'
- if ( nLen == npos )
- nLen = 0;
+ // and now use it
+ return wxRegEx(pattern, wxRE_NOSUB | wxRE_EXTENDED).Matches(c_str());
+#else // !wxUSE_REGEX
+ // TODO: this is, of course, awfully inefficient...
- wxASSERT( nStart + nLen <= Len() );
+ // the char currently being checked
+ const wxChar *pszTxt = c_str();
- return wxString(c_str() + nStart, nLen == npos ? 0 : nLen);
-}
+ // the last location where '*' matched
+ const wxChar *pszLastStarInText = NULL;
+ const wxChar *pszLastStarInMask = NULL;
-wxString& wxString::erase(size_t nStart, size_t nLen)
-{
- wxString strTmp(c_str(), nStart);
- if ( nLen != npos ) {
- wxASSERT( nStart + nLen <= Len() );
+match:
+ for ( ; *pszMask != wxT('\0'); pszMask++, pszTxt++ ) {
+ switch ( *pszMask ) {
+ case wxT('?'):
+ if ( *pszTxt == wxT('\0') )
+ return false;
+
+ // pszTxt and pszMask will be incremented in the loop statement
+
+ break;
+
+ case wxT('*'):
+ {
+ // remember where we started to be able to backtrack later
+ pszLastStarInText = pszTxt;
+ pszLastStarInMask = pszMask;
+
+ // ignore special chars immediately following this one
+ // (should this be an error?)
+ while ( *pszMask == wxT('*') || *pszMask == wxT('?') )
+ pszMask++;
+
+ // if there is nothing more, match
+ if ( *pszMask == wxT('\0') )
+ return true;
+
+ // are there any other metacharacters in the mask?
+ size_t uiLenMask;
+ const wxChar *pEndMask = wxStrpbrk(pszMask, wxT("*?"));
+
+ if ( pEndMask != NULL ) {
+ // we have to match the string between two metachars
+ uiLenMask = pEndMask - pszMask;
+ }
+ else {
+ // we have to match the remainder of the string
+ uiLenMask = wxStrlen(pszMask);
+ }
+
+ wxString strToMatch(pszMask, uiLenMask);
+ const wxChar* pMatch = wxStrstr(pszTxt, strToMatch);
+ if ( pMatch == NULL )
+ return false;
+
+ // -1 to compensate "++" in the loop
+ pszTxt = pMatch + uiLenMask - 1;
+ pszMask += uiLenMask - 1;
+ }
+ break;
- strTmp.append(c_str() + nStart + nLen);
+ default:
+ if ( *pszMask != *pszTxt )
+ return false;
+ break;
+ }
}
- *this = strTmp;
- return *this;
-}
+ // match only if nothing left
+ if ( *pszTxt == wxT('\0') )
+ return true;
-wxString& wxString::replace(size_t nStart, size_t nLen, const char *sz)
-{
- wxASSERT( nStart + nLen <= Strlen(sz) );
+ // if we failed to match, backtrack if we can
+ if ( pszLastStarInText ) {
+ pszTxt = pszLastStarInText + 1;
+ pszMask = pszLastStarInMask;
- wxString strTmp;
- if ( nStart != 0 )
- strTmp.append(c_str(), nStart);
- strTmp += sz;
- strTmp.append(c_str() + nStart + nLen);
-
- *this = strTmp;
- return *this;
-}
+ pszLastStarInText = NULL;
-wxString& wxString::replace(size_t nStart, size_t nLen, size_t nCount, char ch)
-{
- return replace(nStart, nLen, wxString(ch, nCount));
-}
+ // don't bother resetting pszLastStarInMask, it's unnecessary
-wxString& wxString::replace(size_t nStart, size_t nLen,
- const wxString& str, size_t nStart2, size_t nLen2)
-{
- return replace(nStart, nLen, str.substr(nStart2, nLen2));
+ goto match;
+ }
+
+ return false;
+#endif // wxUSE_REGEX/!wxUSE_REGEX
}
-wxString& wxString::replace(size_t nStart, size_t nLen,
- const char* sz, size_t nCount)
+// Count the number of chars
+int wxString::Freq(wxChar ch) const
{
- return replace(nStart, nLen, wxString(sz, nCount));
+ int count = 0;
+ int len = length();
+ for (int i = 0; i < len; i++)
+ {
+ if (GetChar(i) == ch)
+ count ++;
+ }
+ return count;
}
-#endif //std::string compatibility
+// convert to upper case, return the copy of the string
+wxString wxString::Upper() const
+{ wxString s(*this); return s.MakeUpper(); }
+
+// convert to lower case, return the copy of the string
+wxString wxString::Lower() const { wxString s(*this); return s.MakeLower(); }
+
+int wxString::sprintf(const wxChar *pszFormat, ...)
+ {
+ va_list argptr;
+ va_start(argptr, pszFormat);
+ int iLen = PrintfV(pszFormat, argptr);
+ va_end(argptr);
+ return iLen;
+ }
// ============================================================================
// ArrayString
// ============================================================================
-// size increment = max(50% of current size, ARRAY_MAXSIZE_INCREMENT)
+#include "wx/arrstr.h"
+
+wxArrayString::wxArrayString(size_t sz, const wxChar** a)
+{
+#if !wxUSE_STL
+ Init(false);
+#endif
+ for (size_t i=0; i < sz; i++)
+ Add(a[i]);
+}
+
+wxArrayString::wxArrayString(size_t sz, const wxString* a)
+{
+#if !wxUSE_STL
+ Init(false);
+#endif
+ for (size_t i=0; i < sz; i++)
+ Add(a[i]);
+}
+
+#if !wxUSE_STL
+
+// size increment = min(50% of current size, ARRAY_MAXSIZE_INCREMENT)
#define ARRAY_MAXSIZE_INCREMENT 4096
+
#ifndef ARRAY_DEFAULT_INITIAL_SIZE // also defined in dynarray.h
- #define ARRAY_DEFAULT_INITIAL_SIZE (16)
+#define ARRAY_DEFAULT_INITIAL_SIZE (16)
#endif
#define STRING(p) ((wxString *)(&(p)))
// ctor
-wxArrayString::wxArrayString()
+void wxArrayString::Init(bool autoSort)
{
m_nSize =
m_nCount = 0;
- m_pItems = NULL;
+ m_pItems = (wxChar **) NULL;
+ m_autoSort = autoSort;
}
// copy ctor
wxArrayString::wxArrayString(const wxArrayString& src)
{
- m_nSize = src.m_nSize;
- m_nCount = src.m_nCount;
-
- if ( m_nSize != 0 )
- m_pItems = new char *[m_nSize];
- else
- m_pItems = NULL;
+ Init(src.m_autoSort);
- if ( m_nCount != 0 )
- memcpy(m_pItems, src.m_pItems, m_nCount*sizeof(char *));
+ *this = src;
}
-// copy operator
+// assignment operator
wxArrayString& wxArrayString::operator=(const wxArrayString& src)
{
- DELETEA(m_pItems);
-
- m_nSize = src.m_nSize;
- m_nCount = src.m_nCount;
+ if ( m_nSize > 0 )
+ Clear();
- if ( m_nSize != 0 )
- m_pItems = new char *[m_nSize];
- else
- m_pItems = NULL;
+ Copy(src);
- if ( m_nCount != 0 )
- memcpy(m_pItems, src.m_pItems, m_nCount*sizeof(char *));
+ m_autoSort = src.m_autoSort;
return *this;
}
+void wxArrayString::Copy(const wxArrayString& src)
+{
+ if ( src.m_nCount > ARRAY_DEFAULT_INITIAL_SIZE )
+ Alloc(src.m_nCount);
+
+ for ( size_t n = 0; n < src.m_nCount; n++ )
+ Add(src[n]);
+}
+
// grow the array
-void wxArrayString::Grow()
+void wxArrayString::Grow(size_t nIncrement)
{
// only do it if no more place
- if( m_nCount == m_nSize ) {
- if( m_nSize == 0 ) {
+ if ( (m_nSize - m_nCount) < nIncrement ) {
+ // if ARRAY_DEFAULT_INITIAL_SIZE were set to 0, the initially empty would
+ // be never resized!
+ #if ARRAY_DEFAULT_INITIAL_SIZE == 0
+ #error "ARRAY_DEFAULT_INITIAL_SIZE must be > 0!"
+ #endif
+
+ if ( m_nSize == 0 ) {
// was empty, alloc some memory
m_nSize = ARRAY_DEFAULT_INITIAL_SIZE;
- m_pItems = new char *[m_nSize];
+ if (m_nSize < nIncrement)
+ m_nSize = nIncrement;
+ m_pItems = new wxChar *[m_nSize];
}
else {
+ // otherwise when it's called for the first time, nIncrement would be 0
+ // and the array would never be expanded
// add 50% but not too much
- size_t nIncrement = m_nSize >> 1;
- if ( nIncrement > ARRAY_MAXSIZE_INCREMENT )
- nIncrement = ARRAY_MAXSIZE_INCREMENT;
+ size_t ndefIncrement = m_nSize < ARRAY_DEFAULT_INITIAL_SIZE
+ ? ARRAY_DEFAULT_INITIAL_SIZE : m_nSize >> 1;
+ if ( ndefIncrement > ARRAY_MAXSIZE_INCREMENT )
+ ndefIncrement = ARRAY_MAXSIZE_INCREMENT;
+ if ( nIncrement < ndefIncrement )
+ nIncrement = ndefIncrement;
m_nSize += nIncrement;
- char **pNew = new char *[m_nSize];
+ wxChar **pNew = new wxChar *[m_nSize];
// copy data to new location
- memcpy(pNew, m_pItems, m_nCount*sizeof(char *));
+ memcpy(pNew, m_pItems, m_nCount*sizeof(wxChar *));
// delete old memory (but do not release the strings!)
- DELETEA(m_pItems);
+ wxDELETEA(m_pItems);
m_pItems = pNew;
}
{
Free();
- m_nSize =
+ m_nSize =
m_nCount = 0;
- DELETEA(m_pItems);
- m_pItems = NULL;
+ wxDELETEA(m_pItems);
}
// dtor
{
Free();
- DELETEA(m_pItems);
+ wxDELETEA(m_pItems);
+}
+
+void wxArrayString::reserve(size_t nSize)
+{
+ Alloc(nSize);
}
// pre-allocates memory (frees the previous data!)
void wxArrayString::Alloc(size_t nSize)
{
- wxASSERT( nSize > 0 );
-
// only if old buffer was not big enough
if ( nSize > m_nSize ) {
Free();
- DELETEA(m_pItems);
- m_pItems = new char *[nSize];
+ wxDELETEA(m_pItems);
+ m_pItems = new wxChar *[nSize];
m_nSize = nSize;
}
m_nCount = 0;
}
-// searches the array for an item (forward or backwards)
+// minimizes the memory usage by freeing unused memory
+void wxArrayString::Shrink()
+{
+ // only do it if we have some memory to free
+ if( m_nCount < m_nSize ) {
+ // allocates exactly as much memory as we need
+ wxChar **pNew = new wxChar *[m_nCount];
-// Robert Roebling (changed to bool from bool)
+ // copy data to new location
+ memcpy(pNew, m_pItems, m_nCount*sizeof(wxChar *));
+ delete [] m_pItems;
+ m_pItems = pNew;
+ }
+}
-int wxArrayString::Index(const char *sz, bool bCase, bool bFromEnd) const
+#if WXWIN_COMPATIBILITY_2_4
+
+// return a wxString[] as required for some control ctors.
+wxString* wxArrayString::GetStringArray() const
{
- if ( bFromEnd ) {
- if ( m_nCount > 0 ) {
- uint ui = m_nCount;
- do {
- if ( STRING(m_pItems[--ui])->IsSameAs(sz, bCase) )
- return ui;
- }
- while ( ui != 0 );
+ wxString *array = 0;
+
+ if( m_nCount > 0 )
+ {
+ array = new wxString[m_nCount];
+ for( size_t i = 0; i < m_nCount; i++ )
+ array[i] = m_pItems[i];
+ }
+
+ return array;
+}
+
+void wxArrayString::Remove(size_t nIndex, size_t nRemove)
+{
+ RemoveAt(nIndex, nRemove);
+}
+
+#endif // WXWIN_COMPATIBILITY_2_4
+
+// searches the array for an item (forward or backwards)
+int wxArrayString::Index(const wxChar *sz, bool bCase, bool bFromEnd) const
+{
+ if ( m_autoSort ) {
+ // use binary search in the sorted array
+ wxASSERT_MSG( bCase && !bFromEnd,
+ wxT("search parameters ignored for auto sorted array") );
+
+ size_t i,
+ lo = 0,
+ hi = m_nCount;
+ int res;
+ while ( lo < hi ) {
+ i = (lo + hi)/2;
+
+ res = wxStrcmp(sz, m_pItems[i]);
+ if ( res < 0 )
+ hi = i;
+ else if ( res > 0 )
+ lo = i + 1;
+ else
+ return i;
}
+
+ return wxNOT_FOUND;
}
else {
- for( uint ui = 0; ui < m_nCount; ui++ ) {
- if( STRING(m_pItems[ui])->IsSameAs(sz, bCase) )
- return ui;
+ // use linear search in unsorted array
+ if ( bFromEnd ) {
+ if ( m_nCount > 0 ) {
+ size_t ui = m_nCount;
+ do {
+ if ( STRING(m_pItems[--ui])->IsSameAs(sz, bCase) )
+ return ui;
+ }
+ while ( ui != 0 );
+ }
+ }
+ else {
+ for( size_t ui = 0; ui < m_nCount; ui++ ) {
+ if( STRING(m_pItems[ui])->IsSameAs(sz, bCase) )
+ return ui;
+ }
}
}
- return NOT_FOUND;
+ return wxNOT_FOUND;
}
// add item at the end
-void wxArrayString::Add(const wxString& src)
-{
- Grow();
+size_t wxArrayString::Add(const wxString& str, size_t nInsert)
+{
+ if ( m_autoSort ) {
+ // insert the string at the correct position to keep the array sorted
+ size_t i,
+ lo = 0,
+ hi = m_nCount;
+ int res;
+ while ( lo < hi ) {
+ i = (lo + hi)/2;
+
+ res = str.Cmp(m_pItems[i]);
+ if ( res < 0 )
+ hi = i;
+ else if ( res > 0 )
+ lo = i + 1;
+ else {
+ lo = hi = i;
+ break;
+ }
+ }
+
+ wxASSERT_MSG( lo == hi, wxT("binary search broken") );
+
+ Insert(str, lo, nInsert);
+
+ return (size_t)lo;
+ }
+ else {
+ wxASSERT( str.GetStringData()->IsValid() );
+
+ Grow(nInsert);
+
+ for (size_t i = 0; i < nInsert; i++)
+ {
+ // the string data must not be deleted!
+ str.GetStringData()->Lock();
- // the string data must not be deleted!
- src.GetStringData()->Lock();
- m_pItems[m_nCount++] = (char *)src.c_str();
+ // just append
+ m_pItems[m_nCount + i] = (wxChar *)str.c_str(); // const_cast
+ }
+ size_t ret = m_nCount;
+ m_nCount += nInsert;
+ return ret;
+ }
}
// add item at the given position
-void wxArrayString::Insert(const wxString& src, size_t nIndex)
+void wxArrayString::Insert(const wxString& str, size_t nIndex, size_t nInsert)
+{
+ wxASSERT( str.GetStringData()->IsValid() );
+
+ wxCHECK_RET( nIndex <= m_nCount, wxT("bad index in wxArrayString::Insert") );
+ wxCHECK_RET( m_nCount <= m_nCount + nInsert,
+ wxT("array size overflow in wxArrayString::Insert") );
+
+ Grow(nInsert);
+
+ memmove(&m_pItems[nIndex + nInsert], &m_pItems[nIndex],
+ (m_nCount - nIndex)*sizeof(wxChar *));
+
+ for (size_t i = 0; i < nInsert; i++)
+ {
+ str.GetStringData()->Lock();
+ m_pItems[nIndex + i] = (wxChar *)str.c_str();
+ }
+ m_nCount += nInsert;
+}
+
+// range insert (STL 23.2.4.3)
+void
+wxArrayString::insert(iterator it, const_iterator first, const_iterator last)
{
- wxCHECK_RET( nIndex <= m_nCount, "bad index in wxArrayString::Insert" );
+ const int idx = it - begin();
+
+ // grow it once
+ Grow(last - first);
- Grow();
+ // reset "it" since it can change inside Grow()
+ it = begin() + idx;
- memmove(&m_pItems[nIndex + 1], &m_pItems[nIndex],
- (m_nCount - nIndex)*sizeof(char *));
+ while ( first != last )
+ {
+ it = insert(it, *first);
- src.GetStringData()->Lock();
- m_pItems[nIndex] = (char *)src.c_str();
+ // insert returns an iterator to the last element inserted but we need
+ // insert the next after this one, that is before the next one
+ ++it;
- m_nCount++;
+ ++first;
+ }
+}
+
+// expand the array
+void wxArrayString::SetCount(size_t count)
+{
+ Alloc(count);
+
+ wxString s;
+ while ( m_nCount < count )
+ m_pItems[m_nCount++] = (wxChar *)s.c_str();
}
// removes item from array (by index)
-void wxArrayString::Remove(size_t nIndex)
+void wxArrayString::RemoveAt(size_t nIndex, size_t nRemove)
{
- wxCHECK_RET( nIndex <= m_nCount, "bad index in wxArrayString::Remove" );
+ wxCHECK_RET( nIndex < m_nCount, wxT("bad index in wxArrayString::Remove") );
+ wxCHECK_RET( nIndex + nRemove <= m_nCount,
+ wxT("removing too many elements in wxArrayString::Remove") );
// release our lock
- Item(nIndex).GetStringData()->Unlock();
+ for (size_t i = 0; i < nRemove; i++)
+ Item(nIndex + i).GetStringData()->Unlock();
- memmove(&m_pItems[nIndex], &m_pItems[nIndex + 1],
- (m_nCount - nIndex - 1)*sizeof(char *));
- m_nCount--;
+ memmove(&m_pItems[nIndex], &m_pItems[nIndex + nRemove],
+ (m_nCount - nIndex - nRemove)*sizeof(wxChar *));
+ m_nCount -= nRemove;
}
// removes item from array (by value)
-void wxArrayString::Remove(const char *sz)
+void wxArrayString::Remove(const wxChar *sz)
{
int iIndex = Index(sz);
- wxCHECK_RET( iIndex != NOT_FOUND,
- "removing inexistent element in wxArrayString::Remove" );
+ wxCHECK_RET( iIndex != wxNOT_FOUND,
+ wxT("removing inexistent element in wxArrayString::Remove") );
+
+ RemoveAt(iIndex);
+}
+
+void wxArrayString::assign(const_iterator first, const_iterator last)
+{
+ reserve(last - first);
+ for(; first != last; ++first)
+ push_back(*first);
+}
+
+// ----------------------------------------------------------------------------
+// sorting
+// ----------------------------------------------------------------------------
+
+// we can only sort one array at a time with the quick-sort based
+// implementation
+#if wxUSE_THREADS
+ // need a critical section to protect access to gs_compareFunction and
+ // gs_sortAscending variables
+ static wxCriticalSection gs_critsectStringSort;
+#endif // wxUSE_THREADS
+
+// function to use for string comparaison
+static wxArrayString::CompareFunction gs_compareFunction = NULL;
+
+// if we don't use the compare function, this flag tells us if we sort the
+// array in ascending or descending order
+static bool gs_sortAscending = true;
+
+// function which is called by quick sort
+extern "C" int wxC_CALLING_CONV // LINKAGEMODE
+wxStringCompareFunction(const void *first, const void *second)
+{
+ wxString *strFirst = (wxString *)first;
+ wxString *strSecond = (wxString *)second;
+
+ if ( gs_compareFunction ) {
+ return gs_compareFunction(*strFirst, *strSecond);
+ }
+ else {
+ // maybe we should use wxStrcoll
+ int result = strFirst->Cmp(*strSecond);
- Remove((size_t)iIndex);
+ return gs_sortAscending ? result : -result;
+ }
}
// sort array elements using passed comparaison function
+void wxArrayString::Sort(CompareFunction compareFunction)
+{
+ wxCRIT_SECT_LOCKER(lockCmpFunc, gs_critsectStringSort);
+
+ wxASSERT( !gs_compareFunction ); // must have been reset to NULL
+ gs_compareFunction = compareFunction;
+
+ DoSort();
+
+ // reset it to NULL so that Sort(bool) will work the next time
+ gs_compareFunction = NULL;
+}
+
+extern "C"
+{
+ typedef int (wxC_CALLING_CONV * wxStringCompareFn)(const void *first,
+ const void *second);
+}
+
+void wxArrayString::Sort(CompareFunction2 compareFunction)
+{
+ qsort(m_pItems, m_nCount, sizeof(wxChar *), (wxStringCompareFn)compareFunction);
+}
+
+void wxArrayString::Sort(bool reverseOrder)
+{
+ Sort(reverseOrder ? wxStringSortDescending : wxStringSortAscending);
+}
+
+void wxArrayString::DoSort()
+{
+ wxCHECK_RET( !m_autoSort, wxT("can't use this method with sorted arrays") );
+
+ // just sort the pointers using qsort() - of course it only works because
+ // wxString() *is* a pointer to its data
+ qsort(m_pItems, m_nCount, sizeof(wxChar *), wxStringCompareFunction);
+}
+
+bool wxArrayString::operator==(const wxArrayString& a) const
+{
+ if ( m_nCount != a.m_nCount )
+ return false;
+
+ for ( size_t n = 0; n < m_nCount; n++ )
+ {
+ if ( Item(n) != a[n] )
+ return false;
+ }
+
+ return true;
+}
+
+#endif // !wxUSE_STL
+
+int wxCMPFUNC_CONV wxStringSortAscending(wxString* s1, wxString* s2)
+{
+ return s1->Cmp(*s2);
+}
-void wxArrayString::Sort(bool bCase, bool bReverse)
+int wxCMPFUNC_CONV wxStringSortDescending(wxString* s1, wxString* s2)
{
- //@@@@ TO DO
- //qsort(m_pItems, m_nCount, sizeof(char *), fCmp);
+ return -s1->Cmp(*s2);
}