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