X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/0c5d3e1ccd1d27ab0cf913ddbffb1b54b5802f3a..f5ebf253a53742e49bbe7da79966c9a7b60405b1:/src/common/string.cpp diff --git a/src/common/string.cpp b/src/common/string.cpp index baa9fafce3..e232c8a79d 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 @@ -48,17 +46,9 @@ #include #endif -#if wxUSE_WCSRTOMBS - #include // for wcsrtombs(), see comments where it's used -#endif // GNU - -#ifdef WXSTRING_IS_WXOBJECT - IMPLEMENT_DYNAMIC_CLASS(wxString, wxObject) -#endif //WXSTRING_IS_WXOBJECT - #if wxUSE_UNICODE -#undef wxUSE_EXPERIMENTAL_PRINTF -#define wxUSE_EXPERIMENTAL_PRINTF 1 + #undef wxUSE_EXPERIMENTAL_PRINTF + #define wxUSE_EXPERIMENTAL_PRINTF 1 #endif // allocating extra space for each string consumes more memory but speeds up @@ -70,6 +60,12 @@ // static class variables definition // --------------------------------------------------------------------------- +#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 // Visual Age + #ifdef wxSTD_STRING_COMPATIBILITY const size_t wxString::npos = wxSTRING_MAXLEN; #endif // wxSTD_STRING_COMPATIBILITY @@ -85,10 +81,10 @@ static const struct { wxStringData data; wxChar dummy; -} g_strEmpty = { {-1, 0, 0}, _T('\0') }; +} g_strEmpty = { {-1, 0, 0}, wxT('\0') }; // empty C style string: points to 'string data' byte of g_strEmpty -extern const wxChar WXDLLEXPORT *g_szNul = &g_strEmpty.dummy; +extern const wxChar WXDLLEXPORT *wxEmptyString = &g_strEmpty.dummy; // ---------------------------------------------------------------------------- // conditional compilation @@ -104,32 +100,36 @@ extern const wxChar WXDLLEXPORT *g_szNul = &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 +#elif defined(__WXMAC__) + #define wxVsnprintfA vsnprintf #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__) + #elif defined(__GNUG__) #warning "Using sprintf() because no snprintf()-like function defined" - #elif defined(__MWERKS__) - #warning "Using sprintf() because no snprintf()-like function defined" #endif //compiler #endif // no vsnprintf -#ifdef _AIX +#if defined(_AIX) // AIX has vsnprintf, but there's no prototype in the system headers. extern "C" int vsnprintf(char* str, size_t n, const char* format, va_list ap); #endif @@ -145,7 +145,7 @@ extern const wxChar WXDLLEXPORT *g_szNul = &g_strEmpty.dummy; // // ATTN: you can _not_ use both of these in the same program! -istream& operator>>(istream& is, wxString& WXUNUSED(str)) +wxSTD istream& operator>>(wxSTD istream& is, wxString& WXUNUSED(str)) { #if 0 int w = is.width(0); @@ -176,8 +176,54 @@ istream& operator>>(istream& is, wxString& WXUNUSED(str)) return is; } +wxSTD ostream& operator<<(wxSTD 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(), len); + buf[len-1] = wxT('\0'); + } + + 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 // ---------------------------------------------------------------------------- @@ -221,7 +267,10 @@ wxString::wxString(wxChar ch, size_t nLength) Init(); if ( nLength > 0 ) { - AllocBuffer(nLength); + if ( !AllocBuffer(nLength) ) { + wxFAIL_MSG( _T("out of memory in wxString::wxString") ); + return; + } #if wxUSE_UNICODE // memset only works on char @@ -237,16 +286,21 @@ 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); if ( nLength > 0 ) { // trailing '\0' is written in AllocBuffer() - AllocBuffer(nLength); + if ( !AllocBuffer(nLength) ) { + wxFAIL_MSG( _T("out of memory in wxString::InitWith") ); + return; + } memcpy(m_pchData, psz + nPos, nLength*sizeof(wxChar)); } } @@ -276,7 +330,10 @@ wxString::wxString(const char *psz, wxMBConv& conv, size_t nLength) // empty? if ( (nLen != 0) && (nLen != (size_t)-1) ) { - AllocBuffer(nLen); + if ( !AllocBuffer(nLen) ) { + wxFAIL_MSG( _T("out of memory in wxString::wxString") ); + return; + } conv.MB2WC(m_pchData, psz, nLen); } else { @@ -284,37 +341,52 @@ wxString::wxString(const char *psz, wxMBConv& conv, size_t nLength) } } -#else +#else // ANSI #if wxUSE_WCHAR_T // from wide string -wxString::wxString(const wchar_t *pwz) +wxString::wxString(const wchar_t *pwz, wxMBConv& conv, size_t nLength) { // first get necessary size - size_t nLen = pwz ? wxWC2MB((char *) NULL, pwz, 0) : 0; + size_t nLen = 0; + if (pwz) + { + if (nLength == wxSTRING_MAXLEN) + nLen = conv.WC2MB((char *) NULL, pwz, 0); + else + nLen = nLength; + } // empty? if ( (nLen != 0) && (nLen != (size_t)-1) ) { - AllocBuffer(nLen); - wxWC2MB(m_pchData, pwz, nLen); + if ( !AllocBuffer(nLen) ) { + wxFAIL_MSG( _T("out of memory in wxString::wxString") ); + return; + } + conv.WC2MB(m_pchData, pwz, nLen); } else { Init(); } } -#endif +#endif // wxUSE_WCHAR_T -#endif +#endif // Unicode/ANSI // --------------------------------------------------------------------------- // memory allocation // --------------------------------------------------------------------------- // allocates memory needed to store a C string of length nLen -void wxString::AllocBuffer(size_t nLen) +bool 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); @@ -323,50 +395,87 @@ void wxString::AllocBuffer(size_t nLen) // 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] = _T('\0'); + m_pchData[nLen] = wxT('\0'); + return TRUE; } // must be called before changing this string -void wxString::CopyBeforeWrite() +bool wxString::CopyBeforeWrite() { wxStringData* pData = GetStringData(); if ( pData->IsShared() ) { pData->Unlock(); // memory not freed because shared size_t nLen = pData->nDataLength; - AllocBuffer(nLen); + if ( !AllocBuffer(nLen) ) { + // allocation failures are handled by the caller + return FALSE; + } memcpy(m_pchData, pData->data(), nLen*sizeof(wxChar)); } wxASSERT( !GetStringData()->IsShared() ); // we must be the only owner + + return TRUE; } // must be called before replacing contents of this string -void wxString::AllocBeforeWrite(size_t nLen) +bool wxString::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() || (nLen > pData->nAllocLength) ) { + if ( pData->IsShared() || pData->IsEmpty() ) { // can't work with old buffer, get new one pData->Unlock(); - AllocBuffer(nLen); + if ( !AllocBuffer(nLen) ) { + // allocation failures are handled by the caller + return FALSE; + } } 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; + + 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(); + } + + // now we have enough space, just update the string length pData->nDataLength = nLen; } wxASSERT( !GetStringData()->IsShared() ); // we must be the only owner + + return TRUE; } // allocate enough memory for nLen characters -void wxString::Alloc(size_t nLen) +bool wxString::Alloc(size_t nLen) { wxStringData *pData = GetStringData(); if ( pData->nAllocLength <= nLen ) { @@ -374,59 +483,85 @@ void wxString::Alloc(size_t nLen) nLen += EXTRA_ALLOC; wxStringData* pData = (wxStringData*) - malloc(sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar)); + 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] = _T('\0'); + m_pchData[0u] = wxT('\0'); } else if ( pData->IsShared() ) { pData->Unlock(); // memory not freed because shared size_t nOldLen = pData->nDataLength; - AllocBuffer(nLen); + if ( !AllocBuffer(nLen) ) { + // allocation failure handled by caller + return FALSE; + } memcpy(m_pchData, pData->data(), nOldLen*sizeof(wxChar)); } else { nLen += EXTRA_ALLOC; - wxStringData *p = (wxStringData *) + pData = (wxStringData *) realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar)); - if ( p == NULL ) { - // @@@ what to do on memory error? - return; + 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) - p->nAllocLength = nLen; - m_pchData = p->data(); + pData->nAllocLength = nLen; + m_pchData = pData->data(); } } //else: we've already got enough + return TRUE; } // shrink to minimal size (releasing extra memory) -void wxString::Shrink() +bool wxString::Shrink() { wxStringData *pData = GetStringData(); - // this variable is unused in release build, so avoid the compiler warning by - // just not declaring it -#ifdef __WXDEBUG__ - void *p = -#endif - realloc(pData, sizeof(wxStringData) + (pData->nDataLength + 1)*sizeof(wxChar)); + size_t nLen = pData->nDataLength; + void *p = realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar)); - wxASSERT( p != NULL ); // can't free memory? - wxASSERT( p == pData ); // we're decrementing the size - block shouldn't move! + if ( p == NULL) { + wxFAIL_MSG( _T("out of memory reallocating wxString data") ); + // keep previous data since reallocation failed + return FALSE; + } + + if ( p != pData ) + { + // contrary to what one might believe, some realloc() implementation do + // move the memory block even when its size is reduced + pData = (wxStringData *)p; + + m_pchData = pData->data(); + } + + pData->nAllocLength = nLen; + + return TRUE; } // get the pointer to writable buffer of (at least) nLen bytes wxChar *wxString::GetWriteBuf(size_t nLen) { - AllocBeforeWrite(nLen); + if ( !AllocBeforeWrite(nLen) ) { + // allocation failure handled by caller + return NULL; + } wxASSERT( GetStringData()->nRefs == 1 ); GetStringData()->Validate(FALSE); @@ -441,6 +576,12 @@ void wxString::UngetWriteBuf() GetStringData()->Validate(TRUE); } +void wxString::UngetWriteBuf(size_t nLen) +{ + GetStringData()->nDataLength = nLen; + GetStringData()->Validate(TRUE); +} + // --------------------------------------------------------------------------- // data access // --------------------------------------------------------------------------- @@ -452,17 +593,21 @@ void wxString::UngetWriteBuf() // --------------------------------------------------------------------------- // helper function: does real copy -void wxString::AssignCopy(size_t nSrcLen, const wxChar *pszSrcData) +bool wxString::AssignCopy(size_t nSrcLen, const wxChar *pszSrcData) { if ( nSrcLen == 0 ) { Reinit(); } else { - AllocBeforeWrite(nSrcLen); + if ( !AllocBeforeWrite(nSrcLen) ) { + // allocation failure handled by caller + return FALSE; + } memcpy(m_pchData, pszSrcData, nSrcLen*sizeof(wxChar)); GetStringData()->nDataLength = nSrcLen; - m_pchData[nSrcLen] = _T('\0'); + m_pchData[nSrcLen] = wxT('\0'); } + return TRUE; } // assigns one string to another @@ -489,14 +634,19 @@ wxString& wxString::operator=(const wxString& stringSrc) // assigns a single character wxString& wxString::operator=(wxChar ch) { - AssignCopy(1, &ch); + if ( !AssignCopy(1, &ch) ) { + wxFAIL_MSG( _T("out of memory in wxString::operator=(wxChar)") ); + } return *this; } + // assigns C string wxString& wxString::operator=(const wxChar *psz) { - AssignCopy(wxStrlen(psz), psz); + if ( !AssignCopy(wxStrlen(psz), psz) ) { + wxFAIL_MSG( _T("out of memory in wxString::operator=(const wxChar *)") ); + } return *this; } @@ -525,7 +675,7 @@ wxString& wxString::operator=(const wchar_t *pwz) // --------------------------------------------------------------------------- // add something to this string -void wxString::ConcatSelf(int nSrcLen, const wxChar *pszSrcData) +bool wxString::ConcatSelf(int nSrcLen, const wxChar *pszSrcData) { STATISTICS_ADD(SummandLength, nSrcLen); @@ -541,7 +691,10 @@ void wxString::ConcatSelf(int nSrcLen, const wxChar *pszSrcData) // we have to allocate another buffer wxStringData* pOldData = GetStringData(); - AllocBuffer(nNewLen); + if ( !AllocBuffer(nNewLen) ) { + // allocation failure handled by caller + return FALSE; + } memcpy(m_pchData, pOldData->data(), nLen*sizeof(wxChar)); pOldData->Unlock(); } @@ -549,7 +702,10 @@ void wxString::ConcatSelf(int nSrcLen, const wxChar *pszSrcData) STATISTICS_ADD(ConcatHit, 0); // we have to grow the buffer - Alloc(nNewLen); + if ( !Alloc(nNewLen) ) { + // allocation failure handled by caller + return FALSE; + } } else { STATISTICS_ADD(ConcatHit, 1); @@ -563,10 +719,11 @@ void wxString::ConcatSelf(int nSrcLen, const wxChar *pszSrcData) // fast concatenation - all is done in our buffer memcpy(m_pchData + nLen, pszSrcData, nSrcLen*sizeof(wxChar)); - m_pchData[nNewLen] = _T('\0'); // put terminating '\0' + m_pchData[nNewLen] = wxT('\0'); // put terminating '\0' GetStringData()->nDataLength = nNewLen; // and fix the length } //else: the string to append was empty + return TRUE; } /* @@ -576,57 +733,61 @@ void wxString::ConcatSelf(int nSrcLen, const wxChar *pszSrcData) * C str + string and string + C str */ -wxString operator+(const wxString& string1, const wxString& string2) +wxString operator+(const wxString& str1, const wxString& str2) { - wxASSERT( string1.GetStringData()->IsValid() ); - wxASSERT( string2.GetStringData()->IsValid() ); + wxASSERT( str1.GetStringData()->IsValid() ); + wxASSERT( str2.GetStringData()->IsValid() ); - wxString s = string1; - s += string2; + wxString s = str1; + s += str2; return s; } -wxString operator+(const wxString& string, wxChar ch) +wxString operator+(const wxString& str, wxChar ch) { - wxASSERT( string.GetStringData()->IsValid() ); + wxASSERT( str.GetStringData()->IsValid() ); - wxString s = string; + wxString s = str; s += ch; return s; } -wxString operator+(wxChar ch, const wxString& string) +wxString operator+(wxChar ch, const wxString& str) { - wxASSERT( string.GetStringData()->IsValid() ); + wxASSERT( str.GetStringData()->IsValid() ); wxString s = ch; - s += string; + s += str; return s; } -wxString operator+(const wxString& string, const wxChar *psz) +wxString operator+(const wxString& str, const wxChar *psz) { - wxASSERT( string.GetStringData()->IsValid() ); + wxASSERT( str.GetStringData()->IsValid() ); wxString s; - s.Alloc(wxStrlen(psz) + string.Len()); - s = string; + if ( !s.Alloc(wxStrlen(psz) + str.Len()) ) { + wxFAIL_MSG( _T("out of memory in wxString::operator+") ); + } + s = str; s += psz; return s; } -wxString operator+(const wxChar *psz, const wxString& string) +wxString operator+(const wxChar *psz, const wxString& str) { - wxASSERT( string.GetStringData()->IsValid() ); + wxASSERT( str.GetStringData()->IsValid() ); wxString s; - s.Alloc(wxStrlen(psz) + string.Len()); + if ( !s.Alloc(wxStrlen(psz) + str.Len()) ) { + wxFAIL_MSG( _T("out of memory in wxString::operator+") ); + } s = psz; - s += string; + s += str; return s; } @@ -640,15 +801,19 @@ wxString operator+(const wxChar *psz, const wxString& string) // --------------------------------------------------------------------------- // helper function: clone the data attached to this string -void wxString::AllocCopy(wxString& dest, int nCopyLen, int nCopyIndex) const +bool wxString::AllocCopy(wxString& dest, int nCopyLen, int nCopyIndex) const { if ( nCopyLen == 0 ) { dest.Init(); } else { - dest.AllocBuffer(nCopyLen); + if ( !dest.AllocBuffer(nCopyLen) ) { + // allocation failure handled by caller + return FALSE; + } memcpy(dest.m_pchData, m_pchData + nCopyIndex, nCopyLen*sizeof(wxChar)); } + return TRUE; } // extract string of length nCount starting at nFirst @@ -676,11 +841,42 @@ wxString wxString::Mid(size_t nFirst, size_t nCount) const } wxString dest; - AllocCopy(dest, nCount, nFirst); + if ( !AllocCopy(dest, nCount, nFirst) ) { + wxFAIL_MSG( _T("out of memory in wxString::Mid") ); + } 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 { @@ -688,7 +884,9 @@ wxString wxString::Right(size_t nCount) const nCount = GetStringData()->nDataLength; wxString dest; - AllocCopy(dest, nCount, GetStringData()->nDataLength - nCount); + if ( !AllocCopy(dest, nCount, GetStringData()->nDataLength - nCount) ) { + wxFAIL_MSG( _T("out of memory in wxString::Right") ); + } return dest; } @@ -713,7 +911,9 @@ wxString wxString::Left(size_t nCount) const nCount = GetStringData()->nDataLength; wxString dest; - AllocCopy(dest, nCount, 0); + if ( !AllocCopy(dest, nCount, 0) ) { + wxFAIL_MSG( _T("out of memory in wxString::Left") ); + } return dest; } @@ -722,7 +922,7 @@ wxString wxString::Left(size_t nCount) const wxString wxString::BeforeFirst(wxChar ch) const { wxString str; - for ( const wxChar *pc = m_pchData; *pc != _T('\0') && *pc != ch; pc++ ) + for ( const wxChar *pc = m_pchData; *pc != wxT('\0') && *pc != ch; pc++ ) str += *pc; return str; @@ -762,7 +962,7 @@ size_t wxString::Replace(const wxChar *szOld, const wxChar *szNew, bool bReplace wxString strTemp; const wxChar *pCurrent = m_pchData; const wxChar *pSubstr; - while ( *pCurrent != _T('\0') ) { + while ( *pCurrent != wxT('\0') ) { pSubstr = wxStrstr(pCurrent, szOld); if ( pSubstr == NULL ) { // strTemp is unused if no replacements were made, so avoid the copy @@ -774,7 +974,10 @@ size_t wxString::Replace(const wxChar *szOld, const wxChar *szNew, bool bReplace } else { // take chars before match - strTemp.ConcatSelf(pSubstr - pCurrent, pCurrent); + if ( !strTemp.ConcatSelf(pSubstr - pCurrent, pCurrent) ) { + wxFAIL_MSG( _T("out of memory in wxString::Replace") ); + return 0; + } strTemp += szNew; pCurrent = pSubstr + uiOldLen; // restart after match @@ -817,6 +1020,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++; @@ -838,8 +1043,11 @@ wxString wxString::Strip(stripType w) const wxString& wxString::MakeUpper() { - CopyBeforeWrite(); - + if ( !CopyBeforeWrite() ) { + wxFAIL_MSG( _T("out of memory in wxString::MakeUpper") ); + return *this; + } + for ( wxChar *p = m_pchData; *p; p++ ) *p = (wxChar)wxToupper(*p); @@ -848,7 +1056,10 @@ wxString& wxString::MakeUpper() wxString& wxString::MakeLower() { - CopyBeforeWrite(); + if ( !CopyBeforeWrite() ) { + wxFAIL_MSG( _T("out of memory in wxString::MakeLower") ); + return *this; + } for ( wxChar *p = m_pchData; *p; p++ ) *p = (wxChar)wxTolower(*p); @@ -860,36 +1071,47 @@ wxString& wxString::MakeLower() // 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 +// 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 ( !IsEmpty() && ( - (bFromRight && wxIsspace(GetChar(Len() - 1))) || - (!bFromRight && wxIsspace(GetChar(0u))) + (bFromRight && wxSafeIsspace(GetChar(Len() - 1))) || + (!bFromRight && wxSafeIsspace(GetChar(0u))) ) ) { // ok, there is at least one space to trim - CopyBeforeWrite(); + if ( !CopyBeforeWrite() ) { + wxFAIL_MSG( _T("out of memory in wxString::Trim") ); + return *this; + } if ( bFromRight ) { // find last non-space character wxChar *psz = m_pchData + GetStringData()->nDataLength - 1; - while ( wxIsspace(*psz) && (psz >= m_pchData) ) + while ( wxSafeIsspace(*psz) && (psz >= m_pchData) ) psz--; // truncate at trailing space start - *++psz = _T('\0'); + *++psz = wxT('\0'); GetStringData()->nDataLength = psz - m_pchData; } else { // find first non-space character const wxChar *psz = m_pchData; - while ( wxIsspace(*psz) ) + while ( wxSafeIsspace(*psz) ) psz++; // fix up data and length @@ -922,9 +1144,12 @@ wxString& wxString::Pad(size_t nCount, wxChar chPad, bool bFromRight) wxString& wxString::Truncate(size_t uiLen) { if ( uiLen < Len() ) { - CopyBeforeWrite(); + if ( !CopyBeforeWrite() ) { + wxFAIL_MSG( _T("out of memory in wxString::Truncate") ); + return *this; + } - *(m_pchData + uiLen) = _T('\0'); + *(m_pchData + uiLen) = wxT('\0'); GetStringData()->nDataLength = uiLen; } //else: nothing to do, string is already short enough @@ -952,36 +1177,77 @@ 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, int base) const { - wxString res; - res.Printf(_T("%d"), i); + wxCHECK_MSG( val, FALSE, _T("NULL pointer in wxString::ToLong") ); + wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); - return (*this) << res; + const wxChar *start = c_str(); + wxChar *end; + *val = wxStrtol(start, &end, base); + + // 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, int base) const { - wxString res; - res.Printf(_T("%f"), f); + wxCHECK_MSG( val, FALSE, _T("NULL pointer in wxString::ToULong") ); + wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); + + const wxChar *start = c_str(); + wxChar *end; + *val = wxStrtoul(start, &end, base); - 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(_T("%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.PrintfV(pszFormat, argptr); + return s; +} + int wxString::Printf(const wxChar *pszFormat, ...) { va_list argptr; @@ -996,22 +1262,15 @@ 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++) - if (pszFormat[n] == _T('%')) { + if (pszFormat[n] == wxT('%')) { static char s_szFlags[256] = "%"; size_t flagofs = 1; bool adj_left = FALSE, in_prec = FALSE, @@ -1021,55 +1280,55 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr) do { #define CHECK_PREC if (in_prec && !prec_dot) { s_szFlags[flagofs++] = '.'; prec_dot = TRUE; } switch (pszFormat[++n]) { - case _T('\0'): + case wxT('\0'): done = TRUE; break; - case _T('%'): - *this += _T('%'); + case wxT('%'): + *this += wxT('%'); done = TRUE; break; - case _T('#'): - case _T('0'): - case _T(' '): - case _T('+'): - case _T('\''): + case wxT('#'): + case wxT('0'): + case wxT(' '): + case wxT('+'): + case wxT('\''): CHECK_PREC s_szFlags[flagofs++] = pszFormat[n]; break; - case _T('-'): + case wxT('-'): CHECK_PREC adj_left = TRUE; s_szFlags[flagofs++] = pszFormat[n]; break; - case _T('.'): + case wxT('.'): CHECK_PREC in_prec = TRUE; prec_dot = FALSE; max_width = 0; // dot will be auto-added to s_szFlags if non-negative number follows break; - case _T('h'): + case wxT('h'): ilen = -1; CHECK_PREC s_szFlags[flagofs++] = pszFormat[n]; break; - case _T('l'): + case wxT('l'): ilen = 1; CHECK_PREC s_szFlags[flagofs++] = pszFormat[n]; break; - case _T('q'): - case _T('L'): + case wxT('q'): + case wxT('L'): ilen = 2; CHECK_PREC s_szFlags[flagofs++] = pszFormat[n]; break; - case _T('Z'): + case wxT('Z'): ilen = 3; CHECK_PREC s_szFlags[flagofs++] = pszFormat[n]; break; - case _T('*'): + case wxT('*'): { int len = va_arg(argptr, int); if (in_prec) { @@ -1087,15 +1346,15 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr) flagofs += ::sprintf(s_szFlags+flagofs,"%d",len); } break; - case _T('1'): case _T('2'): case _T('3'): - case _T('4'): case _T('5'): case _T('6'): - case _T('7'): case _T('8'): case _T('9'): + case wxT('1'): case wxT('2'): case wxT('3'): + case wxT('4'): case wxT('5'): case wxT('6'): + case wxT('7'): case wxT('8'): case wxT('9'): { int len = 0; CHECK_PREC - while ((pszFormat[n]>=_T('0')) && (pszFormat[n]<=_T('9'))) { + while ((pszFormat[n]>=wxT('0')) && (pszFormat[n]<=wxT('9'))) { s_szFlags[flagofs++] = pszFormat[n]; - len = len*10 + (pszFormat[n] - _T('0')); + len = len*10 + (pszFormat[n] - wxT('0')); n++; } if (in_prec) max_width = len; @@ -1103,73 +1362,73 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr) n--; // the main loop pre-increments n again } break; - case _T('d'): - case _T('i'): - case _T('o'): - case _T('u'): - case _T('x'): - case _T('X'): + case wxT('d'): + case wxT('i'): + case wxT('o'): + case wxT('u'): + case wxT('x'): + case wxT('X'): CHECK_PREC s_szFlags[flagofs++] = pszFormat[n]; 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 _T('e'): - case _T('E'): - case _T('f'): - case _T('g'): - case _T('G'): + case wxT('e'): + case wxT('E'): + case wxT('f'): + case wxT('g'): + case wxT('G'): CHECK_PREC s_szFlags[flagofs++] = pszFormat[n]; 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 _T('p'): + case wxT('p'): { void *val = va_arg(argptr, void *); 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; - case _T('c'): + case wxT('c'): { wxChar val = va_arg(argptr, int); // we don't need to honor padding here, do we? @@ -1177,7 +1436,7 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr) done = TRUE; } break; - case _T('s'): + case wxT('s'): if (ilen == -1) { // wx extension: we'll let %hs mean non-Unicode strings char *val = va_arg(argptr, char *); @@ -1188,26 +1447,26 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr) size_t len = wxSTRING_MAXLEN; if (val) { for (len = 0; val[len] && (len len ) + { + *this += wxString(ch, nSize - len); + } + //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 = tmp; +} + wxString& wxString::insert(size_t nPos, const wxString& str) { wxASSERT( str.GetStringData()->IsValid() ); @@ -1408,7 +1773,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 @@ -1427,7 +1792,7 @@ size_t wxString::find(wxChar ch, size_t nStart) const size_t wxString::rfind(const wxString& str, size_t nStart) const { wxASSERT( str.GetStringData()->IsValid() ); - wxASSERT( nStart <= Len() ); + wxASSERT( nStart == npos || nStart <= Len() ); // TODO could be made much quicker than that const wxChar *p = c_str() + (nStart == npos ? Len() : nStart); @@ -1444,7 +1809,7 @@ size_t wxString::rfind(const wxString& str, size_t nStart) const #if !defined(__VISUALC__) || defined(__WIN32__) size_t wxString::rfind(const wxChar* sz, size_t nStart, size_t n) const { - return rfind(wxString(sz, n == npos ? 0 : n), nStart); + return rfind(wxString(sz, n == npos ? wxSTRING_MAXLEN : n), nStart); } size_t wxString::rfind(wxChar ch, size_t nStart) const @@ -1473,7 +1838,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 +1934,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 +1949,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; @@ -1630,28 +1986,28 @@ wxString& wxString::replace(size_t nStart, size_t nLen, // ArrayString // ============================================================================ -// size increment = max(50% of current size, ARRAY_MAXSIZE_INCREMENT) +// 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) +#define ARRAY_DEFAULT_INITIAL_SIZE (16) #endif #define STRING(p) ((wxString *)(&(p))) // ctor -wxArrayString::wxArrayString() +void wxArrayString::Init(bool autoSort) { m_nSize = m_nCount = 0; m_pItems = (wxChar **) NULL; + m_autoSort = autoSort; } // copy ctor wxArrayString::wxArrayString(const wxArrayString& src) { - m_nSize = - m_nCount = 0; - m_pItems = (wxChar **) NULL; + Init(src.m_autoSort); *this = src; } @@ -1662,26 +2018,34 @@ 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); - // we can't just copy the pointers here because otherwise we would share - // the strings with another array for ( size_t n = 0; n < src.m_nCount; n++ ) Add(src[n]); - - if ( m_nCount != 0 ) - memcpy(m_pItems, src.m_pItems, m_nCount*sizeof(wxChar *)); - - return *this; } // grow the array -void wxArrayString::Grow() +void wxArrayString::Grow(size_t nIncrement) { // only do it if no more place - if( m_nCount == m_nSize ) { - if( m_nSize == 0 ) { + if ( m_nCount == m_nSize ) { + // 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; m_pItems = new wxChar *[m_nSize]; @@ -1689,18 +2053,13 @@ void wxArrayString::Grow() else { // otherwise when it's called for the first time, nIncrement would be 0 // and the array would never be expanded -#if defined(__VISAGECPP__) - int array_size = ARRAY_DEFAULT_INITIAL_SIZE; - wxASSERT( array_size != 0 ); -#else - wxASSERT( ARRAY_DEFAULT_INITIAL_SIZE != 0 ); -#endif - // add 50% but not too much - size_t nIncrement = m_nSize < ARRAY_DEFAULT_INITIAL_SIZE + size_t ndefIncrement = m_nSize < ARRAY_DEFAULT_INITIAL_SIZE ? ARRAY_DEFAULT_INITIAL_SIZE : m_nSize >> 1; - if ( nIncrement > ARRAY_MAXSIZE_INCREMENT ) - nIncrement = ARRAY_MAXSIZE_INCREMENT; + if ( ndefIncrement > ARRAY_MAXSIZE_INCREMENT ) + ndefIncrement = ARRAY_MAXSIZE_INCREMENT; + if ( nIncrement < ndefIncrement ) + nIncrement = ndefIncrement; m_nSize += nIncrement; wxChar **pNew = new wxChar *[m_nSize]; @@ -1752,8 +2111,6 @@ wxArrayString::~wxArrayString() // pre-allocates memory (frees the previous data!) void wxArrayString::Alloc(size_t nSize) { - wxASSERT( nSize > 0 ); - // only if old buffer was not big enough if ( nSize > m_nSize ) { Free(); @@ -1780,23 +2137,64 @@ void wxArrayString::Shrink() } } +// 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; +} + // searches the array for an item (forward or backwards) int wxArrayString::Index(const wxChar *sz, bool bCase, bool bFromEnd) const { - 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 ); + 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 { - for( size_t ui = 0; ui < m_nCount; ui++ ) { - if( STRING(m_pItems[ui])->IsSameAs(sz, bCase) ) - return ui; + // 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; + } } } @@ -1804,46 +2202,99 @@ int wxArrayString::Index(const wxChar *sz, bool bCase, bool bFromEnd) const } // add item at the end -void wxArrayString::Add(const wxString& str) -{ - wxASSERT( str.GetStringData()->IsValid() ); +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 = wxStrcmp(str, 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") ); - Grow(); + Insert(str, lo, nInsert); - // the string data must not be deleted! - str.GetStringData()->Lock(); - m_pItems[m_nCount++] = (wxChar *)str.c_str(); + 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) +void wxArrayString::Insert(const wxString& str, size_t nIndex, size_t nInsert) { wxASSERT( str.GetStringData()->IsValid() ); - wxCHECK_RET( nIndex <= m_nCount, _("bad index in wxArrayString::Insert") ); + 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(); + Grow(nInsert); - memmove(&m_pItems[nIndex + 1], &m_pItems[nIndex], + memmove(&m_pItems[nIndex + nInsert], &m_pItems[nIndex], (m_nCount - nIndex)*sizeof(wxChar *)); - str.GetStringData()->Lock(); - m_pItems[nIndex] = (wxChar *)str.c_str(); + for (size_t i = 0; i < nInsert; i++) + { + str.GetStringData()->Lock(); + m_pItems[nIndex + i] = (wxChar *)str.c_str(); + } + m_nCount += nInsert; +} + +// expand the array +void wxArrayString::SetCount(size_t count) +{ + Alloc(count); - m_nCount++; + wxString s; + while ( m_nCount < count ) + m_pItems[m_nCount++] = (wxChar *)s.c_str(); } // removes item from array (by index) -void wxArrayString::Remove(size_t nIndex) +void wxArrayString::Remove(size_t nIndex, size_t nRemove) { - wxCHECK_RET( nIndex <= m_nCount, _("bad index in wxArrayString::Remove") ); + 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 - Item(nIndex).GetStringData()->Unlock(); + for (size_t i = 0; i < nRemove; i++) + Item(nIndex + i).GetStringData()->Unlock(); - memmove(&m_pItems[nIndex], &m_pItems[nIndex + 1], - (m_nCount - nIndex - 1)*sizeof(wxChar *)); - m_nCount--; + memmove(&m_pItems[nIndex], &m_pItems[nIndex + nRemove], + (m_nCount - nIndex - nRemove)*sizeof(wxChar *)); + m_nCount -= nRemove; } // removes item from array (by value) @@ -1852,7 +2303,7 @@ void wxArrayString::Remove(const wxChar *sz) int iIndex = Index(sz); wxCHECK_RET( iIndex != wxNOT_FOUND, - _("removing inexistent element in wxArrayString::Remove") ); + wxT("removing inexistent element in wxArrayString::Remove") ); Remove(iIndex); } @@ -1889,7 +2340,8 @@ static wxArrayString::CompareFunction gs_compareFunction = NULL; static bool gs_sortAscending = TRUE; // function which is called by quick sort -static int LINKAGEMODE wxStringCompareFunction(const void *first, const void *second) +extern "C" int LINKAGEMODE +wxStringCompareFunction(const void *first, const void *second) { wxString *strFirst = (wxString *)first; wxString *strSecond = (wxString *)second; @@ -1915,6 +2367,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(); } @@ -1932,8 +2387,24 @@ void wxArrayString::Sort(bool reverseOrder) 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; +} +