1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/stringimpl.cpp
3 // Purpose: wxString class
4 // Author: Vadim Zeitlin, Ryan Norton
7 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
8 // (c) 2004 Ryan Norton <wxprojects@comcast.net>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
14 * 1) all empty strings use g_strEmpty, nRefs = -1 (set in Init())
15 * 2) AllocBuffer() sets nRefs to 1, Lock() increments it by one
16 * 3) Unlock() decrements nRefs and frees memory if it goes to 0
19 // ===========================================================================
20 // headers, declarations, constants
21 // ===========================================================================
23 // For compilers that support precompilation, includes "wx.h".
24 #include "wx/wxprec.h"
31 #include "wx/stringimpl.h"
44 // allocating extra space for each string consumes more memory but speeds up
45 // the concatenation operations (nLen is the current string's length)
46 // NB: EXTRA_ALLOC must be >= 0!
47 #define EXTRA_ALLOC (19 - nLen % 16)
50 // string handling functions used by wxString:
51 #if wxUSE_UNICODE_UTF8
52 #define wxStringMemcpy memcpy
53 #define wxStringMemcmp memcmp
54 #define wxStringMemchr memchr
56 #define wxStringMemcpy wxTmemcpy
57 #define wxStringMemcmp wxTmemcmp
58 #define wxStringMemchr wxTmemchr
62 // ---------------------------------------------------------------------------
63 // static class variables definition
64 // ---------------------------------------------------------------------------
66 #if !wxUSE_STL_BASED_WXSTRING
67 //According to STL _must_ be a -1 size_t
68 const size_t wxStringImpl::npos
= (size_t) -1;
71 // ----------------------------------------------------------------------------
73 // ----------------------------------------------------------------------------
75 #if wxUSE_STL_BASED_WXSTRING
77 // FIXME-UTF8: get rid of this, have only one wxEmptyString
78 #if wxUSE_UNICODE_UTF8
79 const wxStringCharType WXDLLIMPEXP_BASE
*wxEmptyStringImpl
= "";
81 const wxChar WXDLLIMPEXP_BASE
*wxEmptyString
= wxT("");
85 // for an empty string, GetStringData() will return this address: this
86 // structure has the same layout as wxStringData and it's data() method will
87 // return the empty string (dummy pointer)
91 wxStringCharType dummy
;
92 } g_strEmpty
= { {-1, 0, 0}, wxT('\0') };
94 // empty C style string: points to 'string data' byte of g_strEmpty
95 #if wxUSE_UNICODE_UTF8
96 // FIXME-UTF8: get rid of this, have only one wxEmptyString
97 const wxStringCharType WXDLLIMPEXP_BASE
*wxEmptyStringImpl
= &g_strEmpty
.dummy
;
98 const wxChar WXDLLIMPEXP_BASE
*wxEmptyString
= wxT("");
100 const wxStringCharType WXDLLIMPEXP_BASE
*wxEmptyString
= &g_strEmpty
.dummy
;
106 #if !wxUSE_STL_BASED_WXSTRING
108 // ----------------------------------------------------------------------------
110 // ----------------------------------------------------------------------------
112 // this small class is used to gather statistics for performance tuning
114 // uncomment this to enable gathering of some statistics about wxString
116 //#define WXSTRING_STATISTICS
118 #ifdef WXSTRING_STATISTICS
122 Averager(const wxStringCharType
*sz
) { m_sz
= sz
; m_nTotal
= m_nCount
= 0; }
125 wxPrintf("wxString %s: total = %lu, average = %f\n",
126 m_sz
, m_nTotal
, ((float)m_nTotal
)/m_nCount
);
129 void Add(size_t n
) { m_nTotal
+= n
; m_nCount
++; }
132 unsigned long m_nCount
, m_nTotal
;
133 const wxStringCharType
*m_sz
;
134 } g_averageLength("allocation size"),
135 g_averageSummandLength("summand length"),
136 g_averageConcatHit("hit probability in concat"),
137 g_averageInitialLength("initial string length");
139 #define STATISTICS_ADD(av, val) g_average##av.Add(val)
141 #define STATISTICS_ADD(av, val)
142 #endif // WXSTRING_STATISTICS
144 // ===========================================================================
145 // wxStringData class deallocation
146 // ===========================================================================
148 #if defined(__VISUALC__) && defined(_MT) && !defined(_DLL)
149 # pragma message (__FILE__ ": building with Multithreaded non DLL runtime has a performance impact on wxString!")
150 void wxStringData::Free()
156 // ===========================================================================
158 // ===========================================================================
160 // takes nLength elements of psz starting at nPos
161 void wxStringImpl::InitWith(const wxStringCharType
*psz
,
162 size_t nPos
, size_t nLength
)
166 // if the length is not given, assume the string to be NUL terminated
167 if ( nLength
== npos
) {
168 wxASSERT_MSG( nPos
<= wxStrlen(psz
), wxT("index out of bounds") );
170 nLength
= wxStrlen(psz
+ nPos
);
173 STATISTICS_ADD(InitialLength
, nLength
);
176 // trailing '\0' is written in AllocBuffer()
177 if ( !AllocBuffer(nLength
) ) {
178 wxFAIL_MSG( wxT("out of memory in wxStringImpl::InitWith") );
181 wxStringMemcpy(m_pchData
, psz
+ nPos
, nLength
);
185 wxStringImpl::wxStringImpl(const_iterator first
, const_iterator last
)
189 InitWith(first
.GetPtr(), 0, last
- first
);
193 wxFAIL_MSG( wxT("first must be before last") );
198 wxStringImpl::wxStringImpl(size_type n
, wxStringCharType ch
)
204 // ---------------------------------------------------------------------------
206 // ---------------------------------------------------------------------------
208 // allocates memory needed to store a C string of length nLen
209 bool wxStringImpl::AllocBuffer(size_t nLen
)
211 // allocating 0 sized buffer doesn't make sense, all empty strings should
213 wxASSERT( nLen
> 0 );
215 // make sure that we don't overflow
216 wxCHECK( nLen
< (INT_MAX
/ sizeof(wxStringCharType
)) -
217 (sizeof(wxStringData
) + EXTRA_ALLOC
+ 1), false );
219 STATISTICS_ADD(Length
, nLen
);
222 // 1) one extra character for '\0' termination
223 // 2) sizeof(wxStringData) for housekeeping info
224 wxStringData
* pData
= (wxStringData
*)
225 malloc(sizeof(wxStringData
) + (nLen
+ EXTRA_ALLOC
+ 1)*sizeof(wxStringCharType
));
227 if ( pData
== NULL
) {
228 // allocation failures are handled by the caller
233 pData
->nDataLength
= nLen
;
234 pData
->nAllocLength
= nLen
+ EXTRA_ALLOC
;
235 m_pchData
= pData
->data(); // data starts after wxStringData
236 m_pchData
[nLen
] = wxT('\0');
240 // must be called before changing this string
241 bool wxStringImpl::CopyBeforeWrite()
243 wxStringData
* pData
= GetStringData();
245 if ( pData
->IsShared() ) {
246 pData
->Unlock(); // memory not freed because shared
247 size_t nLen
= pData
->nDataLength
;
248 if ( !AllocBuffer(nLen
) ) {
249 // allocation failures are handled by the caller
252 wxStringMemcpy(m_pchData
, pData
->data(), nLen
);
255 wxASSERT( !GetStringData()->IsShared() ); // we must be the only owner
260 // must be called before replacing contents of this string
261 bool wxStringImpl::AllocBeforeWrite(size_t nLen
)
263 wxASSERT( nLen
!= 0 ); // doesn't make any sense
265 // must not share string and must have enough space
266 wxStringData
* pData
= GetStringData();
267 if ( pData
->IsShared() || pData
->IsEmpty() ) {
268 // can't work with old buffer, get new one
270 if ( !AllocBuffer(nLen
) ) {
271 // allocation failures are handled by the caller
276 if ( nLen
> pData
->nAllocLength
) {
277 // realloc the buffer instead of calling malloc() again, this is more
279 STATISTICS_ADD(Length
, nLen
);
283 pData
= (wxStringData
*)
285 sizeof(wxStringData
) + (nLen
+ 1)*sizeof(wxStringCharType
));
287 if ( pData
== NULL
) {
288 // allocation failures are handled by the caller
289 // keep previous data since reallocation failed
293 pData
->nAllocLength
= nLen
;
294 m_pchData
= pData
->data();
298 wxASSERT( !GetStringData()->IsShared() ); // we must be the only owner
300 // it doesn't really matter what the string length is as it's going to be
301 // overwritten later but, for extra safety, set it to 0 for now as we may
302 // have some junk in m_pchData
303 GetStringData()->nDataLength
= 0;
308 wxStringImpl
& wxStringImpl::append(size_t n
, wxStringCharType ch
)
310 size_type len
= length();
312 if ( !Alloc(len
+ n
) || !CopyBeforeWrite() ) {
313 wxFAIL_MSG( wxT("out of memory in wxStringImpl::append") );
316 GetStringData()->nDataLength
= len
+ n
;
317 m_pchData
[len
+ n
] = '\0';
318 for ( size_t i
= 0; i
< n
; ++i
)
319 m_pchData
[len
+ i
] = ch
;
323 void wxStringImpl::resize(size_t nSize
, wxStringCharType ch
)
325 size_t len
= length();
329 erase(begin() + nSize
, end());
331 else if ( nSize
> len
)
333 append(nSize
- len
, ch
);
335 //else: we have exactly the specified length, nothing to do
338 // allocate enough memory for nLen characters
339 bool wxStringImpl::Alloc(size_t nLen
)
341 wxStringData
*pData
= GetStringData();
342 if ( pData
->nAllocLength
<= nLen
) {
343 if ( pData
->IsEmpty() ) {
344 STATISTICS_ADD(Length
, nLen
);
348 pData
= (wxStringData
*)
349 malloc(sizeof(wxStringData
) + (nLen
+ 1)*sizeof(wxStringCharType
));
351 if ( pData
== NULL
) {
352 // allocation failure handled by caller
357 pData
->nDataLength
= 0;
358 pData
->nAllocLength
= nLen
;
359 m_pchData
= pData
->data(); // data starts after wxStringData
360 m_pchData
[0u] = wxT('\0');
362 else if ( pData
->IsShared() ) {
363 pData
->Unlock(); // memory not freed because shared
364 size_t nOldLen
= pData
->nDataLength
;
365 if ( !AllocBuffer(nLen
) ) {
366 // allocation failure handled by caller
369 // +1 to copy the terminator, too
370 memcpy(m_pchData
, pData
->data(), (nOldLen
+1)*sizeof(wxStringCharType
));
371 GetStringData()->nDataLength
= nOldLen
;
376 pData
= (wxStringData
*)
377 realloc(pData
, sizeof(wxStringData
) + (nLen
+ 1)*sizeof(wxStringCharType
));
379 if ( pData
== NULL
) {
380 // allocation failure handled by caller
381 // keep previous data since reallocation failed
385 // it's not important if the pointer changed or not (the check for this
386 // is not faster than assigning to m_pchData in all cases)
387 pData
->nAllocLength
= nLen
;
388 m_pchData
= pData
->data();
391 //else: we've already got enough
395 wxStringImpl::iterator
wxStringImpl::begin()
402 wxStringImpl::iterator
wxStringImpl::end()
406 return m_pchData
+ length();
409 wxStringImpl::iterator
wxStringImpl::erase(iterator it
)
411 size_type idx
= it
- begin();
413 return begin() + idx
;
416 wxStringImpl
& wxStringImpl::erase(size_t nStart
, size_t nLen
)
418 wxASSERT(nStart
<= length());
419 size_t strLen
= length() - nStart
;
420 // delete nLen or up to the end of the string characters
421 nLen
= strLen
< nLen
? strLen
: nLen
;
422 wxStringImpl
strTmp(c_str(), nStart
);
423 strTmp
.append(c_str() + nStart
+ nLen
, length() - nStart
- nLen
);
429 wxStringImpl
& wxStringImpl::insert(size_t nPos
,
430 const wxStringCharType
*sz
, size_t n
)
432 wxASSERT( nPos
<= length() );
434 if ( n
== npos
) n
= wxStrlen(sz
);
435 if ( n
== 0 ) return *this;
437 if ( !Alloc(length() + n
) || !CopyBeforeWrite() ) {
438 wxFAIL_MSG( wxT("out of memory in wxStringImpl::insert") );
442 memmove(m_pchData
+ nPos
+ n
, m_pchData
+ nPos
,
443 (length() - nPos
) * sizeof(wxStringCharType
));
444 memcpy(m_pchData
+ nPos
, sz
, n
* sizeof(wxStringCharType
));
445 GetStringData()->nDataLength
= length() + n
;
446 m_pchData
[length()] = '\0';
451 void wxStringImpl::swap(wxStringImpl
& str
)
453 wxStringCharType
* tmp
= str
.m_pchData
;
454 str
.m_pchData
= m_pchData
;
458 size_t wxStringImpl::find(const wxStringImpl
& str
, size_t nStart
) const
460 // deal with the special case of empty string first
461 const size_t nLen
= length();
462 const size_t nLenOther
= str
.length();
466 // empty string is a substring of anything
472 // the other string is non empty so can't be our substring
476 wxASSERT( str
.GetStringData()->IsValid() );
477 wxASSERT( nStart
<= nLen
);
479 const wxStringCharType
* const other
= str
.c_str();
482 const wxStringCharType
* p
=
483 (const wxStringCharType
*)wxStringMemchr(c_str() + nStart
,
490 while ( p
- c_str() + nLenOther
<= nLen
&&
491 wxStringMemcmp(p
, other
, nLenOther
) )
496 p
= (const wxStringCharType
*)
497 wxStringMemchr(p
, *other
, nLen
- (p
- c_str()));
503 return p
- c_str() + nLenOther
<= nLen
? p
- c_str() : npos
;
506 size_t wxStringImpl::find(const wxStringCharType
* sz
,
507 size_t nStart
, size_t n
) const
509 return find(wxStringImpl(sz
, n
), nStart
);
512 size_t wxStringImpl::find(wxStringCharType ch
, size_t nStart
) const
514 wxASSERT( nStart
<= length() );
516 const wxStringCharType
*p
= (const wxStringCharType
*)
517 wxStringMemchr(c_str() + nStart
, ch
, length() - nStart
);
519 return p
== NULL
? npos
: p
- c_str();
522 size_t wxStringImpl::rfind(const wxStringImpl
& str
, size_t nStart
) const
524 wxASSERT( str
.GetStringData()->IsValid() );
525 wxASSERT( nStart
== npos
|| nStart
<= length() );
527 if ( length() >= str
.length() )
529 // avoids a corner case later
530 if ( empty() && str
.empty() )
533 // "top" is the point where search starts from
534 size_t top
= length() - str
.length();
536 if ( nStart
== npos
)
537 nStart
= length() - 1;
541 const wxStringCharType
*cursor
= c_str() + top
;
544 if ( wxStringMemcmp(cursor
, str
.c_str(), str
.length()) == 0 )
546 return cursor
- c_str();
548 } while ( cursor
-- > c_str() );
554 size_t wxStringImpl::rfind(const wxStringCharType
* sz
,
555 size_t nStart
, size_t n
) const
557 return rfind(wxStringImpl(sz
, n
), nStart
);
560 size_t wxStringImpl::rfind(wxStringCharType ch
, size_t nStart
) const
562 if ( nStart
== npos
)
568 wxASSERT( nStart
<= length() );
571 const wxStringCharType
*actual
;
572 for ( actual
= c_str() + ( nStart
== npos
? length() : nStart
+ 1 );
573 actual
> c_str(); --actual
)
575 if ( *(actual
- 1) == ch
)
576 return (actual
- 1) - c_str();
582 wxStringImpl
& wxStringImpl::replace(size_t nStart
, size_t nLen
,
583 const wxStringCharType
*sz
, size_t nCount
)
585 // check and adjust parameters
586 const size_t lenOld
= length();
588 wxASSERT_MSG( nStart
<= lenOld
,
589 wxT("index out of bounds in wxStringImpl::replace") );
590 size_t nEnd
= nStart
+ nLen
;
591 if ( nLen
> lenOld
- nStart
)
593 // nLen may be out of range, as it can be npos, just clump it down
594 nLen
= lenOld
- nStart
;
598 if ( nCount
== npos
)
599 nCount
= wxStrlen(sz
);
601 // build the new string from 3 pieces: part of this string before nStart,
602 // the new substring and the part of this string after nStart+nLen
604 const size_t lenNew
= lenOld
+ nCount
- nLen
;
607 tmp
.AllocBuffer(lenOld
+ nCount
- nLen
);
609 wxStringCharType
*dst
= tmp
.m_pchData
;
610 memcpy(dst
, m_pchData
, nStart
*sizeof(wxStringCharType
));
613 memcpy(dst
, sz
, nCount
*sizeof(wxStringCharType
));
616 memcpy(dst
, m_pchData
+ nEnd
, (lenOld
- nEnd
)*sizeof(wxStringCharType
));
619 // and replace this string contents with the new one
624 wxStringImpl
wxStringImpl::substr(size_t nStart
, size_t nLen
) const
627 nLen
= length() - nStart
;
628 return wxStringImpl(*this, nStart
, nLen
);
631 // assigns one string to another
632 wxStringImpl
& wxStringImpl::operator=(const wxStringImpl
& stringSrc
)
634 wxASSERT( stringSrc
.GetStringData()->IsValid() );
636 // don't copy string over itself
637 if ( m_pchData
!= stringSrc
.m_pchData
) {
638 if ( stringSrc
.GetStringData()->IsEmpty() ) {
643 GetStringData()->Unlock();
644 m_pchData
= stringSrc
.m_pchData
;
645 GetStringData()->Lock();
652 // assigns a single character
653 wxStringImpl
& wxStringImpl::operator=(wxStringCharType ch
)
655 wxStringCharType
c(ch
);
656 if ( !AssignCopy(1, &c
) ) {
657 wxFAIL_MSG( wxT("out of memory in wxStringImpl::operator=(wxStringCharType)") );
663 wxStringImpl
& wxStringImpl::operator=(const wxStringCharType
*psz
)
665 if ( !AssignCopy(wxStrlen(psz
), psz
) ) {
666 wxFAIL_MSG( wxT("out of memory in wxStringImpl::operator=(const wxStringCharType *)") );
671 // helper function: does real copy
672 bool wxStringImpl::AssignCopy(size_t nSrcLen
,
673 const wxStringCharType
*pszSrcData
)
675 if ( nSrcLen
== 0 ) {
679 if ( !AllocBeforeWrite(nSrcLen
) ) {
680 // allocation failure handled by caller
684 // use memmove() and not memcpy() here as we might be copying from our own
685 // buffer in case of assignment such as "s = s.c_str()" (see #11294)
686 memmove(m_pchData
, pszSrcData
, nSrcLen
*sizeof(wxStringCharType
));
688 GetStringData()->nDataLength
= nSrcLen
;
689 m_pchData
[nSrcLen
] = wxT('\0');
694 // ---------------------------------------------------------------------------
695 // string concatenation
696 // ---------------------------------------------------------------------------
698 // add something to this string
699 bool wxStringImpl::ConcatSelf(size_t nSrcLen
,
700 const wxStringCharType
*pszSrcData
,
703 STATISTICS_ADD(SummandLength
, nSrcLen
);
705 nSrcLen
= nSrcLen
< nMaxLen
? nSrcLen
: nMaxLen
;
707 // concatenating an empty string is a NOP
709 wxStringData
*pData
= GetStringData();
710 size_t nLen
= pData
->nDataLength
;
712 // take special care when appending part of this string to itself: the code
713 // below reallocates our buffer and this invalidates pszSrcData pointer so
714 // we have to copy it in another temporary string in this case (but avoid
715 // doing this unnecessarily)
716 if ( pszSrcData
>= m_pchData
&& pszSrcData
< m_pchData
+ nLen
)
718 wxStringImpl
tmp(pszSrcData
, nSrcLen
);
719 return ConcatSelf(nSrcLen
, tmp
.m_pchData
, nSrcLen
);
722 size_t nNewLen
= nLen
+ nSrcLen
;
724 // alloc new buffer if current is too small
725 if ( pData
->IsShared() ) {
726 STATISTICS_ADD(ConcatHit
, 0);
728 // we have to allocate another buffer
729 wxStringData
* pOldData
= GetStringData();
730 if ( !AllocBuffer(nNewLen
) ) {
731 // allocation failure handled by caller
734 memcpy(m_pchData
, pOldData
->data(), nLen
*sizeof(wxStringCharType
));
737 else if ( nNewLen
> pData
->nAllocLength
) {
738 STATISTICS_ADD(ConcatHit
, 0);
741 // we have to grow the buffer
742 if ( capacity() < nNewLen
) {
743 // allocation failure handled by caller
748 STATISTICS_ADD(ConcatHit
, 1);
750 // the buffer is already big enough
753 // should be enough space
754 wxASSERT( nNewLen
<= GetStringData()->nAllocLength
);
756 // fast concatenation - all is done in our buffer
757 memcpy(m_pchData
+ nLen
, pszSrcData
, nSrcLen
*sizeof(wxStringCharType
));
759 m_pchData
[nNewLen
] = wxT('\0'); // put terminating '\0'
760 GetStringData()->nDataLength
= nNewLen
; // and fix the length
762 //else: the string to append was empty
766 // get the pointer to writable buffer of (at least) nLen bytes
767 wxStringCharType
*wxStringImpl::DoGetWriteBuf(size_t nLen
)
769 if ( !AllocBeforeWrite(nLen
) ) {
770 // allocation failure handled by caller
774 wxASSERT( GetStringData()->nRefs
== 1 );
775 GetStringData()->Validate(false);
780 // put string back in a reasonable state after GetWriteBuf
781 void wxStringImpl::DoUngetWriteBuf()
783 DoUngetWriteBuf(wxStrlen(m_pchData
));
786 void wxStringImpl::DoUngetWriteBuf(size_t nLen
)
788 wxStringData
* const pData
= GetStringData();
790 wxASSERT_MSG( nLen
< pData
->nAllocLength
, wxT("buffer overrun") );
792 // the strings we store are always NUL-terminated
793 pData
->data()[nLen
] = wxT('\0');
794 pData
->nDataLength
= nLen
;
795 pData
->Validate(true);
798 #endif // !wxUSE_STL_BASED_WXSTRING