X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e87271f35887a50e011c7c97fc63c35f5d623e50..7e99520bd3fc092bbbcbc38bb699a0236618ea40:/src/common/string.cpp?ds=sidebyside diff --git a/src/common/string.cpp b/src/common/string.cpp index 4a667a59cc..f8d1fc4594 100644 --- a/src/common/string.cpp +++ b/src/common/string.cpp @@ -35,9 +35,7 @@ #include "wx/defs.h" #include "wx/string.h" #include "wx/intl.h" -#if wxUSE_THREADS - #include -#endif + #include "wx/thread.h" #endif #include @@ -87,6 +85,12 @@ static const struct wxChar dummy; } g_strEmpty = { {-1, 0, 0}, wxT('\0') }; +#if defined(__VISAGECPP__) && __IBMCPP__ >= 400 +// must define this static for VA or else you get multiply defined symbols everywhere +const unsigned int wxSTRING_MAXLEN = UINT_MAX - 100; + +#endif + // empty C style string: points to 'string data' byte of g_strEmpty extern const wxChar WXDLLEXPORT *wxEmptyString = &g_strEmpty.dummy; @@ -104,28 +108,32 @@ extern const wxChar WXDLLEXPORT *wxEmptyString = &g_strEmpty.dummy; // we want to find out if the current platform supports vsnprintf()-like // function: for Unix this is done with configure, for Windows we test the // compiler explicitly. +// +// FIXME currently, this is only for ANSI (!Unicode) strings, so we call this +// function wxVsnprintfA (A for ANSI), should also find one for Unicode +// strings in Unicode build #ifdef __WXMSW__ - #ifdef __VISUALC__ - #define wxVsnprintf _vsnprintf + #if defined(__VISUALC__) || (defined(__MINGW32__) && wxUSE_NORLANDER_HEADERS) + #define wxVsnprintfA _vsnprintf #endif #else // !Windows #ifdef HAVE_VSNPRINTF - #define wxVsnprintf vsnprintf + #define wxVsnprintfA vsnprintf #endif #endif // Windows/!Windows -#ifndef wxVsnprintf +#ifndef wxVsnprintfA // in this case we'll use vsprintf() (which is ANSI and thus should be // always available), but it's unsafe because it doesn't check for buffer // size - so give a warning - #define wxVsnprintf(buffer,len,format,argptr) vsprintf(buffer,format, argptr) + #define wxVsnprintfA(buf, len, format, arg) vsprintf(buf, format, arg) #if defined(__VISUALC__) #pragma message("Using sprintf() because no snprintf()-like function defined") #elif defined(__GNUG__) && !defined(__UNIX__) #warning "Using sprintf() because no snprintf()-like function defined" #elif defined(__MWERKS__) - #warning "Using sprintf() because no snprintf()-like function defined" + #warning "Using sprintf() because no snprintf()-like function defined" #endif //compiler #endif // no vsnprintf @@ -176,8 +184,53 @@ istream& operator>>(istream& is, wxString& WXUNUSED(str)) return is; } +ostream& operator<<(ostream& os, const wxString& str) +{ + os << str.c_str(); + return os; +} + #endif //std::string compatibility +extern int WXDLLEXPORT wxVsnprintf(wxChar *buf, size_t len, + const wxChar *format, va_list argptr) +{ +#if wxUSE_UNICODE + // FIXME should use wvsnprintf() or whatever if it's available + wxString s; + int iLen = s.PrintfV(format, argptr); + if ( iLen != -1 ) + { + wxStrncpy(buf, s.c_str(), iLen); + } + + return iLen; +#else // ANSI + // vsnprintf() will not terminate the string with '\0' if there is not + // enough place, but we want the string to always be NUL terminated + int rc = wxVsnprintfA(buf, len - 1, format, argptr); + if ( rc == -1 ) + { + buf[len] = 0; + } + + return rc; +#endif // Unicode/ANSI +} + +extern int WXDLLEXPORT wxSnprintf(wxChar *buf, size_t len, + const wxChar *format, ...) +{ + va_list argptr; + va_start(argptr, format); + + int iLen = wxVsnprintf(buf, len, format, argptr); + + va_end(argptr); + + return iLen; +} + // ---------------------------------------------------------------------------- // private classes // ---------------------------------------------------------------------------- @@ -237,10 +290,12 @@ void wxString::InitWith(const wxChar *psz, size_t nPos, size_t nLength) { Init(); - wxASSERT( nPos <= wxStrlen(psz) ); + // if the length is not given, assume the string to be NUL terminated + if ( nLength == wxSTRING_MAXLEN ) { + wxASSERT_MSG( nPos <= wxStrlen(psz), _T("index out of bounds") ); - if ( nLength == wxSTRING_MAXLEN ) nLength = wxStrlen(psz + nPos); + } STATISTICS_ADD(InitialLength, nLength); @@ -288,15 +343,15 @@ wxString::wxString(const char *psz, wxMBConv& conv, size_t nLength) #if wxUSE_WCHAR_T // from wide string -wxString::wxString(const wchar_t *pwz) +wxString::wxString(const wchar_t *pwz, wxMBConv& conv) { // first get necessary size - size_t nLen = pwz ? wxWC2MB((char *) NULL, pwz, 0) : 0; + size_t nLen = pwz ? conv.WC2MB((char *) NULL, pwz, 0) : 0; // empty? if ( (nLen != 0) && (nLen != (size_t)-1) ) { AllocBuffer(nLen); - wxWC2MB(m_pchData, pwz, nLen); + conv.WC2MB(m_pchData, pwz, nLen); } else { Init(); @@ -313,8 +368,13 @@ wxString::wxString(const wchar_t *pwz) // allocates memory needed to store a C string of length nLen void wxString::AllocBuffer(size_t nLen) { - wxASSERT( nLen > 0 ); // - wxASSERT( nLen <= INT_MAX-1 ); // max size (enough room for 1 extra) + // allocating 0 sized buffer doesn't make sense, all empty strings should + // reuse g_strEmpty + wxASSERT( nLen > 0 ); + + // make sure that we don't overflow + wxASSERT( nLen < (INT_MAX / sizeof(wxChar)) - + (sizeof(wxStringData) + EXTRA_ALLOC + 1) ); STATISTICS_ADD(Length, nLen); @@ -352,13 +412,35 @@ void wxString::AllocBeforeWrite(size_t nLen) // must not share string and must have enough space wxStringData* pData = GetStringData(); - if ( pData->IsShared() || (nLen > pData->nAllocLength) ) { + if ( pData->IsShared() || pData->IsEmpty() ) { // can't work with old buffer, get new one pData->Unlock(); AllocBuffer(nLen); } else { - // update the string length + if ( nLen > pData->nAllocLength ) { + // realloc the buffer instead of calling malloc() again, this is more + // efficient + STATISTICS_ADD(Length, nLen); + + nLen += EXTRA_ALLOC; + + wxStringData *pDataOld = pData; + pData = (wxStringData*) + realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar)); + if ( !pData ) { + // out of memory + free(pDataOld); + + // FIXME we're going to crash... + return; + } + + pData->nAllocLength = nLen; + m_pchData = pData->data(); + } + + // now we have enough space, just update the string length pData->nDataLength = nLen; } @@ -390,11 +472,15 @@ void wxString::Alloc(size_t nLen) else { nLen += EXTRA_ALLOC; + wxStringData *pDataOld = pData; wxStringData *p = (wxStringData *) realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar)); if ( p == NULL ) { - // @@@ what to do on memory error? + // don't leak memory + free(pDataOld); + + // FIXME what to do on memory error? return; } @@ -412,13 +498,16 @@ void wxString::Shrink() { wxStringData *pData = GetStringData(); - // this variable is unused in release build, so avoid the compiler warning by - // just not declaring it + // this variable is unused in release build, so avoid the compiler warning + // by just not declaring it #ifdef __WXDEBUG__ void *p = #endif realloc(pData, sizeof(wxStringData) + (pData->nDataLength + 1)*sizeof(wxChar)); + // we rely on a reasonable realloc() implementation here - so far I haven't + // seen any which wouldn't behave like this + wxASSERT( p != NULL ); // can't free memory? wxASSERT( p == pData ); // we're decrementing the size - block shouldn't move! } @@ -441,6 +530,12 @@ void wxString::UngetWriteBuf() GetStringData()->Validate(TRUE); } +void wxString::UngetWriteBuf(size_t nLen) +{ + GetStringData()->nDataLength = nLen; + GetStringData()->Validate(TRUE); +} + // --------------------------------------------------------------------------- // data access // --------------------------------------------------------------------------- @@ -681,6 +776,35 @@ wxString wxString::Mid(size_t nFirst, size_t nCount) const return dest; } +// check that the tring 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; +} + // extract nCount last (rightmost) characters wxString wxString::Right(size_t nCount) const { @@ -817,6 +941,8 @@ bool wxString::IsWord() const bool wxString::IsNumber() const { const wxChar *s = (const wxChar*) *this; + if (wxStrlen(s)) + if ((s[0] == '-') || (s[0] == '+')) s++; while(*s){ if(!wxIsdigit(*s)) return(FALSE); s++; @@ -952,36 +1078,75 @@ int wxString::Find(const wxChar *pszSub) const return (psz == NULL) ? wxNOT_FOUND : psz - (const wxChar*) m_pchData; } -// --------------------------------------------------------------------------- -// stream-like operators -// --------------------------------------------------------------------------- -wxString& wxString::operator<<(int i) +// ---------------------------------------------------------------------------- +// conversion to numbers +// ---------------------------------------------------------------------------- + +bool wxString::ToLong(long *val) const { - wxString res; - res.Printf(wxT("%d"), i); + wxCHECK_MSG( val, FALSE, _T("NULL pointer in wxString::ToLong") ); - return (*this) << res; + const wxChar *start = c_str(); + wxChar *end; + *val = wxStrtol(start, &end, 10); + + // return TRUE only if scan was stopped by the terminating NUL and if the + // string was not empty to start with + return !*end && (end != start); } -wxString& wxString::operator<<(float f) +bool wxString::ToULong(unsigned long *val) const { - wxString res; - res.Printf(wxT("%f"), f); + wxCHECK_MSG( val, FALSE, _T("NULL pointer in wxString::ToULong") ); + + const wxChar *start = c_str(); + wxChar *end; + *val = wxStrtoul(start, &end, 10); - return (*this) << res; + // return TRUE only if scan was stopped by the terminating NUL and if the + // string was not empty to start with + return !*end && (end != start); } -wxString& wxString::operator<<(double d) +bool wxString::ToDouble(double *val) const { - wxString res; - res.Printf(wxT("%g"), d); + wxCHECK_MSG( val, FALSE, _T("NULL pointer in wxString::ToDouble") ); - return (*this) << res; + 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 + return !*end && (end != start); } // --------------------------------------------------------------------------- // 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.Printf(pszFormat, argptr); + return s; +} + int wxString::Printf(const wxChar *pszFormat, ...) { va_list argptr; @@ -996,18 +1161,11 @@ int wxString::Printf(const wxChar *pszFormat, ...) int wxString::PrintfV(const wxChar* pszFormat, va_list argptr) { - // static buffer to avoid dynamic memory allocation each time - char s_szScratch[1024]; // using static buffer causes internal compiler err -#if 0 -#if wxUSE_THREADS - // protect the static buffer - static wxCriticalSection critsect; - wxCriticalSectionLocker lock(critsect); -#endif -#endif - #if wxUSE_EXPERIMENTAL_PRINTF -// the new implementation + // the new implementation + + // buffer to avoid dynamic memory allocation each time for small strings + char szScratch[1024]; Reinit(); for (size_t n = 0; pszFormat[n]; n++) @@ -1114,30 +1272,30 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr) s_szFlags[flagofs] = '\0'; if (ilen == 0 ) { int val = va_arg(argptr, int); - ::sprintf(s_szScratch, s_szFlags, val); + ::sprintf(szScratch, s_szFlags, val); } else if (ilen == -1) { short int val = va_arg(argptr, short int); - ::sprintf(s_szScratch, s_szFlags, val); + ::sprintf(szScratch, s_szFlags, val); } else if (ilen == 1) { long int val = va_arg(argptr, long int); - ::sprintf(s_szScratch, s_szFlags, val); + ::sprintf(szScratch, s_szFlags, val); } else if (ilen == 2) { #if SIZEOF_LONG_LONG long long int val = va_arg(argptr, long long int); - ::sprintf(s_szScratch, s_szFlags, val); + ::sprintf(szScratch, s_szFlags, val); #else long int val = va_arg(argptr, long int); - ::sprintf(s_szScratch, s_szFlags, val); + ::sprintf(szScratch, s_szFlags, val); #endif } else if (ilen == 3) { size_t val = va_arg(argptr, size_t); - ::sprintf(s_szScratch, s_szFlags, val); + ::sprintf(szScratch, s_szFlags, val); } - *this += wxString(s_szScratch); + *this += wxString(szScratch); done = TRUE; break; case wxT('e'): @@ -1150,12 +1308,12 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr) s_szFlags[flagofs] = '\0'; if (ilen == 2) { long double val = va_arg(argptr, long double); - ::sprintf(s_szScratch, s_szFlags, val); + ::sprintf(szScratch, s_szFlags, val); } else { double val = va_arg(argptr, double); - ::sprintf(s_szScratch, s_szFlags, val); + ::sprintf(szScratch, s_szFlags, val); } - *this += wxString(s_szScratch); + *this += wxString(szScratch); done = TRUE; break; case wxT('p'): @@ -1164,8 +1322,8 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr) CHECK_PREC s_szFlags[flagofs++] = pszFormat[n]; s_szFlags[flagofs] = '\0'; - ::sprintf(s_szScratch, s_szFlags, val); - *this += wxString(s_szScratch); + ::sprintf(szScratch, s_szFlags, val); + *this += wxString(szScratch); done = TRUE; } break; @@ -1239,39 +1397,43 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr) } else *this += pszFormat[n]; #else - // NB: wxVsnprintf() may return either less than the buffer size or -1 if there - // is not enough place depending on implementation - int iLen = wxVsnprintf(s_szScratch, WXSIZEOF(s_szScratch), pszFormat, argptr); - char *buffer; - if ( iLen < (int)WXSIZEOF(s_szScratch) ) { - buffer = s_szScratch; + // buffer to avoid dynamic memory allocation each time for small strings + char szScratch[1024]; + + // NB: wxVsnprintf() may return either less than the buffer size or -1 if + // there is not enough place depending on implementation + int iLen = wxVsnprintfA(szScratch, WXSIZEOF(szScratch), pszFormat, argptr); + if ( iLen != -1 ) { + // the whole string is in szScratch + *this = szScratch; } else { - int size = WXSIZEOF(s_szScratch) * 2; - buffer = (char *)malloc(size); - while ( buffer != NULL ) { - iLen = wxVsnprintf(buffer, WXSIZEOF(s_szScratch), pszFormat, argptr); - if ( iLen < size ) { + bool outOfMemory = FALSE; + int size = 2*WXSIZEOF(szScratch); + while ( !outOfMemory ) { + char *buf = GetWriteBuf(size); + if ( buf ) + iLen = wxVsnprintfA(buf, size, pszFormat, argptr); + else + outOfMemory = TRUE; + + UngetWriteBuf(); + + if ( iLen != -1 ) { // ok, there was enough space break; } // still not enough, double it again - buffer = (char *)realloc(buffer, size *= 2); + size *= 2; } - if ( !buffer ) { + if ( outOfMemory ) { // out of memory return -1; } } - - wxString s(buffer); - *this = s; - - if ( buffer != s_szScratch ) - free(buffer); -#endif +#endif // wxUSE_EXPERIMENTAL_PRINTF/!wxUSE_EXPERIMENTAL_PRINTF return Len(); } @@ -1374,8 +1536,34 @@ int wxString::sprintf(const wxChar *pszFormat, ...) // --------------------------------------------------------------------------- // standard C++ library string functions // --------------------------------------------------------------------------- + #ifdef wxSTD_STRING_COMPATIBILITY +void wxString::resize(size_t nSize, wxChar ch) +{ + size_t len = length(); + + if ( nSize < len ) + { + Truncate(nSize); + } + else if ( nSize > len ) + { + *this += wxString(ch, len - nSize); + } + //else: we have exactly the specified length, nothing to do +} + +void wxString::swap(wxString& str) +{ + // this is slightly less efficient than fiddling with m_pchData directly, + // but it is still quite efficient as we don't copy the string here because + // ref count always stays positive + wxString tmp = str; + str = *this; + *this = str; +} + wxString& wxString::insert(size_t nPos, const wxString& str) { wxASSERT( str.GetStringData()->IsValid() ); @@ -1408,7 +1596,7 @@ size_t wxString::find(const wxString& str, size_t nStart) const #if !defined(__VISUALC__) || defined(__WIN32__) size_t wxString::find(const wxChar* sz, size_t nStart, size_t n) const { - return find(wxString(sz, n == npos ? 0 : n), nStart); + return find(wxString(sz, n), nStart); } #endif // VC++ 1.5 @@ -1473,7 +1661,7 @@ size_t wxString::find_first_of(const wxChar* sz, size_t nStart) const const wxChar *start = c_str() + nStart; const wxChar *firstOf = wxStrpbrk(start, sz); if ( firstOf ) - return firstOf - start; + return firstOf - c_str(); else return npos; } @@ -1569,17 +1757,6 @@ size_t wxString::find_last_not_of(wxChar ch, size_t nStart) const return npos; } -wxString wxString::substr(size_t nStart, size_t nLen) const -{ - // npos means 'take all' - if ( nLen == npos ) - nLen = 0; - - wxASSERT( nStart + nLen <= Len() ); - - return wxString(c_str() + nStart, nLen == npos ? 0 : nLen); -} - wxString& wxString::erase(size_t nStart, size_t nLen) { wxString strTmp(c_str(), nStart); @@ -1595,13 +1772,15 @@ wxString& wxString::erase(size_t nStart, size_t nLen) wxString& wxString::replace(size_t nStart, size_t nLen, const wxChar *sz) { - wxASSERT( nStart + nLen <= wxStrlen(sz) ); + wxASSERT_MSG( nStart + nLen <= Len(), + _T("index out of bounds in wxString::replace") ); wxString strTmp; + strTmp.Alloc(Len()); // micro optimisation to avoid multiple mem allocs + if ( nStart != 0 ) strTmp.append(c_str(), nStart); - strTmp += sz; - strTmp.append(c_str() + nStart + nLen); + strTmp << sz << c_str() + nStart + nLen; *this = strTmp; return *this; @@ -1674,20 +1853,8 @@ void wxArrayString::Copy(const wxArrayString& src) if ( src.m_nCount > ARRAY_DEFAULT_INITIAL_SIZE ) Alloc(src.m_nCount); - // we can't just copy the pointers here because otherwise we would share - // the strings with another array because strings are ref counted -#if 0 - if ( m_nCount != 0 ) - memcpy(m_pItems, src.m_pItems, m_nCount*sizeof(wxChar *)); -#endif // 0 - for ( size_t n = 0; n < src.m_nCount; n++ ) Add(src[n]); - - // if the other array is auto sorted too, we're already sorted, but - // otherwise we should rearrange the items - if ( m_autoSort && !src.m_autoSort ) - Sort(); } // grow the array @@ -1844,7 +2011,7 @@ int wxArrayString::Index(const wxChar *sz, bool bCase, bool bFromEnd) const } // add item at the end -void wxArrayString::Add(const wxString& str) +size_t wxArrayString::Add(const wxString& str) { if ( m_autoSort ) { // insert the string at the correct position to keep the array sorted @@ -1869,6 +2036,8 @@ void wxArrayString::Add(const wxString& str) wxASSERT_MSG( lo == hi, wxT("binary search broken") ); Insert(str, lo); + + return (size_t)lo; } else { wxASSERT( str.GetStringData()->IsValid() ); @@ -1879,7 +2048,9 @@ void wxArrayString::Add(const wxString& str) str.GetStringData()->Lock(); // just append - m_pItems[m_nCount++] = (wxChar *)str.c_str(); + m_pItems[m_nCount] = (wxChar *)str.c_str(); // const_cast + + return m_nCount++; } } @@ -1888,8 +2059,6 @@ void wxArrayString::Insert(const wxString& str, size_t nIndex) { wxASSERT( str.GetStringData()->IsValid() ); - wxCHECK_RET( !m_autoSort, wxT("can't use this method with sorted arrays") ); - wxCHECK_RET( nIndex <= m_nCount, wxT("bad index in wxArrayString::Insert") ); Grow(); @@ -1985,6 +2154,9 @@ void wxArrayString::Sort(CompareFunction compareFunction) DoSort(); + // reset it to NULL so that Sort(bool) will work the next time + gs_compareFunction = NULL; + END_SORT(); } @@ -2009,3 +2181,17 @@ void wxArrayString::DoSort() 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; +} +