]> git.saurik.com Git - wxWidgets.git/blame - src/common/stringimpl.cpp
implement GetBestSize() (patch 1386199)
[wxWidgets.git] / src / common / stringimpl.cpp
CommitLineData
a7ea63e2
VS
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
74const size_t wxStringImpl::npos = (size_t) -1;
75#endif
76
77// ----------------------------------------------------------------------------
78// static data
79// ----------------------------------------------------------------------------
80
81#if wxUSE_STL_BASED_WXSTRING
82
83extern 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)
90static 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
97extern 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!")
139void wxStringData::Free()
140{
141 free(this);
142}
143#endif
144
145// ===========================================================================
146// wxStringImpl
147// ===========================================================================
148
149// takes nLength elements of psz starting at nPos
150void 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
174wxStringImpl::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
188wxStringImpl::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
199bool 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
231bool 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
251bool 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
297wxStringImpl& 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
311void 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
327bool 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
381wxStringImpl::iterator wxStringImpl::begin()
382{
383 if (length() > 0)
384 CopyBeforeWrite();
385 return m_pchData;
386}
387
388wxStringImpl::iterator wxStringImpl::end()
389{
390 if (length() > 0)
391 CopyBeforeWrite();
392 return m_pchData + length();
393}
394
395wxStringImpl::iterator wxStringImpl::erase(iterator it)
396{
397 size_type idx = it - begin();
398 erase(idx, 1);
399 return begin() + idx;
400}
401
402wxStringImpl& 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
415wxStringImpl& 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
435void wxStringImpl::swap(wxStringImpl& str)
436{
437 wxStringCharType* tmp = str.m_pchData;
438 str.m_pchData = m_pchData;
439 m_pchData = tmp;
440}
441
442size_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
490size_t wxStringImpl::find(const wxChar* sz, size_t nStart, size_t n) const
491{
492 return find(wxStringImpl(sz, n), nStart);
493}
494
495size_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
505size_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
537size_t wxStringImpl::rfind(const wxChar* sz, size_t nStart, size_t n) const
538{
539 return rfind(wxStringImpl(sz, n), nStart);
540}
541
542size_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
564wxStringImpl& 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
596wxStringImpl& 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
602wxStringImpl& 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
609wxStringImpl& 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
615wxStringImpl 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
623wxStringImpl& 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
644wxStringImpl& 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
654wxStringImpl& 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
663bool 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
685bool 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
742wxChar *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
756void wxStringImpl::DoUngetWriteBuf()
757{
758 DoUngetWriteBuf(wxStrlen(m_pchData));
759}
760
761void 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