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