Modernize PDFium strings operator==().
Use the technique of friending the two-arg form to avoid having to
create reflective forms of the comparisons and the != operator.
-- Delegate to views when efficient to do so.
-- Avoid walking the entire string when comparing against a ptr.
-- Move TODO() to header.
Change-Id: I27c5e00354e77aa597d0614523a85477e5a1d75a
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/131290
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxcrt/bytestring.cpp b/core/fxcrt/bytestring.cpp
index 5dc5c8f..70eaf43 100644
--- a/core/fxcrt/bytestring.cpp
+++ b/core/fxcrt/bytestring.cpp
@@ -157,7 +157,6 @@
}
}
-// TODO(tsepez): Should be UNSAFE_BUFFER_USAGE.
ByteString& ByteString::operator=(const char* str) {
if (!str || !str[0]) {
clear();
@@ -224,51 +223,16 @@
return *this;
}
-// TODO(tsepez): Should be UNSAFE_BUFFER_USAGE
-bool ByteString::operator==(const char* ptr) const {
- if (!data_) {
- return !ptr || !ptr[0];
+bool operator==(const ByteString& lhs, const char* rhs) {
+ if (lhs.IsEmpty()) {
+ return !rhs || !rhs[0];
}
-
- if (!ptr) {
- return data_->data_length_ == 0;
- }
-
- // SAFETY: `data_length_` is within `string_`, and the strlen() call
- // (whose own safety is required from the caller) ensures there are
- // `data_length_` bytes at `ptr` before the terminator.
- return UNSAFE_BUFFERS(strlen(ptr)) == data_->data_length_ &&
- UNSAFE_BUFFERS(
- FXSYS_memcmp(ptr, data_->string_, data_->data_length_)) == 0;
-}
-
-bool ByteString::operator==(ByteStringView str) const {
- if (!data_) {
- return str.IsEmpty();
- }
-
- // SAFETY: `str` has `GetLength()` valid bytes in `unterminated_c_str()`,
- // `data_length_` is within `string_`, and equality comparison.
- return data_->data_length_ == str.GetLength() &&
- UNSAFE_BUFFERS(FXSYS_memcmp(data_->string_, str.unterminated_c_str(),
- str.GetLength())) == 0;
-}
-
-bool ByteString::operator==(const ByteString& other) const {
- if (data_ == other.data_) {
- return true;
- }
- if (IsEmpty()) {
- return other.IsEmpty();
- }
- if (other.IsEmpty()) {
+ if (!rhs) {
return false;
}
- // SAFETY: data_length_ describes the length of string_.
- return other.data_->data_length_ == data_->data_length_ &&
- UNSAFE_BUFFERS(memcmp(other.data_->string_, data_->string_,
- data_->data_length_)) == 0;
+ // SAFETY: required from caller.
+ return UNSAFE_BUFFERS(strcmp(lhs.data_->string_, rhs)) == 0;
}
// TODO(tsepez): Should be UNSAFE_BUFFER_USAGE.
diff --git a/core/fxcrt/bytestring.h b/core/fxcrt/bytestring.h
index 2ba59fc..3beb998 100644
--- a/core/fxcrt/bytestring.h
+++ b/core/fxcrt/bytestring.h
@@ -58,13 +58,14 @@
int Compare(ByteStringView str) const;
bool EqualNoCase(ByteStringView str) const;
- bool operator==(const char* ptr) const;
- bool operator==(ByteStringView str) const;
- bool operator==(const ByteString& other) const;
-
- bool operator!=(const char* ptr) const { return !(*this == ptr); }
- bool operator!=(ByteStringView str) const { return !(*this == str); }
- bool operator!=(const ByteString& other) const { return !(*this == other); }
+ // TODO(tsepez): The char* form should be UNSAFE_BUFFER_USAGE.
+ friend bool operator==(const ByteString& lhs, const char* rhs);
+ friend bool operator==(const ByteString& lhs, ByteStringView rhs) {
+ return lhs.AsStringView() == rhs;
+ }
+ friend bool operator==(const ByteString& lhs, const ByteString& rhs) {
+ return lhs.AsStringView() == rhs.AsStringView();
+ }
bool operator<(const char* ptr) const;
bool operator<(ByteStringView str) const;
@@ -106,18 +107,6 @@
friend class StringPool_ByteString_Test;
};
-inline bool operator==(const char* lhs, const ByteString& rhs) {
- return rhs == lhs;
-}
-inline bool operator==(ByteStringView lhs, const ByteString& rhs) {
- return rhs == lhs;
-}
-inline bool operator!=(const char* lhs, const ByteString& rhs) {
- return rhs != lhs;
-}
-inline bool operator!=(ByteStringView lhs, const ByteString& rhs) {
- return rhs != lhs;
-}
inline bool operator<(const char* lhs, const ByteString& rhs) {
return rhs.Compare(lhs) > 0;
}
diff --git a/core/fxcrt/widestring.cpp b/core/fxcrt/widestring.cpp
index 55fc79c..ad17637 100644
--- a/core/fxcrt/widestring.cpp
+++ b/core/fxcrt/widestring.cpp
@@ -525,52 +525,16 @@
return *this;
}
-// Should be UNSAFE_BUFFER_USAGE.
-bool WideString::operator==(const wchar_t* ptr) const {
- if (!data_) {
- return !ptr || !ptr[0];
+bool operator==(const WideString& lhs, const wchar_t* rhs) {
+ if (lhs.IsEmpty()) {
+ return !rhs || !rhs[0];
}
- if (!ptr) {
- return data_->data_length_ == 0;
- }
-
- // SAFTEY: `wsclen()` comparison (whose own safety depends upoon the caller)
- // ensures there are `data_length_` wchars at `ptr` before the terminator,
- // and `data_length_` is within `string_`.
- return UNSAFE_BUFFERS(wcslen(ptr)) == data_->data_length_ &&
- UNSAFE_BUFFERS(
- FXSYS_wmemcmp(ptr, data_->string_, data_->data_length_)) == 0;
-}
-
-bool WideString::operator==(WideStringView str) const {
- if (!data_) {
- return str.IsEmpty();
- }
-
- // SAFTEY: Comparison ensure there are `data_length_` wchars in `str`
- // and `data_length_ is within `string_`.
- return data_->data_length_ == str.GetLength() &&
- UNSAFE_BUFFERS(FXSYS_wmemcmp(data_->string_, str.unterminated_c_str(),
- str.GetLength())) == 0;
-}
-
-bool WideString::operator==(const WideString& other) const {
- if (data_ == other.data_) {
- return true;
- }
-
- if (IsEmpty()) {
- return other.IsEmpty();
- }
-
- if (other.IsEmpty()) {
+ if (!rhs) {
return false;
}
- // SAFETY: data_length_ bytes available at string_.
- return other.data_->data_length_ == data_->data_length_ &&
- UNSAFE_BUFFERS(FXSYS_wmemcmp(other.data_->string_, data_->string_,
- data_->data_length_)) == 0;
+ // SAFTEY: required from caller.
+ return UNSAFE_BUFFERS(wcscmp(lhs.data_->string_, rhs)) == 0;
}
bool WideString::operator<(const wchar_t* ptr) const {
diff --git a/core/fxcrt/widestring.h b/core/fxcrt/widestring.h
index 2e7345b..7dc81c4 100644
--- a/core/fxcrt/widestring.h
+++ b/core/fxcrt/widestring.h
@@ -76,13 +76,14 @@
WideString& operator+=(const WideString& str);
WideString& operator+=(WideStringView str);
- bool operator==(const wchar_t* ptr) const;
- bool operator==(WideStringView str) const;
- bool operator==(const WideString& other) const;
-
- bool operator!=(const wchar_t* ptr) const { return !(*this == ptr); }
- bool operator!=(WideStringView str) const { return !(*this == str); }
- bool operator!=(const WideString& other) const { return !(*this == other); }
+ // TODO(tsepez): The wchar_t* form should be UNSAFE_BUFFER_USAGE.
+ friend bool operator==(const WideString& lhs, const wchar_t* rhs);
+ friend bool operator==(const WideString& lhs, WideStringView rhs) {
+ return lhs.AsStringView() == rhs;
+ }
+ friend bool operator==(const WideString& lhs, const WideString& rhs) {
+ return lhs.AsStringView() == rhs.AsStringView();
+ }
bool operator<(const wchar_t* ptr) const;
bool operator<(WideStringView str) const;
@@ -174,18 +175,6 @@
inline WideString operator+(WideStringView str1, const WideString& str2) {
return WideString(str1, str2.AsStringView());
}
-inline bool operator==(const wchar_t* lhs, const WideString& rhs) {
- return rhs == lhs;
-}
-inline bool operator==(WideStringView lhs, const WideString& rhs) {
- return rhs == lhs;
-}
-inline bool operator!=(const wchar_t* lhs, const WideString& rhs) {
- return rhs != lhs;
-}
-inline bool operator!=(WideStringView lhs, const WideString& rhs) {
- return rhs != lhs;
-}
inline bool operator<(const wchar_t* lhs, const WideString& rhs) {
return rhs.Compare(lhs) > 0;
}