]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/string.cpp
fix misc typos
[wxWidgets.git] / src / common / string.cpp
index 5db2ba07a60fb909748ff456fdf5e6b1f6fd3ae6..f3d684f1a468832e6f2d3cf91513e45a828e46c1 100644 (file)
 #include <string.h>
 #include <stdlib.h>
 
-#ifdef __SALFORDC__
-    #include <clib.h>
-#endif
-
 #include "wx/hashmap.h"
+#include "wx/vector.h"
+#include "wx/xlocale.h"
 
 // string handling functions used by wxString:
 #if wxUSE_UNICODE_UTF8
     #define wxStringStrlen   wxStrlen
 #endif
 
+// ----------------------------------------------------------------------------
+// global variables
+// ----------------------------------------------------------------------------
+
+namespace wxPrivate
+{
+
+static UntypedBufferData s_untypedNullData(NULL);
+
+UntypedBufferData * const untypedNullDataPtr = &s_untypedNullData;
+
+} // namespace wxPrivate
 
 // ---------------------------------------------------------------------------
 // static class variables definition
 //According to STL _must_ be a -1 size_t
 const size_t wxString::npos = (size_t) -1;
 
+#if wxUSE_STRING_POS_CACHE
+
+#ifdef wxHAS_COMPILER_TLS
+
+wxTLS_TYPE(wxString::Cache) wxString::ms_cache;
+
+#else // !wxHAS_COMPILER_TLS
+
+struct wxStrCacheInitializer
+{
+    wxStrCacheInitializer()
+    {
+        // calling this function triggers s_cache initialization in it, and
+        // from now on it becomes safe to call from multiple threads
+        wxString::GetCache();
+    }
+};
+
+/*
+wxString::Cache& wxString::GetCache()
+{
+    static wxTLS_TYPE(Cache) s_cache;
+
+    return wxTLS_VALUE(s_cache);
+}
+*/
+
+static wxStrCacheInitializer gs_stringCacheInit;
+
+#endif // wxHAS_COMPILER_TLS/!wxHAS_COMPILER_TLS
+
+// gdb seems to be unable to display thread-local variables correctly, at least
+// not my 6.4.98 version under amd64, so provide this debugging helper to do it
+#if wxDEBUG_LEVEL >= 2
+
+struct wxStrCacheDumper
+{
+    static void ShowAll()
+    {
+        puts("*** wxString cache dump:");
+        for ( unsigned n = 0; n < wxString::Cache::SIZE; n++ )
+        {
+            const wxString::Cache::Element&
+                c = wxString::GetCacheBegin()[n];
+
+            printf("\t%u%s\t%p: pos=(%lu, %lu), len=%ld\n",
+                   n,
+                   n == wxString::LastUsedCacheElement() ? " [*]" : "",
+                   c.str,
+                   (unsigned long)c.pos,
+                   (unsigned long)c.impl,
+                   (long)c.len);
+        }
+    }
+};
+
+void wxDumpStrCache() { wxStrCacheDumper::ShowAll(); }
+
+#endif // wxDEBUG_LEVEL >= 2
+
+#ifdef wxPROFILE_STRING_CACHE
+
+wxString::CacheStats wxString::ms_cacheStats;
+
+struct wxStrCacheStatsDumper
+{
+    ~wxStrCacheStatsDumper()
+    {
+        const wxString::CacheStats& stats = wxString::ms_cacheStats;
+
+        if ( stats.postot )
+        {
+            puts("*** wxString cache statistics:");
+            printf("\tTotal non-trivial calls to PosToImpl(): %u\n",
+                   stats.postot);
+            printf("\tHits %u (of which %u not used) or %.2f%%\n",
+                   stats.poshits,
+                   stats.mishits,
+                   100.*float(stats.poshits - stats.mishits)/stats.postot);
+            printf("\tAverage position requested: %.2f\n",
+                   float(stats.sumpos) / stats.postot);
+            printf("\tAverage offset after cached hint: %.2f\n",
+                   float(stats.sumofs) / stats.postot);
+        }
+
+        if ( stats.lentot )
+        {
+            printf("\tNumber of calls to length(): %u, hits=%.2f%%\n",
+                   stats.lentot, 100.*float(stats.lenhits)/stats.lentot);
+        }
+    }
+};
+
+static wxStrCacheStatsDumper s_showCacheStats;
+
+#endif // wxPROFILE_STRING_CACHE
+
+#endif // wxUSE_STRING_POS_CACHE
+
 // ----------------------------------------------------------------------------
 // global functions
 // ----------------------------------------------------------------------------
@@ -73,7 +182,13 @@ const size_t wxString::npos = (size_t) -1;
 wxSTD ostream& operator<<(wxSTD ostream& os, const wxCStrData& str)
 {
 #if wxUSE_UNICODE && !wxUSE_UNICODE_UTF8
-    return os << (const char *)str.AsCharBuf();
+    const wxCharBuffer buf(str.AsCharBuf());
+    if ( !buf )
+        os.clear(wxSTD ios_base::failbit);
+    else
+        os << buf.data();
+
+    return os;
 #else
     return os << str.AsInternal();
 #endif
@@ -127,22 +242,30 @@ void wxString::PosLenToImpl(size_t pos, size_t len,
                             size_t *implPos, size_t *implLen) const
 {
     if ( pos == npos )
+    {
         *implPos = npos;
-    else
+    }
+    else // have valid start position
     {
-        const_iterator i = begin() + pos;
-        *implPos = wxStringImpl::const_iterator(i.impl()) - m_impl.begin();
+        const const_iterator b = GetIterForNthChar(pos);
+        *implPos = wxStringImpl::const_iterator(b.impl()) - m_impl.begin();
         if ( len == npos )
+        {
             *implLen = npos;
-        else
+        }
+        else // have valid length too
         {
-            // too large length is interpreted as "to the end of the string"
-            // FIXME-UTF8: verify this is the case in std::string, assert
-            // otherwise
-            if ( pos + len > length() )
-                len = length() - pos;
-
-            *implLen = (i + len).impl() - i.impl();
+            // we need to handle the case of length specifying a substring
+            // going beyond the end of the string, just as std::string does
+            const const_iterator e(end());
+            const_iterator i(b);
+            while ( len && i <= e )
+            {
+                ++i;
+                --len;
+            }
+
+            *implLen = i.impl() - b.impl();
         }
     }
 }
@@ -373,6 +496,9 @@ wxString::SubstrBufFromMB wxString::ConvertStr(const char *psz, size_t nLength,
         // UTF-8 sequence and psz may be invalid:
         if ( wxStringOperations::IsValidUtf8String(psz, nLength) )
         {
+            // we must pass the real string length to SubstrBufFromMB ctor
+            if ( nLength == npos )
+                nLength = psz ? strlen(psz) : 0;
             return SubstrBufFromMB(wxCharBuffer::CreateNonOwned(psz), nLength);
         }
         // else: do the roundtrip through wchar_t*
@@ -991,6 +1117,7 @@ size_t wxString::find_last_not_of(const wxOtherCharType* sz, size_t nStart,
 
 int wxString::CmpNoCase(const wxString& s) const
 {
+#if wxUSE_UNICODE_UTF8
     // FIXME-UTF8: use wxUniChar::ToLower/ToUpper once added
 
     const_iterator i1 = begin();
@@ -1014,6 +1141,9 @@ int wxString::CmpNoCase(const wxString& s) const
     else if ( len1 > len2 )
         return 1;
     return 0;
+#else // wxUSE_UNICODE_WCHAR or ANSI
+    return wxStricmp(m_impl.c_str(), s.m_impl.c_str());
+#endif
 }
 
 
@@ -1170,7 +1300,7 @@ wxString wxString::Right(size_t nCount) const
   return dest;
 }
 
-// get all characters after the last occurence of ch
+// get all characters after the last occurrence of ch
 // (returns the whole string if ch not found)
 wxString wxString::AfterLast(wxUniChar ch) const
 {
@@ -1179,7 +1309,7 @@ wxString wxString::AfterLast(wxUniChar ch) const
   if ( iPos == wxNOT_FOUND )
     str = *this;
   else
-    str = wx_str() + iPos + 1;
+    str.assign(*this, iPos + 1, npos);
 
   return str;
 }
@@ -1197,16 +1327,17 @@ wxString wxString::Left(size_t nCount) const
   return dest;
 }
 
-// get all characters before the first occurence of ch
+// get all characters before the first occurrence of ch
 // (returns the whole string if ch not found)
 wxString wxString::BeforeFirst(wxUniChar ch) const
 {
   int iPos = Find(ch);
-  if ( iPos == wxNOT_FOUND ) iPos = length();
+  if ( iPos == wxNOT_FOUND )
+      iPos = length();
   return wxString(*this, 0, iPos);
 }
 
-/// get all characters before the last occurence of ch
+/// get all characters before the last occurrence of ch
 /// (returns empty string if ch not found)
 wxString wxString::BeforeLast(wxUniChar ch) const
 {
@@ -1218,19 +1349,19 @@ wxString wxString::BeforeLast(wxUniChar ch) const
   return str;
 }
 
-/// get all characters after the first occurence of ch
+/// get all characters after the first occurrence of ch
 /// (returns empty string if ch not found)
 wxString wxString::AfterFirst(wxUniChar ch) const
 {
   wxString str;
   int iPos = Find(ch);
   if ( iPos != wxNOT_FOUND )
-    str = wx_str() + iPos + 1;
+      str.assign(*this, iPos + 1, npos);
 
   return str;
 }
 
-// replace first (or all) occurences of some substring with another one
+// replace first (or all) occurrences of some substring with another one
 size_t wxString::Replace(const wxString& strOld,
                          const wxString& strNew, bool bReplaceAll)
 {
@@ -1238,36 +1369,92 @@ size_t wxString::Replace(const wxString& strOld,
     wxCHECK_MSG( !strOld.empty(), 0,
                  _T("wxString::Replace(): invalid parameter") );
 
+    wxSTRING_INVALIDATE_CACHE();
+
     size_t uiCount = 0;   // count of replacements made
 
-    size_t uiOldLen = strOld.length();
-    size_t uiNewLen = strNew.length();
+    // optimize the special common case: replacement of one character by
+    // another one (in UTF-8 case we can only do this for ASCII characters)
+    //
+    // benchmarks show that this special version is around 3 times faster
+    // (depending on the proportion of matching characters and UTF-8/wchar_t
+    // build)
+    if ( strOld.m_impl.length() == 1 && strNew.m_impl.length() == 1 )
+    {
+        const wxStringCharType chOld = strOld.m_impl[0],
+                               chNew = strNew.m_impl[0];
+
+        // this loop is the simplified version of the one below
+        for ( size_t pos = 0; ; )
+        {
+            pos = m_impl.find(chOld, pos);
+            if ( pos == npos )
+                break;
+
+            m_impl[pos++] = chNew;
 
-    size_t dwPos = 0;
+            uiCount++;
 
-    while ( (*this)[dwPos] != wxT('\0') )
+            if ( !bReplaceAll )
+                break;
+        }
+    }
+    else if ( !bReplaceAll)
     {
-        //DO NOT USE STRSTR HERE
-        //this string can contain embedded null characters,
-        //so strstr will function incorrectly
-        dwPos = find(strOld, dwPos);
-        if ( dwPos == npos )
-            break;                  // exit the loop
-        else
+        size_t pos = m_impl.find(strOld, 0);
+        if ( pos != npos )
         {
-            //replace this occurance of the old string with the new one
-            replace(dwPos, uiOldLen, strNew, uiNewLen);
+            m_impl.replace(pos, strOld.m_impl.length(), strNew.m_impl);
+            uiCount = 1;
+        }
+    }
+    else // replace all occurrences
+    {
+        const size_t uiOldLen = strOld.m_impl.length();
+        const size_t uiNewLen = strNew.m_impl.length();
 
-            //move up pos past the string that was replaced
-            dwPos += uiNewLen;
+        // first scan the string to find all positions at which the replacement
+        // should be made
+        wxVector<size_t> replacePositions;
 
-            //increase replace count
+        size_t pos;
+        for ( pos = m_impl.find(strOld.m_impl, 0);
+              pos != npos;
+              pos = m_impl.find(strOld.m_impl, pos + uiOldLen))
+        {
+            replacePositions.push_back(pos);
             ++uiCount;
+        }
 
-            // stop now?
-            if ( !bReplaceAll )
-                break;                  // exit the loop
+        if ( !uiCount )
+            return 0;
+
+        // allocate enough memory for the whole new string
+        wxString tmp;
+        tmp.m_impl.reserve(m_impl.length() + uiCount*(uiNewLen - uiOldLen));
+
+        // copy this string to tmp doing replacements on the fly
+        size_t replNum = 0;
+        for ( pos = 0; replNum < uiCount; replNum++ )
+        {
+            const size_t nextReplPos = replacePositions[replNum];
+
+            if ( pos != nextReplPos )
+            {
+                tmp.m_impl.append(m_impl, pos, nextReplPos - pos);
+            }
+
+            tmp.m_impl.append(strNew.m_impl);
+            pos = nextReplPos + uiOldLen;
+        }
+
+        if ( pos != m_impl.length() )
+        {
+            // append the rest of the string unchanged
+            tmp.m_impl.append(m_impl, pos, m_impl.length() - pos);
         }
+
+        swap(tmp);
     }
 
     return uiCount;
@@ -1342,14 +1529,28 @@ wxString& wxString::MakeLower()
   return *this;
 }
 
+wxString& wxString::MakeCapitalized()
+{
+    const iterator en = end();
+    iterator it = begin();
+    if ( it != en )
+    {
+        *it = (wxChar)wxToupper(*it);
+        for ( ++it; 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
+// isspace('\xEA') 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); }
@@ -1444,63 +1645,105 @@ int wxString::Find(wxUniChar ch, bool bFromEnd) const
     #define DO_IF_NOT_WINCE(x)
 #endif
 
-#define WX_STRING_TO_INT_TYPE(val, base, func)                              \
-    wxCHECK_MSG( val, false, _T("NULL output pointer") );                   \
-    wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") );  \
-                                                                            \
+#define WX_STRING_TO_X_TYPE_START                                           \
+    wxCHECK_MSG( pVal, false, _T("NULL output pointer") );                  \
     DO_IF_NOT_WINCE( errno = 0; )                                           \
-                                                                            \
     const wxStringCharType *start = wx_str();                               \
-    wxStringCharType *end;                                                  \
-    *val = func(start, &end, base);                                         \
-                                                                            \
+    wxStringCharType *end;
+
+#define WX_STRING_TO_X_TYPE_END                                             \
     /* 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)                                          \
-        DO_IF_NOT_WINCE( && (errno != ERANGE) )
+    if ( *end || end == start DO_IF_NOT_WINCE(|| errno == ERANGE) )         \
+        return false;                                                       \
+    *pVal = val;                                                            \
+    return true;
+
+bool wxString::ToLong(long *pVal, int base) const
+{
+    wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") );
+
+    WX_STRING_TO_X_TYPE_START
+    long val = wxStrtol(start, &end, base);
+    WX_STRING_TO_X_TYPE_END
+}
 
-bool wxString::ToLong(long *val, int base) const
+bool wxString::ToULong(unsigned long *pVal, int base) const
 {
-    WX_STRING_TO_INT_TYPE(val, base, wxStrtol);
+    wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") );
+
+    WX_STRING_TO_X_TYPE_START
+    unsigned long val = wxStrtoul(start, &end, base);
+    WX_STRING_TO_X_TYPE_END
 }
 
-bool wxString::ToULong(unsigned long *val, int base) const
+bool wxString::ToLongLong(wxLongLong_t *pVal, int base) const
 {
-    WX_STRING_TO_INT_TYPE(val, base, wxStrtoul);
+    wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") );
+
+    WX_STRING_TO_X_TYPE_START
+    wxLongLong_t val = wxStrtoll(start, &end, base);
+    WX_STRING_TO_X_TYPE_END
 }
 
-bool wxString::ToLongLong(wxLongLong_t *val, int base) const
+bool wxString::ToULongLong(wxULongLong_t *pVal, int base) const
 {
-    WX_STRING_TO_INT_TYPE(val, base, wxStrtoll);
+    wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") );
+
+    WX_STRING_TO_X_TYPE_START
+    wxULongLong_t val = wxStrtoull(start, &end, base);
+    WX_STRING_TO_X_TYPE_END
 }
 
-bool wxString::ToULongLong(wxULongLong_t *val, int base) const
+bool wxString::ToDouble(double *pVal) const
 {
-    WX_STRING_TO_INT_TYPE(val, base, wxStrtoull);
+    WX_STRING_TO_X_TYPE_START
+    double val = wxStrtod(start, &end);
+    WX_STRING_TO_X_TYPE_END
 }
 
-bool wxString::ToDouble(double *val) const
+#if wxUSE_XLOCALE
+
+bool wxString::ToCLong(long *pVal, int base) const
 {
-    wxCHECK_MSG( val, false, _T("NULL pointer in wxString::ToDouble") );
+    wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") );
 
-#ifndef __WXWINCE__
-    errno = 0;
+    WX_STRING_TO_X_TYPE_START
+#if wxUSE_UNICODE_UTF8 || !wxUSE_UNICODE
+    long val = wxStrtol_lA(start, &end, base, wxCLocale);
+#else
+    long val = wxStrtol_l(start, &end, base, wxCLocale);
 #endif
+    WX_STRING_TO_X_TYPE_END
+}
 
-    const wxChar *start = c_str();
-    wxChar *end;
-    *val = wxStrtod(start, &end);
+bool wxString::ToCULong(unsigned long *pVal, int base) const
+{
+    wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") );
 
-    // 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)
-#ifndef __WXWINCE__
-        && (errno != ERANGE)
+    WX_STRING_TO_X_TYPE_START
+#if wxUSE_UNICODE_UTF8 || !wxUSE_UNICODE
+    unsigned long val = wxStrtoul_lA(start, &end, base, wxCLocale);
+#else
+    unsigned long val = wxStrtoul_l(start, &end, base, wxCLocale);
+#endif
+    WX_STRING_TO_X_TYPE_END
+}
+
+bool wxString::ToCDouble(double *pVal) const
+{
+    WX_STRING_TO_X_TYPE_START
+#if wxUSE_UNICODE_UTF8 || !wxUSE_UNICODE
+    double val = wxStrtod_lA(start, &end, wxCLocale);
+#else
+    double val = wxStrtod_l(start, &end, wxCLocale);
 #endif
-    ;
+    WX_STRING_TO_X_TYPE_END
 }
 
+#endif  // wxUSE_XLOCALE
+
 // ---------------------------------------------------------------------------
 // formatted output
 // ---------------------------------------------------------------------------
@@ -1620,7 +1863,7 @@ int wxString::DoPrintfUtf8(const char *format, ...)
     an undersized buffer and no other errno are defined we treat those two
     as meaning hard errors and everything else gets the old behavior which
     is to keep looping and increasing buffer size until the function succeeds.
+
     In practice it's impossible to determine before compilation which behavior
     may be used.  The vswprintf function may have vsnprintf-like behavior or
     vice-versa.  Behavior detected on one release can theoretically change
@@ -1730,7 +1973,7 @@ static int DoStringPrintfV(wxString& str,
         else if ( len >= size )
         {
 #if wxUSE_WXVSNPRINTF
-            // we know that our own implementation of wxVsnprintf() returns 
+            // we know that our own implementation of wxVsnprintf() returns
             // size+1 when there's not enough space but that's not the size
             // of the required buffer!
             size *= 2;      // so we just double the current size of the buffer
@@ -1945,38 +2188,3 @@ int wxString::Freq(wxUniChar ch) const
     return count;
 }
 
-// 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(); }
-
-// ----------------------------------------------------------------------------
-// wxUTF8StringBuffer
-// ----------------------------------------------------------------------------
-
-#if wxUSE_UNICODE_WCHAR
-wxUTF8StringBuffer::~wxUTF8StringBuffer()
-{
-    wxMBConvStrictUTF8 conv;
-    size_t wlen = conv.ToWChar(NULL, 0, m_buf);
-    wxCHECK_RET( wlen != wxCONV_FAILED, "invalid UTF-8 data in string buffer?" );
-
-    wxStringInternalBuffer wbuf(m_str, wlen);
-    conv.ToWChar(wbuf, wlen, m_buf);
-}
-
-wxUTF8StringBufferLength::~wxUTF8StringBufferLength()
-{
-    wxCHECK_RET(m_lenSet, "length not set");
-
-    wxMBConvStrictUTF8 conv;
-    size_t wlen = conv.ToWChar(NULL, 0, m_buf, m_len);
-    wxCHECK_RET( wlen != wxCONV_FAILED, "invalid UTF-8 data in string buffer?" );
-
-    wxStringInternalBufferLength wbuf(m_str, wlen);
-    conv.ToWChar(wbuf, wlen, m_buf, m_len);
-    wbuf.SetLength(wlen);
-}
-#endif // wxUSE_UNICODE_WCHAR