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