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