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