/////////////////////////////////////////////////////////////////////////////
-// Name: src/common/string.cpp
+// Name: src/common/stringimpl.cpp
// Purpose: wxString class
// Author: Vadim Zeitlin, Ryan Norton
// Modified by:
#ifndef WX_PRECOMP
#include "wx/stringimpl.h"
+ #include "wx/wxcrt.h"
#endif
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
-#ifdef __SALFORDC__
- #include <clib.h>
-#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 wxStringMemcpy memcpy
#define wxStringMemcmp memcmp
#define wxStringMemchr memchr
- #define wxStringStrlen strlen
#else
#define wxStringMemcpy wxTmemcpy
#define wxStringMemcmp wxTmemcmp
#define wxStringMemchr wxTmemchr
- #define wxStringStrlen wxStrlen
#endif
#if wxUSE_STL_BASED_WXSTRING
-extern const wxChar WXDLLIMPEXP_BASE *wxEmptyString = _T("");
+// FIXME-UTF8: get rid of this, have only one wxEmptyString
+#if wxUSE_UNICODE_UTF8
+const wxStringCharType WXDLLIMPEXP_BASE *wxEmptyStringImpl = "";
+#endif
+const wxChar WXDLLIMPEXP_BASE *wxEmptyString = wxT("");
#else
static const struct
{
wxStringData data;
- wxChar dummy;
+ wxStringCharType 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;
+#if wxUSE_UNICODE_UTF8
+// FIXME-UTF8: get rid of this, have only one wxEmptyString
+const wxStringCharType WXDLLIMPEXP_BASE *wxEmptyStringImpl = &g_strEmpty.dummy;
+const wxChar WXDLLIMPEXP_BASE *wxEmptyString = wxT("");
+#else
+const wxStringCharType WXDLLIMPEXP_BASE *wxEmptyString = &g_strEmpty.dummy;
+#endif
#endif
// ----------------------------------------------------------------------------
// this small class is used to gather statistics for performance tuning
+
+// uncomment this to enable gathering of some statistics about wxString
+// efficiency
//#define WXSTRING_STATISTICS
+
#ifdef WXSTRING_STATISTICS
class Averager
{
public:
- Averager(const wxChar *sz) { m_sz = sz; m_nTotal = m_nCount = 0; }
+ Averager(const wxStringCharType *sz) { m_sz = sz; m_nTotal = m_nCount = 0; }
~Averager()
- { wxPrintf("wxString: average %s = %f\n", m_sz, ((float)m_nTotal)/m_nCount); }
+ {
+ wxPrintf("wxString %s: total = %lu, average = %f\n",
+ m_sz, m_nTotal, ((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;
+ unsigned long m_nCount, m_nTotal;
+ const wxStringCharType *m_sz;
} g_averageLength("allocation size"),
g_averageSummandLength("summand length"),
g_averageConcatHit("hit probability in concat"),
// ===========================================================================
// takes nLength elements of psz starting at nPos
-void wxStringImpl::InitWith(const wxChar *psz, size_t nPos, size_t nLength)
+void wxStringImpl::InitWith(const wxStringCharType *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") );
+ wxASSERT_MSG( nPos <= wxStrlen(psz), wxT("index out of bounds") );
nLength = wxStrlen(psz + nPos);
}
if ( nLength > 0 ) {
// trailing '\0' is written in AllocBuffer()
if ( !AllocBuffer(nLength) ) {
- wxFAIL_MSG( _T("out of memory in wxStringImpl::InitWith") );
+ wxFAIL_MSG( wxT("out of memory in wxStringImpl::InitWith") );
return;
}
wxStringMemcpy(m_pchData, psz + nPos, nLength);
{
if ( last >= first )
{
- InitWith(first, 0, last - first);
+ InitWith(first.GetPtr(), 0, last - first);
}
else
{
- wxFAIL_MSG( _T("first must be before last") );
+ wxFAIL_MSG( wxT("first must be before last") );
Init();
}
}
wxASSERT( nLen > 0 );
// make sure that we don't overflow
- wxASSERT( nLen < (INT_MAX / sizeof(wxChar)) -
- (sizeof(wxStringData) + EXTRA_ALLOC + 1) );
+ wxCHECK( nLen < (INT_MAX / sizeof(wxStringCharType)) -
+ (sizeof(wxStringData) + EXTRA_ALLOC + 1), false );
STATISTICS_ADD(Length, nLen);
// 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));
+ malloc(sizeof(wxStringData) + (nLen + EXTRA_ALLOC + 1)*sizeof(wxStringCharType));
if ( pData == NULL ) {
// allocation failures are handled by the caller
nLen += EXTRA_ALLOC;
pData = (wxStringData*)
- realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar));
+ realloc(pData,
+ sizeof(wxStringData) + (nLen + 1)*sizeof(wxStringCharType));
if ( pData == NULL ) {
// allocation failures are handled by the caller
size_type len = length();
if ( !Alloc(len + n) || !CopyBeforeWrite() ) {
- wxFAIL_MSG( _T("out of memory in wxStringImpl::append") );
+ wxFAIL_MSG( wxT("out of memory in wxStringImpl::append") );
+ return *this;
}
GetStringData()->nDataLength = len + n;
m_pchData[len + n] = '\0';
wxStringData *pData = GetStringData();
if ( pData->nAllocLength <= nLen ) {
if ( pData->IsEmpty() ) {
+ STATISTICS_ADD(Length, nLen);
+
nLen += EXTRA_ALLOC;
pData = (wxStringData *)
- malloc(sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar));
+ malloc(sizeof(wxStringData) + (nLen + 1)*sizeof(wxStringCharType));
if ( pData == NULL ) {
// allocation failure handled by caller
return false;
}
// +1 to copy the terminator, too
- memcpy(m_pchData, pData->data(), (nOldLen+1)*sizeof(wxChar));
+ memcpy(m_pchData, pData->data(), (nOldLen+1)*sizeof(wxStringCharType));
GetStringData()->nDataLength = nOldLen;
}
else {
nLen += EXTRA_ALLOC;
pData = (wxStringData *)
- realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar));
+ realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(wxStringCharType));
if ( pData == NULL ) {
// allocation failure handled by caller
wxStringImpl::iterator wxStringImpl::begin()
{
- if (length() > 0)
+ if ( !empty() )
CopyBeforeWrite();
return m_pchData;
}
wxStringImpl::iterator wxStringImpl::end()
{
- if (length() > 0)
+ if ( !empty() )
CopyBeforeWrite();
return m_pchData + length();
}
return *this;
}
-wxStringImpl& wxStringImpl::insert(size_t nPos, const wxChar *sz, size_t n)
+wxStringImpl& wxStringImpl::insert(size_t nPos,
+ const wxStringCharType *sz, size_t n)
{
wxASSERT( nPos <= length() );
if ( n == 0 ) return *this;
if ( !Alloc(length() + n) || !CopyBeforeWrite() ) {
- wxFAIL_MSG( _T("out of memory in wxStringImpl::insert") );
+ wxFAIL_MSG( wxT("out of memory in wxStringImpl::insert") );
+ return *this;
}
memmove(m_pchData + nPos + n, m_pchData + nPos,
- (length() - nPos) * sizeof(wxChar));
- memcpy(m_pchData + nPos, sz, n * sizeof(wxChar));
+ (length() - nPos) * sizeof(wxStringCharType));
+ memcpy(m_pchData + nPos, sz, n * sizeof(wxStringCharType));
GetStringData()->nDataLength = length() + n;
m_pchData[length()] = '\0';
return p - c_str() + nLenOther <= nLen ? p - c_str() : npos;
}
-size_t wxStringImpl::find(const wxChar* sz, size_t nStart, size_t n) const
+size_t wxStringImpl::find(const wxStringCharType* sz,
+ size_t nStart, size_t n) const
{
return find(wxStringImpl(sz, n), nStart);
}
if ( length() >= str.length() )
{
// avoids a corner case later
- if ( length() == 0 && str.length() == 0 )
+ if ( empty() && str.empty() )
return 0;
// "top" is the point where search starts from
return npos;
}
-size_t wxStringImpl::rfind(const wxChar* sz, size_t nStart, size_t n) const
+size_t wxStringImpl::rfind(const wxStringCharType* sz,
+ size_t nStart, size_t n) const
{
return rfind(wxStringImpl(sz, n), nStart);
}
}
wxStringImpl& wxStringImpl::replace(size_t nStart, size_t nLen,
- const wxChar *sz)
-{
- wxASSERT_MSG( nStart <= length(),
- _T("index out of bounds in wxStringImpl::replace") );
- size_t strLen = length() - nStart;
- nLen = strLen < nLen ? strLen : nLen;
-
- wxStringImpl 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;
-}
-
-wxStringImpl& wxStringImpl::replace(size_t nStart, size_t nLen,
- size_t nCount, wxStringCharType ch)
+ const wxStringCharType *sz, size_t nCount)
{
- return replace(nStart, nLen, wxStringImpl(nCount, ch).c_str());
-}
+ // check and adjust parameters
+ const size_t lenOld = length();
-wxStringImpl& wxStringImpl::replace(size_t nStart, size_t nLen,
- const wxStringImpl& str,
- size_t nStart2, size_t nLen2)
-{
- return replace(nStart, nLen, str.substr(nStart2, nLen2));
-}
+ wxASSERT_MSG( nStart <= lenOld,
+ wxT("index out of bounds in wxStringImpl::replace") );
+ size_t nEnd = nStart + nLen;
+ if ( nLen > lenOld - nStart )
+ {
+ // nLen may be out of range, as it can be npos, just clump it down
+ nLen = lenOld - nStart;
+ nEnd = lenOld;
+ }
-wxStringImpl& wxStringImpl::replace(size_t nStart, size_t nLen,
- const wxChar* sz, size_t nCount)
-{
- return replace(nStart, nLen, wxStringImpl(sz, nCount).c_str());
+ if ( nCount == npos )
+ nCount = wxStrlen(sz);
+
+ // build the new string from 3 pieces: part of this string before nStart,
+ // the new substring and the part of this string after nStart+nLen
+ wxStringImpl tmp;
+ const size_t lenNew = lenOld + nCount - nLen;
+ if ( lenNew )
+ {
+ tmp.AllocBuffer(lenOld + nCount - nLen);
+
+ wxStringCharType *dst = tmp.m_pchData;
+ memcpy(dst, m_pchData, nStart*sizeof(wxStringCharType));
+ dst += nStart;
+
+ memcpy(dst, sz, nCount*sizeof(wxStringCharType));
+ dst += nCount;
+
+ memcpy(dst, m_pchData + nEnd, (lenOld - nEnd)*sizeof(wxStringCharType));
+ }
+
+ // and replace this string contents with the new one
+ swap(tmp);
+ return *this;
}
wxStringImpl wxStringImpl::substr(size_t nStart, size_t nLen) const
// assigns a single character
wxStringImpl& wxStringImpl::operator=(wxStringCharType ch)
{
- wxChar c(ch);
+ wxStringCharType c(ch);
if ( !AssignCopy(1, &c) ) {
- wxFAIL_MSG( _T("out of memory in wxStringImpl::operator=(wxChar)") );
+ wxFAIL_MSG( wxT("out of memory in wxStringImpl::operator=(wxStringCharType)") );
}
return *this;
}
// assigns C string
-wxStringImpl& wxStringImpl::operator=(const wxChar *psz)
+wxStringImpl& wxStringImpl::operator=(const wxStringCharType *psz)
{
if ( !AssignCopy(wxStrlen(psz), psz) ) {
- wxFAIL_MSG( _T("out of memory in wxStringImpl::operator=(const wxChar *)") );
+ wxFAIL_MSG( wxT("out of memory in wxStringImpl::operator=(const wxStringCharType *)") );
}
return *this;
}
// helper function: does real copy
-bool wxStringImpl::AssignCopy(size_t nSrcLen, const wxChar *pszSrcData)
+bool wxStringImpl::AssignCopy(size_t nSrcLen,
+ const wxStringCharType *pszSrcData)
{
if ( nSrcLen == 0 ) {
Reinit();
// allocation failure handled by caller
return false;
}
- memcpy(m_pchData, pszSrcData, nSrcLen*sizeof(wxChar));
+
+ // 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(wxStringCharType));
+
GetStringData()->nDataLength = nSrcLen;
m_pchData[nSrcLen] = wxT('\0');
}
// ---------------------------------------------------------------------------
// add something to this string
-bool wxStringImpl::ConcatSelf(size_t nSrcLen, const wxChar *pszSrcData,
+bool wxStringImpl::ConcatSelf(size_t nSrcLen,
+ const wxStringCharType *pszSrcData,
size_t nMaxLen)
{
STATISTICS_ADD(SummandLength, nSrcLen);
if ( nSrcLen > 0 ) {
wxStringData *pData = GetStringData();
size_t nLen = pData->nDataLength;
+
+ // 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 )
+ {
+ wxStringImpl tmp(pszSrcData, nSrcLen);
+ return ConcatSelf(nSrcLen, tmp.m_pchData, nSrcLen);
+ }
+
size_t nNewLen = nLen + nSrcLen;
// alloc new buffer if current is too small
// allocation failure handled by caller
return false;
}
- memcpy(m_pchData, pOldData->data(), nLen*sizeof(wxChar));
+ memcpy(m_pchData, pOldData->data(), nLen*sizeof(wxStringCharType));
pOldData->Unlock();
}
else if ( nNewLen > pData->nAllocLength ) {
wxASSERT( nNewLen <= GetStringData()->nAllocLength );
// fast concatenation - all is done in our buffer
- memcpy(m_pchData + nLen, pszSrcData, nSrcLen*sizeof(wxChar));
+ memcpy(m_pchData + nLen, pszSrcData, nSrcLen*sizeof(wxStringCharType));
m_pchData[nNewLen] = wxT('\0'); // put terminating '\0'
GetStringData()->nDataLength = nNewLen; // and fix the length
return true;
}
-#if !wxUSE_UNICODE_UTF8
// get the pointer to writable buffer of (at least) nLen bytes
-wxChar *wxStringImpl::DoGetWriteBuf(size_t nLen)
+wxStringCharType *wxStringImpl::DoGetWriteBuf(size_t nLen)
{
if ( !AllocBeforeWrite(nLen) ) {
// allocation failure handled by caller
{
wxStringData * const pData = GetStringData();
- wxASSERT_MSG( nLen < pData->nAllocLength, _T("buffer overrun") );
+ wxASSERT_MSG( nLen < pData->nAllocLength, wxT("buffer overrun") );
// the strings we store are always NUL-terminated
- pData->data()[nLen] = _T('\0');
+ pData->data()[nLen] = wxT('\0');
pData->nDataLength = nLen;
pData->Validate(true);
}
-#endif // !wxUSE_UNICODE_UTF8
#endif // !wxUSE_STL_BASED_WXSTRING