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);