absl::string_view: Add support for starts_with() and ends_with()
when targeting at least C++20

These methods were added to C++20, so they are not available in
earlier language standards. Users requiring compatibility prior to C++20
should use absl::StartsWith() and absl::EndsWith() from
//absl/strings/match.h.

Most users are not affected by this change. By default when targeting at least
C++20 absl::string_view will be an alias for std::string_view. Only users
that have modified //absl/base/options.h will see this change.

PiperOrigin-RevId: 575238435
Change-Id: I7b03fde02c987b30b88c794640c2a616851997d1
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h
index eae11b2..f88b05b 100644
--- a/absl/strings/string_view.h
+++ b/absl/strings/string_view.h
@@ -506,7 +506,7 @@
   // Overload of `string_view::find_first_of()` for finding a substring of a
   // different C-style string `s` within the `string_view`.
   size_type find_first_of(const char* s, size_type pos,
-                                    size_type count) const {
+                          size_type count) const {
     return find_first_of(string_view(s, count), pos);
   }
 
@@ -590,6 +590,58 @@
     return find_last_not_of(string_view(s), pos);
   }
 
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+  // string_view::starts_with()
+  //
+  // Returns true if the `string_view` starts with the prefix `s`.
+  //
+  // This method only exists when targeting at least C++20.
+  // If support for C++ prior to C++20 is required, use `absl::StartsWith()`
+  // from `//absl/strings/match.h` for compatibility.
+  constexpr bool starts_with(string_view s) const noexcept {
+    return s.empty() ||
+           (size() >= s.size() &&
+            ABSL_INTERNAL_STRING_VIEW_MEMCMP(data(), s.data(), s.size()) == 0);
+  }
+
+  // Overload of `string_view::starts_with()` that returns true if `c` is the
+  // first character of the `string_view`.
+  constexpr bool starts_with(char c) const noexcept {
+    return !empty() && front() == c;
+  }
+
+  // Overload of `string_view::starts_with()` that returns true if the
+  // `string_view` starts with the C-style prefix `s`.
+  constexpr bool starts_with(const char* s) const {
+    return starts_with(string_view(s));
+  }
+
+  // string_view::ends_with()
+  //
+  // Returns true if the `string_view` ends with the suffix `s`.
+  //
+  // This method only exists when targeting at least C++20.
+  // If support for C++ prior to C++20 is required, use `absl::EndsWith()`
+  // from `//absl/strings/match.h` for compatibility.
+  constexpr bool ends_with(string_view s) const noexcept {
+    return s.empty() || (size() >= s.size() && ABSL_INTERNAL_STRING_VIEW_MEMCMP(
+                                                   data() + (size() - s.size()),
+                                                   s.data(), s.size()) == 0);
+  }
+
+  // Overload of `string_view::ends_with()` that returns true if `c` is the
+  // last character of the `string_view`.
+  constexpr bool ends_with(char c) const noexcept {
+    return !empty() && back() == c;
+  }
+
+  // Overload of `string_view::ends_with()` that returns true if the
+  // `string_view` ends with the C-style suffix `s`.
+  constexpr bool ends_with(const char* s) const {
+    return ends_with(string_view(s));
+  }
+#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+
  private:
   // The constructor from std::string delegates to this constructor.
   // See the comment on that constructor for the rationale.
diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc
index 642989b..5b1eb01 100644
--- a/absl/strings/string_view_test.cc
+++ b/absl/strings/string_view_test.cc
@@ -951,6 +951,76 @@
 #endif
 }
 
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+TEST(StringViewTest, StartsWith) {
+  const absl::string_view a("foobar");
+  const absl::string_view b("123\0abc", 7);
+  const absl::string_view e;
+  EXPECT_TRUE(a.starts_with(a));
+  EXPECT_TRUE(a.starts_with("foo"));
+  EXPECT_TRUE(a.starts_with('f'));
+  EXPECT_TRUE(a.starts_with(e));
+  EXPECT_TRUE(b.starts_with(b));
+  EXPECT_TRUE(b.starts_with('1'));
+  EXPECT_TRUE(b.starts_with(e));
+  EXPECT_TRUE(e.starts_with(""));
+  EXPECT_FALSE(a.starts_with(b));
+  EXPECT_FALSE(b.starts_with(a));
+  EXPECT_FALSE(e.starts_with(a));
+  EXPECT_FALSE(a.starts_with('r'));
+  EXPECT_FALSE(a.starts_with('\0'));
+  EXPECT_FALSE(e.starts_with('r'));
+  EXPECT_FALSE(e.starts_with('\0'));
+
+  // Test that constexpr compiles.
+  constexpr absl::string_view kFooBar("foobar");
+  constexpr absl::string_view kFoo("foo");
+  constexpr absl::string_view kBar("bar");
+  constexpr bool k1 = kFooBar.starts_with(kFoo);
+  EXPECT_TRUE(k1);
+  constexpr bool k2 = kFooBar.starts_with(kBar);
+  EXPECT_FALSE(k2);
+  constexpr bool k3 = kFooBar.starts_with('f');
+  EXPECT_TRUE(k3);
+  constexpr bool k4 = kFooBar.starts_with("fo");
+  EXPECT_TRUE(k4);
+}
+
+TEST(StringViewTest, EndsWith) {
+  const absl::string_view a("foobar");
+  const absl::string_view b("123\0abc", 7);
+  const absl::string_view e;
+  EXPECT_TRUE(a.ends_with(a));
+  EXPECT_TRUE(a.ends_with('r'));
+  EXPECT_TRUE(a.ends_with("bar"));
+  EXPECT_TRUE(a.ends_with(e));
+  EXPECT_TRUE(b.ends_with(b));
+  EXPECT_TRUE(b.ends_with('c'));
+  EXPECT_TRUE(b.ends_with(e));
+  EXPECT_TRUE(e.ends_with(""));
+  EXPECT_FALSE(a.ends_with(b));
+  EXPECT_FALSE(b.ends_with(a));
+  EXPECT_FALSE(e.ends_with(a));
+  EXPECT_FALSE(a.ends_with('f'));
+  EXPECT_FALSE(a.ends_with('\0'));
+  EXPECT_FALSE(e.ends_with('r'));
+  EXPECT_FALSE(e.ends_with('\0'));
+
+  // Test that constexpr compiles.
+  constexpr absl::string_view kFooBar("foobar");
+  constexpr absl::string_view kFoo("foo");
+  constexpr absl::string_view kBar("bar");
+  constexpr bool k1 = kFooBar.ends_with(kFoo);
+  EXPECT_FALSE(k1);
+  constexpr bool k2 = kFooBar.ends_with(kBar);
+  EXPECT_TRUE(k2);
+  constexpr bool k3 = kFooBar.ends_with('r');
+  EXPECT_TRUE(k3);
+  constexpr bool k4 = kFooBar.ends_with("ar");
+  EXPECT_TRUE(k4);
+}
+#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+
 struct MyCharAlloc : std::allocator<char> {};
 
 TEST(StringViewTest, ExplicitConversionOperator) {