]> git.saurik.com Git - wxWidgets.git/blame - src/common/stringimpl.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / stringimpl.cpp
CommitLineData
a7ea63e2 1/////////////////////////////////////////////////////////////////////////////
81727065 2// Name: src/common/stringimpl.cpp
a7ea63e2
VS
3// Purpose: wxString class
4// Author: Vadim Zeitlin, Ryan Norton
5// Modified by:
6// Created: 29/01/98
7// RCS-ID: $Id$
8// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9// (c) 2004 Ryan Norton <wxprojects@comcast.net>
10// Licence: wxWindows licence
11/////////////////////////////////////////////////////////////////////////////
12
13/*
14 * About ref counting:
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
18 */
19
20// ===========================================================================
21// headers, declarations, constants
22// ===========================================================================
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
28 #pragma hdrstop
29#endif
30
31#ifndef WX_PRECOMP
32 #include "wx/stringimpl.h"
d2384936 33 #include "wx/wxcrt.h"
a7ea63e2
VS
34#endif
35
36#include <ctype.h>
37
38#ifndef __WXWINCE__
39 #include <errno.h>
40#endif
41
42#include <string.h>
43#include <stdlib.h>
44
a7ea63e2
VS
45// allocating extra space for each string consumes more memory but speeds up
46// the concatenation operations (nLen is the current string's length)
47// NB: EXTRA_ALLOC must be >= 0!
48#define EXTRA_ALLOC (19 - nLen % 16)
49
50
51// string handling functions used by wxString:
52#if wxUSE_UNICODE_UTF8
53 #define wxStringMemcpy memcpy
54 #define wxStringMemcmp memcmp
55 #define wxStringMemchr memchr
a7ea63e2
VS
56#else
57 #define wxStringMemcpy wxTmemcpy
58 #define wxStringMemcmp wxTmemcmp
59 #define wxStringMemchr wxTmemchr
a7ea63e2
VS
60#endif
61
62
63// ---------------------------------------------------------------------------
64// static class variables definition
65// ---------------------------------------------------------------------------
66
67#if !wxUSE_STL_BASED_WXSTRING
68//According to STL _must_ be a -1 size_t
69const size_t wxStringImpl::npos = (size_t) -1;
70#endif
71
72// ----------------------------------------------------------------------------
73// static data
74// ----------------------------------------------------------------------------
75
76#if wxUSE_STL_BASED_WXSTRING
77
81727065
VS
78// FIXME-UTF8: get rid of this, have only one wxEmptyString
79#if wxUSE_UNICODE_UTF8
bba60a82 80const wxStringCharType WXDLLIMPEXP_BASE *wxEmptyStringImpl = "";
81727065 81#endif
9a83f860 82const wxChar WXDLLIMPEXP_BASE *wxEmptyString = wxT("");
a7ea63e2
VS
83
84#else
85
86// for an empty string, GetStringData() will return this address: this
87// structure has the same layout as wxStringData and it's data() method will
88// return the empty string (dummy pointer)
89static const struct
90{
91 wxStringData data;
81727065 92 wxStringCharType dummy;
a7ea63e2
VS
93} g_strEmpty = { {-1, 0, 0}, wxT('\0') };
94
95// empty C style string: points to 'string data' byte of g_strEmpty
81727065
VS
96#if wxUSE_UNICODE_UTF8
97// FIXME-UTF8: get rid of this, have only one wxEmptyString
bba60a82 98const wxStringCharType WXDLLIMPEXP_BASE *wxEmptyStringImpl = &g_strEmpty.dummy;
9a83f860 99const wxChar WXDLLIMPEXP_BASE *wxEmptyString = wxT("");
81727065 100#else
bba60a82 101const wxStringCharType WXDLLIMPEXP_BASE *wxEmptyString = &g_strEmpty.dummy;
81727065 102#endif
a7ea63e2
VS
103
104#endif
105
106
107#if !wxUSE_STL_BASED_WXSTRING
108
109// ----------------------------------------------------------------------------
110// private classes
111// ----------------------------------------------------------------------------
112
113// this small class is used to gather statistics for performance tuning
a4f270bc
VZ
114
115// uncomment this to enable gathering of some statistics about wxString
116// efficiency
a7ea63e2 117//#define WXSTRING_STATISTICS
a4f270bc 118
a7ea63e2
VS
119#ifdef WXSTRING_STATISTICS
120 class Averager
121 {
122 public:
81727065 123 Averager(const wxStringCharType *sz) { m_sz = sz; m_nTotal = m_nCount = 0; }
a7ea63e2 124 ~Averager()
a4f270bc
VZ
125 {
126 wxPrintf("wxString %s: total = %lu, average = %f\n",
127 m_sz, m_nTotal, ((float)m_nTotal)/m_nCount);
128 }
a7ea63e2
VS
129
130 void Add(size_t n) { m_nTotal += n; m_nCount++; }
131
132 private:
a4f270bc 133 unsigned long m_nCount, m_nTotal;
81727065 134 const wxStringCharType *m_sz;
a7ea63e2
VS
135 } g_averageLength("allocation size"),
136 g_averageSummandLength("summand length"),
137 g_averageConcatHit("hit probability in concat"),
138 g_averageInitialLength("initial string length");
139
140 #define STATISTICS_ADD(av, val) g_average##av.Add(val)
141#else
142 #define STATISTICS_ADD(av, val)
143#endif // WXSTRING_STATISTICS
144
145// ===========================================================================
146// wxStringData class deallocation
147// ===========================================================================
148
149#if defined(__VISUALC__) && defined(_MT) && !defined(_DLL)
150# pragma message (__FILE__ ": building with Multithreaded non DLL runtime has a performance impact on wxString!")
151void wxStringData::Free()
152{
153 free(this);
154}
155#endif
156
157// ===========================================================================
158// wxStringImpl
159// ===========================================================================
160
161// takes nLength elements of psz starting at nPos
81727065
VS
162void wxStringImpl::InitWith(const wxStringCharType *psz,
163 size_t nPos, size_t nLength)
a7ea63e2
VS
164{
165 Init();
166
167 // if the length is not given, assume the string to be NUL terminated
168 if ( nLength == npos ) {
9a83f860 169 wxASSERT_MSG( nPos <= wxStrlen(psz), wxT("index out of bounds") );
a7ea63e2 170
52de37c7 171 nLength = wxStrlen(psz + nPos);
a7ea63e2
VS
172 }
173
174 STATISTICS_ADD(InitialLength, nLength);
175
176 if ( nLength > 0 ) {
177 // trailing '\0' is written in AllocBuffer()
178 if ( !AllocBuffer(nLength) ) {
9a83f860 179 wxFAIL_MSG( wxT("out of memory in wxStringImpl::InitWith") );
a7ea63e2
VS
180 return;
181 }
182 wxStringMemcpy(m_pchData, psz + nPos, nLength);
183 }
184}
185
f2a1b1bd 186wxStringImpl::wxStringImpl(const_iterator first, const_iterator last)
a7ea63e2 187{
f2a1b1bd 188 if ( last >= first )
a7ea63e2 189 {
fb252067 190 InitWith(first.GetPtr(), 0, last - first);
a7ea63e2
VS
191 }
192 else
193 {
9a83f860 194 wxFAIL_MSG( wxT("first must be before last") );
a7ea63e2
VS
195 Init();
196 }
197}
198
199wxStringImpl::wxStringImpl(size_type n, wxStringCharType ch)
200{
201 Init();
202 append(n, ch);
203}
204
205// ---------------------------------------------------------------------------
206// memory allocation
207// ---------------------------------------------------------------------------
208
209// allocates memory needed to store a C string of length nLen
210bool wxStringImpl::AllocBuffer(size_t nLen)
211{
212 // allocating 0 sized buffer doesn't make sense, all empty strings should
213 // reuse g_strEmpty
214 wxASSERT( nLen > 0 );
215
216 // make sure that we don't overflow
a0b7b493
VZ
217 wxCHECK( nLen < (INT_MAX / sizeof(wxStringCharType)) -
218 (sizeof(wxStringData) + EXTRA_ALLOC + 1), false );
a7ea63e2
VS
219
220 STATISTICS_ADD(Length, nLen);
221
222 // allocate memory:
223 // 1) one extra character for '\0' termination
224 // 2) sizeof(wxStringData) for housekeeping info
225 wxStringData* pData = (wxStringData*)
81727065 226 malloc(sizeof(wxStringData) + (nLen + EXTRA_ALLOC + 1)*sizeof(wxStringCharType));
a7ea63e2
VS
227
228 if ( pData == NULL ) {
229 // allocation failures are handled by the caller
230 return false;
231 }
232
233 pData->nRefs = 1;
234 pData->nDataLength = nLen;
235 pData->nAllocLength = nLen + EXTRA_ALLOC;
236 m_pchData = pData->data(); // data starts after wxStringData
237 m_pchData[nLen] = wxT('\0');
238 return true;
239}
240
241// must be called before changing this string
242bool wxStringImpl::CopyBeforeWrite()
243{
244 wxStringData* pData = GetStringData();
245
246 if ( pData->IsShared() ) {
247 pData->Unlock(); // memory not freed because shared
248 size_t nLen = pData->nDataLength;
249 if ( !AllocBuffer(nLen) ) {
250 // allocation failures are handled by the caller
251 return false;
252 }
253 wxStringMemcpy(m_pchData, pData->data(), nLen);
254 }
255
256 wxASSERT( !GetStringData()->IsShared() ); // we must be the only owner
257
258 return true;
259}
260
261// must be called before replacing contents of this string
262bool wxStringImpl::AllocBeforeWrite(size_t nLen)
263{
264 wxASSERT( nLen != 0 ); // doesn't make any sense
265
266 // must not share string and must have enough space
267 wxStringData* pData = GetStringData();
268 if ( pData->IsShared() || pData->IsEmpty() ) {
269 // can't work with old buffer, get new one
270 pData->Unlock();
271 if ( !AllocBuffer(nLen) ) {
272 // allocation failures are handled by the caller
273 return false;
274 }
275 }
276 else {
277 if ( nLen > pData->nAllocLength ) {
278 // realloc the buffer instead of calling malloc() again, this is more
279 // efficient
280 STATISTICS_ADD(Length, nLen);
281
282 nLen += EXTRA_ALLOC;
283
284 pData = (wxStringData*)
81727065
VS
285 realloc(pData,
286 sizeof(wxStringData) + (nLen + 1)*sizeof(wxStringCharType));
a7ea63e2
VS
287
288 if ( pData == NULL ) {
289 // allocation failures are handled by the caller
290 // keep previous data since reallocation failed
291 return false;
292 }
293
294 pData->nAllocLength = nLen;
295 m_pchData = pData->data();
296 }
297 }
298
299 wxASSERT( !GetStringData()->IsShared() ); // we must be the only owner
300
301 // it doesn't really matter what the string length is as it's going to be
302 // overwritten later but, for extra safety, set it to 0 for now as we may
303 // have some junk in m_pchData
304 GetStringData()->nDataLength = 0;
305
306 return true;
307}
308
309wxStringImpl& wxStringImpl::append(size_t n, wxStringCharType ch)
310{
311 size_type len = length();
312
313 if ( !Alloc(len + n) || !CopyBeforeWrite() ) {
9a83f860 314 wxFAIL_MSG( wxT("out of memory in wxStringImpl::append") );
6859e6fc 315 return *this;
a7ea63e2
VS
316 }
317 GetStringData()->nDataLength = len + n;
318 m_pchData[len + n] = '\0';
319 for ( size_t i = 0; i < n; ++i )
320 m_pchData[len + i] = ch;
321 return *this;
322}
323
324void wxStringImpl::resize(size_t nSize, wxStringCharType ch)
325{
326 size_t len = length();
327
328 if ( nSize < len )
329 {
330 erase(begin() + nSize, end());
331 }
332 else if ( nSize > len )
333 {
334 append(nSize - len, ch);
335 }
336 //else: we have exactly the specified length, nothing to do
337}
338
339// allocate enough memory for nLen characters
340bool wxStringImpl::Alloc(size_t nLen)
341{
342 wxStringData *pData = GetStringData();
343 if ( pData->nAllocLength <= nLen ) {
344 if ( pData->IsEmpty() ) {
a4f270bc
VZ
345 STATISTICS_ADD(Length, nLen);
346
a7ea63e2
VS
347 nLen += EXTRA_ALLOC;
348
349 pData = (wxStringData *)
81727065 350 malloc(sizeof(wxStringData) + (nLen + 1)*sizeof(wxStringCharType));
a7ea63e2
VS
351
352 if ( pData == NULL ) {
353 // allocation failure handled by caller
354 return false;
355 }
356
357 pData->nRefs = 1;
358 pData->nDataLength = 0;
359 pData->nAllocLength = nLen;
360 m_pchData = pData->data(); // data starts after wxStringData
361 m_pchData[0u] = wxT('\0');
362 }
363 else if ( pData->IsShared() ) {
364 pData->Unlock(); // memory not freed because shared
365 size_t nOldLen = pData->nDataLength;
366 if ( !AllocBuffer(nLen) ) {
367 // allocation failure handled by caller
368 return false;
369 }
370 // +1 to copy the terminator, too
81727065 371 memcpy(m_pchData, pData->data(), (nOldLen+1)*sizeof(wxStringCharType));
a7ea63e2
VS
372 GetStringData()->nDataLength = nOldLen;
373 }
374 else {
375 nLen += EXTRA_ALLOC;
376
377 pData = (wxStringData *)
81727065 378 realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(wxStringCharType));
a7ea63e2
VS
379
380 if ( pData == NULL ) {
381 // allocation failure handled by caller
382 // keep previous data since reallocation failed
383 return false;
384 }
385
386 // it's not important if the pointer changed or not (the check for this
387 // is not faster than assigning to m_pchData in all cases)
388 pData->nAllocLength = nLen;
389 m_pchData = pData->data();
390 }
391 }
392 //else: we've already got enough
393 return true;
394}
395
396wxStringImpl::iterator wxStringImpl::begin()
397{
6636ef8d 398 if ( !empty() )
a7ea63e2
VS
399 CopyBeforeWrite();
400 return m_pchData;
401}
402
403wxStringImpl::iterator wxStringImpl::end()
404{
6636ef8d 405 if ( !empty() )
a7ea63e2
VS
406 CopyBeforeWrite();
407 return m_pchData + length();
408}
409
410wxStringImpl::iterator wxStringImpl::erase(iterator it)
411{
412 size_type idx = it - begin();
413 erase(idx, 1);
414 return begin() + idx;
415}
416
417wxStringImpl& wxStringImpl::erase(size_t nStart, size_t nLen)
418{
419 wxASSERT(nStart <= length());
420 size_t strLen = length() - nStart;
421 // delete nLen or up to the end of the string characters
422 nLen = strLen < nLen ? strLen : nLen;
423 wxStringImpl strTmp(c_str(), nStart);
424 strTmp.append(c_str() + nStart + nLen, length() - nStart - nLen);
425
426 swap(strTmp);
427 return *this;
428}
429
81727065
VS
430wxStringImpl& wxStringImpl::insert(size_t nPos,
431 const wxStringCharType *sz, size_t n)
a7ea63e2
VS
432{
433 wxASSERT( nPos <= length() );
434
52de37c7 435 if ( n == npos ) n = wxStrlen(sz);
a7ea63e2
VS
436 if ( n == 0 ) return *this;
437
438 if ( !Alloc(length() + n) || !CopyBeforeWrite() ) {
9a83f860 439 wxFAIL_MSG( wxT("out of memory in wxStringImpl::insert") );
6859e6fc 440 return *this;
a7ea63e2
VS
441 }
442
443 memmove(m_pchData + nPos + n, m_pchData + nPos,
81727065
VS
444 (length() - nPos) * sizeof(wxStringCharType));
445 memcpy(m_pchData + nPos, sz, n * sizeof(wxStringCharType));
a7ea63e2
VS
446 GetStringData()->nDataLength = length() + n;
447 m_pchData[length()] = '\0';
448
449 return *this;
450}
451
452void wxStringImpl::swap(wxStringImpl& str)
453{
454 wxStringCharType* tmp = str.m_pchData;
455 str.m_pchData = m_pchData;
456 m_pchData = tmp;
457}
458
459size_t wxStringImpl::find(const wxStringImpl& str, size_t nStart) const
460{
461 // deal with the special case of empty string first
462 const size_t nLen = length();
463 const size_t nLenOther = str.length();
464
465 if ( !nLenOther )
466 {
467 // empty string is a substring of anything
468 return 0;
469 }
470
471 if ( !nLen )
472 {
473 // the other string is non empty so can't be our substring
474 return npos;
475 }
476
477 wxASSERT( str.GetStringData()->IsValid() );
478 wxASSERT( nStart <= nLen );
479
480 const wxStringCharType * const other = str.c_str();
481
482 // anchor
483 const wxStringCharType* p =
484 (const wxStringCharType*)wxStringMemchr(c_str() + nStart,
485 *other,
486 nLen - nStart);
487
488 if ( !p )
489 return npos;
490
491 while ( p - c_str() + nLenOther <= nLen &&
492 wxStringMemcmp(p, other, nLenOther) )
493 {
494 p++;
495
496 // anchor again
497 p = (const wxStringCharType*)
498 wxStringMemchr(p, *other, nLen - (p - c_str()));
499
500 if ( !p )
501 return npos;
502 }
503
504 return p - c_str() + nLenOther <= nLen ? p - c_str() : npos;
505}
506
81727065
VS
507size_t wxStringImpl::find(const wxStringCharType* sz,
508 size_t nStart, size_t n) const
a7ea63e2
VS
509{
510 return find(wxStringImpl(sz, n), nStart);
511}
512
513size_t wxStringImpl::find(wxStringCharType ch, size_t nStart) const
514{
515 wxASSERT( nStart <= length() );
516
517 const wxStringCharType *p = (const wxStringCharType*)
518 wxStringMemchr(c_str() + nStart, ch, length() - nStart);
519
520 return p == NULL ? npos : p - c_str();
521}
522
523size_t wxStringImpl::rfind(const wxStringImpl& str, size_t nStart) const
524{
525 wxASSERT( str.GetStringData()->IsValid() );
526 wxASSERT( nStart == npos || nStart <= length() );
527
528 if ( length() >= str.length() )
529 {
530 // avoids a corner case later
6636ef8d 531 if ( empty() && str.empty() )
a7ea63e2
VS
532 return 0;
533
534 // "top" is the point where search starts from
535 size_t top = length() - str.length();
536
537 if ( nStart == npos )
538 nStart = length() - 1;
539 if ( nStart < top )
540 top = nStart;
541
542 const wxStringCharType *cursor = c_str() + top;
543 do
544 {
545 if ( wxStringMemcmp(cursor, str.c_str(), str.length()) == 0 )
546 {
547 return cursor - c_str();
548 }
549 } while ( cursor-- > c_str() );
550 }
551
552 return npos;
553}
554
81727065
VS
555size_t wxStringImpl::rfind(const wxStringCharType* sz,
556 size_t nStart, size_t n) const
a7ea63e2
VS
557{
558 return rfind(wxStringImpl(sz, n), nStart);
559}
560
561size_t wxStringImpl::rfind(wxStringCharType ch, size_t nStart) const
562{
563 if ( nStart == npos )
564 {
565 nStart = length();
566 }
567 else
568 {
569 wxASSERT( nStart <= length() );
570 }
571
572 const wxStringCharType *actual;
573 for ( actual = c_str() + ( nStart == npos ? length() : nStart + 1 );
574 actual > c_str(); --actual )
575 {
576 if ( *(actual - 1) == ch )
577 return (actual - 1) - c_str();
578 }
579
580 return npos;
581}
582
583wxStringImpl& wxStringImpl::replace(size_t nStart, size_t nLen,
d545bded 584 const wxStringCharType *sz, size_t nCount)
a7ea63e2 585{
d545bded
VZ
586 // check and adjust parameters
587 const size_t lenOld = length();
a7ea63e2 588
d545bded 589 wxASSERT_MSG( nStart <= lenOld,
9a83f860 590 wxT("index out of bounds in wxStringImpl::replace") );
d545bded 591 size_t nEnd = nStart + nLen;
dfaae3e6 592 if ( nLen > lenOld - nStart )
d545bded
VZ
593 {
594 // nLen may be out of range, as it can be npos, just clump it down
595 nLen = lenOld - nStart;
596 nEnd = lenOld;
597 }
a7ea63e2 598
d545bded
VZ
599 if ( nCount == npos )
600 nCount = wxStrlen(sz);
a7ea63e2 601
d545bded
VZ
602 // build the new string from 3 pieces: part of this string before nStart,
603 // the new substring and the part of this string after nStart+nLen
604 wxStringImpl tmp;
605 const size_t lenNew = lenOld + nCount - nLen;
606 if ( lenNew )
607 {
608 tmp.AllocBuffer(lenOld + nCount - nLen);
609
610 wxStringCharType *dst = tmp.m_pchData;
611 memcpy(dst, m_pchData, nStart*sizeof(wxStringCharType));
612 dst += nStart;
613
614 memcpy(dst, sz, nCount*sizeof(wxStringCharType));
615 dst += nCount;
616
617 memcpy(dst, m_pchData + nEnd, (lenOld - nEnd)*sizeof(wxStringCharType));
618 }
619
620 // and replace this string contents with the new one
621 swap(tmp);
622 return *this;
a7ea63e2
VS
623}
624
625wxStringImpl wxStringImpl::substr(size_t nStart, size_t nLen) const
626{
627 if ( nLen == npos )
628 nLen = length() - nStart;
629 return wxStringImpl(*this, nStart, nLen);
630}
631
632// assigns one string to another
633wxStringImpl& wxStringImpl::operator=(const wxStringImpl& stringSrc)
634{
635 wxASSERT( stringSrc.GetStringData()->IsValid() );
636
637 // don't copy string over itself
638 if ( m_pchData != stringSrc.m_pchData ) {
639 if ( stringSrc.GetStringData()->IsEmpty() ) {
640 Reinit();
641 }
642 else {
643 // adjust references
644 GetStringData()->Unlock();
645 m_pchData = stringSrc.m_pchData;
646 GetStringData()->Lock();
647 }
648 }
649
650 return *this;
651}
652
653// assigns a single character
654wxStringImpl& wxStringImpl::operator=(wxStringCharType ch)
655{
81727065 656 wxStringCharType c(ch);
a7ea63e2 657 if ( !AssignCopy(1, &c) ) {
9a83f860 658 wxFAIL_MSG( wxT("out of memory in wxStringImpl::operator=(wxStringCharType)") );
a7ea63e2
VS
659 }
660 return *this;
661}
662
663// assigns C string
81727065 664wxStringImpl& wxStringImpl::operator=(const wxStringCharType *psz)
a7ea63e2 665{
52de37c7 666 if ( !AssignCopy(wxStrlen(psz), psz) ) {
9a83f860 667 wxFAIL_MSG( wxT("out of memory in wxStringImpl::operator=(const wxStringCharType *)") );
a7ea63e2
VS
668 }
669 return *this;
670}
671
672// helper function: does real copy
81727065
VS
673bool wxStringImpl::AssignCopy(size_t nSrcLen,
674 const wxStringCharType *pszSrcData)
a7ea63e2
VS
675{
676 if ( nSrcLen == 0 ) {
677 Reinit();
678 }
679 else {
680 if ( !AllocBeforeWrite(nSrcLen) ) {
681 // allocation failure handled by caller
682 return false;
683 }
c2cd367f
VZ
684
685 // use memmove() and not memcpy() here as we might be copying from our own
686 // buffer in case of assignment such as "s = s.c_str()" (see #11294)
687 memmove(m_pchData, pszSrcData, nSrcLen*sizeof(wxStringCharType));
688
a7ea63e2
VS
689 GetStringData()->nDataLength = nSrcLen;
690 m_pchData[nSrcLen] = wxT('\0');
691 }
692 return true;
693}
694
695// ---------------------------------------------------------------------------
696// string concatenation
697// ---------------------------------------------------------------------------
698
699// add something to this string
81727065
VS
700bool wxStringImpl::ConcatSelf(size_t nSrcLen,
701 const wxStringCharType *pszSrcData,
a7ea63e2
VS
702 size_t nMaxLen)
703{
704 STATISTICS_ADD(SummandLength, nSrcLen);
705
706 nSrcLen = nSrcLen < nMaxLen ? nSrcLen : nMaxLen;
707
708 // concatenating an empty string is a NOP
709 if ( nSrcLen > 0 ) {
710 wxStringData *pData = GetStringData();
711 size_t nLen = pData->nDataLength;
d545bded
VZ
712
713 // take special care when appending part of this string to itself: the code
714 // below reallocates our buffer and this invalidates pszSrcData pointer so
715 // we have to copy it in another temporary string in this case (but avoid
716 // doing this unnecessarily)
717 if ( pszSrcData >= m_pchData && pszSrcData < m_pchData + nLen )
718 {
719 wxStringImpl tmp(pszSrcData, nSrcLen);
720 return ConcatSelf(nSrcLen, tmp.m_pchData, nSrcLen);
721 }
722
a7ea63e2
VS
723 size_t nNewLen = nLen + nSrcLen;
724
725 // alloc new buffer if current is too small
726 if ( pData->IsShared() ) {
727 STATISTICS_ADD(ConcatHit, 0);
728
729 // we have to allocate another buffer
730 wxStringData* pOldData = GetStringData();
731 if ( !AllocBuffer(nNewLen) ) {
732 // allocation failure handled by caller
733 return false;
734 }
81727065 735 memcpy(m_pchData, pOldData->data(), nLen*sizeof(wxStringCharType));
a7ea63e2
VS
736 pOldData->Unlock();
737 }
738 else if ( nNewLen > pData->nAllocLength ) {
739 STATISTICS_ADD(ConcatHit, 0);
740
741 reserve(nNewLen);
742 // we have to grow the buffer
743 if ( capacity() < nNewLen ) {
744 // allocation failure handled by caller
745 return false;
746 }
747 }
748 else {
749 STATISTICS_ADD(ConcatHit, 1);
750
751 // the buffer is already big enough
752 }
753
754 // should be enough space
755 wxASSERT( nNewLen <= GetStringData()->nAllocLength );
756
757 // fast concatenation - all is done in our buffer
81727065 758 memcpy(m_pchData + nLen, pszSrcData, nSrcLen*sizeof(wxStringCharType));
a7ea63e2
VS
759
760 m_pchData[nNewLen] = wxT('\0'); // put terminating '\0'
761 GetStringData()->nDataLength = nNewLen; // and fix the length
762 }
763 //else: the string to append was empty
764 return true;
765}
766
a7ea63e2 767// get the pointer to writable buffer of (at least) nLen bytes
c87a0bc8 768wxStringCharType *wxStringImpl::DoGetWriteBuf(size_t nLen)
a7ea63e2
VS
769{
770 if ( !AllocBeforeWrite(nLen) ) {
771 // allocation failure handled by caller
772 return NULL;
773 }
774
775 wxASSERT( GetStringData()->nRefs == 1 );
776 GetStringData()->Validate(false);
777
778 return m_pchData;
779}
780
781// put string back in a reasonable state after GetWriteBuf
782void wxStringImpl::DoUngetWriteBuf()
783{
52de37c7 784 DoUngetWriteBuf(wxStrlen(m_pchData));
a7ea63e2
VS
785}
786
787void wxStringImpl::DoUngetWriteBuf(size_t nLen)
788{
789 wxStringData * const pData = GetStringData();
790
9a83f860 791 wxASSERT_MSG( nLen < pData->nAllocLength, wxT("buffer overrun") );
a7ea63e2
VS
792
793 // the strings we store are always NUL-terminated
9a83f860 794 pData->data()[nLen] = wxT('\0');
a7ea63e2
VS
795 pData->nDataLength = nLen;
796 pData->Validate(true);
797}
a7ea63e2
VS
798
799#endif // !wxUSE_STL_BASED_WXSTRING