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