Add one-arg form of string (and stringview) Substr().
Then use in CXFA_Document, CJS_Document, and CJX_Object where this
will avoid some manual length calculations.
-- Add comment about range checking.
Change-Id: Ica359122572d1e17597a209748026ebc9ac29166
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/86810
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcrt/bytestring.cpp b/core/fxcrt/bytestring.cpp
index 1dd99cc..a547aad 100644
--- a/core/fxcrt/bytestring.cpp
+++ b/core/fxcrt/bytestring.cpp
@@ -469,6 +469,13 @@
return m_pData ? m_pData->m_nRefs : 0;
}
+ByteString ByteString::Substr(size_t offset) const {
+ if (offset >= GetLength())
+ return ByteString();
+
+ return Substr(offset, GetLength() - offset);
+}
+
ByteString ByteString::Substr(size_t first, size_t count) const {
if (!m_pData)
return ByteString();
diff --git a/core/fxcrt/bytestring.h b/core/fxcrt/bytestring.h
index 76be3563..c2e45f2 100644
--- a/core/fxcrt/bytestring.h
+++ b/core/fxcrt/bytestring.h
@@ -167,6 +167,7 @@
pdfium::span<char> GetBuffer(size_t nMinBufLength);
void ReleaseBuffer(size_t nNewLength);
+ ByteString Substr(size_t offset) const;
ByteString Substr(size_t first, size_t count) const;
ByteString First(size_t count) const;
ByteString Last(size_t count) const;
diff --git a/core/fxcrt/bytestring_unittest.cpp b/core/fxcrt/bytestring_unittest.cpp
index 0be68b9..7cbbd2e 100644
--- a/core/fxcrt/bytestring_unittest.cpp
+++ b/core/fxcrt/bytestring_unittest.cpp
@@ -644,7 +644,20 @@
EXPECT_EQ("", empty);
}
-TEST(ByteString, Substr) {
+TEST(ByteString, OneArgSubstr) {
+ ByteString fred("FRED");
+ EXPECT_EQ("FRED", fred.Substr(0));
+ EXPECT_EQ("RED", fred.Substr(1));
+ EXPECT_EQ("ED", fred.Substr(2));
+ EXPECT_EQ("D", fred.Substr(3));
+ EXPECT_EQ("", fred.Substr(4));
+
+ ByteString empty;
+ EXPECT_EQ("", empty.Substr(0));
+ EXPECT_EQ("", empty.Substr(1));
+}
+
+TEST(ByteString, TwoArgSubstr) {
ByteString fred("FRED");
EXPECT_EQ("", fred.Substr(0, 0));
EXPECT_EQ("", fred.Substr(3, 0));
@@ -1298,7 +1311,28 @@
EXPECT_EQ(2u, result.value());
}
-TEST(ByteStringView, Substr) {
+TEST(ByteStringView, OneArgSubstr) {
+ ByteStringView null_string;
+ EXPECT_EQ(null_string, null_string.Substr(0));
+ EXPECT_EQ(null_string, null_string.Substr(1));
+
+ ByteStringView empty_string("");
+ EXPECT_EQ("", empty_string.Substr(0));
+ EXPECT_EQ("", empty_string.Substr(1));
+
+ ByteStringView single_character("a");
+ EXPECT_EQ(single_character, single_character.Substr(0));
+ EXPECT_EQ("", single_character.Substr(1));
+
+ ByteStringView longer_string("abcdef");
+ EXPECT_EQ(longer_string, longer_string.Substr(0));
+ EXPECT_EQ("", longer_string.Substr(187));
+
+ ByteStringView trailing_substring("ef");
+ EXPECT_EQ(trailing_substring, longer_string.Substr(4));
+}
+
+TEST(ByteStringView, TwoArgSubstr) {
ByteStringView null_string;
EXPECT_EQ(null_string, null_string.Substr(0, 1));
EXPECT_EQ(null_string, null_string.Substr(1, 1));
diff --git a/core/fxcrt/string_view_template.h b/core/fxcrt/string_view_template.h
index 16e61d6..77f761b 100644
--- a/core/fxcrt/string_view_template.h
+++ b/core/fxcrt/string_view_template.h
@@ -26,6 +26,11 @@
//
// 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:
@@ -199,6 +204,13 @@
bool Contains(CharType ch) const { return Find(ch).has_value(); }
+ StringViewTemplate Substr(size_t offset) const {
+ if (offset >= GetLength())
+ return StringViewTemplate();
+
+ return Substr(offset, GetLength() - offset);
+ }
+
StringViewTemplate Substr(size_t first, size_t count) const {
if (!m_Span.data())
return StringViewTemplate();
diff --git a/core/fxcrt/widestring.cpp b/core/fxcrt/widestring.cpp
index 21d1422..e3d7369 100644
--- a/core/fxcrt/widestring.cpp
+++ b/core/fxcrt/widestring.cpp
@@ -704,6 +704,13 @@
return ret;
}
+WideString WideString::Substr(size_t offset) const {
+ if (offset >= GetLength())
+ return WideString();
+
+ return Substr(offset, GetLength() - offset);
+}
+
WideString WideString::Substr(size_t first, size_t count) const {
if (!m_pData)
return WideString();
diff --git a/core/fxcrt/widestring.h b/core/fxcrt/widestring.h
index 59013fc..2b77321 100644
--- a/core/fxcrt/widestring.h
+++ b/core/fxcrt/widestring.h
@@ -155,6 +155,7 @@
int Compare(const WideString& str) const;
int CompareNoCase(const wchar_t* str) const;
+ WideString Substr(size_t offset) const;
WideString Substr(size_t first, size_t count) const;
WideString First(size_t count) const;
WideString Last(size_t count) const;
diff --git a/core/fxcrt/widestring_unittest.cpp b/core/fxcrt/widestring_unittest.cpp
index 8b7f2ec..b05f26b 100644
--- a/core/fxcrt/widestring_unittest.cpp
+++ b/core/fxcrt/widestring_unittest.cpp
@@ -651,7 +651,20 @@
EXPECT_EQ(L"", empty);
}
-TEST(WideString, Substr) {
+TEST(WideString, OneArgSubstr) {
+ WideString fred(L"FRED");
+ EXPECT_EQ(L"FRED", fred.Substr(0));
+ EXPECT_EQ(L"RED", fred.Substr(1));
+ EXPECT_EQ(L"ED", fred.Substr(2));
+ EXPECT_EQ(L"D", fred.Substr(3));
+ EXPECT_EQ(L"", fred.Substr(4));
+
+ WideString empty;
+ EXPECT_EQ(L"", empty.Substr(0));
+ EXPECT_EQ(L"", empty.Substr(1));
+}
+
+TEST(WideString, TwoArgSubstr) {
WideString fred(L"FRED");
EXPECT_EQ(L"", fred.Substr(0, 0));
EXPECT_EQ(L"", fred.Substr(3, 0));
diff --git a/fxjs/cjs_document.cpp b/fxjs/cjs_document.cpp
index 3f598cd..fb072e6 100644
--- a/fxjs/cjs_document.cpp
+++ b/fxjs/cjs_document.cpp
@@ -947,9 +947,10 @@
if (wsFilePath[i - 1] == L'\\' || wsFilePath[i - 1] == L'/')
break;
}
- if (i > 0 && i < wsFilePath.GetLength())
- return CJS_Result::Success(pRuntime->NewString(wsFilePath.c_str() + i));
-
+ if (i > 0 && i < wsFilePath.GetLength()) {
+ return CJS_Result::Success(
+ pRuntime->NewString(wsFilePath.AsStringView().Substr(i)));
+ }
return CJS_Result::Success(pRuntime->NewString(""));
}
diff --git a/fxjs/xfa/cjx_object.cpp b/fxjs/xfa/cjx_object.cpp
index cf487c5..b38b7dc 100644
--- a/fxjs/xfa/cjx_object.cpp
+++ b/fxjs/xfa/cjx_object.cpp
@@ -1010,7 +1010,7 @@
WideString wsSOM;
if (!wsValue.IsEmpty()) {
if (wsValue[0] == '#')
- wsID = wsValue.Substr(1, wsValue.GetLength() - 1);
+ wsID = wsValue.Substr(1);
else
wsSOM = std::move(wsValue);
}
diff --git a/xfa/fxfa/parser/cxfa_document.cpp b/xfa/fxfa/parser/cxfa_document.cpp
index 0ea2395..572e78c 100644
--- a/xfa/fxfa/parser/cxfa_document.cpp
+++ b/xfa/fxfa/parser/cxfa_document.cpp
@@ -1491,11 +1491,7 @@
int8_t iMajor = FXSYS_wtoi(
wsTemplateNS.Substr(prefixLength, nDotPos.value() - prefixLength)
.c_str());
- int8_t iMinor =
- FXSYS_wtoi(wsTemplateNS
- .Substr(nDotPos.value() + 1,
- wsTemplateNS.GetLength() - nDotPos.value() - 1)
- .c_str());
+ int8_t iMinor = FXSYS_wtoi(wsTemplateNS.Substr(nDotPos.value() + 1).c_str());
XFA_VERSION eVersion =
static_cast<XFA_VERSION>(static_cast<int32_t>(iMajor) * 100 + iMinor);
if (eVersion < XFA_VERSION_MIN || eVersion > XFA_VERSION_MAX)
@@ -1604,16 +1600,14 @@
return;
}
wsURI = wsUseVal.AsStringView().First(uSharpPos.value());
- size_t uLen = wsUseVal.GetLength();
- if (uLen >= uSharpPos.value() + 5 &&
- wsUseVal.AsStringView().Substr(uSharpPos.value(), 5) == L"#som(" &&
- wsUseVal[uLen - 1] == ')') {
- wsSOM = wsUseVal.AsStringView().Substr(uSharpPos.value() + 5,
- uLen - 1 - uSharpPos.value() - 5);
+ if (wsUseVal.AsStringView().Substr(uSharpPos.value(), 5) == L"#som(" &&
+ wsUseVal.Back() == ')') {
+ wsSOM = wsUseVal.AsStringView().Substr(
+ uSharpPos.value() + 5,
+ wsUseVal.GetLength() - 1 - uSharpPos.value() - 5);
return;
}
- wsID = wsUseVal.AsStringView().Substr(uSharpPos.value() + 1,
- uLen - uSharpPos.value() - 1);
+ wsID = wsUseVal.AsStringView().Substr(uSharpPos.value() + 1);
}
// static
@@ -1624,7 +1618,7 @@
return;
if (wsUseVal[0] == '#') {
- wsID = wsUseVal.AsStringView().Substr(1, wsUseVal.GetLength() - 1);
+ wsID = wsUseVal.AsStringView().Substr(1);
return;
}
wsSOM = wsUseVal.AsStringView();