1// Copyright 2014 PDFium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include <stddef.h>
8
9#include <algorithm>
10#include <cctype>
11
12#include "core/fxcrt/cfx_string_pool_template.h"
13#include "core/fxcrt/fx_basic.h"
14#include "core/fxcrt/fx_ext.h"
15#include "third_party/base/numerics/safe_math.h"
16
17template class CFX_StringDataTemplate<FX_WCHAR>;
18template class CFX_StringCTemplate<FX_WCHAR>;
19template class CFX_StringPoolTemplate<CFX_WideString>;
20template struct std::hash<CFX_WideString>;
21
22namespace {
23
24#ifndef NDEBUG
25bool IsValidCodePage(uint16_t codepage) {
26  switch (codepage) {
27    case 0:
28    case 932:
29    case 936:
30    case 949:
31    case 950:
32      return true;
33
34    default:
35      return false;
36  }
37}
38#endif
39
40const FX_WCHAR* FX_wcsstr(const FX_WCHAR* haystack,
41                          int haystack_len,
42                          const FX_WCHAR* needle,
43                          int needle_len) {
44  if (needle_len > haystack_len || needle_len == 0) {
45    return nullptr;
46  }
47  const FX_WCHAR* end_ptr = haystack + haystack_len - needle_len;
48  while (haystack <= end_ptr) {
49    int i = 0;
50    while (1) {
51      if (haystack[i] != needle[i]) {
52        break;
53      }
54      i++;
55      if (i == needle_len) {
56        return haystack;
57      }
58    }
59    haystack++;
60  }
61  return nullptr;
62}
63
64}  // namespace
65
66static_assert(sizeof(CFX_WideString) <= sizeof(FX_WCHAR*),
67              "Strings must not require more space than pointers");
68
69CFX_WideString::CFX_WideString() {}
70
71CFX_WideString::CFX_WideString(const CFX_WideString& other)
72    : m_pData(other.m_pData) {}
73
74CFX_WideString::CFX_WideString(CFX_WideString&& other) {
75  m_pData.Swap(other.m_pData);
76}
77
78CFX_WideString::CFX_WideString(const FX_WCHAR* pStr, FX_STRSIZE nLen) {
79  if (nLen < 0)
80    nLen = pStr ? FXSYS_wcslen(pStr) : 0;
81
82  if (nLen)
83    m_pData.Reset(StringData::Create(pStr, nLen));
84}
85
86CFX_WideString::CFX_WideString(FX_WCHAR ch) {
87  m_pData.Reset(StringData::Create(1));
88  m_pData->m_String[0] = ch;
89}
90
91CFX_WideString::CFX_WideString(const FX_WCHAR* ptr)
92    : CFX_WideString(ptr, ptr ? FXSYS_wcslen(ptr) : 0) {}
93
94CFX_WideString::CFX_WideString(const CFX_WideStringC& stringSrc) {
95  if (!stringSrc.IsEmpty()) {
96    m_pData.Reset(StringData::Create(stringSrc.c_str(), stringSrc.GetLength()));
97  }
98}
99
100CFX_WideString::CFX_WideString(const CFX_WideStringC& str1,
101                               const CFX_WideStringC& str2) {
102  int nNewLen = str1.GetLength() + str2.GetLength();
103  if (nNewLen == 0)
104    return;
105
106  m_pData.Reset(StringData::Create(nNewLen));
107  m_pData->CopyContents(str1.c_str(), str1.GetLength());
108  m_pData->CopyContentsAt(str1.GetLength(), str2.c_str(), str2.GetLength());
109}
110
111CFX_WideString::~CFX_WideString() {}
112
113const CFX_WideString& CFX_WideString::operator=(const FX_WCHAR* pStr) {
114  if (!pStr || !pStr[0])
115    clear();
116  else
117    AssignCopy(pStr, FXSYS_wcslen(pStr));
118
119  return *this;
120}
121
122const CFX_WideString& CFX_WideString::operator=(
123    const CFX_WideStringC& stringSrc) {
124  if (stringSrc.IsEmpty())
125    clear();
126  else
127    AssignCopy(stringSrc.c_str(), stringSrc.GetLength());
128
129  return *this;
130}
131
132const CFX_WideString& CFX_WideString::operator=(
133    const CFX_WideString& stringSrc) {
134  if (m_pData != stringSrc.m_pData)
135    m_pData = stringSrc.m_pData;
136
137  return *this;
138}
139
140const CFX_WideString& CFX_WideString::operator+=(const FX_WCHAR* pStr) {
141  if (pStr)
142    Concat(pStr, FXSYS_wcslen(pStr));
143
144  return *this;
145}
146
147const CFX_WideString& CFX_WideString::operator+=(FX_WCHAR ch) {
148  Concat(&ch, 1);
149  return *this;
150}
151
152const CFX_WideString& CFX_WideString::operator+=(const CFX_WideString& str) {
153  if (str.m_pData)
154    Concat(str.m_pData->m_String, str.m_pData->m_nDataLength);
155
156  return *this;
157}
158
159const CFX_WideString& CFX_WideString::operator+=(const CFX_WideStringC& str) {
160  if (!str.IsEmpty())
161    Concat(str.c_str(), str.GetLength());
162
163  return *this;
164}
165
166bool CFX_WideString::operator==(const wchar_t* ptr) const {
167  if (!m_pData)
168    return !ptr || !ptr[0];
169
170  if (!ptr)
171    return m_pData->m_nDataLength == 0;
172
173  return wcslen(ptr) == static_cast<size_t>(m_pData->m_nDataLength) &&
174         wmemcmp(ptr, m_pData->m_String, m_pData->m_nDataLength) == 0;
175}
176
177bool CFX_WideString::operator==(const CFX_WideStringC& str) const {
178  if (!m_pData)
179    return str.IsEmpty();
180
181  return m_pData->m_nDataLength == str.GetLength() &&
182         wmemcmp(m_pData->m_String, str.c_str(), str.GetLength()) == 0;
183}
184
185bool CFX_WideString::operator==(const CFX_WideString& other) const {
186  if (m_pData == other.m_pData)
187    return true;
188
189  if (IsEmpty())
190    return other.IsEmpty();
191
192  if (other.IsEmpty())
193    return false;
194
195  return other.m_pData->m_nDataLength == m_pData->m_nDataLength &&
196         wmemcmp(other.m_pData->m_String, m_pData->m_String,
197                 m_pData->m_nDataLength) == 0;
198}
199
200bool CFX_WideString::operator<(const CFX_WideString& str) const {
201  if (m_pData == str.m_pData)
202    return false;
203
204  int result =
205      wmemcmp(c_str(), str.c_str(), std::min(GetLength(), str.GetLength()));
206  return result < 0 || (result == 0 && GetLength() < str.GetLength());
207}
208
209void CFX_WideString::AssignCopy(const FX_WCHAR* pSrcData, FX_STRSIZE nSrcLen) {
210  AllocBeforeWrite(nSrcLen);
211  m_pData->CopyContents(pSrcData, nSrcLen);
212  m_pData->m_nDataLength = nSrcLen;
213}
214
215void CFX_WideString::ReallocBeforeWrite(FX_STRSIZE nNewLength) {
216  if (m_pData && m_pData->CanOperateInPlace(nNewLength))
217    return;
218
219  if (nNewLength <= 0) {
220    clear();
221    return;
222  }
223
224  CFX_RetainPtr<StringData> pNewData(StringData::Create(nNewLength));
225  if (m_pData) {
226    FX_STRSIZE nCopyLength = std::min(m_pData->m_nDataLength, nNewLength);
227    pNewData->CopyContents(m_pData->m_String, nCopyLength);
228    pNewData->m_nDataLength = nCopyLength;
229  } else {
230    pNewData->m_nDataLength = 0;
231  }
232  pNewData->m_String[pNewData->m_nDataLength] = 0;
233  m_pData.Swap(pNewData);
234}
235
236void CFX_WideString::AllocBeforeWrite(FX_STRSIZE nNewLength) {
237  if (m_pData && m_pData->CanOperateInPlace(nNewLength))
238    return;
239
240  if (nNewLength <= 0) {
241    clear();
242    return;
243  }
244
245  m_pData.Reset(StringData::Create(nNewLength));
246}
247
248void CFX_WideString::ReleaseBuffer(FX_STRSIZE nNewLength) {
249  if (!m_pData)
250    return;
251
252  if (nNewLength == -1)
253    nNewLength = FXSYS_wcslen(m_pData->m_String);
254
255  nNewLength = std::min(nNewLength, m_pData->m_nAllocLength);
256  if (nNewLength == 0) {
257    clear();
258    return;
259  }
260
261  ASSERT(m_pData->m_nRefs == 1);
262  m_pData->m_nDataLength = nNewLength;
263  m_pData->m_String[nNewLength] = 0;
264  if (m_pData->m_nAllocLength - nNewLength >= 32) {
265    // Over arbitrary threshold, so pay the price to relocate.  Force copy to
266    // always occur by holding a second reference to the string.
267    CFX_WideString preserve(*this);
268    ReallocBeforeWrite(nNewLength);
269  }
270}
271
272void CFX_WideString::Reserve(FX_STRSIZE len) {
273  GetBuffer(len);
274}
275
276FX_WCHAR* CFX_WideString::GetBuffer(FX_STRSIZE nMinBufLength) {
277  if (!m_pData) {
278    if (nMinBufLength == 0)
279      return nullptr;
280
281    m_pData.Reset(StringData::Create(nMinBufLength));
282    m_pData->m_nDataLength = 0;
283    m_pData->m_String[0] = 0;
284    return m_pData->m_String;
285  }
286
287  if (m_pData->CanOperateInPlace(nMinBufLength))
288    return m_pData->m_String;
289
290  nMinBufLength = std::max(nMinBufLength, m_pData->m_nDataLength);
291  if (nMinBufLength == 0)
292    return nullptr;
293
294  CFX_RetainPtr<StringData> pNewData(StringData::Create(nMinBufLength));
295  pNewData->CopyContents(*m_pData);
296  pNewData->m_nDataLength = m_pData->m_nDataLength;
297  m_pData.Swap(pNewData);
298  return m_pData->m_String;
299}
300
301FX_STRSIZE CFX_WideString::Delete(FX_STRSIZE nIndex, FX_STRSIZE nCount) {
302  if (!m_pData)
303    return 0;
304
305  if (nIndex < 0)
306    nIndex = 0;
307
308  FX_STRSIZE nOldLength = m_pData->m_nDataLength;
309  if (nCount > 0 && nIndex < nOldLength) {
310    FX_STRSIZE mLength = nIndex + nCount;
311    if (mLength >= nOldLength) {
312      m_pData->m_nDataLength = nIndex;
313      return m_pData->m_nDataLength;
314    }
315    ReallocBeforeWrite(nOldLength);
316    int nCharsToCopy = nOldLength - mLength + 1;
317    wmemmove(m_pData->m_String + nIndex, m_pData->m_String + mLength,
318             nCharsToCopy);
319    m_pData->m_nDataLength = nOldLength - nCount;
320  }
321  return m_pData->m_nDataLength;
322}
323
324void CFX_WideString::Concat(const FX_WCHAR* pSrcData, FX_STRSIZE nSrcLen) {
325  if (!pSrcData || nSrcLen <= 0)
326    return;
327
328  if (!m_pData) {
329    m_pData.Reset(StringData::Create(pSrcData, nSrcLen));
330    return;
331  }
332
333  if (m_pData->CanOperateInPlace(m_pData->m_nDataLength + nSrcLen)) {
334    m_pData->CopyContentsAt(m_pData->m_nDataLength, pSrcData, nSrcLen);
335    m_pData->m_nDataLength += nSrcLen;
336    return;
337  }
338
339  CFX_RetainPtr<StringData> pNewData(
340      StringData::Create(m_pData->m_nDataLength + nSrcLen));
341  pNewData->CopyContents(*m_pData);
342  pNewData->CopyContentsAt(m_pData->m_nDataLength, pSrcData, nSrcLen);
343  m_pData.Swap(pNewData);
344}
345
346CFX_ByteString CFX_WideString::UTF8Encode() const {
347  return FX_UTF8Encode(AsStringC());
348}
349
350CFX_ByteString CFX_WideString::UTF16LE_Encode() const {
351  if (!m_pData) {
352    return CFX_ByteString("\0\0", 2);
353  }
354  int len = m_pData->m_nDataLength;
355  CFX_ByteString result;
356  FX_CHAR* buffer = result.GetBuffer(len * 2 + 2);
357  for (int i = 0; i < len; i++) {
358    buffer[i * 2] = m_pData->m_String[i] & 0xff;
359    buffer[i * 2 + 1] = m_pData->m_String[i] >> 8;
360  }
361  buffer[len * 2] = 0;
362  buffer[len * 2 + 1] = 0;
363  result.ReleaseBuffer(len * 2 + 2);
364  return result;
365}
366
367CFX_WideString CFX_WideString::Mid(FX_STRSIZE nFirst) const {
368  if (!m_pData)
369    return CFX_WideString();
370
371  return Mid(nFirst, m_pData->m_nDataLength - nFirst);
372}
373
374CFX_WideString CFX_WideString::Mid(FX_STRSIZE nFirst, FX_STRSIZE nCount) const {
375  if (!m_pData)
376    return CFX_WideString();
377
378  nFirst = std::min(std::max(nFirst, 0), m_pData->m_nDataLength);
379  nCount = std::min(std::max(nCount, 0), m_pData->m_nDataLength - nFirst);
380  if (nCount == 0)
381    return CFX_WideString();
382
383  if (nFirst == 0 && nCount == m_pData->m_nDataLength)
384    return *this;
385
386  CFX_WideString dest;
387  AllocCopy(dest, nCount, nFirst);
388  return dest;
389}
390
391void CFX_WideString::AllocCopy(CFX_WideString& dest,
392                               FX_STRSIZE nCopyLen,
393                               FX_STRSIZE nCopyIndex) const {
394  if (nCopyLen <= 0)
395    return;
396
397  CFX_RetainPtr<StringData> pNewData(
398      StringData::Create(m_pData->m_String + nCopyIndex, nCopyLen));
399  dest.m_pData.Swap(pNewData);
400}
401
402#define FORCE_ANSI 0x10000
403#define FORCE_UNICODE 0x20000
404#define FORCE_INT64 0x40000
405
406void CFX_WideString::FormatV(const FX_WCHAR* pFormat, va_list argList) {
407  va_list argListSave;
408#if defined(__ARMCC_VERSION) ||                                              \
409    (!defined(_MSC_VER) && (_FX_CPU_ == _FX_X64_ || _FX_CPU_ == _FX_IA64_ || \
410                            _FX_CPU_ == _FX_ARM64_)) ||                      \
411    defined(__native_client__)
412  va_copy(argListSave, argList);
413#else
414  argListSave = argList;
415#endif
416  int nMaxLen = 0;
417  for (const FX_WCHAR* pStr = pFormat; *pStr != 0; pStr++) {
418    if (*pStr != '%' || *(pStr = pStr + 1) == '%') {
419      nMaxLen += FXSYS_wcslen(pStr);
420      continue;
421    }
422    int nItemLen = 0;
423    int nWidth = 0;
424    for (; *pStr != 0; pStr++) {
425      if (*pStr == '#') {
426        nMaxLen += 2;
427      } else if (*pStr == '*') {
428        nWidth = va_arg(argList, int);
429      } else if (*pStr != '-' && *pStr != '+' && *pStr != '0' && *pStr != ' ') {
430        break;
431      }
432    }
433    if (nWidth == 0) {
434      nWidth = FXSYS_wtoi(pStr);
435      while (std::iswdigit(*pStr))
436        ++pStr;
437    }
438    if (nWidth < 0 || nWidth > 128 * 1024) {
439      pFormat = L"Bad width";
440      nMaxLen = 10;
441      break;
442    }
443    int nPrecision = 0;
444    if (*pStr == '.') {
445      pStr++;
446      if (*pStr == '*') {
447        nPrecision = va_arg(argList, int);
448        pStr++;
449      } else {
450        nPrecision = FXSYS_wtoi(pStr);
451        while (std::iswdigit(*pStr))
452          ++pStr;
453      }
454    }
455    if (nPrecision < 0 || nPrecision > 128 * 1024) {
456      pFormat = L"Bad precision";
457      nMaxLen = 14;
458      break;
459    }
460    int nModifier = 0;
461    if (*pStr == L'I' && *(pStr + 1) == L'6' && *(pStr + 2) == L'4') {
462      pStr += 3;
463      nModifier = FORCE_INT64;
464    } else {
465      switch (*pStr) {
466        case 'h':
467          nModifier = FORCE_ANSI;
468          pStr++;
469          break;
470        case 'l':
471          nModifier = FORCE_UNICODE;
472          pStr++;
473          break;
474        case 'F':
475        case 'N':
476        case 'L':
477          pStr++;
478          break;
479      }
480    }
481    switch (*pStr | nModifier) {
482      case 'c':
483      case 'C':
484        nItemLen = 2;
485        va_arg(argList, int);
486        break;
487      case 'c' | FORCE_ANSI:
488      case 'C' | FORCE_ANSI:
489        nItemLen = 2;
490        va_arg(argList, int);
491        break;
492      case 'c' | FORCE_UNICODE:
493      case 'C' | FORCE_UNICODE:
494        nItemLen = 2;
495        va_arg(argList, int);
496        break;
497      case 's': {
498        const FX_WCHAR* pstrNextArg = va_arg(argList, const FX_WCHAR*);
499        if (pstrNextArg) {
500          nItemLen = FXSYS_wcslen(pstrNextArg);
501          if (nItemLen < 1) {
502            nItemLen = 1;
503          }
504        } else {
505          nItemLen = 6;
506        }
507      } break;
508      case 'S': {
509        const FX_CHAR* pstrNextArg = va_arg(argList, const FX_CHAR*);
510        if (pstrNextArg) {
511          nItemLen = FXSYS_strlen(pstrNextArg);
512          if (nItemLen < 1) {
513            nItemLen = 1;
514          }
515        } else {
516          nItemLen = 6;
517        }
518      } break;
519      case 's' | FORCE_ANSI:
520      case 'S' | FORCE_ANSI: {
521        const FX_CHAR* pstrNextArg = va_arg(argList, const FX_CHAR*);
522        if (pstrNextArg) {
523          nItemLen = FXSYS_strlen(pstrNextArg);
524          if (nItemLen < 1) {
525            nItemLen = 1;
526          }
527        } else {
528          nItemLen = 6;
529        }
530      } break;
531      case 's' | FORCE_UNICODE:
532      case 'S' | FORCE_UNICODE: {
533        const FX_WCHAR* pstrNextArg = va_arg(argList, FX_WCHAR*);
534        if (pstrNextArg) {
535          nItemLen = FXSYS_wcslen(pstrNextArg);
536          if (nItemLen < 1) {
537            nItemLen = 1;
538          }
539        } else {
540          nItemLen = 6;
541        }
542      } break;
543    }
544    if (nItemLen != 0) {
545      if (nPrecision != 0 && nItemLen > nPrecision) {
546        nItemLen = nPrecision;
547      }
548      if (nItemLen < nWidth) {
549        nItemLen = nWidth;
550      }
551    } else {
552      switch (*pStr) {
553        case 'd':
554        case 'i':
555        case 'u':
556        case 'x':
557        case 'X':
558        case 'o':
559          if (nModifier & FORCE_INT64) {
560            va_arg(argList, int64_t);
561          } else {
562            va_arg(argList, int);
563          }
564          nItemLen = 32;
565          if (nItemLen < nWidth + nPrecision) {
566            nItemLen = nWidth + nPrecision;
567          }
568          break;
569        case 'a':
570        case 'A':
571        case 'e':
572        case 'E':
573        case 'g':
574        case 'G':
575          va_arg(argList, double);
576          nItemLen = 128;
577          if (nItemLen < nWidth + nPrecision) {
578            nItemLen = nWidth + nPrecision;
579          }
580          break;
581        case 'f':
582          if (nWidth + nPrecision > 100) {
583            nItemLen = nPrecision + nWidth + 128;
584          } else {
585            double f;
586            char pszTemp[256];
587            f = va_arg(argList, double);
588            FXSYS_snprintf(pszTemp, sizeof(pszTemp), "%*.*f", nWidth,
589                           nPrecision + 6, f);
590            nItemLen = FXSYS_strlen(pszTemp);
591          }
592          break;
593        case 'p':
594          va_arg(argList, void*);
595          nItemLen = 32;
596          if (nItemLen < nWidth + nPrecision) {
597            nItemLen = nWidth + nPrecision;
598          }
599          break;
600        case 'n':
601          va_arg(argList, int*);
602          break;
603      }
604    }
605    nMaxLen += nItemLen;
606  }
607  GetBuffer(nMaxLen);
608  if (m_pData) {
609    FXSYS_vswprintf((wchar_t*)m_pData->m_String, nMaxLen + 1,
610                    (const wchar_t*)pFormat, argListSave);
611    ReleaseBuffer();
612  }
613  va_end(argListSave);
614}
615
616void CFX_WideString::Format(const FX_WCHAR* pFormat, ...) {
617  va_list argList;
618  va_start(argList, pFormat);
619  FormatV(pFormat, argList);
620  va_end(argList);
621}
622
623FX_STRSIZE CFX_WideString::Insert(FX_STRSIZE nIndex, FX_WCHAR ch) {
624  FX_STRSIZE nNewLength = m_pData ? m_pData->m_nDataLength : 0;
625  nIndex = std::max(nIndex, 0);
626  nIndex = std::min(nIndex, nNewLength);
627  nNewLength++;
628
629  ReallocBeforeWrite(nNewLength);
630  wmemmove(m_pData->m_String + nIndex + 1, m_pData->m_String + nIndex,
631           nNewLength - nIndex);
632  m_pData->m_String[nIndex] = ch;
633  m_pData->m_nDataLength = nNewLength;
634  return nNewLength;
635}
636
637CFX_WideString CFX_WideString::Right(FX_STRSIZE nCount) const {
638  if (!m_pData)
639    return CFX_WideString();
640
641  nCount = std::max(nCount, 0);
642  if (nCount >= m_pData->m_nDataLength)
643    return *this;
644
645  CFX_WideString dest;
646  AllocCopy(dest, nCount, m_pData->m_nDataLength - nCount);
647  return dest;
648}
649
650CFX_WideString CFX_WideString::Left(FX_STRSIZE nCount) const {
651  if (!m_pData)
652    return CFX_WideString();
653
654  nCount = std::max(nCount, 0);
655  if (nCount >= m_pData->m_nDataLength)
656    return *this;
657
658  CFX_WideString dest;
659  AllocCopy(dest, nCount, 0);
660  return dest;
661}
662
663FX_STRSIZE CFX_WideString::Find(FX_WCHAR ch, FX_STRSIZE nStart) const {
664  if (!m_pData)
665    return -1;
666
667  if (nStart < 0 || nStart >= m_pData->m_nDataLength)
668    return -1;
669
670  const FX_WCHAR* pStr =
671      wmemchr(m_pData->m_String + nStart, ch, m_pData->m_nDataLength - nStart);
672  return pStr ? pStr - m_pData->m_String : -1;
673}
674
675FX_STRSIZE CFX_WideString::Find(const CFX_WideStringC& pSub,
676                                FX_STRSIZE nStart) const {
677  if (!m_pData)
678    return -1;
679
680  FX_STRSIZE nLength = m_pData->m_nDataLength;
681  if (nStart > nLength)
682    return -1;
683
684  const FX_WCHAR* pStr =
685      FX_wcsstr(m_pData->m_String + nStart, m_pData->m_nDataLength - nStart,
686                pSub.c_str(), pSub.GetLength());
687  return pStr ? (int)(pStr - m_pData->m_String) : -1;
688}
689
690void CFX_WideString::MakeLower() {
691  if (!m_pData)
692    return;
693
694  ReallocBeforeWrite(m_pData->m_nDataLength);
695  FXSYS_wcslwr(m_pData->m_String);
696}
697
698void CFX_WideString::MakeUpper() {
699  if (!m_pData)
700    return;
701
702  ReallocBeforeWrite(m_pData->m_nDataLength);
703  FXSYS_wcsupr(m_pData->m_String);
704}
705
706FX_STRSIZE CFX_WideString::Remove(FX_WCHAR chRemove) {
707  if (!m_pData || m_pData->m_nDataLength < 1)
708    return 0;
709
710  FX_WCHAR* pstrSource = m_pData->m_String;
711  FX_WCHAR* pstrEnd = m_pData->m_String + m_pData->m_nDataLength;
712  while (pstrSource < pstrEnd) {
713    if (*pstrSource == chRemove)
714      break;
715    pstrSource++;
716  }
717  if (pstrSource == pstrEnd)
718    return 0;
719
720  ptrdiff_t copied = pstrSource - m_pData->m_String;
721  ReallocBeforeWrite(m_pData->m_nDataLength);
722  pstrSource = m_pData->m_String + copied;
723  pstrEnd = m_pData->m_String + m_pData->m_nDataLength;
724
725  FX_WCHAR* pstrDest = pstrSource;
726  while (pstrSource < pstrEnd) {
727    if (*pstrSource != chRemove) {
728      *pstrDest = *pstrSource;
729      pstrDest++;
730    }
731    pstrSource++;
732  }
733
734  *pstrDest = 0;
735  FX_STRSIZE nCount = (FX_STRSIZE)(pstrSource - pstrDest);
736  m_pData->m_nDataLength -= nCount;
737  return nCount;
738}
739
740FX_STRSIZE CFX_WideString::Replace(const CFX_WideStringC& pOld,
741                                   const CFX_WideStringC& pNew) {
742  if (!m_pData || pOld.IsEmpty())
743    return 0;
744
745  FX_STRSIZE nSourceLen = pOld.GetLength();
746  FX_STRSIZE nReplacementLen = pNew.GetLength();
747  FX_STRSIZE nCount = 0;
748  const FX_WCHAR* pStart = m_pData->m_String;
749  FX_WCHAR* pEnd = m_pData->m_String + m_pData->m_nDataLength;
750  while (1) {
751    const FX_WCHAR* pTarget = FX_wcsstr(pStart, (FX_STRSIZE)(pEnd - pStart),
752                                        pOld.c_str(), nSourceLen);
753    if (!pTarget)
754      break;
755
756    nCount++;
757    pStart = pTarget + nSourceLen;
758  }
759  if (nCount == 0)
760    return 0;
761
762  FX_STRSIZE nNewLength =
763      m_pData->m_nDataLength + (nReplacementLen - nSourceLen) * nCount;
764
765  if (nNewLength == 0) {
766    clear();
767    return nCount;
768  }
769
770  CFX_RetainPtr<StringData> pNewData(StringData::Create(nNewLength));
771  pStart = m_pData->m_String;
772  FX_WCHAR* pDest = pNewData->m_String;
773  for (FX_STRSIZE i = 0; i < nCount; i++) {
774    const FX_WCHAR* pTarget = FX_wcsstr(pStart, (FX_STRSIZE)(pEnd - pStart),
775                                        pOld.c_str(), nSourceLen);
776    wmemcpy(pDest, pStart, pTarget - pStart);
777    pDest += pTarget - pStart;
778    wmemcpy(pDest, pNew.c_str(), pNew.GetLength());
779    pDest += pNew.GetLength();
780    pStart = pTarget + nSourceLen;
781  }
782  wmemcpy(pDest, pStart, pEnd - pStart);
783  m_pData.Swap(pNewData);
784  return nCount;
785}
786
787void CFX_WideString::SetAt(FX_STRSIZE nIndex, FX_WCHAR ch) {
788  if (!m_pData) {
789    return;
790  }
791  ASSERT(nIndex >= 0);
792  ASSERT(nIndex < m_pData->m_nDataLength);
793  ReallocBeforeWrite(m_pData->m_nDataLength);
794  m_pData->m_String[nIndex] = ch;
795}
796
797// static
798CFX_WideString CFX_WideString::FromLocal(const CFX_ByteStringC& str) {
799  return FromCodePage(str, 0);
800}
801
802// static
803CFX_WideString CFX_WideString::FromCodePage(const CFX_ByteStringC& str,
804                                            uint16_t codepage) {
805  return CFX_CharMap::GetWideString(codepage, str);
806}
807
808// static
809CFX_WideString CFX_WideString::FromUTF8(const CFX_ByteStringC& str) {
810  if (str.IsEmpty())
811    return CFX_WideString();
812
813  CFX_UTF8Decoder decoder;
814  for (FX_STRSIZE i = 0; i < str.GetLength(); i++)
815    decoder.Input(str[i]);
816
817  return CFX_WideString(decoder.GetResult());
818}
819
820// static
821CFX_WideString CFX_WideString::FromUTF16LE(const unsigned short* wstr,
822                                           FX_STRSIZE wlen) {
823  if (!wstr || 0 == wlen) {
824    return CFX_WideString();
825  }
826
827  CFX_WideString result;
828  FX_WCHAR* buf = result.GetBuffer(wlen);
829  for (int i = 0; i < wlen; i++) {
830    buf[i] = wstr[i];
831  }
832  result.ReleaseBuffer(wlen);
833  return result;
834}
835
836int CFX_WideString::Compare(const FX_WCHAR* lpsz) const {
837  if (m_pData)
838    return FXSYS_wcscmp(m_pData->m_String, lpsz);
839  return (!lpsz || lpsz[0] == 0) ? 0 : -1;
840}
841
842int CFX_WideString::Compare(const CFX_WideString& str) const {
843  if (!m_pData) {
844    if (!str.m_pData) {
845      return 0;
846    }
847    return -1;
848  }
849  if (!str.m_pData) {
850    return 1;
851  }
852  int this_len = m_pData->m_nDataLength;
853  int that_len = str.m_pData->m_nDataLength;
854  int min_len = this_len < that_len ? this_len : that_len;
855  for (int i = 0; i < min_len; i++) {
856    if (m_pData->m_String[i] < str.m_pData->m_String[i]) {
857      return -1;
858    }
859    if (m_pData->m_String[i] > str.m_pData->m_String[i]) {
860      return 1;
861    }
862  }
863  if (this_len < that_len) {
864    return -1;
865  }
866  if (this_len > that_len) {
867    return 1;
868  }
869  return 0;
870}
871
872int CFX_WideString::CompareNoCase(const FX_WCHAR* lpsz) const {
873  if (!m_pData) {
874    return (!lpsz || lpsz[0] == 0) ? 0 : -1;
875  }
876  return FXSYS_wcsicmp(m_pData->m_String, lpsz);
877}
878
879FX_STRSIZE CFX_WideString::WStringLength(const unsigned short* str) {
880  FX_STRSIZE len = 0;
881  if (str)
882    while (str[len])
883      len++;
884  return len;
885}
886
887void CFX_WideString::TrimRight(const CFX_WideStringC& pTargets) {
888  if (IsEmpty() || pTargets.IsEmpty())
889    return;
890
891  FX_STRSIZE pos = GetLength();
892  while (pos && pTargets.Find(m_pData->m_String[pos - 1]) != -1)
893    pos--;
894
895  if (pos < m_pData->m_nDataLength) {
896    ReallocBeforeWrite(m_pData->m_nDataLength);
897    m_pData->m_String[pos] = 0;
898    m_pData->m_nDataLength = pos;
899  }
900}
901
902void CFX_WideString::TrimRight(FX_WCHAR chTarget) {
903  FX_WCHAR str[2] = {chTarget, 0};
904  TrimRight(str);
905}
906
907void CFX_WideString::TrimRight() {
908  TrimRight(L"\x09\x0a\x0b\x0c\x0d\x20");
909}
910
911void CFX_WideString::TrimLeft(const CFX_WideStringC& pTargets) {
912  if (!m_pData || pTargets.IsEmpty())
913    return;
914
915  FX_STRSIZE len = GetLength();
916  if (len < 1)
917    return;
918
919  FX_STRSIZE pos = 0;
920  while (pos < len) {
921    FX_STRSIZE i = 0;
922    while (i < pTargets.GetLength() &&
923           pTargets.CharAt(i) != m_pData->m_String[pos]) {
924      i++;
925    }
926    if (i == pTargets.GetLength()) {
927      break;
928    }
929    pos++;
930  }
931  if (pos) {
932    ReallocBeforeWrite(len);
933    FX_STRSIZE nDataLength = len - pos;
934    FXSYS_memmove(m_pData->m_String, m_pData->m_String + pos,
935                  (nDataLength + 1) * sizeof(FX_WCHAR));
936    m_pData->m_nDataLength = nDataLength;
937  }
938}
939
940void CFX_WideString::TrimLeft(FX_WCHAR chTarget) {
941  FX_WCHAR str[2] = {chTarget, 0};
942  TrimLeft(str);
943}
944
945void CFX_WideString::TrimLeft() {
946  TrimLeft(L"\x09\x0a\x0b\x0c\x0d\x20");
947}
948FX_FLOAT FX_wtof(const FX_WCHAR* str, int len) {
949  if (len == 0) {
950    return 0.0;
951  }
952  int cc = 0;
953  bool bNegative = false;
954  if (str[0] == '+') {
955    cc++;
956  } else if (str[0] == '-') {
957    bNegative = true;
958    cc++;
959  }
960  int integer = 0;
961  while (cc < len) {
962    if (str[cc] == '.') {
963      break;
964    }
965    integer = integer * 10 + FXSYS_toDecimalDigit(str[cc]);
966    cc++;
967  }
968  FX_FLOAT fraction = 0;
969  if (str[cc] == '.') {
970    cc++;
971    FX_FLOAT scale = 0.1f;
972    while (cc < len) {
973      fraction += scale * FXSYS_toDecimalDigit(str[cc]);
974      scale *= 0.1f;
975      cc++;
976    }
977  }
978  fraction += (FX_FLOAT)integer;
979  return bNegative ? -fraction : fraction;
980}
981
982int CFX_WideString::GetInteger() const {
983  return m_pData ? FXSYS_wtoi(m_pData->m_String) : 0;
984}
985
986FX_FLOAT CFX_WideString::GetFloat() const {
987  return m_pData ? FX_wtof(m_pData->m_String, m_pData->m_nDataLength) : 0.0f;
988}
989
990// static
991CFX_ByteString CFX_CharMap::GetByteString(uint16_t codepage,
992                                          const CFX_WideStringC& wstr) {
993  ASSERT(IsValidCodePage(codepage));
994  int src_len = wstr.GetLength();
995  int dest_len = FXSYS_WideCharToMultiByte(codepage, 0, wstr.c_str(), src_len,
996                                           nullptr, 0, nullptr, nullptr);
997  CFX_ByteString bstr;
998  if (dest_len) {
999    FX_CHAR* dest_buf = bstr.GetBuffer(dest_len);
1000    FXSYS_WideCharToMultiByte(codepage, 0, wstr.c_str(), src_len, dest_buf,
1001                              dest_len, nullptr, nullptr);
1002    bstr.ReleaseBuffer(dest_len);
1003  }
1004  return bstr;
1005}
1006
1007// static
1008CFX_WideString CFX_CharMap::GetWideString(uint16_t codepage,
1009                                          const CFX_ByteStringC& bstr) {
1010  ASSERT(IsValidCodePage(codepage));
1011  int src_len = bstr.GetLength();
1012  int dest_len =
1013      FXSYS_MultiByteToWideChar(codepage, 0, bstr.c_str(), src_len, nullptr, 0);
1014  CFX_WideString wstr;
1015  if (dest_len) {
1016    FX_WCHAR* dest_buf = wstr.GetBuffer(dest_len);
1017    FXSYS_MultiByteToWideChar(codepage, 0, bstr.c_str(), src_len, dest_buf,
1018                              dest_len);
1019    wstr.ReleaseBuffer(dest_len);
1020  }
1021  return wstr;
1022}
1023