X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/394b2900ffb066b20f3a06482b470521e3b876fe..b53aea81d2e102224b452ef5bf7aee1132f37c6f:/src/common/string.cpp diff --git a/src/common/string.cpp b/src/common/string.cpp index a287759822..56af51ae22 100644 --- a/src/common/string.cpp +++ b/src/common/string.cpp @@ -50,6 +50,18 @@ #define wxStringStrlen wxStrlen #endif +// ---------------------------------------------------------------------------- +// global variables +// ---------------------------------------------------------------------------- + +namespace wxPrivate +{ + +static UntypedBufferData s_untypedNullData(NULL); + +UntypedBufferData * const untypedNullDataPtr = &s_untypedNullData; + +} // namespace wxPrivate // --------------------------------------------------------------------------- // static class variables definition @@ -58,6 +70,105 @@ //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 +#ifdef __WXDEBUG__ + +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__ + +#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 // ---------------------------------------------------------------------------- @@ -69,7 +180,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 @@ -123,22 +240,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(); } } } @@ -369,6 +494,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* @@ -987,6 +1115,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(); @@ -1010,6 +1139,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 } @@ -1166,7 +1298,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 { @@ -1175,7 +1307,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; } @@ -1193,16 +1325,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 { @@ -1214,19 +1347,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) { @@ -1234,29 +1367,60 @@ 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]; - for ( size_t dwPos = 0; dwPos < length(); ) + // 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; + + uiCount++; + + if ( !bReplaceAll ) + break; + } + } + else // general case { - dwPos = find(strOld, dwPos); - if ( dwPos == npos ) - break; + const size_t uiOldLen = strOld.m_impl.length(); + const size_t uiNewLen = strNew.m_impl.length(); - // replace this occurance of the old string with the new one - replace(dwPos, uiOldLen, strNew, uiNewLen); + for ( size_t pos = 0; ; ) + { + pos = m_impl.find(strOld.m_impl, pos); + if ( pos == npos ) + break; - // move up pos past the string that was replaced - dwPos += uiNewLen; + // replace this occurrence of the old string with the new one + m_impl.replace(pos, uiOldLen, strNew.m_impl); - // increase replace count - ++uiCount; + // move up pos past the string that was replaced + pos += uiNewLen; - // stop after the first one? - if ( !bReplaceAll ) - break; + // increase replace count + uiCount++; + + // stop after the first one? + if ( !bReplaceAll ) + break; + } } return uiCount; @@ -1331,6 +1495,20 @@ 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 // --------------------------------------------------------------------------- @@ -1935,38 +2113,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