]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/string.cpp
New Unix configure system
[wxWidgets.git] / src / common / string.cpp
index 6247bb0fabcb3815045e5c545787011f779de2aa..407d143e3c3ec2f337722b8dcf9e04966cd0ba78 100644 (file)
@@ -6,7 +6,7 @@
 // Created:     29/01/98
 // RCS-ID:      $Id$
 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
 // Created:     29/01/98
 // RCS-ID:      $Id$
 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
-// Licence:    wxWindows license
+// Licence:     wxWindows license
 /////////////////////////////////////////////////////////////////////////////
 
 #ifdef __GNUG__
 /////////////////////////////////////////////////////////////////////////////
 
 #ifdef __GNUG__
   IMPLEMENT_DYNAMIC_CLASS(wxString, wxObject)
 #endif  //WXSTRING_IS_WXOBJECT
 
   IMPLEMENT_DYNAMIC_CLASS(wxString, wxObject)
 #endif  //WXSTRING_IS_WXOBJECT
 
+// allocating extra space for each string consumes more memory but speeds up
+// the concatenation operations (nLen is the current string's length)
+#define EXTRA_ALLOC       16
+
 // ---------------------------------------------------------------------------
 // static class variables definition
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 // static class variables definition
 // ---------------------------------------------------------------------------
   const size_t wxString::npos = STRING_MAXLEN;
 #endif
 
   const size_t wxString::npos = STRING_MAXLEN;
 #endif
 
-// ===========================================================================
-// static class data, special inlines
-// ===========================================================================
+// ----------------------------------------------------------------------------
+// static data
+// ----------------------------------------------------------------------------
 
 // 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
 
 // 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]);
 
 // empty C style string: points to 'string data' byte of g_strEmpty
 extern const char *g_szNul = (const char *)(&g_strEmpty[3]);
 
-// ===========================================================================
+// ----------------------------------------------------------------------------
 // global functions
 // global functions
-// ===========================================================================
+// ----------------------------------------------------------------------------
 
 #ifdef  STD_STRING_COMPATIBILITY
 
 
 #ifdef  STD_STRING_COMPATIBILITY
 
@@ -101,7 +103,7 @@ NAMESPACE istream& operator>>(NAMESPACE istream& is, wxString& WXUNUSED(str))
         sb->sungetc();
         break;
       }
         sb->sungetc();
         break;
       }
-      
+
       str += ch;
       if ( --w == 1 )
         break;
       str += ch;
       if ( --w == 1 )
         break;
@@ -117,6 +119,35 @@ NAMESPACE istream& operator>>(NAMESPACE istream& is, wxString& WXUNUSED(str))
 
 #endif  //std::string compatibility
 
 
 #endif  //std::string compatibility
 
+// ----------------------------------------------------------------------------
+// private classes
+// ----------------------------------------------------------------------------
+
+// this small class is used to gather statistics for performance tuning
+//#define WXSTRING_STATISTICS
+#ifdef  WXSTRING_STATISTICS
+  class Averager
+  {
+  public:
+    Averager(const char *sz) { m_sz = sz; m_nTotal = m_nCount = 0; }
+   ~Averager() 
+   { printf("wxString: average %s = %f\n", m_sz, ((float)m_nTotal)/m_nCount); }
+
+    void Add(uint n) { m_nTotal += n; m_nCount++; }
+
+  private:
+    uint m_nCount, m_nTotal;
+    const char *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
+
 // ===========================================================================
 // wxString class core
 // ===========================================================================
 // ===========================================================================
 // wxString class core
 // ===========================================================================
@@ -125,27 +156,6 @@ NAMESPACE istream& operator>>(NAMESPACE istream& is, wxString& WXUNUSED(str))
 // construction
 // ---------------------------------------------------------------------------
 
 // construction
 // ---------------------------------------------------------------------------
 
-// construct an empty string
-wxString::wxString()
-{
-  Init();
-}
-
-// copy constructor
-wxString::wxString(const wxString& stringSrc)
-{
-  wxASSERT( stringSrc.GetStringData()->IsValid() );
-
-  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
-  }
-}
-
 // constructs string of <nLength> copies of character <ch>
 wxString::wxString(char ch, size_t nLength)
 {
 // constructs string of <nLength> copies of character <ch>
 wxString::wxString(char ch, size_t nLength)
 {
@@ -153,7 +163,7 @@ wxString::wxString(char ch, size_t nLength)
 
   if ( nLength > 0 ) {
     AllocBuffer(nLength);
 
   if ( nLength > 0 ) {
     AllocBuffer(nLength);
-    
+
     wxASSERT( sizeof(char) == 1 );  // can't use memset if not
 
     memset(m_pchData, ch, nLength);
     wxASSERT( sizeof(char) == 1 );  // can't use memset if not
 
     memset(m_pchData, ch, nLength);
@@ -170,38 +180,27 @@ void wxString::InitWith(const char *psz, size_t nPos, size_t nLength)
   if ( nLength == STRING_MAXLEN )
     nLength = Strlen(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 ( nLength > 0 ) {
     // trailing '\0' is written in AllocBuffer()
     AllocBuffer(nLength);
     memcpy(m_pchData, psz + nPos, nLength*sizeof(char));
   }
 }
-        
-// 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);
 
 // 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);
 }
 
 }
 
+#ifdef  STD_STRING_COMPATIBILITY
+
 // poor man's iterators are "void *" pointers
 wxString::wxString(const void *pStart, const void *pEnd)
 {
 // poor man's iterators are "void *" pointers
 wxString::wxString(const void *pStart, const void *pEnd)
 {
-  InitWith((const char *)pStart, 0, 
+  InitWith((const char *)pStart, 0,
            (const char *)pEnd - (const char *)pStart);
 }
 
            (const char *)pEnd - (const char *)pStart);
 }
 
@@ -233,33 +232,18 @@ void wxString::AllocBuffer(size_t nLen)
   wxASSERT( nLen >  0         );    //
   wxASSERT( nLen <= INT_MAX-1 );    // max size (enough room for 1 extra)
 
   wxASSERT( nLen >  0         );    //
   wxASSERT( nLen <= INT_MAX-1 );    // max size (enough room for 1 extra)
 
+  STATISTICS_ADD(Length, nLen);
+
   // allocate memory:
   // 1) one extra character for '\0' termination
   // 2) sizeof(wxStringData) for housekeeping info
   // 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(char));
   pData->nRefs        = 1;
   pData->nRefs        = 1;
-  pData->data()[nLen] = '\0';
   pData->nDataLength  = nLen;
   pData->nDataLength  = nLen;
-  pData->nAllocLength = nLen;
+  pData->nAllocLength = nLen + EXTRA_ALLOC;
   m_pchData           = pData->data();  // data starts after wxStringData
   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]     = '\0';
 }
 
 // must be called before changing this string
 }
 
 // must be called before changing this string
@@ -269,11 +253,12 @@ void wxString::CopyBeforeWrite()
 
   if ( pData->IsShared() ) {
     pData->Unlock();                // memory not freed because shared
 
   if ( pData->IsShared() ) {
     pData->Unlock();                // memory not freed because shared
-    AllocBuffer(pData->nDataLength);
-    memcpy(m_pchData, pData->data(), (pData->nDataLength + 1)*sizeof(char));
+    uint nLen = pData->nDataLength;
+    AllocBuffer(nLen);
+    memcpy(m_pchData, pData->data(), nLen*sizeof(char));
   }
 
   }
 
-  wxASSERT( !pData->IsShared() );  // we must be the only owner
+  wxASSERT( !GetStringData()->IsShared() );  // we must be the only owner
 }
 
 // must be called before replacing contents of this string
 }
 
 // must be called before replacing contents of this string
@@ -282,27 +267,90 @@ void wxString::AllocBeforeWrite(size_t nLen)
   wxASSERT( nLen != 0 );  // doesn't make any sense
 
   // must not share string and must have enough space
   wxASSERT( nLen != 0 );  // doesn't make any sense
 
   // must not share string and must have enough space
-  register wxStringData* pData = GetStringData();  
+  wxStringData* pData = GetStringData();
   if ( pData->IsShared() || (nLen > pData->nAllocLength) ) {
     // can't work with old buffer, get new one
     pData->Unlock();
     AllocBuffer(nLen);
   }
 
   if ( pData->IsShared() || (nLen > pData->nAllocLength) ) {
     // can't work with old buffer, get new one
     pData->Unlock();
     AllocBuffer(nLen);
   }
 
-  wxASSERT( !pData->IsShared() );  // we must be the only owner
+  wxASSERT( !GetStringData()->IsShared() );  // we must be the only owner
+}
+
+// allocate enough memory for nLen characters
+void wxString::Alloc(uint nLen)
+{
+  wxStringData *pData = GetStringData();
+  if ( pData->nAllocLength <= nLen ) {
+    if ( pData->IsEmpty() ) {
+      nLen += EXTRA_ALLOC;
+
+      wxStringData* pData = (wxStringData*)
+        malloc(sizeof(wxStringData) + (nLen + 1)*sizeof(char));
+      pData->nRefs = 1;
+      pData->nDataLength = 0;
+      pData->nAllocLength = nLen;
+      m_pchData = pData->data();  // data starts after wxStringData
+      m_pchData[0u] = '\0';
+    }
+    else if ( pData->IsShared() ) {
+      pData->Unlock();                // memory not freed because shared
+      uint nOldLen = pData->nDataLength;
+      AllocBuffer(nLen);
+      memcpy(m_pchData, pData->data(), nOldLen*sizeof(char));
+    }
+    else {
+      nLen += EXTRA_ALLOC;
+
+      wxStringData *p = (wxStringData *)
+        realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(char));
+
+      if ( p == NULL ) {
+        // @@@ what to do on memory error?
+        return;
+      }
+
+      // 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)
+      p->nAllocLength = nLen;
+      m_pchData = p->data();
+    }
+  }
+  //else: we've already got enough
+}
+
+// shrink to minimal size (releasing extra memory)
+void wxString::Shrink()
+{
+  wxStringData *pData = GetStringData();
+
+  // this variable is unused in release build, so avoid the compiler warning by
+  // just not declaring it
+#ifdef __WXDEBUG__
+  void *p =
+#endif
+  realloc(pData, sizeof(wxStringData) + (pData->nDataLength + 1)*sizeof(char));
+
+  wxASSERT( p != NULL );  // can't free memory?
+  wxASSERT( p == pData ); // we're decrementing the size - block shouldn't move!
 }
 
 // get the pointer to writable buffer of (at least) nLen bytes
 }
 
 // get the pointer to writable buffer of (at least) nLen bytes
-char *wxString::GetWriteBuf(size_t nLen)
+char *wxString::GetWriteBuf(uint nLen)
 {
   AllocBeforeWrite(nLen);
 {
   AllocBeforeWrite(nLen);
+
+  wxASSERT( GetStringData()->nRefs == 1 );
+  GetStringData()->Validate(FALSE);
+
   return m_pchData;
 }
 
   return m_pchData;
 }
 
-// dtor frees memory if no other strings use it
-wxString::~wxString()
+// put string back in a reasonable state after GetWriteBuf
+void wxString::UngetWriteBuf()
 {
 {
-  GetStringData()->Unlock();
+  GetStringData()->nDataLength = strlen(m_pchData);
+  GetStringData()->Validate(TRUE);
 }
 
 // ---------------------------------------------------------------------------
 }
 
 // ---------------------------------------------------------------------------
@@ -315,7 +363,7 @@ wxString::~wxString()
 // assignment operators
 // ---------------------------------------------------------------------------
 
 // assignment operators
 // ---------------------------------------------------------------------------
 
-// helper function: does real copy 
+// helper function: does real copy
 void wxString::AssignCopy(size_t nSrcLen, const char *pszSrcData)
 {
   if ( nSrcLen == 0 ) {
 void wxString::AssignCopy(size_t nSrcLen, const char *pszSrcData)
 {
   if ( nSrcLen == 0 ) {
@@ -332,6 +380,8 @@ void wxString::AssignCopy(size_t nSrcLen, const char *pszSrcData)
 // assigns one string to another
 wxString& wxString::operator=(const wxString& stringSrc)
 {
 // assigns one string to another
 wxString& wxString::operator=(const wxString& stringSrc)
 {
+  wxASSERT( stringSrc.GetStringData()->IsValid() );
+
   // don't copy string over itself
   if ( m_pchData != stringSrc.m_pchData ) {
     if ( stringSrc.GetStringData()->IsEmpty() ) {
   // don't copy string over itself
   if ( m_pchData != stringSrc.m_pchData ) {
     if ( stringSrc.GetStringData()->IsEmpty() ) {
@@ -380,91 +430,51 @@ wxString& wxString::operator=(const wchar_t *pwz)
 // string concatenation
 // ---------------------------------------------------------------------------
 
 // string concatenation
 // ---------------------------------------------------------------------------
 
-// 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)
-{
-  int nNewLen = nSrc1Len + nSrc2Len;
-  if ( nNewLen != 0 )
-  {
-    AllocBuffer(nNewLen);
-    memcpy(m_pchData, pszSrc1Data, nSrc1Len*sizeof(char));
-    memcpy(m_pchData + nSrc1Len, pszSrc2Data, nSrc2Len*sizeof(char));
-  }
-}
-
 // add something to this string
 void wxString::ConcatSelf(int nSrcLen, const char *pszSrcData)
 {
 // add something to this string
 void wxString::ConcatSelf(int nSrcLen, const char *pszSrcData)
 {
-  // concatenating an empty string is a NOP
-  if ( nSrcLen != 0 ) {
-    register wxStringData *pData = GetStringData();
-
-    // 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();
-    }
-    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 );
-
-      m_pchData[pData->nDataLength] = '\0';   // put terminating '\0'
-    }
-  }
-}
+  STATISTICS_ADD(SummandLength, nSrcLen);
 
 
-/*
- * string may be concatenated with other string, C string or a character
- */
+  // concatenating an empty string is a NOP, but it happens quite rarely,
+  // so we don't waste our time checking for it
+  // if ( nSrcLen > 0 )
+  wxStringData *pData = GetStringData();
+  uint nLen = pData->nDataLength;
+  uint nNewLen = nLen + nSrcLen;
 
 
-void wxString::operator+=(const wxString& string)
-{
-  ConcatSelf(string.Len(), string);
-}
+  // alloc new buffer if current is too small
+  if ( pData->IsShared() ) {
+    STATISTICS_ADD(ConcatHit, 0);
 
 
-void wxString::operator+=(const char *psz)
-{
-  ConcatSelf(Strlen(psz), psz);
-}
+    // we have to allocate another buffer
+    wxStringData* pOldData = GetStringData();
+    AllocBuffer(nNewLen);
+    memcpy(m_pchData, pOldData->data(), nLen*sizeof(char));
+    pOldData->Unlock();
+  }
+  else if ( nNewLen > pData->nAllocLength ) {
+    STATISTICS_ADD(ConcatHit, 0);
 
 
-void wxString::operator+=(char ch)
-{
-  ConcatSelf(1, &ch);
-}
+    // we have to grow the buffer
+    Alloc(nNewLen);
+  }
+  else {
+    STATISTICS_ADD(ConcatHit, 1);
 
 
-/*
- * Same as above but return the result
- */  
+    // the buffer is already big enough
+  }
 
 
-wxString& wxString::operator<<(const wxString& string)
-{
-  ConcatSelf(string.Len(), string);
-  return *this;
-}
+  // should be enough space
+  wxASSERT( nNewLen <= GetStringData()->nAllocLength );
 
 
-wxString& wxString::operator<<(const char *psz)
-{
-  ConcatSelf(Strlen(psz), psz);
-  return *this;
-}
+  // fast concatenation - all is done in our buffer
+  memcpy(m_pchData + nLen, pszSrcData, nSrcLen*sizeof(char));
 
 
-wxString& wxString::operator<<(char ch)
-{
-  ConcatSelf(1, &ch);
-  return *this;
+  m_pchData[nNewLen] = '\0';              // put terminating '\0'
+  GetStringData()->nDataLength = nNewLen; // and fix the length
 }
 
 }
 
-/* 
+/*
  * concatenation functions come in 5 flavours:
  *  string + string
  *  char   + string      and      string + char
  * concatenation functions come in 5 flavours:
  *  string + string
  *  char   + string      and      string + char
@@ -473,39 +483,56 @@ wxString& wxString::operator<<(char ch)
 
 wxString operator+(const wxString& string1, const wxString& string2)
 {
 
 wxString operator+(const wxString& string1, const wxString& string2)
 {
-  wxString s;
-  s.ConcatCopy(string1.GetStringData()->nDataLength, string1.m_pchData,
-               string2.GetStringData()->nDataLength, string2.m_pchData);
+  wxASSERT( string1.GetStringData()->IsValid() );
+  wxASSERT( string2.GetStringData()->IsValid() );
+
+  wxString s = string1;
+  s += string2;
+
   return s;
 }
 
   return s;
 }
 
-wxString operator+(const wxString& string1, char ch)
+wxString operator+(const wxString& string, char ch)
 {
 {
-  wxString s;
-  s.ConcatCopy(string1.GetStringData()->nDataLength, string1.m_pchData, 1, &ch);
+  wxASSERT( string.GetStringData()->IsValid() );
+
+  wxString s = string;
+  s += ch;
+
   return s;
 }
 
 wxString operator+(char ch, const wxString& string)
 {
   return s;
 }
 
 wxString operator+(char ch, const wxString& string)
 {
-  wxString s;
-  s.ConcatCopy(1, &ch, string.GetStringData()->nDataLength, string.m_pchData);
+  wxASSERT( string.GetStringData()->IsValid() );
+
+  wxString s = ch;
+  s += string;
+
   return s;
 }
 
 wxString operator+(const wxString& string, const char *psz)
 {
   return s;
 }
 
 wxString operator+(const wxString& string, const char *psz)
 {
+  wxASSERT( string.GetStringData()->IsValid() );
+
   wxString s;
   wxString s;
-  s.ConcatCopy(string.GetStringData()->nDataLength, string.m_pchData,
-               Strlen(psz), psz);
+  s.Alloc(Strlen(psz) + string.Len());
+  s = string;
+  s += psz;
+
   return s;
 }
 
 wxString operator+(const char *psz, const wxString& string)
 {
   return s;
 }
 
 wxString operator+(const char *psz, const wxString& string)
 {
+  wxASSERT( string.GetStringData()->IsValid() );
+
   wxString s;
   wxString s;
-  s.ConcatCopy(Strlen(psz), psz,
-               string.GetStringData()->nDataLength, string.m_pchData);
+  s.Alloc(Strlen(psz) + string.Len());
+  s = psz;
+  s += string;
+
   return s;
 }
 
   return s;
 }
 
@@ -520,12 +547,10 @@ wxString operator+(const char *psz, const wxString& string)
 // helper function: clone the data attached to this string
 void wxString::AllocCopy(wxString& dest, int nCopyLen, int nCopyIndex) const
 {
 // helper function: clone the data attached to this string
 void wxString::AllocCopy(wxString& dest, int nCopyLen, int nCopyIndex) const
 {
-  if ( nCopyLen == 0 )
-  {
+  if ( nCopyLen == 0 ) {
     dest.Init();
   }
     dest.Init();
   }
-  else
-  {
+  else {
     dest.AllocBuffer(nCopyLen);
     memcpy(dest.m_pchData, m_pchData + nCopyIndex, nCopyLen*sizeof(char));
   }
     dest.AllocBuffer(nCopyLen);
     memcpy(dest.m_pchData, m_pchData + nCopyIndex, nCopyLen*sizeof(char));
   }
@@ -629,7 +654,7 @@ uint wxString::Replace(const char *szOld, const char *szNew, bool bReplaceAll)
 
   wxString strTemp;
   const char *pCurrent = m_pchData;
 
   wxString strTemp;
   const char *pCurrent = m_pchData;
-  const char *pSubstr;           
+  const char *pSubstr;
   while ( *pCurrent != '\0' ) {
     pSubstr = strstr(pCurrent, szOld);
     if ( pSubstr == NULL ) {
   while ( *pCurrent != '\0' ) {
     pSubstr = strstr(pCurrent, szOld);
     if ( pSubstr == NULL ) {
@@ -671,7 +696,7 @@ bool wxString::IsAscii() const
   }
   return(TRUE);
 }
   }
   return(TRUE);
 }
-  
+
 bool wxString::IsWord() const
 {
   const char *s = (const char*) *this;
 bool wxString::IsWord() const
 {
   const char *s = (const char*) *this;
@@ -681,7 +706,7 @@ bool wxString::IsWord() const
   }
   return(TRUE);
 }
   }
   return(TRUE);
 }
-  
+
 bool wxString::IsNumber() const
 {
   const char *s = (const char*) *this;
 bool wxString::IsNumber() const
 {
   const char *s = (const char*) *this;
@@ -692,10 +717,6 @@ bool wxString::IsNumber() const
   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;
 wxString wxString::Strip(stripType w) const
 {
     wxString s = *this;
@@ -704,33 +725,6 @@ wxString wxString::Strip(stripType w) const
     return s;
 }
 
     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
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 // case conversion
 // ---------------------------------------------------------------------------
@@ -748,7 +742,7 @@ wxString& wxString::MakeUpper()
 wxString& wxString::MakeLower()
 {
   CopyBeforeWrite();
 wxString& wxString::MakeLower()
 {
   CopyBeforeWrite();
-  
+
   for ( char *p = m_pchData; *p; p++ )
     *p = (char)tolower(*p);
 
   for ( char *p = m_pchData; *p; p++ )
     *p = (char)tolower(*p);
 
@@ -862,26 +856,66 @@ int wxString::PrintfV(const char* pszFormat, va_list argptr)
   return iLen;
 }
 
   return iLen;
 }
 
-int wxString::Scanf(const char *pszFormat, ...) const
+// ----------------------------------------------------------------------------
+// misc other operations
+// ----------------------------------------------------------------------------
+bool wxString::Matches(const char *pszMask) const
 {
 {
-  va_list argptr;
-  va_start(argptr, pszFormat);
+  // check char by char
+  const char *pszTxt;
+  for ( pszTxt = c_str(); *pszMask != '\0'; pszMask++, pszTxt++ ) {
+    switch ( *pszMask ) {
+      case '?':
+        if ( *pszTxt == '\0' )
+          return FALSE;
 
 
-  int iLen = ScanfV(pszFormat, argptr);
+        pszTxt++;
+        pszMask++;
+        break;
 
 
-  va_end(argptr);
+      case '*':
+        {
+          // ignore special chars immediately following this one
+          while ( *pszMask == '*' || *pszMask == '?' )
+            pszMask++;
+
+          // if there is nothing more, match
+          if ( *pszMask == '\0' )
+            return TRUE;
+
+          // are there any other metacharacters in the mask?
+          uint uiLenMask;
+          const char *pEndMask = strpbrk(pszMask, "*?");
+
+          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 = strlen(pszMask);
+          }
+
+          wxString strToMatch(pszMask, uiLenMask);
+          const char* pMatch = strstr(pszTxt, strToMatch);
+          if ( pMatch == NULL )
+            return FALSE;
+
+          // -1 to compensate "++" in the loop
+          pszTxt = pMatch + uiLenMask - 1;
+          pszMask += uiLenMask - 1;
+        }
+        break;
 
 
-  return iLen;
-}
+      default:
+        if ( *pszMask != *pszTxt )
+          return FALSE;
+        break;
+    }
+  }
 
 
-int wxString::ScanfV(const char *pszFormat, va_list argptr) const
-{
-#ifdef __WINDOWS__
-  wxMessageBox("ScanfV not implemented");
-  return 0;
-#else
-  return vsscanf(c_str(), pszFormat, argptr);
-#endif
+  // match only if nothing left
+  return *pszTxt == '\0';
 }
 
 // ---------------------------------------------------------------------------
 }
 
 // ---------------------------------------------------------------------------
@@ -891,24 +925,27 @@ int wxString::ScanfV(const char *pszFormat, va_list argptr) const
 
 wxString& wxString::insert(size_t nPos, const wxString& str)
 {
 
 wxString& wxString::insert(size_t nPos, const wxString& str)
 {
+  wxASSERT( str.GetStringData()->IsValid() );
   wxASSERT( nPos <= Len() );
 
   wxString strTmp;
   wxASSERT( nPos <= Len() );
 
   wxString strTmp;
-  char *pc = strTmp.GetWriteBuf(Len() + str.Len() + 1);
+  char *pc = strTmp.GetWriteBuf(Len() + str.Len());
   strncpy(pc, c_str(), nPos);
   strcpy(pc + nPos, str);
   strcpy(pc + nPos + str.Len(), c_str() + nPos);
   strncpy(pc, c_str(), nPos);
   strcpy(pc + nPos, str);
   strcpy(pc + nPos + str.Len(), c_str() + nPos);
+  strTmp.UngetWriteBuf();
   *this = strTmp;
   *this = strTmp;
-    
-  return *this; 
+
+  return *this;
 }
 
 size_t wxString::find(const wxString& str, size_t nStart) const
 {
 }
 
 size_t wxString::find(const wxString& str, size_t nStart) const
 {
+  wxASSERT( str.GetStringData()->IsValid() );
   wxASSERT( nStart <= Len() );
 
   const char *p = strstr(c_str() + nStart, str);
   wxASSERT( nStart <= Len() );
 
   const char *p = strstr(c_str() + nStart, str);
-  
+
   return p == NULL ? npos : p - c_str();
 }
 
   return p == NULL ? npos : p - c_str();
 }
 
@@ -919,18 +956,19 @@ size_t wxString::find(const char* sz, size_t nStart, size_t n) const
   return find(wxString(sz, n == npos ? 0 : n), nStart);
 }
 #endif
   return find(wxString(sz, n == npos ? 0 : n), nStart);
 }
 #endif
-        
+
 size_t wxString::find(char ch, size_t nStart) const
 {
   wxASSERT( nStart <= Len() );
 
   const char *p = strchr(c_str() + nStart, ch);
 size_t wxString::find(char ch, size_t nStart) const
 {
   wxASSERT( nStart <= Len() );
 
   const char *p = strchr(c_str() + nStart, ch);
-  
+
   return p == NULL ? npos : p - c_str();
 }
 
 size_t wxString::rfind(const wxString& str, size_t nStart) const
 {
   return p == NULL ? npos : p - c_str();
 }
 
 size_t wxString::rfind(const wxString& str, size_t nStart) const
 {
+  wxASSERT( str.GetStringData()->IsValid() );
   wxASSERT( nStart <= Len() );
 
   // # could be quicker than that
   wxASSERT( nStart <= Len() );
 
   // # could be quicker than that
@@ -940,10 +978,10 @@ size_t wxString::rfind(const wxString& str, size_t nStart) const
       return p - str.Len() - c_str();
     p--;
   }
       return p - str.Len() - c_str();
     p--;
   }
-  
+
   return npos;
 }
   return npos;
 }
-        
+
 // 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
 // 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
@@ -956,7 +994,7 @@ size_t wxString::rfind(char ch, size_t nStart) const
   wxASSERT( nStart <= Len() );
 
   const char *p = strrchr(c_str() + nStart, ch);
   wxASSERT( nStart <= Len() );
 
   const char *p = strrchr(c_str() + nStart, ch);
-  
+
   return p == NULL ? npos : p - c_str();
 }
 #endif
   return p == NULL ? npos : p - c_str();
 }
 #endif
@@ -994,7 +1032,7 @@ wxString& wxString::replace(size_t nStart, size_t nLen, const char *sz)
     strTmp.append(c_str(), nStart);
   strTmp += sz;
   strTmp.append(c_str() + nStart + nLen);
     strTmp.append(c_str(), nStart);
   strTmp += sz;
   strTmp.append(c_str() + nStart + nLen);
-  
+
   *this = strTmp;
   return *this;
 }
   *this = strTmp;
   return *this;
 }
@@ -1004,13 +1042,13 @@ wxString& wxString::replace(size_t nStart, size_t nLen, size_t nCount, char ch)
   return replace(nStart, nLen, wxString(ch, nCount));
 }
 
   return replace(nStart, nLen, wxString(ch, nCount));
 }
 
-wxString& wxString::replace(size_t nStart, size_t nLen, 
-                        const wxString& str, size_t nStart2, size_t nLen2)
+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));
 }
 
 {
   return replace(nStart, nLen, str.substr(nStart2, nLen2));
 }
 
-wxString& wxString::replace(size_t nStart, size_t nLen, 
+wxString& wxString::replace(size_t nStart, size_t nLen,
                         const char* sz, size_t nCount)
 {
   return replace(nStart, nLen, wxString(sz, nCount));
                         const char* sz, size_t nCount)
 {
   return replace(nStart, nLen, wxString(sz, nCount));
@@ -1041,30 +1079,26 @@ wxArrayString::wxArrayString()
 // copy ctor
 wxArrayString::wxArrayString(const wxArrayString& src)
 {
 // 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;
+  m_nSize  =
+  m_nCount = 0;
+  m_pItems = NULL;
 
 
-  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)
 {
 wxArrayString& wxArrayString::operator=(const wxArrayString& src)
 {
-  DELETEA(m_pItems);
+  Clear();
 
 
-  m_nSize  = src.m_nSize;
-  m_nCount = src.m_nCount;
+  m_nSize = 0;
+  if ( src.m_nCount > ARRAY_DEFAULT_INITIAL_SIZE )
+    Alloc(src.m_nCount);
 
 
-  if ( m_nSize != 0 )
-    m_pItems = new char *[m_nSize];
-  else
-    m_pItems = NULL;
+  // we can't just copy the pointers here because otherwise we would share
+  // the strings with another array
+  for ( uint n = 0; n < src.m_nCount; n++ )
+    Add(src[n]);
 
   if ( m_nCount != 0 )
     memcpy(m_pItems, src.m_pItems, m_nCount*sizeof(char *));
 
   if ( m_nCount != 0 )
     memcpy(m_pItems, src.m_pItems, m_nCount*sizeof(char *));
@@ -1083,8 +1117,13 @@ void wxArrayString::Grow()
       m_pItems = new char *[m_nSize];
     }
     else {
       m_pItems = new char *[m_nSize];
     }
     else {
+      // otherwise when it's called for the first time, nIncrement would be 0
+      // and the array would never be expanded
+      wxASSERT( ARRAY_DEFAULT_INITIAL_SIZE != 0 );
+
       // add 50% but not too much
       // add 50% but not too much
-      size_t nIncrement = m_nSize >> 1;
+      size_t nIncrement = m_nSize < ARRAY_DEFAULT_INITIAL_SIZE
+                          ? ARRAY_DEFAULT_INITIAL_SIZE : m_nSize >> 1;
       if ( nIncrement > ARRAY_MAXSIZE_INCREMENT )
         nIncrement = ARRAY_MAXSIZE_INCREMENT;
       m_nSize += nIncrement;
       if ( nIncrement > ARRAY_MAXSIZE_INCREMENT )
         nIncrement = ARRAY_MAXSIZE_INCREMENT;
       m_nSize += nIncrement;
@@ -1121,7 +1160,7 @@ void wxArrayString::Clear()
 {
   Free();
 
 {
   Free();
 
-  m_nSize  = 
+  m_nSize  =
   m_nCount = 0;
 
   DELETEA(m_pItems);
   m_nCount = 0;
 
   DELETEA(m_pItems);
@@ -1153,9 +1192,6 @@ void wxArrayString::Alloc(size_t nSize)
 }
 
 // searches the array for an item (forward or backwards)
 }
 
 // searches the array for an item (forward or backwards)
-
-// Robert Roebling (changed to bool from bool)
-
 int wxArrayString::Index(const char *sz, bool bCase, bool bFromEnd) const
 {
   if ( bFromEnd ) {
 int wxArrayString::Index(const char *sz, bool bCase, bool bFromEnd) const
 {
   if ( bFromEnd ) {
@@ -1179,27 +1215,31 @@ int wxArrayString::Index(const char *sz, bool bCase, bool bFromEnd) const
 }
 
 // add item at the end
 }
 
 // add item at the end
-void wxArrayString::Add(const wxString& src)
+void wxArrayString::Add(const wxString& str)
 {
 {
+  wxASSERT( str.GetStringData()->IsValid() );
+
   Grow();
 
   // the string data must not be deleted!
   Grow();
 
   // the string data must not be deleted!
-  src.GetStringData()->Lock();
-  m_pItems[m_nCount++] = (char *)src.c_str();
+  str.GetStringData()->Lock();
+  m_pItems[m_nCount++] = (char *)str.c_str();
 }
 
 // add item at the given position
 }
 
 // add item at the given position
-void wxArrayString::Insert(const wxString& src, size_t nIndex)
+void wxArrayString::Insert(const wxString& str, size_t nIndex)
 {
 {
+  wxASSERT( str.GetStringData()->IsValid() );
+
   wxCHECK_RET( nIndex <= m_nCount, "bad index in wxArrayString::Insert" );
 
   Grow();
 
   wxCHECK_RET( nIndex <= m_nCount, "bad index in wxArrayString::Insert" );
 
   Grow();
 
-  memmove(&m_pItems[nIndex + 1], &m_pItems[nIndex], 
+  memmove(&m_pItems[nIndex + 1], &m_pItems[nIndex],
           (m_nCount - nIndex)*sizeof(char *));
 
           (m_nCount - nIndex)*sizeof(char *));
 
-  src.GetStringData()->Lock();
-  m_pItems[nIndex] = (char *)src.c_str();
+  str.GetStringData()->Lock();
+  m_pItems[nIndex] = (char *)str.c_str();
 
   m_nCount++;
 }
 
   m_nCount++;
 }
@@ -1212,7 +1252,7 @@ void wxArrayString::Remove(size_t nIndex)
   // release our lock
   Item(nIndex).GetStringData()->Unlock();
 
   // release our lock
   Item(nIndex).GetStringData()->Unlock();
 
-  memmove(&m_pItems[nIndex], &m_pItems[nIndex + 1], 
+  memmove(&m_pItems[nIndex], &m_pItems[nIndex + 1],
           (m_nCount - nIndex - 1)*sizeof(char *));
   m_nCount--;
 }
           (m_nCount - nIndex - 1)*sizeof(char *));
   m_nCount--;
 }
@@ -1230,7 +1270,7 @@ void wxArrayString::Remove(const char *sz)
 
 // sort array elements using passed comparaison function
 
 
 // sort array elements using passed comparaison function
 
-void wxArrayString::Sort(bool bCase, bool bReverse)
+void wxArrayString::Sort(bool WXUNUSED(bCase), bool WXUNUSED(bReverse) )
 {
   //@@@@ TO DO
   //qsort(m_pItems, m_nCount, sizeof(char *), fCmp);
 {
   //@@@@ TO DO
   //qsort(m_pItems, m_nCount, sizeof(char *), fCmp);