blob: a15cf12f1575fa8afa68798f83715eb471d45f03 [file]
// Copyright 2016 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
#ifndef CORE_FXCRT_STRING_VIEW_TEMPLATE_H_
#define CORE_FXCRT_STRING_VIEW_TEMPLATE_H_
#include <ctype.h>
#include <algorithm>
#include <iterator>
#include <optional>
#include <string>
#include <type_traits>
#include "core/fxcrt/compiler_specific.h"
#include "core/fxcrt/fx_system.h"
#include "core/fxcrt/span.h"
#include "core/fxcrt/span_util.h"
namespace fxcrt {
// An immutable string with caller-provided storage which must outlive the
// string itself. These are not necessarily nul-terminated, so that substring
// extraction (via the Substr(), First(), and Last() methods) is copy-free.
//
// String view arguments should be passed by value, since they are small,
// rather than const-ref, even if they are not modified.
//
// Front() and Back() tolerate empty strings and must return NUL in those
// cases. Substr(), First(), and Last() tolerate out-of-range indices and
// must return an empty string view in those cases. The aim here is allowing
// callers to avoid range-checking first.
template <typename T>
class StringViewTemplate {
public:
using CharType = T;
using UnsignedType = typename std::make_unsigned<CharType>::type;
using const_iterator = const CharType*;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
constexpr StringViewTemplate() noexcept = default;
constexpr StringViewTemplate(const StringViewTemplate& src) noexcept =
default;
// Deliberately implicit to avoid calling on every string literal.
// NOLINTNEXTLINE(runtime/explicit)
constexpr StringViewTemplate(const CharType* ptr) noexcept
// SAFETY: caller ensures `ptr` is nul-terminated so `length()` is
// correct.
: span_(UNSAFE_BUFFERS(
pdfium::span(ptr,
ptr ? std::char_traits<CharType>::length(ptr) : 0))) {}
explicit constexpr StringViewTemplate(
const pdfium::span<const CharType>& other) noexcept
: span_(other.empty() ? pdfium::span<const CharType>() : other) {}
explicit StringViewTemplate(
const pdfium::span<const UnsignedType>& other) noexcept
requires(!std::is_same<UnsignedType, CharType>::value)
: span_(other.empty()
? pdfium::span<const CharType>()
: UNSAFE_BUFFERS(pdfium::span(
reinterpret_cast<const CharType*>(other.data()),
other.size()))) {}
// Deliberately implicit to avoid calling on every char literal.
// |ch| must be an lvalue that outlives the StringViewTemplate.
// NOLINTNEXTLINE(runtime/explicit)
constexpr StringViewTemplate(const CharType& ch) noexcept
: span_(pdfium::span_from_ref(ch)) {}
// PRECONDITIONS: `ptr` must point to `size` valid elements.
UNSAFE_BUFFER_USAGE
constexpr StringViewTemplate(const CharType* ptr, size_t size) noexcept
// SAFETY: propagated to caller via UNSAFE_BUFFER_USAGE.
: span_(UNSAFE_BUFFERS(pdfium::span(ptr, size))) {}
// PRECONDITIONS: `ptr` must point to `size` valid elements.
UNSAFE_BUFFER_USAGE StringViewTemplate(const UnsignedType* ptr,
size_t size) noexcept
requires(!std::is_same<UnsignedType, CharType>::value)
// SAFETY: propagated to caller via UNSAFE_BUFFER_USAGE.
: span_(UNSAFE_BUFFERS(
pdfium::span(reinterpret_cast<const CharType*>(ptr), size))) {}
constexpr StringViewTemplate& operator=(const CharType* src) {
// SAFETY: caller ensures `src` is nul-terminated so `length()` is correct.
span_ = UNSAFE_BUFFERS(
pdfium::span(src, src ? std::char_traits<CharType>::length(src) : 0));
return *this;
}
StringViewTemplate& operator=(const StringViewTemplate& src) {
span_ = src.span_;
return *this;
}
constexpr const_iterator begin() const { return span_.begin(); }
constexpr const_iterator end() const { return span_.end(); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
friend bool operator==(const StringViewTemplate& lhs,
const StringViewTemplate& rhs) {
return lhs.span_ == rhs.span_;
}
friend bool operator==(const StringViewTemplate& lhs, const CharType* ptr) {
return lhs == StringViewTemplate(ptr);
}
constexpr bool IsASCII() const {
for (auto c : *this) {
if (static_cast<UnsignedType>(c) > 127) {
return false;
}
}
return true;
}
constexpr bool EqualsASCII(const StringViewTemplate<char>& that) const {
size_t length = GetLength();
if (length != that.GetLength()) {
return false;
}
for (size_t i = 0; i < length; ++i) {
auto c = (*this)[i];
if (c > 127 ||
static_cast<CharType>(c) != static_cast<CharType>(that[i])) {
return false;
}
}
return true;
}
constexpr bool EqualsASCIINoCase(const StringViewTemplate<char>& that) const {
size_t length = GetLength();
if (length != that.GetLength()) {
return false;
}
for (size_t i = 0; i < length; ++i) {
auto c = (*this)[i];
if (c > 127 || ToLowerASCII(static_cast<CharType>(c)) !=
ToLowerASCII(static_cast<CharType>(that[i]))) {
return false;
}
}
return true;
}
uint32_t GetID() const {
if (span_.empty()) {
return 0;
}
uint32_t strid = 0;
size_t size = std::min(static_cast<size_t>(4), span_.size());
for (size_t i = 0; i < size; i++) {
strid = strid * 256 + static_cast<UnsignedType>(span_[i]);
}
return strid << ((4 - size) * 8);
}
pdfium::span<const UnsignedType> unsigned_span() const {
return reinterpret_span<const UnsignedType>(span_);
}
constexpr pdfium::span<const CharType> span() const { return span_; }
const UnsignedType* unterminated_unsigned_str() const {
return reinterpret_cast<const UnsignedType*>(span_.data());
}
constexpr const CharType* unterminated_c_str() const { return span_.data(); }
constexpr size_t GetLength() const { return span_.size(); }
constexpr bool IsEmpty() const { return span_.empty(); }
constexpr bool IsValidIndex(size_t index) const {
return index < span_.size();
}
constexpr bool IsValidLength(size_t length) const {
return length <= span_.size();
}
// CHECK() if index is out of range (via span's operator[]).
constexpr UnsignedType operator[](const size_t index) const {
return static_cast<UnsignedType>(span_[index]);
}
// CHECK() if index is out of range (via span's operator[]).
constexpr CharType CharAt(const size_t index) const { return span_[index]; }
// Unlike std::string_view::front(), this is always safe and returns a
// NUL char when the string is empty.
constexpr UnsignedType Front() const {
return !span_.empty() ? static_cast<UnsignedType>(span_.front()) : 0;
}
// Unlike std::string_view::back(), this is always safe and returns a
// NUL char when the string is empty.
constexpr UnsignedType Back() const {
return !span_.empty() ? static_cast<UnsignedType>(span_.back()) : 0;
}
constexpr std::optional<size_t> Find(CharType ch) const {
const auto* found =
std::char_traits<CharType>::find(span_.data(), span_.size(), ch);
return found ? std::optional<size_t>(found - span_.data()) : std::nullopt;
}
constexpr bool Contains(CharType ch) const { return Find(ch).has_value(); }
constexpr StringViewTemplate Substr(size_t offset) const {
// Unsigned underflow is well-defined and out-of-range is handled by
// Substr().
return Substr(offset, GetLength() - offset);
}
constexpr StringViewTemplate Substr(size_t first, size_t count) const {
if (!span_.data()) {
return StringViewTemplate();
}
if (!IsValidIndex(first)) {
return StringViewTemplate();
}
if (count == 0 || !IsValidLength(count)) {
return StringViewTemplate();
}
if (!IsValidIndex(first + count - 1)) {
return StringViewTemplate();
}
// SAFETY: performance-sensitive, checks above equivalent to subspan()'s.
return UNSAFE_BUFFERS(StringViewTemplate(span_.data() + first, count));
}
constexpr StringViewTemplate First(size_t count) const {
return Substr(0, count);
}
constexpr StringViewTemplate Last(size_t count) const {
// Unsigned underflow is well-defined and out-of-range is handled by
// Substr().
return Substr(GetLength() - count, count);
}
constexpr StringViewTemplate TrimmedRight(CharType ch) const {
if (IsEmpty()) {
return StringViewTemplate();
}
size_t pos = GetLength();
while (pos && CharAt(pos - 1) == ch) {
pos--;
}
if (pos == 0) {
return StringViewTemplate();
}
// SAFETY: Loop above keeps `pos` at length of string or less.
return UNSAFE_BUFFERS(StringViewTemplate(span_.data(), pos));
}
constexpr bool operator<(const StringViewTemplate& that) const {
const size_t common_size = std::min(span_.size(), that.span_.size());
int result = common_size ? std::char_traits<CharType>::compare(
span_.data(), that.span_.data(), common_size)
: 0;
return result < 0 || (result == 0 && span_.size() < that.span_.size());
}
constexpr bool operator>(const StringViewTemplate& that) const {
const size_t common_size = std::min(span_.size(), that.span_.size());
int result = common_size ? std::char_traits<CharType>::compare(
span_.data(), that.span_.data(), common_size)
: 0;
return result > 0 || (result == 0 && span_.size() > that.span_.size());
}
protected:
// This is not a raw_span<> because StringViewTemplates must be passed by
// value without introducing BackupRefPtr churn. Also, repeated re-assignment
// of substrings of a StringViewTemplate to itself must avoid the same issue.
pdfium::span<const CharType> span_;
private:
// Need a constexpr version here rather than the non-constexpr versions
// in fx_extension.h.
static constexpr CharType ToLowerASCII(CharType c) {
return c >= 'A' && c <= 'Z' ? static_cast<CharType>(c + ('a' - 'A')) : c;
}
void* operator new(size_t) = delete;
};
template <typename T>
inline bool operator<(const T* lhs, const StringViewTemplate<T>& rhs) {
return rhs > lhs;
}
extern template class StringViewTemplate<char>;
extern template class StringViewTemplate<wchar_t>;
using ByteStringView = StringViewTemplate<char>;
using WideStringView = StringViewTemplate<wchar_t>;
} // namespace fxcrt
using ByteStringView = fxcrt::ByteStringView;
using WideStringView = fxcrt::WideStringView;
#endif // CORE_FXCRT_STRING_VIEW_TEMPLATE_H_