Add iterators for Byte/WideStringCs.

Change-Id: I40ec07c0da54bcf36c83fa26ff457cd4b98a91cf
Reviewed-on: https://pdfium-review.googlesource.com/4261
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxcrt/cfx_bytestring.h b/core/fxcrt/cfx_bytestring.h
index b9ebd8a..0ffe929 100644
--- a/core/fxcrt/cfx_bytestring.h
+++ b/core/fxcrt/cfx_bytestring.h
@@ -23,6 +23,7 @@
 class CFX_ByteString {
  public:
   using CharType = char;
+  using const_iterator = const CharType*;
 
   CFX_ByteString();
   CFX_ByteString(const CFX_ByteString& other);
@@ -65,6 +66,12 @@
     return CFX_ByteStringC(raw_str(), GetLength());
   }
 
+  // Note: Any subsequent modification of |this| will invalidate iterators.
+  const_iterator begin() const { return m_pData ? m_pData->m_String : nullptr; }
+  const_iterator end() const {
+    return m_pData ? m_pData->m_String + m_pData->m_nDataLength : nullptr;
+  }
+
   FX_STRSIZE GetLength() const { return m_pData ? m_pData->m_nDataLength : 0; }
   bool IsEmpty() const { return !GetLength(); }
 
diff --git a/core/fxcrt/cfx_bytestring_unittest.cpp b/core/fxcrt/cfx_bytestring_unittest.cpp
index 9cfc773..42627c2 100644
--- a/core/fxcrt/cfx_bytestring_unittest.cpp
+++ b/core/fxcrt/cfx_bytestring_unittest.cpp
@@ -4,10 +4,12 @@
 
 #include "core/fxcrt/cfx_bytestring.h"
 
+#include <algorithm>
 #include <vector>
 
 #include "testing/fx_string_testhelpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/stl_util.h"
 
 TEST(fxcrt, ByteStringOperatorSubscript) {
   // CFX_ByteString includes the NUL terminator for non-empty strings.
@@ -1031,6 +1033,70 @@
   EXPECT_TRUE(c_string3 != byte_string_c);
 }
 
+TEST(fxcrt, ByteStringCNullIterator) {
+  CFX_ByteStringC null_str;
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : null_str) {
+    sum += c;  // Avoid unused arg warnings.
+    any_present = true;
+  }
+  EXPECT_FALSE(any_present);
+  EXPECT_EQ(0, sum);
+}
+
+TEST(fxcrt, ByteStringCEmptyIterator) {
+  CFX_ByteStringC empty_str("");
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : empty_str) {
+    any_present = true;
+    sum += c;  // Avoid unused arg warnings.
+  }
+  EXPECT_FALSE(any_present);
+  EXPECT_EQ(0, sum);
+}
+
+TEST(fxcrt, ByteStringCOneCharIterator) {
+  CFX_ByteStringC one_str("a");
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : one_str) {
+    any_present = true;
+    sum += c;  // Avoid unused arg warnings.
+  }
+  EXPECT_TRUE(any_present);
+  EXPECT_EQ('a', sum);
+}
+
+TEST(fxcrt, ByteStringCMultiCharIterator) {
+  CFX_ByteStringC one_str("abc");
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : one_str) {
+    any_present = true;
+    sum += c;  // Avoid unused arg warnings.
+  }
+  EXPECT_TRUE(any_present);
+  EXPECT_EQ('a' + 'b' + 'c', sum);
+}
+
+TEST(fxcrt, ByteStringCAnyAllNoneOf) {
+  CFX_ByteStringC str("aaaaaaaaaaaaaaaaab");
+  EXPECT_FALSE(std::all_of(str.begin(), str.end(),
+                           [](const char& c) { return c == 'a'; }));
+
+  EXPECT_FALSE(std::none_of(str.begin(), str.end(),
+                            [](const char& c) { return c == 'a'; }));
+
+  EXPECT_TRUE(std::any_of(str.begin(), str.end(),
+                          [](const char& c) { return c == 'a'; }));
+
+  EXPECT_TRUE(pdfium::ContainsValue(str, 'a'));
+  EXPECT_TRUE(pdfium::ContainsValue(str, 'b'));
+  EXPECT_FALSE(pdfium::ContainsValue(str, 'z'));
+}
+
 TEST(fxcrt, ByteStringFormatWidth) {
   {
     CFX_ByteString str;
@@ -1109,3 +1175,67 @@
   many_str = {"fish", " and ", "chips", " and ", "soda"};
   EXPECT_EQ("fish and chips and soda", many_str);
 }
+
+TEST(fxcrt, ByteStringNullIterator) {
+  CFX_ByteString null_str;
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : null_str) {
+    sum += c;  // Avoid unused arg warnings.
+    any_present = true;
+  }
+  EXPECT_FALSE(any_present);
+  EXPECT_EQ(0, sum);
+}
+
+TEST(fxcrt, ByteStringEmptyIterator) {
+  CFX_ByteString empty_str("");
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : empty_str) {
+    any_present = true;
+    sum += c;  // Avoid unused arg warnings.
+  }
+  EXPECT_FALSE(any_present);
+  EXPECT_EQ(0, sum);
+}
+
+TEST(fxcrt, ByteStringOneCharIterator) {
+  CFX_ByteString one_str("a");
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : one_str) {
+    any_present = true;
+    sum += c;  // Avoid unused arg warnings.
+  }
+  EXPECT_TRUE(any_present);
+  EXPECT_EQ('a', sum);
+}
+
+TEST(fxcrt, ByteStringMultiCharIterator) {
+  CFX_ByteString one_str("abc");
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : one_str) {
+    any_present = true;
+    sum += c;  // Avoid unused arg warnings.
+  }
+  EXPECT_TRUE(any_present);
+  EXPECT_EQ('a' + 'b' + 'c', sum);
+}
+
+TEST(fxcrt, ByteStringAnyAllNoneOf) {
+  CFX_ByteString str("aaaaaaaaaaaaaaaaab");
+  EXPECT_FALSE(std::all_of(str.begin(), str.end(),
+                           [](const char& c) { return c == 'a'; }));
+
+  EXPECT_FALSE(std::none_of(str.begin(), str.end(),
+                            [](const char& c) { return c == 'a'; }));
+
+  EXPECT_TRUE(std::any_of(str.begin(), str.end(),
+                          [](const char& c) { return c == 'a'; }));
+
+  EXPECT_TRUE(pdfium::ContainsValue(str, 'a'));
+  EXPECT_TRUE(pdfium::ContainsValue(str, 'b'));
+  EXPECT_FALSE(pdfium::ContainsValue(str, 'z'));
+}
diff --git a/core/fxcrt/cfx_string_c_template.h b/core/fxcrt/cfx_string_c_template.h
index 4aba2cd..75c2be0 100644
--- a/core/fxcrt/cfx_string_c_template.h
+++ b/core/fxcrt/cfx_string_c_template.h
@@ -22,6 +22,7 @@
  public:
   using CharType = T;
   using UnsignedType = typename std::make_unsigned<CharType>::type;
+  using const_iterator = const CharType*;
 
   CFX_StringCTemplate() : m_Ptr(nullptr), m_Length(0) {}
 
@@ -73,6 +74,14 @@
     return *this;
   }
 
+  const_iterator begin() const {
+    return reinterpret_cast<const CharType*>(m_Ptr);
+  }
+  const_iterator end() const {
+    return m_Ptr ? reinterpret_cast<const CharType*>(m_Ptr) + m_Length
+                 : nullptr;
+  }
+
   bool operator==(const CharType* ptr) const {
     return FXSYS_len(ptr) == m_Length &&
            FXSYS_cmp(ptr, reinterpret_cast<const CharType*>(m_Ptr), m_Length) ==
diff --git a/core/fxcrt/cfx_widestring.h b/core/fxcrt/cfx_widestring.h
index 52cd65a..fd3c92a 100644
--- a/core/fxcrt/cfx_widestring.h
+++ b/core/fxcrt/cfx_widestring.h
@@ -23,6 +23,7 @@
 class CFX_WideString {
  public:
   using CharType = wchar_t;
+  using const_iterator = const CharType*;
 
   CFX_WideString();
   CFX_WideString(const CFX_WideString& other);
@@ -61,6 +62,12 @@
     return CFX_WideStringC(c_str(), GetLength());
   }
 
+  // Note: Any subsequent modification of |this| will invalidate iterators.
+  const_iterator begin() const { return m_pData ? m_pData->m_String : nullptr; }
+  const_iterator end() const {
+    return m_pData ? m_pData->m_String + m_pData->m_nDataLength : nullptr;
+  }
+
   void clear() { m_pData.Reset(); }
 
   FX_STRSIZE GetLength() const { return m_pData ? m_pData->m_nDataLength : 0; }
diff --git a/core/fxcrt/cfx_widestring_unittest.cpp b/core/fxcrt/cfx_widestring_unittest.cpp
index 498b4f0..a9cb2d3 100644
--- a/core/fxcrt/cfx_widestring_unittest.cpp
+++ b/core/fxcrt/cfx_widestring_unittest.cpp
@@ -4,6 +4,7 @@
 
 #include "core/fxcrt/cfx_widestring.h"
 
+#include <algorithm>
 #include <vector>
 
 #include "testing/fx_string_testhelpers.h"
@@ -871,6 +872,70 @@
   EXPECT_EQ(2, hibyte_string.Find(L'\xff08'));
 }
 
+TEST(fxcrt, WideStringCNullIterator) {
+  CFX_WideStringC null_str;
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : null_str) {
+    sum += c;  // Avoid unused arg warnings.
+    any_present = true;
+  }
+  EXPECT_FALSE(any_present);
+  EXPECT_EQ(0, sum);
+}
+
+TEST(fxcrt, WideStringCEmptyIterator) {
+  CFX_WideStringC empty_str(L"");
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : empty_str) {
+    any_present = true;
+    sum += c;  // Avoid unused arg warnings.
+  }
+  EXPECT_FALSE(any_present);
+  EXPECT_EQ(0, sum);
+}
+
+TEST(fxcrt, WideStringCOneCharIterator) {
+  CFX_WideStringC one_str(L"a");
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : one_str) {
+    any_present = true;
+    sum += c;  // Avoid unused arg warnings.
+  }
+  EXPECT_TRUE(any_present);
+  EXPECT_EQ(static_cast<int32_t>(L'a'), sum);
+}
+
+TEST(fxcrt, WideStringCMultiCharIterator) {
+  CFX_WideStringC one_str(L"abc");
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : one_str) {
+    any_present = true;
+    sum += c;  // Avoid unused arg warnings.
+  }
+  EXPECT_TRUE(any_present);
+  EXPECT_EQ(static_cast<int32_t>(L'a' + L'b' + L'c'), sum);
+}
+
+TEST(fxcrt, WideStringCAnyAllNoneOf) {
+  CFX_WideStringC str(L"aaaaaaaaaaaaaaaaab");
+  EXPECT_FALSE(std::all_of(str.begin(), str.end(),
+                           [](const wchar_t& c) { return c == L'a'; }));
+
+  EXPECT_FALSE(std::none_of(str.begin(), str.end(),
+                            [](const wchar_t& c) { return c == L'a'; }));
+
+  EXPECT_TRUE(std::any_of(str.begin(), str.end(),
+                          [](const wchar_t& c) { return c == L'a'; }));
+
+  EXPECT_TRUE(pdfium::ContainsValue(str, L'a'));
+  EXPECT_TRUE(pdfium::ContainsValue(str, L'b'));
+  EXPECT_FALSE(pdfium::ContainsValue(str, L'z'));
+}
+
 TEST(fxcrt, WideStringFormatWidth) {
   {
     CFX_WideString str;
@@ -966,3 +1031,67 @@
   many_str = {L"fish", L" and ", L"chips", L" and ", L"soda"};
   EXPECT_EQ(L"fish and chips and soda", many_str);
 }
+
+TEST(fxcrt, WideStringNullIterator) {
+  CFX_WideString null_str;
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : null_str) {
+    sum += c;  // Avoid unused arg warnings.
+    any_present = true;
+  }
+  EXPECT_FALSE(any_present);
+  EXPECT_EQ(0, sum);
+}
+
+TEST(fxcrt, WideStringEmptyIterator) {
+  CFX_WideString empty_str(L"");
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : empty_str) {
+    any_present = true;
+    sum += c;  // Avoid unused arg warnings.
+  }
+  EXPECT_FALSE(any_present);
+  EXPECT_EQ(0, sum);
+}
+
+TEST(fxcrt, WideStringOneCharIterator) {
+  CFX_WideString one_str(L"a");
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : one_str) {
+    any_present = true;
+    sum += c;  // Avoid unused arg warnings.
+  }
+  EXPECT_TRUE(any_present);
+  EXPECT_EQ(static_cast<int32_t>(L'a'), sum);
+}
+
+TEST(fxcrt, WideStringMultiCharIterator) {
+  CFX_WideString one_str(L"abc");
+  int32_t sum = 0;
+  bool any_present = false;
+  for (const auto& c : one_str) {
+    any_present = true;
+    sum += c;  // Avoid unused arg warnings.
+  }
+  EXPECT_TRUE(any_present);
+  EXPECT_EQ(static_cast<int32_t>(L'a' + L'b' + L'c'), sum);
+}
+
+TEST(fxcrt, WideStringAnyAllNoneOf) {
+  CFX_WideString str(L"aaaaaaaaaaaaaaaaab");
+  EXPECT_FALSE(std::all_of(str.begin(), str.end(),
+                           [](const wchar_t& c) { return c == L'a'; }));
+
+  EXPECT_FALSE(std::none_of(str.begin(), str.end(),
+                            [](const wchar_t& c) { return c == L'a'; }));
+
+  EXPECT_TRUE(std::any_of(str.begin(), str.end(),
+                          [](const wchar_t& c) { return c == L'a'; }));
+
+  EXPECT_TRUE(pdfium::ContainsValue(str, L'a'));
+  EXPECT_TRUE(pdfium::ContainsValue(str, L'b'));
+  EXPECT_FALSE(pdfium::ContainsValue(str, L'z'));
+}