Move {Byte,Wide}String::Replace() to StringTemplate<>

Change-Id: Iebf22a71c0d06f7d38cc721c023d9c1505380861
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/116676
Reviewed-by: Thomas Sepez <tsepez@google.com>
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 bec3d05..fba02a8 100644
--- a/core/fxcrt/bytestring.cpp
+++ b/core/fxcrt/bytestring.cpp
@@ -338,52 +338,6 @@
   FXSYS_strupr(m_pData->m_String);
 }
 
-size_t ByteString::Replace(ByteStringView pOld, ByteStringView pNew) {
-  if (!m_pData || pOld.IsEmpty())
-    return 0;
-
-  size_t nCount = 0;
-  {
-    // Limit span lifetime.
-    pdfium::span<char> search_span = m_pData->span();
-    while (true) {
-      std::optional<size_t> found = spanpos(search_span, pOld.span());
-      if (!found.has_value()) {
-        break;
-      }
-      nCount++;
-      search_span = search_span.subspan(found.value() + pOld.GetLength());
-    }
-  }
-  if (nCount == 0)
-    return 0;
-
-  size_t nNewLength =
-      m_pData->m_nDataLength + nCount * (pNew.GetLength() - pOld.GetLength());
-
-  if (nNewLength == 0) {
-    clear();
-    return nCount;
-  }
-
-  RetainPtr<StringData> pNewData = StringData::Create(nNewLength);
-  {
-    // Spans can't outlive the StringData buffers.
-    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 = 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 = spancpy(dest_span, search_span);
-    CHECK(dest_span.empty());
-  }
-  m_pData = std::move(pNewData);
-  return nCount;
-}
-
 int ByteString::Compare(ByteStringView str) const {
   if (!m_pData)
     return str.IsEmpty() ? 0 : -1;
diff --git a/core/fxcrt/bytestring.h b/core/fxcrt/bytestring.h
index 62794c8..52894cb 100644
--- a/core/fxcrt/bytestring.h
+++ b/core/fxcrt/bytestring.h
@@ -120,8 +120,6 @@
   void TrimRight(char target);
   void TrimRight(ByteStringView targets);
 
-  size_t Replace(ByteStringView pOld, ByteStringView pNew);
-
   uint32_t GetID() const { return AsStringView().GetID(); }
 
  protected:
diff --git a/core/fxcrt/string_template.cpp b/core/fxcrt/string_template.cpp
index cc3e1f9..bb7328d 100644
--- a/core/fxcrt/string_template.cpp
+++ b/core/fxcrt/string_template.cpp
@@ -182,6 +182,51 @@
 }
 
 template <typename T>
+size_t StringTemplate<T>::Replace(StringView oldstr, StringView newstr) {
+  if (!m_pData || oldstr.IsEmpty()) {
+    return 0;
+  }
+  size_t count = 0;
+  {
+    // Limit span lifetime.
+    pdfium::span<const T> search_span = m_pData->span();
+    while (true) {
+      std::optional<size_t> found = spanpos(search_span, oldstr.span());
+      if (!found.has_value()) {
+        break;
+      }
+      ++count;
+      search_span = search_span.subspan(found.value() + oldstr.GetLength());
+    }
+  }
+  if (count == 0) {
+    return 0;
+  }
+  size_t nNewLength = m_pData->m_nDataLength +
+                      count * (newstr.GetLength() - oldstr.GetLength());
+  if (nNewLength == 0) {
+    clear();
+    return count;
+  }
+  RetainPtr<StringData> newstr_data = StringData::Create(nNewLength);
+  {
+    // Spans can't outlive StringData buffers.
+    pdfium::span<const T> search_span = m_pData->span();
+    pdfium::span<T> dest_span = newstr_data->span();
+    for (size_t i = 0; i < count; i++) {
+      size_t found = spanpos(search_span, oldstr.span()).value();
+      dest_span = spancpy(dest_span, search_span.first(found));
+      dest_span = spancpy(dest_span, newstr.span());
+      search_span = search_span.subspan(found + oldstr.GetLength());
+    }
+    dest_span = spancpy(dest_span, search_span);
+    CHECK(dest_span.empty());
+  }
+  m_pData = std::move(newstr_data);
+  return count;
+}
+
+template <typename T>
 void StringTemplate<T>::ReallocBeforeWrite(size_t nNewLength) {
   if (m_pData && m_pData->CanOperateInPlace(nNewLength)) {
     return;
diff --git a/core/fxcrt/string_template.h b/core/fxcrt/string_template.h
index 4bf0b80..0e1af03 100644
--- a/core/fxcrt/string_template.h
+++ b/core/fxcrt/string_template.h
@@ -114,7 +114,7 @@
   // Returns size of the string following deletion.
   size_t Delete(size_t index, size_t count = 1);
 
-  // Returns the index within in the string when found.
+  // Returns the index within the string when found.
   std::optional<size_t> Find(StringView str, size_t start = 0) const;
   std::optional<size_t> Find(T ch, size_t start = 0) const;
   std::optional<size_t> ReverseFind(T ch) const;
@@ -126,6 +126,9 @@
     return Find(ch, start).has_value();
   }
 
+  // Replace all occurences of `oldstr' with `newstr'.
+  size_t Replace(StringView oldstr, StringView newstr);
+
   // Overwrite character at `index`.
   void SetAt(size_t index, T ch);
 
diff --git a/core/fxcrt/widestring.cpp b/core/fxcrt/widestring.cpp
index bf73577..dfbae22 100644
--- a/core/fxcrt/widestring.cpp
+++ b/core/fxcrt/widestring.cpp
@@ -659,52 +659,6 @@
   FXSYS_wcsupr(m_pData->m_String);
 }
 
-size_t WideString::Replace(WideStringView pOld, WideStringView pNew) {
-  if (!m_pData || pOld.IsEmpty())
-    return 0;
-
-  size_t count = 0;
-  {
-    // Limit span lifetime.
-    pdfium::span<const wchar_t> search_span = m_pData->span();
-    while (true) {
-      std::optional<size_t> found = spanpos(search_span, pOld.span());
-      if (!found.has_value()) {
-        break;
-      }
-      ++count;
-      search_span = search_span.subspan(found.value() + pOld.GetLength());
-    }
-  }
-  if (count == 0)
-    return 0;
-
-  size_t nNewLength =
-      m_pData->m_nDataLength + count * (pNew.GetLength() - pOld.GetLength());
-
-  if (nNewLength == 0) {
-    clear();
-    return count;
-  }
-
-  RetainPtr<StringData> pNewData = StringData::Create(nNewLength);
-  {
-    // Spans can't outlive StrinData buffers.
-    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 = 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 = spancpy(dest_span, search_span);
-    CHECK(dest_span.empty());
-  }
-  m_pData = std::move(pNewData);
-  return count;
-}
-
 // static
 WideString WideString::FromASCII(ByteStringView bstr) {
   WideString result;
diff --git a/core/fxcrt/widestring.h b/core/fxcrt/widestring.h
index a52c301..0e6feeb 100644
--- a/core/fxcrt/widestring.h
+++ b/core/fxcrt/widestring.h
@@ -131,8 +131,6 @@
 
   int GetInteger() const;
 
-  size_t Replace(WideStringView pOld, WideStringView pNew);
-
   bool IsASCII() const { return AsStringView().IsASCII(); }
   bool EqualsASCII(ByteStringView that) const {
     return AsStringView().EqualsASCII(that);