Combine FX_wcspos() and FX_strpos() into fxcrt::spanpos().
The code is nearly identical so make into a generic find span
within span function.
-- Add unit tests.
Change-Id: I154b102f70c7bfbf897c231e337636e8e9df4615
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/116470
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Thomas Sepez <tsepez@google.com>
diff --git a/core/fxcrt/bytestring.cpp b/core/fxcrt/bytestring.cpp
index 7d67743..83109fe 100644
--- a/core/fxcrt/bytestring.cpp
+++ b/core/fxcrt/bytestring.cpp
@@ -34,23 +34,6 @@
constexpr char kTrimChars[] = "\x09\x0a\x0b\x0c\x0d\x20";
-std::optional<size_t> FX_strpos(pdfium::span<const char> haystack,
- pdfium::span<const char> needle) {
- if (needle.empty() || needle.size() > haystack.size()) {
- return std::nullopt;
- }
- // After `end_pos`, not enough characters remain in `haystack` for
- // a full match to occur.
- size_t end_pos = haystack.size() - needle.size();
- for (size_t haystack_pos = 0; haystack_pos <= end_pos; ++haystack_pos) {
- auto candidate = haystack.subspan(haystack_pos, needle.size());
- if (fxcrt::span_equals(candidate, needle)) {
- return haystack_pos;
- }
- }
- return std::nullopt;
-}
-
} // namespace
namespace fxcrt {
@@ -531,7 +514,7 @@
return std::nullopt;
}
std::optional<size_t> result =
- FX_strpos(m_pData->span().subspan(start), subStr.span());
+ spanpos(m_pData->span().subspan(start), subStr.span());
if (!result.has_value()) {
return std::nullopt;
}
@@ -609,7 +592,7 @@
// Limit span lifetime.
pdfium::span<char> search_span = m_pData->span();
while (true) {
- std::optional<size_t> found = FX_strpos(search_span, pOld.span());
+ std::optional<size_t> found = spanpos(search_span, pOld.span());
if (!found.has_value()) {
break;
}
@@ -634,12 +617,12 @@
pdfium::span<const char> search_span = m_pData->span();
pdfium::span<char> dest_span = pNewData->span();
for (size_t i = 0; i < nCount; i++) {
- size_t found = FX_strpos(search_span, pOld.span()).value();
- dest_span = fxcrt::spancpy(dest_span, search_span.first(found));
- dest_span = fxcrt::spancpy(dest_span, pNew.span());
+ size_t found = spanpos(search_span, pOld.span()).value();
+ dest_span = spancpy(dest_span, search_span.first(found));
+ dest_span = spancpy(dest_span, pNew.span());
search_span = search_span.subspan(found + pOld.GetLength());
}
- dest_span = fxcrt::spancpy(dest_span, search_span);
+ dest_span = spancpy(dest_span, search_span);
CHECK(dest_span.empty());
}
m_pData = std::move(pNewData);
diff --git a/core/fxcrt/span_util.h b/core/fxcrt/span_util.h
index 99ae5de..a88532a 100644
--- a/core/fxcrt/span_util.h
+++ b/core/fxcrt/span_util.h
@@ -7,6 +7,7 @@
#include <stdint.h>
+#include <optional>
#include <type_traits>
#include "core/fxcrt/fx_memcpy_wrappers.h"
@@ -69,6 +70,29 @@
FXSYS_memcmp(s1.data(), s2.data(), s1.size_bytes()) == 0;
}
+// Returns the first position where `needle` occurs in `haystack`.
+template <typename T,
+ typename U,
+ typename = std::enable_if_t<sizeof(T) == sizeof(U) &&
+ std::is_trivially_copyable_v<T> &&
+ std::is_trivially_copyable_v<U>>>
+std::optional<size_t> spanpos(pdfium::span<T> haystack,
+ pdfium::span<U> needle) {
+ if (needle.empty() || needle.size() > haystack.size()) {
+ return std::nullopt;
+ }
+ // After this `end_pos`, not enough characters remain in `haystack` for
+ // a full match to occur.
+ size_t end_pos = haystack.size() - needle.size();
+ for (size_t haystack_pos = 0; haystack_pos <= end_pos; ++haystack_pos) {
+ auto candidate = haystack.subspan(haystack_pos, needle.size());
+ if (fxcrt::span_equals(candidate, needle)) {
+ return haystack_pos;
+ }
+ }
+ return std::nullopt;
+}
+
template <typename T,
typename U,
typename = typename std::enable_if_t<std::is_const_v<T> ||
diff --git a/core/fxcrt/span_util_unittest.cpp b/core/fxcrt/span_util_unittest.cpp
index 179c378..98250cc 100644
--- a/core/fxcrt/span_util_unittest.cpp
+++ b/core/fxcrt/span_util_unittest.cpp
@@ -6,6 +6,7 @@
#include <vector>
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(Spanset, Fits) {
@@ -153,3 +154,30 @@
pdfium::make_span(abcabc).subspan(1, 4)),
"");
}
+
+TEST(Spanpos, Empty) {
+ pdfium::span<const uint32_t> kEmpty;
+ const uint32_t kHaystack[] = {0, 1, 2, 3, 4, 5};
+ const uint32_t kNeedle[] = {1, 2};
+ EXPECT_FALSE(fxcrt::spanpos(kEmpty, kEmpty));
+ EXPECT_FALSE(fxcrt::spanpos(pdfium::make_span(kHaystack), kEmpty));
+ EXPECT_FALSE(fxcrt::spanpos(kEmpty, pdfium::make_span(kNeedle)));
+}
+
+TEST(Spanpos, NotEmpty) {
+ const uint32_t kHaystack[] = {0, 1, 2, 3, 4, 5};
+ const uint32_t kStartMatch[] = {0, 1};
+ const uint32_t kEndMatch[] = {4, 5};
+ const uint32_t kNotFound[] = {256, 512}; // test byte-shifted {1,2}.
+ const uint32_t kTooLong[] = {0, 1, 2, 3, 4, 5, 6};
+ EXPECT_THAT(fxcrt::spanpos(pdfium::make_span(kHaystack),
+ pdfium::make_span(kStartMatch)),
+ testing::Optional(0u));
+ EXPECT_THAT(fxcrt::spanpos(pdfium::make_span(kHaystack),
+ pdfium::make_span(kEndMatch)),
+ testing::Optional(4u));
+ EXPECT_FALSE(fxcrt::spanpos(pdfium::make_span(kHaystack),
+ pdfium::make_span(kNotFound)));
+ EXPECT_FALSE(fxcrt::spanpos(pdfium::make_span(kHaystack),
+ pdfium::make_span(kTooLong)));
+}
diff --git a/core/fxcrt/widestring.cpp b/core/fxcrt/widestring.cpp
index d4155c0..a4bcdd8 100644
--- a/core/fxcrt/widestring.cpp
+++ b/core/fxcrt/widestring.cpp
@@ -54,23 +54,6 @@
constexpr wchar_t kWideTrimChars[] = L"\x09\x0a\x0b\x0c\x0d\x20";
-std::optional<size_t> FX_wcspos(pdfium::span<const wchar_t> haystack,
- pdfium::span<const wchar_t> needle) {
- if (needle.empty() || needle.size() > haystack.size()) {
- return std::nullopt;
- }
- // After this `end_pos`, not enough characters remain in `haystack` for
- // a full match to occur.
- size_t end_pos = haystack.size() - needle.size();
- for (size_t haystack_pos = 0; haystack_pos <= end_pos; ++haystack_pos) {
- auto candidate = haystack.subspan(haystack_pos, needle.size());
- if (fxcrt::span_equals(candidate, needle)) {
- return haystack_pos;
- }
- }
- return std::nullopt;
-}
-
std::optional<size_t> GuessSizeForVSWPrintf(const wchar_t* pFormat,
va_list argList) {
size_t nMaxLen = 0;
@@ -847,7 +830,7 @@
return std::nullopt;
}
std::optional<size_t> result =
- FX_wcspos(m_pData->span().subspan(start), subStr.span());
+ spanpos(m_pData->span().subspan(start), subStr.span());
if (!result.has_value()) {
return std::nullopt;
}
@@ -925,7 +908,7 @@
// Limit span lifetime.
pdfium::span<const wchar_t> search_span = m_pData->span();
while (true) {
- std::optional<size_t> found = FX_wcspos(search_span, pOld.span());
+ std::optional<size_t> found = spanpos(search_span, pOld.span());
if (!found.has_value()) {
break;
}
@@ -950,12 +933,12 @@
pdfium::span<const wchar_t> search_span = m_pData->span();
pdfium::span<wchar_t> dest_span = pNewData->span();
for (size_t i = 0; i < count; i++) {
- size_t found = FX_wcspos(search_span, pOld.span()).value();
- dest_span = fxcrt::spancpy(dest_span, search_span.first(found));
- dest_span = fxcrt::spancpy(dest_span, pNew.span());
+ size_t found = spanpos(search_span, pOld.span()).value();
+ dest_span = spancpy(dest_span, search_span.first(found));
+ dest_span = spancpy(dest_span, pNew.span());
search_span = search_span.subspan(found + pOld.GetLength());
}
- dest_span = fxcrt::spancpy(dest_span, search_span);
+ dest_span = spancpy(dest_span, search_span);
CHECK(dest_span.empty());
}
m_pData = std::move(pNewData);