XRC: make wxStaticText's wrap property a dimension.
[wxWidgets.git] / src / common / stringimpl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/stringimpl.cpp
3 // Purpose: wxString class
4 // Author: Vadim Zeitlin, Ryan Norton
5 // Modified by:
6 // Created: 29/01/98
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"
32 #include "wx/wxcrt.h"
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
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
55 #else
56 #define wxStringMemcpy wxTmemcpy
57 #define wxStringMemcmp wxTmemcmp
58 #define wxStringMemchr wxTmemchr
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
68 const size_t wxStringImpl::npos = (size_t) -1;
69 #endif
70
71 // ----------------------------------------------------------------------------
72 // static data
73 // ----------------------------------------------------------------------------
74
75 #if wxUSE_STL_BASED_WXSTRING
76
77 // FIXME-UTF8: get rid of this, have only one wxEmptyString
78 #if wxUSE_UNICODE_UTF8
79 const wxStringCharType WXDLLIMPEXP_BASE *wxEmptyStringImpl = "";
80 #endif
81 const wxChar WXDLLIMPEXP_BASE *wxEmptyString = wxT("");
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)
88 static const struct
89 {
90 wxStringData data;
91 wxStringCharType dummy;
92 } g_strEmpty = { {-1, 0, 0}, wxT('\0') };
93
94 // empty C style string: points to 'string data' byte of g_strEmpty
95 #if wxUSE_UNICODE_UTF8
96 // FIXME-UTF8: get rid of this, have only one wxEmptyString
97 const wxStringCharType WXDLLIMPEXP_BASE *wxEmptyStringImpl = &g_strEmpty.dummy;
98 const wxChar WXDLLIMPEXP_BASE *wxEmptyString = wxT("");
99 #else
100 const wxStringCharType WXDLLIMPEXP_BASE *wxEmptyString = &g_strEmpty.dummy;
101 #endif
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
113
114 // uncomment this to enable gathering of some statistics about wxString
115 // efficiency
116 //#define WXSTRING_STATISTICS
117
118 #ifdef WXSTRING_STATISTICS
119 class Averager
120 {
121 public:
122 Averager(const wxStringCharType *sz) { m_sz = sz; m_nTotal = m_nCount = 0; }
123 ~Averager()
124 {
125 wxPrintf("wxString %s: total = %lu, average = %f\n",
126 m_sz, m_nTotal, ((float)m_nTotal)/m_nCount);
127 }
128
129 void Add(size_t n) { m_nTotal += n; m_nCount++; }
130
131 private:
132 unsigned long m_nCount, m_nTotal;
133 const wxStringCharType *m_sz;
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!")
150 void wxStringData::Free()
151 {
152 free(this);
153 }
154 #endif
155
156 // ===========================================================================
157 // wxStringImpl
158 // ===========================================================================
159
160 // takes nLength elements of psz starting at nPos
161 void wxStringImpl::InitWith(const wxStringCharType *psz,
162 size_t nPos, size_t nLength)
163 {
164 Init();
165
166 // if the length is not given, assume the string to be NUL terminated
167 if ( nLength == npos ) {
168 wxASSERT_MSG( nPos <= wxStrlen(psz), wxT("index out of bounds") );
169
170 nLength = wxStrlen(psz + nPos);
171 }
172
173 STATISTICS_ADD(InitialLength, nLength);
174
175 if ( nLength > 0 ) {
176 // trailing '\0' is written in AllocBuffer()
177 if ( !AllocBuffer(nLength) ) {
178 wxFAIL_MSG( wxT("out of memory in wxStringImpl::InitWith") );
179 return;
180 }
181 wxStringMemcpy(m_pchData, psz + nPos, nLength);
182 }
183 }
184
185 wxStringImpl::wxStringImpl(const_iterator first, const_iterator last)
186 {
187 if ( last >= first )
188 {
189 InitWith(first.GetPtr(), 0, last - first);
190 }
191 else
192 {
193 wxFAIL_MSG( wxT("first must be before last") );
194 Init();
195 }
196 }
197
198 wxStringImpl::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
209 bool 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
216 wxCHECK( nLen < (INT_MAX / sizeof(wxStringCharType)) -
217 (sizeof(wxStringData) + EXTRA_ALLOC + 1), false );
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*)
225 malloc(sizeof(wxStringData) + (nLen + EXTRA_ALLOC + 1)*sizeof(wxStringCharType));
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
241 bool 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
261 bool 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*)
284 realloc(pData,
285 sizeof(wxStringData) + (nLen + 1)*sizeof(wxStringCharType));
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
308 wxStringImpl& wxStringImpl::append(size_t n, wxStringCharType ch)
309 {
310 size_type len = length();
311
312 if ( !Alloc(len + n) || !CopyBeforeWrite() ) {
313 wxFAIL_MSG( wxT("out of memory in wxStringImpl::append") );
314 return *this;
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
323 void 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
339 bool wxStringImpl::Alloc(size_t nLen)
340 {
341 wxStringData *pData = GetStringData();
342 if ( pData->nAllocLength <= nLen ) {
343 if ( pData->IsEmpty() ) {
344 STATISTICS_ADD(Length, nLen);
345
346 nLen += EXTRA_ALLOC;
347
348 pData = (wxStringData *)
349 malloc(sizeof(wxStringData) + (nLen + 1)*sizeof(wxStringCharType));
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
370 memcpy(m_pchData, pData->data(), (nOldLen+1)*sizeof(wxStringCharType));
371 GetStringData()->nDataLength = nOldLen;
372 }
373 else {
374 nLen += EXTRA_ALLOC;
375
376 pData = (wxStringData *)
377 realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(wxStringCharType));
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
395 wxStringImpl::iterator wxStringImpl::begin()
396 {
397 if ( !empty() )
398 CopyBeforeWrite();
399 return m_pchData;
400 }
401
402 wxStringImpl::iterator wxStringImpl::end()
403 {
404 if ( !empty() )
405 CopyBeforeWrite();
406 return m_pchData + length();
407 }
408
409 wxStringImpl::iterator wxStringImpl::erase(iterator it)
410 {
411 size_type idx = it - begin();
412 erase(idx, 1);
413 return begin() + idx;
414 }
415
416 wxStringImpl& 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
429 wxStringImpl& wxStringImpl::insert(size_t nPos,
430 const wxStringCharType *sz, size_t n)
431 {
432 wxASSERT( nPos <= length() );
433
434 if ( n == npos ) n = wxStrlen(sz);
435 if ( n == 0 ) return *this;
436
437 if ( !Alloc(length() + n) || !CopyBeforeWrite() ) {
438 wxFAIL_MSG( wxT("out of memory in wxStringImpl::insert") );
439 return *this;
440 }
441
442 memmove(m_pchData + nPos + n, m_pchData + nPos,
443 (length() - nPos) * sizeof(wxStringCharType));
444 memcpy(m_pchData + nPos, sz, n * sizeof(wxStringCharType));
445 GetStringData()->nDataLength = length() + n;
446 m_pchData[length()] = '\0';
447
448 return *this;
449 }
450
451 void wxStringImpl::swap(wxStringImpl& str)
452 {
453 wxStringCharType* tmp = str.m_pchData;
454 str.m_pchData = m_pchData;
455 m_pchData = tmp;
456 }
457
458 size_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
506 size_t wxStringImpl::find(const wxStringCharType* sz,
507 size_t nStart, size_t n) const
508 {
509 return find(wxStringImpl(sz, n), nStart);
510 }
511
512 size_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
522 size_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
530 if ( empty() && str.empty() )
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
554 size_t wxStringImpl::rfind(const wxStringCharType* sz,
555 size_t nStart, size_t n) const
556 {
557 return rfind(wxStringImpl(sz, n), nStart);
558 }
559
560 size_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
582 wxStringImpl& wxStringImpl::replace(size_t nStart, size_t nLen,
583 const wxStringCharType *sz, size_t nCount)
584 {
585 // check and adjust parameters
586 const size_t lenOld = length();
587
588 wxASSERT_MSG( nStart <= lenOld,
589 wxT("index out of bounds in wxStringImpl::replace") );
590 size_t nEnd = nStart + nLen;
591 if ( nLen > lenOld - nStart )
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 }
597
598 if ( nCount == npos )
599 nCount = wxStrlen(sz);
600
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;
622 }
623
624 wxStringImpl 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
632 wxStringImpl& 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
653 wxStringImpl& wxStringImpl::operator=(wxStringCharType ch)
654 {
655 wxStringCharType c(ch);
656 if ( !AssignCopy(1, &c) ) {
657 wxFAIL_MSG( wxT("out of memory in wxStringImpl::operator=(wxStringCharType)") );
658 }
659 return *this;
660 }
661
662 // assigns C string
663 wxStringImpl& wxStringImpl::operator=(const wxStringCharType *psz)
664 {
665 if ( !AssignCopy(wxStrlen(psz), psz) ) {
666 wxFAIL_MSG( wxT("out of memory in wxStringImpl::operator=(const wxStringCharType *)") );
667 }
668 return *this;
669 }
670
671 // helper function: does real copy
672 bool wxStringImpl::AssignCopy(size_t nSrcLen,
673 const wxStringCharType *pszSrcData)
674 {
675 if ( nSrcLen == 0 ) {
676 Reinit();
677 }
678 else {
679 if ( !AllocBeforeWrite(nSrcLen) ) {
680 // allocation failure handled by caller
681 return false;
682 }
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
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
699 bool wxStringImpl::ConcatSelf(size_t nSrcLen,
700 const wxStringCharType *pszSrcData,
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;
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
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 }
734 memcpy(m_pchData, pOldData->data(), nLen*sizeof(wxStringCharType));
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
757 memcpy(m_pchData + nLen, pszSrcData, nSrcLen*sizeof(wxStringCharType));
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
766 // get the pointer to writable buffer of (at least) nLen bytes
767 wxStringCharType *wxStringImpl::DoGetWriteBuf(size_t nLen)
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
781 void wxStringImpl::DoUngetWriteBuf()
782 {
783 DoUngetWriteBuf(wxStrlen(m_pchData));
784 }
785
786 void wxStringImpl::DoUngetWriteBuf(size_t nLen)
787 {
788 wxStringData * const pData = GetStringData();
789
790 wxASSERT_MSG( nLen < pData->nAllocLength, wxT("buffer overrun") );
791
792 // the strings we store are always NUL-terminated
793 pData->data()[nLen] = wxT('\0');
794 pData->nDataLength = nLen;
795 pData->Validate(true);
796 }
797
798 #endif // !wxUSE_STL_BASED_WXSTRING