1 // Copyright 2014 The PDFium Authors
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/bytestring.h"
8
9 #include <ctype.h>
10 #include <stddef.h>
11
12 #include <algorithm>
13 #include <sstream>
14 #include <string>
15 #include <utility>
16
17 #include "core/fxcrt/fx_codepage.h"
18 #include "core/fxcrt/fx_extension.h"
19 #include "core/fxcrt/fx_memcpy_wrappers.h"
20 #include "core/fxcrt/fx_safe_types.h"
21 #include "core/fxcrt/fx_system.h"
22 #include "core/fxcrt/string_pool_template.h"
23 #include "third_party/base/check.h"
24 #include "third_party/base/check_op.h"
25 #include "third_party/base/containers/span.h"
26 #include "third_party/base/numerics/safe_math.h"
27
28 template class fxcrt::StringDataTemplate<char>;
29 template class fxcrt::StringViewTemplate<char>;
30 template class fxcrt::StringPoolTemplate<ByteString>;
31 template struct std::hash<ByteString>;
32
33 namespace {
34
35 constexpr char kTrimChars[] = "\x09\x0a\x0b\x0c\x0d\x20";
36
FX_strstr(const char * haystack,size_t haystack_len,const char * needle,size_t needle_len)37 const char* FX_strstr(const char* haystack,
38 size_t haystack_len,
39 const char* needle,
40 size_t needle_len) {
41 if (needle_len > haystack_len || needle_len == 0)
42 return nullptr;
43
44 const char* end_ptr = haystack + haystack_len - needle_len;
45 while (haystack <= end_ptr) {
46 size_t i = 0;
47 while (true) {
48 if (haystack[i] != needle[i])
49 break;
50
51 i++;
52 if (i == needle_len)
53 return haystack;
54 }
55 haystack++;
56 }
57 return nullptr;
58 }
59
60 } // namespace
61
62 namespace fxcrt {
63
64 static_assert(sizeof(ByteString) <= sizeof(char*),
65 "Strings must not require more space than pointers");
66
67 // static
FormatInteger(int i)68 ByteString ByteString::FormatInteger(int i) {
69 char buf[32];
70 FXSYS_snprintf(buf, sizeof(buf), "%d", i);
71 return ByteString(buf);
72 }
73
74 // static
FormatFloat(float f)75 ByteString ByteString::FormatFloat(float f) {
76 char buf[32];
77 return ByteString(buf, FloatToString(f, buf));
78 }
79
80 // static
FormatV(const char * pFormat,va_list argList)81 ByteString ByteString::FormatV(const char* pFormat, va_list argList) {
82 va_list argListCopy;
83 va_copy(argListCopy, argList);
84 int nMaxLen = vsnprintf(nullptr, 0, pFormat, argListCopy);
85 va_end(argListCopy);
86
87 if (nMaxLen <= 0)
88 return ByteString();
89
90 ByteString ret;
91 {
92 // Span's lifetime must end before ReleaseBuffer() below.
93 pdfium::span<char> buf = ret.GetBuffer(nMaxLen);
94
95 // In the following two calls, there's always space in the buffer for
96 // a terminating NUL that's not included in nMaxLen.
97 memset(buf.data(), 0, nMaxLen + 1);
98 va_copy(argListCopy, argList);
99 vsnprintf(buf.data(), nMaxLen + 1, pFormat, argListCopy);
100 va_end(argListCopy);
101 }
102 ret.ReleaseBuffer(ret.GetStringLength());
103 return ret;
104 }
105
106 // static
Format(const char * pFormat,...)107 ByteString ByteString::Format(const char* pFormat, ...) {
108 va_list argList;
109 va_start(argList, pFormat);
110 ByteString ret = FormatV(pFormat, argList);
111 va_end(argList);
112
113 return ret;
114 }
115
ByteString(const char * pStr,size_t nLen)116 ByteString::ByteString(const char* pStr, size_t nLen) {
117 if (nLen)
118 m_pData.Reset(StringData::Create(pStr, nLen));
119 }
120
ByteString(const uint8_t * pStr,size_t nLen)121 ByteString::ByteString(const uint8_t* pStr, size_t nLen) {
122 if (nLen)
123 m_pData.Reset(
124 StringData::Create(reinterpret_cast<const char*>(pStr), nLen));
125 }
126
127 ByteString::ByteString() = default;
128
ByteString(const ByteString & other)129 ByteString::ByteString(const ByteString& other) : m_pData(other.m_pData) {}
130
ByteString(ByteString && other)131 ByteString::ByteString(ByteString&& other) noexcept {
132 m_pData.Swap(other.m_pData);
133 }
134
ByteString(char ch)135 ByteString::ByteString(char ch) {
136 m_pData.Reset(StringData::Create(1));
137 m_pData->m_String[0] = ch;
138 }
139
ByteString(const char * ptr)140 ByteString::ByteString(const char* ptr)
141 : ByteString(ptr, ptr ? strlen(ptr) : 0) {}
142
ByteString(ByteStringView bstrc)143 ByteString::ByteString(ByteStringView bstrc) {
144 if (!bstrc.IsEmpty()) {
145 m_pData.Reset(
146 StringData::Create(bstrc.unterminated_c_str(), bstrc.GetLength()));
147 }
148 }
149
ByteString(ByteStringView str1,ByteStringView str2)150 ByteString::ByteString(ByteStringView str1, ByteStringView str2) {
151 FX_SAFE_SIZE_T nSafeLen = str1.GetLength();
152 nSafeLen += str2.GetLength();
153
154 size_t nNewLen = nSafeLen.ValueOrDie();
155 if (nNewLen == 0)
156 return;
157
158 m_pData.Reset(StringData::Create(nNewLen));
159 m_pData->CopyContents(str1.unterminated_c_str(), str1.GetLength());
160 m_pData->CopyContentsAt(str1.GetLength(), str2.unterminated_c_str(),
161 str2.GetLength());
162 }
163
ByteString(const std::initializer_list<ByteStringView> & list)164 ByteString::ByteString(const std::initializer_list<ByteStringView>& list) {
165 FX_SAFE_SIZE_T nSafeLen = 0;
166 for (const auto& item : list)
167 nSafeLen += item.GetLength();
168
169 size_t nNewLen = nSafeLen.ValueOrDie();
170 if (nNewLen == 0)
171 return;
172
173 m_pData.Reset(StringData::Create(nNewLen));
174
175 size_t nOffset = 0;
176 for (const auto& item : list) {
177 m_pData->CopyContentsAt(nOffset, item.unterminated_c_str(),
178 item.GetLength());
179 nOffset += item.GetLength();
180 }
181 }
182
ByteString(const fxcrt::ostringstream & outStream)183 ByteString::ByteString(const fxcrt::ostringstream& outStream) {
184 auto str = outStream.str();
185 if (!str.empty())
186 m_pData.Reset(StringData::Create(str.c_str(), str.size()));
187 }
188
189 ByteString::~ByteString() = default;
190
clear()191 void ByteString::clear() {
192 if (m_pData && m_pData->CanOperateInPlace(0)) {
193 m_pData->m_nDataLength = 0;
194 return;
195 }
196 m_pData.Reset();
197 }
198
operator =(const char * str)199 ByteString& ByteString::operator=(const char* str) {
200 if (!str || !str[0])
201 clear();
202 else
203 AssignCopy(str, strlen(str));
204
205 return *this;
206 }
207
operator =(ByteStringView str)208 ByteString& ByteString::operator=(ByteStringView str) {
209 if (str.IsEmpty())
210 clear();
211 else
212 AssignCopy(str.unterminated_c_str(), str.GetLength());
213
214 return *this;
215 }
216
operator =(const ByteString & that)217 ByteString& ByteString::operator=(const ByteString& that) {
218 if (m_pData != that.m_pData)
219 m_pData = that.m_pData;
220
221 return *this;
222 }
223
operator =(ByteString && that)224 ByteString& ByteString::operator=(ByteString&& that) noexcept {
225 if (m_pData != that.m_pData)
226 m_pData = std::move(that.m_pData);
227
228 return *this;
229 }
230
operator +=(const char * str)231 ByteString& ByteString::operator+=(const char* str) {
232 if (str)
233 Concat(str, strlen(str));
234
235 return *this;
236 }
237
operator +=(char ch)238 ByteString& ByteString::operator+=(char ch) {
239 Concat(&ch, 1);
240 return *this;
241 }
242
operator +=(const ByteString & str)243 ByteString& ByteString::operator+=(const ByteString& str) {
244 if (str.m_pData)
245 Concat(str.m_pData->m_String, str.m_pData->m_nDataLength);
246
247 return *this;
248 }
249
operator +=(ByteStringView str)250 ByteString& ByteString::operator+=(ByteStringView str) {
251 if (!str.IsEmpty())
252 Concat(str.unterminated_c_str(), str.GetLength());
253
254 return *this;
255 }
256
operator ==(const char * ptr) const257 bool ByteString::operator==(const char* ptr) const {
258 if (!m_pData)
259 return !ptr || !ptr[0];
260
261 if (!ptr)
262 return m_pData->m_nDataLength == 0;
263
264 return strlen(ptr) == m_pData->m_nDataLength &&
265 FXSYS_memcmp(ptr, m_pData->m_String, m_pData->m_nDataLength) == 0;
266 }
267
operator ==(ByteStringView str) const268 bool ByteString::operator==(ByteStringView str) const {
269 if (!m_pData)
270 return str.IsEmpty();
271
272 return m_pData->m_nDataLength == str.GetLength() &&
273 FXSYS_memcmp(m_pData->m_String, str.unterminated_c_str(),
274 str.GetLength()) == 0;
275 }
276
operator ==(const ByteString & other) const277 bool ByteString::operator==(const ByteString& other) const {
278 if (m_pData == other.m_pData)
279 return true;
280
281 if (IsEmpty())
282 return other.IsEmpty();
283
284 if (other.IsEmpty())
285 return false;
286
287 return other.m_pData->m_nDataLength == m_pData->m_nDataLength &&
288 memcmp(other.m_pData->m_String, m_pData->m_String,
289 m_pData->m_nDataLength) == 0;
290 }
291
operator <(const char * ptr) const292 bool ByteString::operator<(const char* ptr) const {
293 if (!m_pData && !ptr)
294 return false;
295 if (c_str() == ptr)
296 return false;
297
298 size_t len = GetLength();
299 size_t other_len = ptr ? strlen(ptr) : 0;
300 int result = FXSYS_memcmp(c_str(), ptr, std::min(len, other_len));
301 return result < 0 || (result == 0 && len < other_len);
302 }
303
operator <(ByteStringView str) const304 bool ByteString::operator<(ByteStringView str) const {
305 return Compare(str) < 0;
306 }
307
operator <(const ByteString & other) const308 bool ByteString::operator<(const ByteString& other) const {
309 if (m_pData == other.m_pData)
310 return false;
311
312 size_t len = GetLength();
313 size_t other_len = other.GetLength();
314 int result = FXSYS_memcmp(c_str(), other.c_str(), std::min(len, other_len));
315 return result < 0 || (result == 0 && len < other_len);
316 }
317
EqualNoCase(ByteStringView str) const318 bool ByteString::EqualNoCase(ByteStringView str) const {
319 if (!m_pData)
320 return str.IsEmpty();
321
322 size_t len = str.GetLength();
323 if (m_pData->m_nDataLength != len)
324 return false;
325
326 const uint8_t* pThis = (const uint8_t*)m_pData->m_String;
327 const uint8_t* pThat = str.raw_str();
328 for (size_t i = 0; i < len; i++) {
329 if ((*pThis) != (*pThat)) {
330 uint8_t bThis = tolower(*pThis);
331 uint8_t bThat = tolower(*pThat);
332 if (bThis != bThat)
333 return false;
334 }
335 pThis++;
336 pThat++;
337 }
338 return true;
339 }
340
AssignCopy(const char * pSrcData,size_t nSrcLen)341 void ByteString::AssignCopy(const char* pSrcData, size_t nSrcLen) {
342 AllocBeforeWrite(nSrcLen);
343 m_pData->CopyContents(pSrcData, nSrcLen);
344 m_pData->m_nDataLength = nSrcLen;
345 }
346
ReallocBeforeWrite(size_t nNewLength)347 void ByteString::ReallocBeforeWrite(size_t nNewLength) {
348 if (m_pData && m_pData->CanOperateInPlace(nNewLength))
349 return;
350
351 if (nNewLength == 0) {
352 clear();
353 return;
354 }
355
356 RetainPtr<StringData> pNewData(StringData::Create(nNewLength));
357 if (m_pData) {
358 size_t nCopyLength = std::min(m_pData->m_nDataLength, nNewLength);
359 pNewData->CopyContents(m_pData->m_String, nCopyLength);
360 pNewData->m_nDataLength = nCopyLength;
361 } else {
362 pNewData->m_nDataLength = 0;
363 }
364 pNewData->m_String[pNewData->m_nDataLength] = 0;
365 m_pData.Swap(pNewData);
366 }
367
AllocBeforeWrite(size_t nNewLength)368 void ByteString::AllocBeforeWrite(size_t nNewLength) {
369 if (m_pData && m_pData->CanOperateInPlace(nNewLength))
370 return;
371
372 if (nNewLength == 0) {
373 clear();
374 return;
375 }
376
377 m_pData.Reset(StringData::Create(nNewLength));
378 }
379
ReleaseBuffer(size_t nNewLength)380 void ByteString::ReleaseBuffer(size_t nNewLength) {
381 if (!m_pData)
382 return;
383
384 nNewLength = std::min(nNewLength, m_pData->m_nAllocLength);
385 if (nNewLength == 0) {
386 clear();
387 return;
388 }
389
390 DCHECK_EQ(m_pData->m_nRefs, 1);
391 m_pData->m_nDataLength = nNewLength;
392 m_pData->m_String[nNewLength] = 0;
393 if (m_pData->m_nAllocLength - nNewLength >= 32) {
394 // Over arbitrary threshold, so pay the price to relocate. Force copy to
395 // always occur by holding a second reference to the string.
396 ByteString preserve(*this);
397 ReallocBeforeWrite(nNewLength);
398 }
399 }
400
Reserve(size_t len)401 void ByteString::Reserve(size_t len) {
402 GetBuffer(len);
403 }
404
GetBuffer(size_t nMinBufLength)405 pdfium::span<char> ByteString::GetBuffer(size_t nMinBufLength) {
406 if (!m_pData) {
407 if (nMinBufLength == 0)
408 return pdfium::span<char>();
409
410 m_pData.Reset(StringData::Create(nMinBufLength));
411 m_pData->m_nDataLength = 0;
412 m_pData->m_String[0] = 0;
413 return pdfium::span<char>(m_pData->m_String, m_pData->m_nAllocLength);
414 }
415
416 if (m_pData->CanOperateInPlace(nMinBufLength))
417 return pdfium::span<char>(m_pData->m_String, m_pData->m_nAllocLength);
418
419 nMinBufLength = std::max(nMinBufLength, m_pData->m_nDataLength);
420 if (nMinBufLength == 0)
421 return pdfium::span<char>();
422
423 RetainPtr<StringData> pNewData(StringData::Create(nMinBufLength));
424 pNewData->CopyContents(*m_pData);
425 pNewData->m_nDataLength = m_pData->m_nDataLength;
426 m_pData.Swap(pNewData);
427 return pdfium::span<char>(m_pData->m_String, m_pData->m_nAllocLength);
428 }
429
Delete(size_t index,size_t count)430 size_t ByteString::Delete(size_t index, size_t count) {
431 if (!m_pData)
432 return 0;
433
434 size_t old_length = m_pData->m_nDataLength;
435 if (count == 0 || index != std::clamp<size_t>(index, 0, old_length)) {
436 return old_length;
437 }
438
439 size_t removal_length = index + count;
440 if (removal_length > old_length)
441 return old_length;
442
443 ReallocBeforeWrite(old_length);
444 size_t chars_to_copy = old_length - removal_length + 1;
445 FXSYS_memmove(m_pData->m_String + index, m_pData->m_String + removal_length,
446 chars_to_copy);
447 m_pData->m_nDataLength = old_length - count;
448 return m_pData->m_nDataLength;
449 }
450
Concat(const char * pSrcData,size_t nSrcLen)451 void ByteString::Concat(const char* pSrcData, size_t nSrcLen) {
452 if (!pSrcData || nSrcLen == 0)
453 return;
454
455 if (!m_pData) {
456 m_pData.Reset(StringData::Create(pSrcData, nSrcLen));
457 return;
458 }
459
460 if (m_pData->CanOperateInPlace(m_pData->m_nDataLength + nSrcLen)) {
461 m_pData->CopyContentsAt(m_pData->m_nDataLength, pSrcData, nSrcLen);
462 m_pData->m_nDataLength += nSrcLen;
463 return;
464 }
465
466 size_t nConcatLen = std::max(m_pData->m_nDataLength / 2, nSrcLen);
467 RetainPtr<StringData> pNewData(
468 StringData::Create(m_pData->m_nDataLength + nConcatLen));
469 pNewData->CopyContents(*m_pData);
470 pNewData->CopyContentsAt(m_pData->m_nDataLength, pSrcData, nSrcLen);
471 pNewData->m_nDataLength = m_pData->m_nDataLength + nSrcLen;
472 m_pData.Swap(pNewData);
473 }
474
ReferenceCountForTesting() const475 intptr_t ByteString::ReferenceCountForTesting() const {
476 return m_pData ? m_pData->m_nRefs : 0;
477 }
478
Substr(size_t offset) const479 ByteString ByteString::Substr(size_t offset) const {
480 // Unsigned underflow is well-defined and out-of-range is handled by Substr().
481 return Substr(offset, GetLength() - offset);
482 }
483
Substr(size_t first,size_t count) const484 ByteString ByteString::Substr(size_t first, size_t count) const {
485 if (!m_pData)
486 return ByteString();
487
488 if (!IsValidIndex(first))
489 return ByteString();
490
491 if (count == 0 || !IsValidLength(count))
492 return ByteString();
493
494 if (!IsValidIndex(first + count - 1))
495 return ByteString();
496
497 if (first == 0 && count == m_pData->m_nDataLength)
498 return *this;
499
500 ByteString dest;
501 AllocCopy(dest, count, first);
502 return dest;
503 }
504
First(size_t count) const505 ByteString ByteString::First(size_t count) const {
506 return Substr(0, count);
507 }
508
Last(size_t count) const509 ByteString ByteString::Last(size_t count) const {
510 // Unsigned underflow is well-defined and out-of-range is handled by Substr().
511 return Substr(GetLength() - count, count);
512 }
513
AllocCopy(ByteString & dest,size_t nCopyLen,size_t nCopyIndex) const514 void ByteString::AllocCopy(ByteString& dest,
515 size_t nCopyLen,
516 size_t nCopyIndex) const {
517 if (nCopyLen == 0)
518 return;
519
520 RetainPtr<StringData> pNewData(
521 StringData::Create(m_pData->m_String + nCopyIndex, nCopyLen));
522 dest.m_pData.Swap(pNewData);
523 }
524
SetAt(size_t index,char c)525 void ByteString::SetAt(size_t index, char c) {
526 DCHECK(IsValidIndex(index));
527 ReallocBeforeWrite(m_pData->m_nDataLength);
528 m_pData->m_String[index] = c;
529 }
530
Insert(size_t index,char ch)531 size_t ByteString::Insert(size_t index, char ch) {
532 const size_t cur_length = GetLength();
533 if (!IsValidLength(index))
534 return cur_length;
535
536 const size_t new_length = cur_length + 1;
537 ReallocBeforeWrite(new_length);
538 FXSYS_memmove(m_pData->m_String + index + 1, m_pData->m_String + index,
539 new_length - index);
540 m_pData->m_String[index] = ch;
541 m_pData->m_nDataLength = new_length;
542 return new_length;
543 }
544
Find(char ch,size_t start) const545 absl::optional<size_t> ByteString::Find(char ch, size_t start) const {
546 if (!m_pData)
547 return absl::nullopt;
548
549 if (!IsValidIndex(start))
550 return absl::nullopt;
551
552 const char* pStr = static_cast<const char*>(FXSYS_memchr(
553 m_pData->m_String + start, ch, m_pData->m_nDataLength - start));
554 return pStr ? absl::optional<size_t>(
555 static_cast<size_t>(pStr - m_pData->m_String))
556 : absl::nullopt;
557 }
558
Find(ByteStringView subStr,size_t start) const559 absl::optional<size_t> ByteString::Find(ByteStringView subStr,
560 size_t start) const {
561 if (!m_pData)
562 return absl::nullopt;
563
564 if (!IsValidIndex(start))
565 return absl::nullopt;
566
567 const char* pStr =
568 FX_strstr(m_pData->m_String + start, m_pData->m_nDataLength - start,
569 subStr.unterminated_c_str(), subStr.GetLength());
570 return pStr ? absl::optional<size_t>(
571 static_cast<size_t>(pStr - m_pData->m_String))
572 : absl::nullopt;
573 }
574
ReverseFind(char ch) const575 absl::optional<size_t> ByteString::ReverseFind(char ch) const {
576 if (!m_pData)
577 return absl::nullopt;
578
579 size_t nLength = m_pData->m_nDataLength;
580 while (nLength--) {
581 if (m_pData->m_String[nLength] == ch)
582 return nLength;
583 }
584 return absl::nullopt;
585 }
586
MakeLower()587 void ByteString::MakeLower() {
588 if (IsEmpty())
589 return;
590
591 ReallocBeforeWrite(m_pData->m_nDataLength);
592 FXSYS_strlwr(m_pData->m_String);
593 }
594
MakeUpper()595 void ByteString::MakeUpper() {
596 if (IsEmpty())
597 return;
598
599 ReallocBeforeWrite(m_pData->m_nDataLength);
600 FXSYS_strupr(m_pData->m_String);
601 }
602
Remove(char chRemove)603 size_t ByteString::Remove(char chRemove) {
604 if (IsEmpty())
605 return 0;
606
607 char* pstrSource = m_pData->m_String;
608 char* pstrEnd = m_pData->m_String + m_pData->m_nDataLength;
609 while (pstrSource < pstrEnd) {
610 if (*pstrSource == chRemove)
611 break;
612 pstrSource++;
613 }
614 if (pstrSource == pstrEnd)
615 return 0;
616
617 ptrdiff_t copied = pstrSource - m_pData->m_String;
618 ReallocBeforeWrite(m_pData->m_nDataLength);
619 pstrSource = m_pData->m_String + copied;
620 pstrEnd = m_pData->m_String + m_pData->m_nDataLength;
621
622 char* pstrDest = pstrSource;
623 while (pstrSource < pstrEnd) {
624 if (*pstrSource != chRemove) {
625 *pstrDest = *pstrSource;
626 pstrDest++;
627 }
628 pstrSource++;
629 }
630
631 *pstrDest = 0;
632 size_t nCount = static_cast<size_t>(pstrSource - pstrDest);
633 m_pData->m_nDataLength -= nCount;
634 return nCount;
635 }
636
Replace(ByteStringView pOld,ByteStringView pNew)637 size_t ByteString::Replace(ByteStringView pOld, ByteStringView pNew) {
638 if (!m_pData || pOld.IsEmpty())
639 return 0;
640
641 size_t nSourceLen = pOld.GetLength();
642 size_t nReplacementLen = pNew.GetLength();
643 size_t nCount = 0;
644 const char* pStart = m_pData->m_String;
645 char* pEnd = m_pData->m_String + m_pData->m_nDataLength;
646 while (true) {
647 const char* pTarget = FX_strstr(pStart, static_cast<int>(pEnd - pStart),
648 pOld.unterminated_c_str(), nSourceLen);
649 if (!pTarget)
650 break;
651
652 nCount++;
653 pStart = pTarget + nSourceLen;
654 }
655 if (nCount == 0)
656 return 0;
657
658 size_t nNewLength =
659 m_pData->m_nDataLength + (nReplacementLen - nSourceLen) * nCount;
660
661 if (nNewLength == 0) {
662 clear();
663 return nCount;
664 }
665
666 RetainPtr<StringData> pNewData(StringData::Create(nNewLength));
667 pStart = m_pData->m_String;
668 char* pDest = pNewData->m_String;
669 for (size_t i = 0; i < nCount; i++) {
670 const char* pTarget = FX_strstr(pStart, static_cast<int>(pEnd - pStart),
671 pOld.unterminated_c_str(), nSourceLen);
672 FXSYS_memcpy(pDest, pStart, pTarget - pStart);
673 pDest += pTarget - pStart;
674 FXSYS_memcpy(pDest, pNew.unterminated_c_str(), pNew.GetLength());
675 pDest += pNew.GetLength();
676 pStart = pTarget + nSourceLen;
677 }
678 FXSYS_memcpy(pDest, pStart, pEnd - pStart);
679 m_pData.Swap(pNewData);
680 return nCount;
681 }
682
Compare(ByteStringView str) const683 int ByteString::Compare(ByteStringView str) const {
684 if (!m_pData)
685 return str.IsEmpty() ? 0 : -1;
686
687 size_t this_len = m_pData->m_nDataLength;
688 size_t that_len = str.GetLength();
689 size_t min_len = std::min(this_len, that_len);
690 int result =
691 FXSYS_memcmp(m_pData->m_String, str.unterminated_c_str(), min_len);
692 if (result != 0)
693 return result;
694 if (this_len == that_len)
695 return 0;
696 return this_len < that_len ? -1 : 1;
697 }
698
Trim()699 void ByteString::Trim() {
700 TrimRight(kTrimChars);
701 TrimLeft(kTrimChars);
702 }
703
Trim(char target)704 void ByteString::Trim(char target) {
705 ByteStringView targets(target);
706 TrimRight(targets);
707 TrimLeft(targets);
708 }
709
Trim(ByteStringView targets)710 void ByteString::Trim(ByteStringView targets) {
711 TrimRight(targets);
712 TrimLeft(targets);
713 }
714
TrimLeft()715 void ByteString::TrimLeft() {
716 TrimLeft(kTrimChars);
717 }
718
TrimLeft(char target)719 void ByteString::TrimLeft(char target) {
720 TrimLeft(ByteStringView(target));
721 }
722
TrimLeft(ByteStringView targets)723 void ByteString::TrimLeft(ByteStringView targets) {
724 if (!m_pData || targets.IsEmpty())
725 return;
726
727 size_t len = GetLength();
728 if (len == 0)
729 return;
730
731 size_t pos = 0;
732 while (pos < len) {
733 size_t i = 0;
734 while (i < targets.GetLength() && targets[i] != m_pData->m_String[pos])
735 i++;
736 if (i == targets.GetLength())
737 break;
738 pos++;
739 }
740 if (pos) {
741 ReallocBeforeWrite(len);
742 size_t nDataLength = len - pos;
743 FXSYS_memmove(m_pData->m_String, m_pData->m_String + pos,
744 (nDataLength + 1) * sizeof(char));
745 m_pData->m_nDataLength = nDataLength;
746 }
747 }
748
TrimRight()749 void ByteString::TrimRight() {
750 TrimRight(kTrimChars);
751 }
752
TrimRight(char target)753 void ByteString::TrimRight(char target) {
754 TrimRight(ByteStringView(target));
755 }
756
TrimRight(ByteStringView targets)757 void ByteString::TrimRight(ByteStringView targets) {
758 if (!m_pData || targets.IsEmpty())
759 return;
760
761 size_t pos = GetLength();
762 if (pos == 0)
763 return;
764
765 while (pos) {
766 size_t i = 0;
767 while (i < targets.GetLength() && targets[i] != m_pData->m_String[pos - 1])
768 i++;
769 if (i == targets.GetLength())
770 break;
771 pos--;
772 }
773 if (pos < m_pData->m_nDataLength) {
774 ReallocBeforeWrite(m_pData->m_nDataLength);
775 m_pData->m_String[pos] = 0;
776 m_pData->m_nDataLength = pos;
777 }
778 }
779
operator <<(std::ostream & os,const ByteString & str)780 std::ostream& operator<<(std::ostream& os, const ByteString& str) {
781 return os.write(str.c_str(), str.GetLength());
782 }
783
operator <<(std::ostream & os,ByteStringView str)784 std::ostream& operator<<(std::ostream& os, ByteStringView str) {
785 return os.write(str.unterminated_c_str(), str.GetLength());
786 }
787
788 } // namespace fxcrt
789
FX_HashCode_GetA(ByteStringView str)790 uint32_t FX_HashCode_GetA(ByteStringView str) {
791 uint32_t dwHashCode = 0;
792 for (ByteStringView::UnsignedType c : str)
793 dwHashCode = 31 * dwHashCode + c;
794 return dwHashCode;
795 }
796
FX_HashCode_GetLoweredA(ByteStringView str)797 uint32_t FX_HashCode_GetLoweredA(ByteStringView str) {
798 uint32_t dwHashCode = 0;
799 for (ByteStringView::UnsignedType c : str)
800 dwHashCode = 31 * dwHashCode + tolower(c);
801 return dwHashCode;
802 }
803
FX_HashCode_GetAsIfW(ByteStringView str)804 uint32_t FX_HashCode_GetAsIfW(ByteStringView str) {
805 uint32_t dwHashCode = 0;
806 for (ByteStringView::UnsignedType c : str)
807 dwHashCode = 1313 * dwHashCode + c;
808 return dwHashCode;
809 }
810
FX_HashCode_GetLoweredAsIfW(ByteStringView str)811 uint32_t FX_HashCode_GetLoweredAsIfW(ByteStringView str) {
812 uint32_t dwHashCode = 0;
813 for (ByteStringView::UnsignedType c : str)
814 dwHashCode = 1313 * dwHashCode + FXSYS_towlower(c);
815 return dwHashCode;
816 }
817