///////////////////////////////////////////////////////////////////////////// // Name: src/common/string.cpp // Purpose: wxString class // Author: Vadim Zeitlin, Ryan Norton // Modified by: // Created: 29/01/98 // RCS-ID: $Id: string.cpp 63008 2009-12-28 20:01:39Z VZ $ // Copyright: (c) 1998 Vadim Zeitlin // (c) 2004 Ryan Norton // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// /* * About ref counting: * 1) all empty strings use g_strEmpty, nRefs = -1 (set in Init()) * 2) AllocBuffer() sets nRefs to 1, Lock() increments it by one * 3) Unlock() decrements nRefs and frees memory if it goes to 0 */ // =========================================================================== // headers, declarations, constants // =========================================================================== // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP #include "wx/string.h" #include "wx/intl.h" #include "wx/thread.h" #endif #include #ifndef __WXWINCE__ #include #endif #include #include #ifdef __SALFORDC__ #include #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 // --------------------------------------------------------------------------- #if !wxUSE_STL //According to STL _must_ be a -1 size_t const size_t wxStringBase::npos = (size_t) -1; #endif // ---------------------------------------------------------------------------- // static data // ---------------------------------------------------------------------------- #if wxUSE_STL extern const wxChar WXDLLIMPEXP_BASE *wxEmptyString = _T(""); #else // 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 // ---------------------------------------------------------------------------- // global functions // ---------------------------------------------------------------------------- #if wxUSE_STD_IOSTREAM #include wxSTD ostream& operator<<(wxSTD ostream& os, const wxString& str) { #ifdef __BORLANDC__ os << str.mb_str(); #else os << str.c_str(); #endif return os; } #endif // wxUSE_STD_IOSTREAM // ---------------------------------------------------------------------------- // private classes // ---------------------------------------------------------------------------- // 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 !wxUSE_STL // =========================================================================== // 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 wxStringBase::InitWith(const wxChar *psz, size_t nPos, size_t nLength) { Init(); // 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); } STATISTICS_ADD(InitialLength, nLength); if ( nLength > 0 ) { // trailing '\0' is written in AllocBuffer() if ( !AllocBuffer(nLength) ) { wxFAIL_MSG( _T("out of memory in wxStringBase::InitWith") ); return; } wxTmemcpy(m_pchData, psz + nPos, nLength); } } // poor man's iterators are "void *" pointers wxStringBase::wxStringBase(const void *pStart, const void *pEnd) { if ( pEnd >= pStart ) { InitWith((const wxChar *)pStart, 0, (const wxChar *)pEnd - (const wxChar *)pStart); } 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 bool wxStringBase::AllocBuffer(size_t nLen) { // 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 wxCHECK( nLen < (INT_MAX / sizeof(wxChar)) - (sizeof(wxStringData) + EXTRA_ALLOC + 1), false ); STATISTICS_ADD(Length, nLen); // allocate memory: // 1) one extra character for '\0' termination // 2) sizeof(wxStringData) for housekeeping info 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->nDataLength = nLen; pData->nAllocLength = nLen + EXTRA_ALLOC; m_pchData = pData->data(); // data starts after wxStringData m_pchData[nLen] = wxT('\0'); return true; } // must be called before changing this string bool wxStringBase::CopyBeforeWrite() { wxStringData* pData = GetStringData(); if ( pData->IsShared() ) { pData->Unlock(); // memory not freed because shared size_t nLen = pData->nDataLength; if ( !AllocBuffer(nLen) ) { // allocation failures are handled by the caller return false; } wxTmemcpy(m_pchData, pData->data(), nLen); } wxASSERT( !GetStringData()->IsShared() ); // we must be the only owner return true; } // must be called before replacing contents of this string bool wxStringBase::AllocBeforeWrite(size_t nLen) { wxASSERT( nLen != 0 ); // doesn't make any sense // must not share string and must have enough space wxStringData* pData = GetStringData(); if ( pData->IsShared() || pData->IsEmpty() ) { // can't work with old buffer, get new one pData->Unlock(); 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( !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; } wxStringBase& wxStringBase::append(size_t n, wxChar ch) { 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; } void wxStringBase::resize(size_t nSize, wxChar ch) { 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 } // 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; pData = (wxStringData *) malloc(sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar)); if ( pData == NULL ) { // allocation failure handled by caller return false; } 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; } wxStringBase::iterator wxStringBase::begin() { 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") ); } 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; } void wxStringBase::swap(wxStringBase& str) { wxChar* tmp = str.m_pchData; str.m_pchData = m_pchData; m_pchData = tmp; } size_t wxStringBase::find(const wxStringBase& str, size_t nStart) const { // 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; } size_t wxStringBase::find(const wxChar* sz, size_t nStart, size_t n) const { return find(wxStringBase(sz, n), nStart); } size_t wxStringBase::find(wxChar ch, size_t nStart) const { wxASSERT( nStart <= length() ); const wxChar *p = (const wxChar*)wxTmemchr(c_str() + nStart, ch, length() - nStart); return p == NULL ? npos : p - c_str(); } 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; 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 { return rfind(wxStringBase(sz, n), nStart); } size_t wxStringBase::rfind(wxChar ch, size_t nStart) const { if ( nStart == npos ) { nStart = length(); } else { wxASSERT( nStart <= length() ); } 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(); } return npos; } size_t wxStringBase::find_first_of(const wxChar* sz, size_t nStart) const { 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; } size_t wxStringBase::find_first_of(const wxChar* sz, size_t nStart, size_t n) const { return find_first_of(wxStringBase(sz, n), nStart); } size_t wxStringBase::find_last_of(const wxChar* sz, size_t nStart) const { 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; } size_t wxStringBase::find_last_of(const wxChar* sz, size_t nStart, size_t n) const { return find_last_of(wxStringBase(sz, n), nStart); } size_t wxStringBase::find_first_not_of(const wxChar* sz, size_t nStart) const { 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; } 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); } size_t wxStringBase::find_first_not_of(wxChar ch, size_t nStart) const { wxASSERT( nStart <= length() ); for ( const wxChar *p = c_str() + nStart; *p; p++ ) { if ( *p != ch ) return p - c_str(); } return npos; } size_t wxStringBase::find_last_not_of(const wxChar* sz, size_t nStart) const { 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; } size_t wxStringBase::find_last_not_of(const wxChar* sz, size_t nStart, size_t n) const { return find_last_not_of(wxStringBase(sz, n), nStart); } 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; } // use memmove() and not memcpy() here as we might be copying from our own // buffer in case of assignment such as "s = s.c_str()" (see #11294) memmove(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; // take special care when appending part of this string to itself: the code // below reallocates our buffer and this invalidates pszSrcData pointer so // we have to copy it in another temporary string in this case (but avoid // doing this unnecessarily) if ( pszSrcData >= m_pchData && pszSrcData < m_pchData + nLen ) { wxStringBase tmp(pszSrcData, nSrcLen); return ConcatSelf(nSrcLen, tmp.m_pchData, 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() { UngetWriteBuf(wxStrlen(m_pchData)); } void wxString::UngetWriteBuf(size_t nLen) { wxStringData * const pData = GetStringData(); wxASSERT_MSG( nLen < pData->nAllocLength, _T("buffer overrun") ); // the strings we store are always NUL-terminated pData->data()[nLen] = _T('\0'); pData->nDataLength = nLen; pData->Validate(true); } #endif // !wxUSE_STL // --------------------------------------------------------------------------- // 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; } // out-of-bounds requests return sensible things if ( nFirst + nCount > nLen ) { nCount = nLen - nFirst; } if ( nFirst > nLen ) { // AllocCopy() will return empty string return wxEmptyString; } wxString dest(*this, nFirst, nCount); if ( dest.length() != nCount ) { wxFAIL_MSG( _T("out of memory in wxString::Mid") ); } return dest; } // 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 { wxASSERT_MSG( prefix, _T("invalid parameter in wxString::StartsWith") ); // 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; } } if ( rest ) { // put the rest of the string into provided pointer *rest = p; } return true; } // 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 { wxASSERT_MSG( suffix, _T("invalid parameter in wxString::EndssWith") ); int start = length() - wxStrlen(suffix); if ( start < 0 || wxStrcmp(c_str() + start, suffix) != 0 ) return false; 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 > length() ) nCount = length(); 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::AfterLast(wxChar ch) const { wxString str; int iPos = Find(ch, true); if ( iPos == wxNOT_FOUND ) str = *this; else str = c_str() + iPos + 1; return str; } // extract nCount first (leftmost) characters wxString wxString::Left(size_t nCount) const { if ( nCount > length() ) nCount = length(); 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::BeforeFirst(wxChar ch) const { 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::BeforeLast(wxChar ch) const { wxString str; 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::AfterFirst(wxChar ch) const { wxString str; int iPos = Find(ch); if ( iPos != wxNOT_FOUND ) str = c_str() + iPos + 1; return str; } // replace first (or all) occurences of some substring with another one 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 // optimize the special common case of replacing one character with another // one if ( szOld[1] == '\0' && (szNew[0] != '\0' && szNew[1] == '\0') ) { // this loop is the simplified version of the one below for ( size_t pos = 0; ; ) { pos = find(*szOld, pos); if ( pos == npos ) break; (*this)[pos++] = *szNew; uiCount++; if ( !bReplaceAll ) break; } } else // general case { const size_t uiOldLen = wxStrlen(szOld); const size_t uiNewLen = wxStrlen(szNew); for ( size_t pos = 0; ; ) { pos = find(szOld, pos); if ( pos == npos ) break; // replace this occurrence of the old string with the new one replace(pos, uiOldLen, szNew, uiNewLen); // move past the string that was replaced pos += uiNewLen; // increase replace count uiCount++; // stop now? if ( !bReplaceAll ) break; } } return uiCount; } bool wxString::IsAscii() const { const wxChar *s = (const wxChar*) *this; while(*s){ if(!isascii(*s)) return(false); s++; } return(true); } bool wxString::IsWord() const { const wxChar *s = (const wxChar*) *this; while(*s){ if(!wxIsalpha(*s)) return(false); s++; } return(true); } bool wxString::IsNumber() const { const wxChar *s = (const wxChar*) *this; if (wxStrlen(s)) if ((s[0] == wxT('-')) || (s[0] == wxT('+'))) s++; while(*s){ if(!wxIsdigit(*s)) return(false); s++; } return(true); } wxString wxString::Strip(stripType w) const { wxString s = *this; if ( w & leading ) s.Trim(false); if ( w & trailing ) s.Trim(true); return s; } // --------------------------------------------------------------------------- // case conversion // --------------------------------------------------------------------------- wxString& wxString::MakeUpper() { for ( iterator it = begin(), en = end(); it != en; ++it ) *it = (wxChar)wxToupper(*it); return *this; } wxString& wxString::MakeLower() { 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('\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); } // trims spaces (in the sense of isspace) from left or right side wxString& wxString::Trim(bool bFromRight) { // 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; } // adds nCount characters chPad to the string from either side wxString& wxString::Pad(size_t nCount, wxChar chPad, bool bFromRight) { wxString s(chPad, nCount); if ( bFromRight ) *this += s; else { s += *this; swap(s); } return *this; } // truncate the string wxString& wxString::Truncate(size_t uiLen) { if ( uiLen < length() ) { erase(begin() + uiLen, end()); } //else: nothing to do, string is already short enough return *this; } // --------------------------------------------------------------------------- // finding (return wxNOT_FOUND if not found and index otherwise) // --------------------------------------------------------------------------- // find a character int wxString::Find(wxChar ch, bool bFromEnd) const { size_type idx = bFromEnd ? find_last_of(ch) : find_first_of(ch); return (idx == npos) ? wxNOT_FOUND : (int)idx; } // find a sub-string (like strstr) int wxString::Find(const wxChar *pszSub) const { size_type idx = find(pszSub); return (idx == npos) ? wxNOT_FOUND : (int)idx; } // ---------------------------------------------------------------------------- // conversion to numbers // ---------------------------------------------------------------------------- // the implementation of all the functions below is exactly the same so factor // it out template bool wxStringToIntType(const wxChar *start, T *val, int base, F func) { wxCHECK_MSG( val, false, _T("NULL output pointer") ); wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); #ifndef __WXWINCE__ errno = 0; #endif wxChar *end; *val = (*func)(start, &end, 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) #endif ; } bool wxString::ToLong(long *val, int base) const { return wxStringToIntType(c_str(), val, base, wxStrtol); } bool wxString::ToULong(unsigned long *val, int base) const { return wxStringToIntType(c_str(), val, base, wxStrtoul); } bool wxString::ToLongLong(wxLongLong_t *val, int base) const { #ifdef wxHAS_STRTOLL return wxStringToIntType(c_str(), 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 return wxStringToIntType(c_str(), val, base, wxStrtoull); #else // TODO: implement this ourselves wxUnusedVar(val); wxUnusedVar(base); return false; #endif } bool wxString::ToDouble(double *val) const { wxCHECK_MSG( val, false, _T("NULL pointer in wxString::ToDouble") ); #ifndef __WXWINCE__ errno = 0; #endif const wxChar *start = c_str(); wxChar *end; *val = wxStrtod(start, &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) #ifndef __WXWINCE__ && (errno != ERANGE) #endif ; } // --------------------------------------------------------------------------- // formatted output // --------------------------------------------------------------------------- /* static */ wxString wxString::Format(const wxChar *pszFormat, ...) { va_list argptr; va_start(argptr, pszFormat); wxString s; s.PrintfV(pszFormat, argptr); va_end(argptr); return s; } /* static */ wxString wxString::FormatV(const wxChar *pszFormat, va_list argptr) { wxString s; s.PrintfV(pszFormat, argptr); return s; } int wxString::Printf(const wxChar *pszFormat, ...) { va_list argptr; va_start(argptr, pszFormat); int iLen = PrintfV(pszFormat, argptr); va_end(argptr); return iLen; } /* Uses wxVsnprintf and places the result into the this string. In ANSI build, wxVsnprintf is effectively vsnprintf but in Unicode build it is vswprintf. Due to a discrepancy between vsnprintf and vswprintf in the ISO C99 (and thus SUSv3) standard the return value for the case of an undersized buffer is inconsistent. For conforming vsnprintf implementations the function must return the number of characters that would have been printed had the buffer been large enough. For conforming vswprintf implementations the function must return a negative number and set errno. What vswprintf sets errno to is undefined but Darwin seems to set it to EOVERFLOW. The only expected errno are EILSEQ and EINVAL. Both of those are defined in the standard and backed up by several conformance statements. Note that ENOMEM mentioned in the manual page does not apply to swprintf, only wprintf and fwprintf. Official manual page: http://www.opengroup.org/onlinepubs/009695399/functions/swprintf.html Some conformance statements (AIX, Solaris): http://www.opengroup.org/csq/view.mhtml?RID=ibm%2FSD1%2F3 http://www.theopengroup.org/csq/view.mhtml?norationale=1&noreferences=1&RID=Fujitsu%2FSE2%2F10 Since EILSEQ and EINVAL are rather common but EOVERFLOW is not and since EILSEQ and EINVAL are specifically defined to mean the error is other than 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 with an updated release. Not to mention that configure testing for it would require the test to be run on the host system, not the build system which makes cross compilation difficult. Therefore, we make no assumptions about behavior and try our best to handle every known case, including the case where wxVsnprintf returns a negative number and fails to set errno. There is yet one more non-standard implementation and that is our own. Fortunately, that can be detected at compile-time. On top of all that, ISO C99 explicitly defines snprintf to write a null character to the last position of the specified buffer. That would be at at the given buffer size minus 1. It is supposed to do this even if it turns out that the buffer is sized too small. Darwin (tested on 10.5) follows the C99 behavior exactly. Glibc 2.6 almost follows the C99 behavior except vswprintf never sets errno even when it fails. However, it only seems to ever fail due to an undersized buffer. */ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr) { int size = 1024; for ( ;; ) { // Allocate 1 more character than we tell wxVsnprintf about // just in case it is buggy. // FIXME: I have a feeling that the underlying function was not buggy // and I suspect it was to fix the buf[size] = '\0' line below 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); #ifndef __WXWINCE__ // Set errno to 0 to make it determinate if wxVsnprintf fails to set it. errno = 0; #endif 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 // FIXME: This really seems to be the wrong and would be an off-by-one // bug except the code above allocates an extra character. 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 ) { #if wxUSE_WXVSNPRINTF // we know that our own implementation of wxVsnprintf() returns -1 // only for a format error - thus there's something wrong with // the user's format string return -1; #else // assume that system version only returns error if not enough space #if !defined(__WXWINCE__) && (!defined(__OS2__) || defined(__INNOTEK_LIBC__)) if( (errno == EILSEQ) || (errno == EINVAL) ) // If errno was set to one of the two well-known hard errors // then fail immediately to avoid an infinite loop. return -1; else #endif // __WXWINCE__ // still not enough, as we don't know how much we need, double the // current size of the buffer size *= 2; #endif // wxUSE_WXVSNPRINTF/!wxUSE_WXVSNPRINTF } else if ( len >= size ) { #if wxUSE_WXVSNPRINTF // 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 #else // some vsnprintf() implementations NUL-terminate the buffer and // some don't in len == size case, to be safe always add 1 // FIXME: I don't quite understand this comment. The vsnprintf // function is specifically defined to return the number of // characters printed not including the null terminator. // So OF COURSE you need to add 1 to get the right buffer size. // The following line is definitely correct, no question. size = len + 1; #endif } else // ok, there was enough space { break; } } // 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('$'); // and now use it return wxRegEx(pattern, wxRE_NOSUB | wxRE_EXTENDED).Matches(c_str()); #else // !wxUSE_REGEX // TODO: this is, of course, awfully inefficient... // the char currently being checked const wxChar *pszTxt = c_str(); // the last location where '*' matched const wxChar *pszLastStarInText = NULL; const wxChar *pszLastStarInMask = NULL; 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; default: if ( *pszMask != *pszTxt ) return false; break; } } // match only if nothing left if ( *pszTxt == wxT('\0') ) return true; // if we failed to match, backtrack if we can if ( pszLastStarInText ) { pszTxt = pszLastStarInText + 1; pszMask = pszLastStarInMask; pszLastStarInText = NULL; // don't bother resetting pszLastStarInMask, it's unnecessary goto match; } return false; #endif // wxUSE_REGEX/!wxUSE_REGEX } // Count the number of chars int wxString::Freq(wxChar ch) const { int count = 0; int len = length(); for (int i = 0; i < len; i++) { if (GetChar(i) == ch) count ++; } 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(); } int wxString::sprintf(const wxChar *pszFormat, ...) { va_list argptr; va_start(argptr, pszFormat); int iLen = PrintfV(pszFormat, argptr); va_end(argptr); return iLen; } // ============================================================================ // ArrayString // ============================================================================ #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) #endif #define STRING(p) ((wxString *)(&(p))) // ctor void wxArrayString::Init(bool autoSort) { m_nSize = m_nCount = 0; m_pItems = (wxChar **) NULL; m_autoSort = autoSort; } // copy ctor wxArrayString::wxArrayString(const wxArrayString& src) { Init(src.m_autoSort); *this = src; } // assignment operator wxArrayString& wxArrayString::operator=(const wxArrayString& src) { if ( m_nSize > 0 ) Clear(); Copy(src); 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(size_t nIncrement) { // only do it if no more place 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; 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 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; wxChar **pNew = new wxChar *[m_nSize]; // copy data to new location memcpy(pNew, m_pItems, m_nCount*sizeof(wxChar *)); // delete old memory (but do not release the strings!) wxDELETEA(m_pItems); m_pItems = pNew; } } } void wxArrayString::Free() { for ( size_t n = 0; n < m_nCount; n++ ) { STRING(m_pItems[n])->GetStringData()->Unlock(); } } // deletes all the strings from the list void wxArrayString::Empty() { Free(); m_nCount = 0; } // as Empty, but also frees memory void wxArrayString::Clear() { Free(); m_nSize = m_nCount = 0; wxDELETEA(m_pItems); } // dtor wxArrayString::~wxArrayString() { Free(); wxDELETEA(m_pItems); } void wxArrayString::reserve(size_t nSize) { Alloc(nSize); } // pre-allocates memory (frees the previous data!) void wxArrayString::Alloc(size_t nSize) { // only if old buffer was not big enough if ( nSize > m_nSize ) { wxChar **pNew = new wxChar *[nSize]; if ( !pNew ) return; memcpy(pNew, m_pItems, m_nCount*sizeof(wxChar *)); delete [] m_pItems; m_pItems = pNew; m_nSize = nSize; } } // 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]; // copy data to new location memcpy(pNew, m_pItems, m_nCount*sizeof(wxChar *)); delete [] m_pItems; m_pItems = pNew; m_nSize = m_nCount; } } #if WXWIN_COMPATIBILITY_2_4 // return a wxString[] as required for some control ctors. wxString* wxArrayString::GetStringArray() const { 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 { // 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 wxNOT_FOUND; } // add item at the end 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(); // 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& 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) { const int idx = it - begin(); // grow it once Grow(last - first); // reset "it" since it can change inside Grow() it = begin() + idx; while ( first != last ) { it = insert(it, *first); // 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; ++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::RemoveAt(size_t nIndex, size_t nRemove) { 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 for (size_t i = 0; i < nRemove; i++) Item(nIndex + i).GetStringData()->Unlock(); 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 wxChar *sz) { int iIndex = Index(sz); 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); 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); } int wxCMPFUNC_CONV wxStringSortDescending(wxString* s1, wxString* s2) { return -s1->Cmp(*s2); } wxString* wxCArrayString::Release() { wxString *r = GetStrings(); m_strings = NULL; return r; }