1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/stringimpl.cpp
3 // Purpose: wxString class
4 // Author: Vadim Zeitlin, Ryan Norton
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // (c) 2004 Ryan Norton <wxprojects@comcast.net>
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
15 * 1) all empty strings use g_strEmpty, nRefs = -1 (set in Init())
16 * 2) AllocBuffer() sets nRefs to 1, Lock() increments it by one
17 * 3) Unlock() decrements nRefs and frees memory if it goes to 0
20 // ===========================================================================
21 // headers, declarations, constants
22 // ===========================================================================
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
32 #include "wx/stringimpl.h"
48 // allocating extra space for each string consumes more memory but speeds up
49 // the concatenation operations (nLen is the current string's length)
50 // NB: EXTRA_ALLOC must be >= 0!
51 #define EXTRA_ALLOC (19 - nLen % 16)
54 // string handling functions used by wxString:
55 #if wxUSE_UNICODE_UTF8
56 #define wxStringMemcpy memcpy
57 #define wxStringMemcmp memcmp
58 #define wxStringMemchr memchr
60 #define wxStringMemcpy wxTmemcpy
61 #define wxStringMemcmp wxTmemcmp
62 #define wxStringMemchr wxTmemchr
66 // ---------------------------------------------------------------------------
67 // static class variables definition
68 // ---------------------------------------------------------------------------
70 #if !wxUSE_STL_BASED_WXSTRING
71 //According to STL _must_ be a -1 size_t
72 const size_t wxStringImpl::npos
= (size_t) -1;
75 // ----------------------------------------------------------------------------
77 // ----------------------------------------------------------------------------
79 #if wxUSE_STL_BASED_WXSTRING
81 // FIXME-UTF8: get rid of this, have only one wxEmptyString
82 #if wxUSE_UNICODE_UTF8
83 extern const wxStringCharType WXDLLIMPEXP_BASE
*wxEmptyStringImpl
= "";
85 extern const wxChar WXDLLIMPEXP_BASE
*wxEmptyString
= _T("");
89 // for an empty string, GetStringData() will return this address: this
90 // structure has the same layout as wxStringData and it's data() method will
91 // return the empty string (dummy pointer)
95 wxStringCharType dummy
;
96 } g_strEmpty
= { {-1, 0, 0}, wxT('\0') };
98 // empty C style string: points to 'string data' byte of g_strEmpty
99 #if wxUSE_UNICODE_UTF8
100 // FIXME-UTF8: get rid of this, have only one wxEmptyString
101 extern const wxStringCharType WXDLLIMPEXP_BASE
*wxEmptyStringImpl
= &g_strEmpty
.dummy
;
102 extern const wxChar WXDLLIMPEXP_BASE
*wxEmptyString
= _T("");
104 extern const wxStringCharType WXDLLIMPEXP_BASE
*wxEmptyString
= &g_strEmpty
.dummy
;
110 #if !wxUSE_STL_BASED_WXSTRING
112 // ----------------------------------------------------------------------------
114 // ----------------------------------------------------------------------------
116 // this small class is used to gather statistics for performance tuning
117 //#define WXSTRING_STATISTICS
118 #ifdef WXSTRING_STATISTICS
122 Averager(const wxStringCharType
*sz
) { m_sz
= sz
; m_nTotal
= m_nCount
= 0; }
124 { wxPrintf("wxString: average %s = %f\n", m_sz
, ((float)m_nTotal
)/m_nCount
); }
126 void Add(size_t n
) { m_nTotal
+= n
; m_nCount
++; }
129 size_t m_nCount
, m_nTotal
;
130 const wxStringCharType
*m_sz
;
131 } g_averageLength("allocation size"),
132 g_averageSummandLength("summand length"),
133 g_averageConcatHit("hit probability in concat"),
134 g_averageInitialLength("initial string length");
136 #define STATISTICS_ADD(av, val) g_average##av.Add(val)
138 #define STATISTICS_ADD(av, val)
139 #endif // WXSTRING_STATISTICS
141 // ===========================================================================
142 // wxStringData class deallocation
143 // ===========================================================================
145 #if defined(__VISUALC__) && defined(_MT) && !defined(_DLL)
146 # pragma message (__FILE__ ": building with Multithreaded non DLL runtime has a performance impact on wxString!")
147 void wxStringData::Free()
153 // ===========================================================================
155 // ===========================================================================
157 // takes nLength elements of psz starting at nPos
158 void wxStringImpl::InitWith(const wxStringCharType
*psz
,
159 size_t nPos
, size_t nLength
)
163 // if the length is not given, assume the string to be NUL terminated
164 if ( nLength
== npos
) {
165 wxASSERT_MSG( nPos
<= Strsize(psz
), _T("index out of bounds") );
167 nLength
= Strsize(psz
+ nPos
);
170 STATISTICS_ADD(InitialLength
, nLength
);
173 // trailing '\0' is written in AllocBuffer()
174 if ( !AllocBuffer(nLength
) ) {
175 wxFAIL_MSG( _T("out of memory in wxStringImpl::InitWith") );
178 wxStringMemcpy(m_pchData
, psz
+ nPos
, nLength
);
182 wxStringImpl::wxStringImpl(const_iterator first
, const_iterator last
)
186 InitWith(first
, 0, last
- first
);
190 wxFAIL_MSG( _T("first must be before last") );
195 wxStringImpl::wxStringImpl(size_type n
, wxStringCharType ch
)
201 // ---------------------------------------------------------------------------
203 // ---------------------------------------------------------------------------
205 // allocates memory needed to store a C string of length nLen
206 bool wxStringImpl::AllocBuffer(size_t nLen
)
208 // allocating 0 sized buffer doesn't make sense, all empty strings should
210 wxASSERT( nLen
> 0 );
212 // make sure that we don't overflow
213 wxASSERT( nLen
< (INT_MAX
/ sizeof(wxStringCharType
)) -
214 (sizeof(wxStringData
) + EXTRA_ALLOC
+ 1) );
216 STATISTICS_ADD(Length
, nLen
);
219 // 1) one extra character for '\0' termination
220 // 2) sizeof(wxStringData) for housekeeping info
221 wxStringData
* pData
= (wxStringData
*)
222 malloc(sizeof(wxStringData
) + (nLen
+ EXTRA_ALLOC
+ 1)*sizeof(wxStringCharType
));
224 if ( pData
== NULL
) {
225 // allocation failures are handled by the caller
230 pData
->nDataLength
= nLen
;
231 pData
->nAllocLength
= nLen
+ EXTRA_ALLOC
;
232 m_pchData
= pData
->data(); // data starts after wxStringData
233 m_pchData
[nLen
] = wxT('\0');
237 // must be called before changing this string
238 bool wxStringImpl::CopyBeforeWrite()
240 wxStringData
* pData
= GetStringData();
242 if ( pData
->IsShared() ) {
243 pData
->Unlock(); // memory not freed because shared
244 size_t nLen
= pData
->nDataLength
;
245 if ( !AllocBuffer(nLen
) ) {
246 // allocation failures are handled by the caller
249 wxStringMemcpy(m_pchData
, pData
->data(), nLen
);
252 wxASSERT( !GetStringData()->IsShared() ); // we must be the only owner
257 // must be called before replacing contents of this string
258 bool wxStringImpl::AllocBeforeWrite(size_t nLen
)
260 wxASSERT( nLen
!= 0 ); // doesn't make any sense
262 // must not share string and must have enough space
263 wxStringData
* pData
= GetStringData();
264 if ( pData
->IsShared() || pData
->IsEmpty() ) {
265 // can't work with old buffer, get new one
267 if ( !AllocBuffer(nLen
) ) {
268 // allocation failures are handled by the caller
273 if ( nLen
> pData
->nAllocLength
) {
274 // realloc the buffer instead of calling malloc() again, this is more
276 STATISTICS_ADD(Length
, nLen
);
280 pData
= (wxStringData
*)
282 sizeof(wxStringData
) + (nLen
+ 1)*sizeof(wxStringCharType
));
284 if ( pData
== NULL
) {
285 // allocation failures are handled by the caller
286 // keep previous data since reallocation failed
290 pData
->nAllocLength
= nLen
;
291 m_pchData
= pData
->data();
295 wxASSERT( !GetStringData()->IsShared() ); // we must be the only owner
297 // it doesn't really matter what the string length is as it's going to be
298 // overwritten later but, for extra safety, set it to 0 for now as we may
299 // have some junk in m_pchData
300 GetStringData()->nDataLength
= 0;
305 wxStringImpl
& wxStringImpl::append(size_t n
, wxStringCharType ch
)
307 size_type len
= length();
309 if ( !Alloc(len
+ n
) || !CopyBeforeWrite() ) {
310 wxFAIL_MSG( _T("out of memory in wxStringImpl::append") );
313 GetStringData()->nDataLength
= len
+ n
;
314 m_pchData
[len
+ n
] = '\0';
315 for ( size_t i
= 0; i
< n
; ++i
)
316 m_pchData
[len
+ i
] = ch
;
320 void wxStringImpl::resize(size_t nSize
, wxStringCharType ch
)
322 size_t len
= length();
326 erase(begin() + nSize
, end());
328 else if ( nSize
> len
)
330 append(nSize
- len
, ch
);
332 //else: we have exactly the specified length, nothing to do
335 // allocate enough memory for nLen characters
336 bool wxStringImpl::Alloc(size_t nLen
)
338 wxStringData
*pData
= GetStringData();
339 if ( pData
->nAllocLength
<= nLen
) {
340 if ( pData
->IsEmpty() ) {
343 pData
= (wxStringData
*)
344 malloc(sizeof(wxStringData
) + (nLen
+ 1)*sizeof(wxStringCharType
));
346 if ( pData
== NULL
) {
347 // allocation failure handled by caller
352 pData
->nDataLength
= 0;
353 pData
->nAllocLength
= nLen
;
354 m_pchData
= pData
->data(); // data starts after wxStringData
355 m_pchData
[0u] = wxT('\0');
357 else if ( pData
->IsShared() ) {
358 pData
->Unlock(); // memory not freed because shared
359 size_t nOldLen
= pData
->nDataLength
;
360 if ( !AllocBuffer(nLen
) ) {
361 // allocation failure handled by caller
364 // +1 to copy the terminator, too
365 memcpy(m_pchData
, pData
->data(), (nOldLen
+1)*sizeof(wxStringCharType
));
366 GetStringData()->nDataLength
= nOldLen
;
371 pData
= (wxStringData
*)
372 realloc(pData
, sizeof(wxStringData
) + (nLen
+ 1)*sizeof(wxStringCharType
));
374 if ( pData
== NULL
) {
375 // allocation failure handled by caller
376 // keep previous data since reallocation failed
380 // it's not important if the pointer changed or not (the check for this
381 // is not faster than assigning to m_pchData in all cases)
382 pData
->nAllocLength
= nLen
;
383 m_pchData
= pData
->data();
386 //else: we've already got enough
390 wxStringImpl::iterator
wxStringImpl::begin()
397 wxStringImpl::iterator
wxStringImpl::end()
401 return m_pchData
+ length();
404 wxStringImpl::iterator
wxStringImpl::erase(iterator it
)
406 size_type idx
= it
- begin();
408 return begin() + idx
;
411 wxStringImpl
& wxStringImpl::erase(size_t nStart
, size_t nLen
)
413 wxASSERT(nStart
<= length());
414 size_t strLen
= length() - nStart
;
415 // delete nLen or up to the end of the string characters
416 nLen
= strLen
< nLen
? strLen
: nLen
;
417 wxStringImpl
strTmp(c_str(), nStart
);
418 strTmp
.append(c_str() + nStart
+ nLen
, length() - nStart
- nLen
);
424 wxStringImpl
& wxStringImpl::insert(size_t nPos
,
425 const wxStringCharType
*sz
, size_t n
)
427 wxASSERT( nPos
<= length() );
429 if ( n
== npos
) n
= Strsize(sz
);
430 if ( n
== 0 ) return *this;
432 if ( !Alloc(length() + n
) || !CopyBeforeWrite() ) {
433 wxFAIL_MSG( _T("out of memory in wxStringImpl::insert") );
437 memmove(m_pchData
+ nPos
+ n
, m_pchData
+ nPos
,
438 (length() - nPos
) * sizeof(wxStringCharType
));
439 memcpy(m_pchData
+ nPos
, sz
, n
* sizeof(wxStringCharType
));
440 GetStringData()->nDataLength
= length() + n
;
441 m_pchData
[length()] = '\0';
446 void wxStringImpl::swap(wxStringImpl
& str
)
448 wxStringCharType
* tmp
= str
.m_pchData
;
449 str
.m_pchData
= m_pchData
;
453 size_t wxStringImpl::find(const wxStringImpl
& str
, size_t nStart
) const
455 // deal with the special case of empty string first
456 const size_t nLen
= length();
457 const size_t nLenOther
= str
.length();
461 // empty string is a substring of anything
467 // the other string is non empty so can't be our substring
471 wxASSERT( str
.GetStringData()->IsValid() );
472 wxASSERT( nStart
<= nLen
);
474 const wxStringCharType
* const other
= str
.c_str();
477 const wxStringCharType
* p
=
478 (const wxStringCharType
*)wxStringMemchr(c_str() + nStart
,
485 while ( p
- c_str() + nLenOther
<= nLen
&&
486 wxStringMemcmp(p
, other
, nLenOther
) )
491 p
= (const wxStringCharType
*)
492 wxStringMemchr(p
, *other
, nLen
- (p
- c_str()));
498 return p
- c_str() + nLenOther
<= nLen
? p
- c_str() : npos
;
501 size_t wxStringImpl::find(const wxStringCharType
* sz
,
502 size_t nStart
, size_t n
) const
504 return find(wxStringImpl(sz
, n
), nStart
);
507 size_t wxStringImpl::find(wxStringCharType ch
, size_t nStart
) const
509 wxASSERT( nStart
<= length() );
511 const wxStringCharType
*p
= (const wxStringCharType
*)
512 wxStringMemchr(c_str() + nStart
, ch
, length() - nStart
);
514 return p
== NULL
? npos
: p
- c_str();
517 size_t wxStringImpl::rfind(const wxStringImpl
& str
, size_t nStart
) const
519 wxASSERT( str
.GetStringData()->IsValid() );
520 wxASSERT( nStart
== npos
|| nStart
<= length() );
522 if ( length() >= str
.length() )
524 // avoids a corner case later
525 if ( length() == 0 && str
.length() == 0 )
528 // "top" is the point where search starts from
529 size_t top
= length() - str
.length();
531 if ( nStart
== npos
)
532 nStart
= length() - 1;
536 const wxStringCharType
*cursor
= c_str() + top
;
539 if ( wxStringMemcmp(cursor
, str
.c_str(), str
.length()) == 0 )
541 return cursor
- c_str();
543 } while ( cursor
-- > c_str() );
549 size_t wxStringImpl::rfind(const wxStringCharType
* sz
,
550 size_t nStart
, size_t n
) const
552 return rfind(wxStringImpl(sz
, n
), nStart
);
555 size_t wxStringImpl::rfind(wxStringCharType ch
, size_t nStart
) const
557 if ( nStart
== npos
)
563 wxASSERT( nStart
<= length() );
566 const wxStringCharType
*actual
;
567 for ( actual
= c_str() + ( nStart
== npos
? length() : nStart
+ 1 );
568 actual
> c_str(); --actual
)
570 if ( *(actual
- 1) == ch
)
571 return (actual
- 1) - c_str();
577 wxStringImpl
& wxStringImpl::replace(size_t nStart
, size_t nLen
,
578 const wxStringCharType
*sz
)
580 wxASSERT_MSG( nStart
<= length(),
581 _T("index out of bounds in wxStringImpl::replace") );
582 size_t strLen
= length() - nStart
;
583 nLen
= strLen
< nLen
? strLen
: nLen
;
586 strTmp
.reserve(length()); // micro optimisation to avoid multiple mem allocs
588 //This is kind of inefficient, but its pretty good considering...
589 //we don't want to use character access operators here because on STL
590 //it will freeze the reference count of strTmp, which means a deep copy
591 //at the end when swap is called
593 //Also, we can't use append with the full character pointer and must
594 //do it manually because this string can contain null characters
595 for(size_t i1
= 0; i1
< nStart
; ++i1
)
596 strTmp
.append(1, this->c_str()[i1
]);
598 //its safe to do the full version here because
599 //sz must be a normal c string
602 for(size_t i2
= nStart
+ nLen
; i2
< length(); ++i2
)
603 strTmp
.append(1, this->c_str()[i2
]);
609 wxStringImpl
& wxStringImpl::replace(size_t nStart
, size_t nLen
,
610 size_t nCount
, wxStringCharType ch
)
612 return replace(nStart
, nLen
, wxStringImpl(nCount
, ch
).c_str());
615 wxStringImpl
& wxStringImpl::replace(size_t nStart
, size_t nLen
,
616 const wxStringImpl
& str
,
617 size_t nStart2
, size_t nLen2
)
619 return replace(nStart
, nLen
, str
.substr(nStart2
, nLen2
));
622 wxStringImpl
& wxStringImpl::replace(size_t nStart
, size_t nLen
,
623 const wxStringCharType
* sz
, size_t nCount
)
625 return replace(nStart
, nLen
, wxStringImpl(sz
, nCount
).c_str());
628 wxStringImpl
wxStringImpl::substr(size_t nStart
, size_t nLen
) const
631 nLen
= length() - nStart
;
632 return wxStringImpl(*this, nStart
, nLen
);
635 // assigns one string to another
636 wxStringImpl
& wxStringImpl::operator=(const wxStringImpl
& stringSrc
)
638 wxASSERT( stringSrc
.GetStringData()->IsValid() );
640 // don't copy string over itself
641 if ( m_pchData
!= stringSrc
.m_pchData
) {
642 if ( stringSrc
.GetStringData()->IsEmpty() ) {
647 GetStringData()->Unlock();
648 m_pchData
= stringSrc
.m_pchData
;
649 GetStringData()->Lock();
656 // assigns a single character
657 wxStringImpl
& wxStringImpl::operator=(wxStringCharType ch
)
659 wxStringCharType
c(ch
);
660 if ( !AssignCopy(1, &c
) ) {
661 wxFAIL_MSG( _T("out of memory in wxStringImpl::operator=(wxStringCharType)") );
667 wxStringImpl
& wxStringImpl::operator=(const wxStringCharType
*psz
)
669 if ( !AssignCopy(Strsize(psz
), psz
) ) {
670 wxFAIL_MSG( _T("out of memory in wxStringImpl::operator=(const wxStringCharType *)") );
675 // helper function: does real copy
676 bool wxStringImpl::AssignCopy(size_t nSrcLen
,
677 const wxStringCharType
*pszSrcData
)
679 if ( nSrcLen
== 0 ) {
683 if ( !AllocBeforeWrite(nSrcLen
) ) {
684 // allocation failure handled by caller
687 memcpy(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
;
711 size_t nNewLen
= nLen
+ nSrcLen
;
713 // alloc new buffer if current is too small
714 if ( pData
->IsShared() ) {
715 STATISTICS_ADD(ConcatHit
, 0);
717 // we have to allocate another buffer
718 wxStringData
* pOldData
= GetStringData();
719 if ( !AllocBuffer(nNewLen
) ) {
720 // allocation failure handled by caller
723 memcpy(m_pchData
, pOldData
->data(), nLen
*sizeof(wxStringCharType
));
726 else if ( nNewLen
> pData
->nAllocLength
) {
727 STATISTICS_ADD(ConcatHit
, 0);
730 // we have to grow the buffer
731 if ( capacity() < nNewLen
) {
732 // allocation failure handled by caller
737 STATISTICS_ADD(ConcatHit
, 1);
739 // the buffer is already big enough
742 // should be enough space
743 wxASSERT( nNewLen
<= GetStringData()->nAllocLength
);
745 // fast concatenation - all is done in our buffer
746 memcpy(m_pchData
+ nLen
, pszSrcData
, nSrcLen
*sizeof(wxStringCharType
));
748 m_pchData
[nNewLen
] = wxT('\0'); // put terminating '\0'
749 GetStringData()->nDataLength
= nNewLen
; // and fix the length
751 //else: the string to append was empty
755 #if !wxUSE_UNICODE_UTF8
756 // get the pointer to writable buffer of (at least) nLen bytes
757 wxChar
*wxStringImpl::DoGetWriteBuf(size_t nLen
)
759 if ( !AllocBeforeWrite(nLen
) ) {
760 // allocation failure handled by caller
764 wxASSERT( GetStringData()->nRefs
== 1 );
765 GetStringData()->Validate(false);
770 // put string back in a reasonable state after GetWriteBuf
771 void wxStringImpl::DoUngetWriteBuf()
773 DoUngetWriteBuf(Strsize(m_pchData
));
776 void wxStringImpl::DoUngetWriteBuf(size_t nLen
)
778 wxStringData
* const pData
= GetStringData();
780 wxASSERT_MSG( nLen
< pData
->nAllocLength
, _T("buffer overrun") );
782 // the strings we store are always NUL-terminated
783 pData
->data()[nLen
] = _T('\0');
784 pData
->nDataLength
= nLen
;
785 pData
->Validate(true);
787 #endif // !wxUSE_UNICODE_UTF8
789 #endif // !wxUSE_STL_BASED_WXSTRING