// Copyright 2014 The PDFium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "core/fxcrt/bytestring.h" #include #include #include #include #include #include #include "core/fxcrt/check.h" #include "core/fxcrt/check_op.h" #include "core/fxcrt/fx_codepage.h" #include "core/fxcrt/fx_extension.h" #include "core/fxcrt/fx_memcpy_wrappers.h" #include "core/fxcrt/fx_safe_types.h" #include "core/fxcrt/fx_system.h" #include "core/fxcrt/span.h" #include "core/fxcrt/span_util.h" #include "core/fxcrt/string_pool_template.h" // Instantiate. template class fxcrt::StringViewTemplate; template class fxcrt::StringPoolTemplate; template struct std::hash; namespace { constexpr char kTrimChars[] = "\x09\x0a\x0b\x0c\x0d\x20"; } // namespace namespace fxcrt { static_assert(sizeof(ByteString) <= sizeof(char*), "Strings must not require more space than pointers"); // static ByteString ByteString::FormatInteger(int i) { char buf[32]; FXSYS_snprintf(buf, sizeof(buf), "%d", i); return ByteString(buf); } // static ByteString ByteString::FormatV(const char* pFormat, va_list argList) { va_list argListCopy; va_copy(argListCopy, argList); int nMaxLen = vsnprintf(nullptr, 0, pFormat, argListCopy); va_end(argListCopy); if (nMaxLen <= 0) return ByteString(); ByteString ret; { // Span's lifetime must end before ReleaseBuffer() below. pdfium::span buf = ret.GetBuffer(nMaxLen); // SAFETY: In the following two calls, there's always space in the buffer // for a terminating NUL that's not included in nMaxLen, and hence not // included in the span. UNSAFE_BUFFERS(FXSYS_memset(buf.data(), 0, nMaxLen + 1)); va_copy(argListCopy, argList); vsnprintf(buf.data(), nMaxLen + 1, pFormat, argListCopy); va_end(argListCopy); } ret.ReleaseBuffer(ret.GetStringLength()); return ret; } // static ByteString ByteString::Format(const char* pFormat, ...) { va_list argList; va_start(argList, pFormat); ByteString ret = FormatV(pFormat, argList); va_end(argList); return ret; } ByteString::ByteString(const char* pStr, size_t nLen) { if (nLen) { // SAFETY: caller ensures `pStr` points to at least `nLen` chars. m_pData = StringData::Create(UNSAFE_BUFFERS(pdfium::make_span(pStr, nLen))); } } ByteString::ByteString(const uint8_t* pStr, size_t nLen) : ByteString(reinterpret_cast(pStr), nLen) {} ByteString::ByteString(char ch) { m_pData = StringData::Create(1); m_pData->m_String[0] = ch; } ByteString::ByteString(const char* ptr) : ByteString(ptr, ptr ? strlen(ptr) : 0) {} ByteString::ByteString(ByteStringView bstrc) { if (!bstrc.IsEmpty()) { m_pData = StringData::Create(bstrc.span()); } } ByteString::ByteString(ByteStringView str1, ByteStringView str2) { FX_SAFE_SIZE_T nSafeLen = str1.GetLength(); nSafeLen += str2.GetLength(); size_t nNewLen = nSafeLen.ValueOrDie(); if (nNewLen == 0) return; m_pData = StringData::Create(nNewLen); m_pData->CopyContents(str1.span()); m_pData->CopyContentsAt(str1.GetLength(), str2.span()); } ByteString::ByteString(const std::initializer_list& list) { FX_SAFE_SIZE_T nSafeLen = 0; for (const auto& item : list) nSafeLen += item.GetLength(); size_t nNewLen = nSafeLen.ValueOrDie(); if (nNewLen == 0) return; m_pData = StringData::Create(nNewLen); size_t nOffset = 0; for (const auto& item : list) { m_pData->CopyContentsAt(nOffset, item.span()); nOffset += item.GetLength(); } } ByteString::ByteString(const fxcrt::ostringstream& outStream) { auto str = outStream.str(); if (!str.empty()) { m_pData = StringData::Create(pdfium::make_span(str)); } } ByteString& ByteString::operator=(const char* str) { if (!str || !str[0]) clear(); else AssignCopy(str, strlen(str)); return *this; } ByteString& ByteString::operator=(ByteStringView str) { if (str.IsEmpty()) clear(); else AssignCopy(str.unterminated_c_str(), str.GetLength()); return *this; } ByteString& ByteString::operator=(const ByteString& that) { if (m_pData != that.m_pData) m_pData = that.m_pData; return *this; } ByteString& ByteString::operator=(ByteString&& that) noexcept { if (m_pData != that.m_pData) m_pData = std::move(that.m_pData); return *this; } ByteString& ByteString::operator+=(const char* str) { if (str) Concat(str, strlen(str)); return *this; } ByteString& ByteString::operator+=(char ch) { Concat(&ch, 1); return *this; } ByteString& ByteString::operator+=(const ByteString& str) { if (str.m_pData) Concat(str.m_pData->m_String, str.m_pData->m_nDataLength); return *this; } ByteString& ByteString::operator+=(ByteStringView str) { if (!str.IsEmpty()) Concat(str.unterminated_c_str(), str.GetLength()); return *this; } bool ByteString::operator==(const char* ptr) const { if (!m_pData) return !ptr || !ptr[0]; if (!ptr) return m_pData->m_nDataLength == 0; // SAFETY: `m_nDataLength` is within `m_String`, and the strlen() call // ensures there are `m_nDataLength` bytes at `ptr` before the terminator. return strlen(ptr) == m_pData->m_nDataLength && UNSAFE_BUFFERS( FXSYS_memcmp(ptr, m_pData->m_String, m_pData->m_nDataLength)) == 0; } bool ByteString::operator==(ByteStringView str) const { if (!m_pData) return str.IsEmpty(); // SAFETY: `str` has `GetLength()` valid bytes in `unterminated_c_str()`, // `m_nDataLength` is within `m_String`, and equality comparison. return m_pData->m_nDataLength == str.GetLength() && UNSAFE_BUFFERS(FXSYS_memcmp( m_pData->m_String, str.unterminated_c_str(), str.GetLength())) == 0; } bool ByteString::operator==(const ByteString& other) const { if (m_pData == other.m_pData) return true; if (IsEmpty()) return other.IsEmpty(); if (other.IsEmpty()) return false; return other.m_pData->m_nDataLength == m_pData->m_nDataLength && memcmp(other.m_pData->m_String, m_pData->m_String, m_pData->m_nDataLength) == 0; } bool ByteString::operator<(const char* ptr) const { if (!m_pData && !ptr) return false; if (c_str() == ptr) return false; size_t len = GetLength(); size_t other_len = ptr ? strlen(ptr) : 0; // SAFETY: Comparison limited to minimum valid length of either argument. int result = UNSAFE_BUFFERS(FXSYS_memcmp(c_str(), ptr, std::min(len, other_len))); return result < 0 || (result == 0 && len < other_len); } bool ByteString::operator<(ByteStringView str) const { return Compare(str) < 0; } bool ByteString::operator<(const ByteString& other) const { if (m_pData == other.m_pData) return false; size_t len = GetLength(); size_t other_len = other.GetLength(); // SAFETY: Comparison limited to minimum valid length of either argument. int result = UNSAFE_BUFFERS( FXSYS_memcmp(c_str(), other.c_str(), std::min(len, other_len))); return result < 0 || (result == 0 && len < other_len); } bool ByteString::EqualNoCase(ByteStringView str) const { if (!m_pData) { return str.IsEmpty(); } if (m_pData->m_nDataLength != str.GetLength()) { return false; } pdfium::span this_span = pdfium::as_bytes(m_pData->span()); pdfium::span that_span = str.unsigned_span(); while (!this_span.empty()) { uint8_t this_char = this_span.front(); uint8_t that_char = that_span.front(); if (this_char != that_char && tolower(this_char) != tolower(that_char)) { return false; } this_span = this_span.subspan(1); that_span = that_span.subspan(1); } return true; } intptr_t ByteString::ReferenceCountForTesting() const { return m_pData ? m_pData->m_nRefs : 0; } ByteString ByteString::Substr(size_t offset) const { // Unsigned underflow is well-defined and out-of-range is handled by Substr(). return Substr(offset, GetLength() - offset); } ByteString ByteString::Substr(size_t first, size_t count) const { if (!m_pData) { return ByteString(); } if (first == 0 && count == m_pData->m_nDataLength) { return *this; } return ByteString(AsStringView().Substr(first, count)); } ByteString ByteString::First(size_t count) const { return Substr(0, count); } ByteString ByteString::Last(size_t count) const { // Unsigned underflow is well-defined and out-of-range is handled by Substr(). return Substr(GetLength() - count, count); } void ByteString::MakeLower() { if (IsEmpty()) return; ReallocBeforeWrite(m_pData->m_nDataLength); FXSYS_strlwr(m_pData->m_String); } void ByteString::MakeUpper() { if (IsEmpty()) return; ReallocBeforeWrite(m_pData->m_nDataLength); FXSYS_strupr(m_pData->m_String); } int ByteString::Compare(ByteStringView str) const { if (!m_pData) return str.IsEmpty() ? 0 : -1; size_t this_len = m_pData->m_nDataLength; size_t that_len = str.GetLength(); size_t min_len = std::min(this_len, that_len); // SAFETY: Comparison limited to minimum valid length of either argument. int result = UNSAFE_BUFFERS( FXSYS_memcmp(m_pData->m_String, str.unterminated_c_str(), min_len)); if (result != 0) return result; if (this_len == that_len) return 0; return this_len < that_len ? -1 : 1; } void ByteString::TrimWhitespace() { TrimWhitespaceBack(); TrimWhitespaceFront(); } void ByteString::TrimWhitespaceFront() { TrimFront(kTrimChars); } void ByteString::TrimWhitespaceBack() { TrimBack(kTrimChars); } std::ostream& operator<<(std::ostream& os, const ByteString& str) { return os.write(str.c_str(), str.GetLength()); } std::ostream& operator<<(std::ostream& os, ByteStringView str) { return os.write(str.unterminated_c_str(), str.GetLength()); } } // namespace fxcrt uint32_t FX_HashCode_GetA(ByteStringView str) { uint32_t dwHashCode = 0; for (ByteStringView::UnsignedType c : str) dwHashCode = 31 * dwHashCode + c; return dwHashCode; } uint32_t FX_HashCode_GetLoweredA(ByteStringView str) { uint32_t dwHashCode = 0; for (ByteStringView::UnsignedType c : str) dwHashCode = 31 * dwHashCode + tolower(c); return dwHashCode; } uint32_t FX_HashCode_GetAsIfW(ByteStringView str) { uint32_t dwHashCode = 0; for (ByteStringView::UnsignedType c : str) dwHashCode = 1313 * dwHashCode + c; return dwHashCode; } uint32_t FX_HashCode_GetLoweredAsIfW(ByteStringView str) { uint32_t dwHashCode = 0; for (ByteStringView::UnsignedType c : str) dwHashCode = 1313 * dwHashCode + FXSYS_towlower(c); return dwHashCode; }