about summary refs log tree commit diff
path: root/absl
diff options
context:
space:
mode:
Diffstat (limited to 'absl')
-rw-r--r--absl/strings/BUILD.bazel1
-rw-r--r--absl/strings/CMakeLists.txt1
-rw-r--r--absl/strings/cord.h19
-rw-r--r--absl/strings/cord_test.cc71
-rw-r--r--absl/types/span.h34
5 files changed, 101 insertions, 25 deletions
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index b950ec769fa4..e72db82c02aa 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -285,6 +285,7 @@ cc_library(
         "//absl/container:inlined_vector",
         "//absl/functional:function_ref",
         "//absl/meta:type_traits",
+        "//absl/types:optional",
     ],
 )
 
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index cebc59289347..0757a9cb602c 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -546,6 +546,7 @@ absl_cc_library(
     absl::fixed_array
     absl::function_ref
     absl::inlined_vector
+    absl::optional
     absl::raw_logging_internal
     absl::type_traits
   PUBLIC
diff --git a/absl/strings/cord.h b/absl/strings/cord.h
index 68a7e52feb7a..29ed7f755d11 100644
--- a/absl/strings/cord.h
+++ b/absl/strings/cord.h
@@ -53,6 +53,7 @@
 #include "absl/strings/internal/cord_internal.h"
 #include "absl/strings/internal/resize_uninitialized.h"
 #include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -512,6 +513,10 @@ class Cord {
   // REQUIRES: 0 <= i < size()
   char operator[](size_t i) const;
 
+  // If this cord's representation is a single flat array, return a
+  // string_view referencing that array.  Otherwise return nullopt.
+  absl::optional<absl::string_view> TryFlat() const;
+
   // Flattens the cord into a single array and returns a view of the data.
   //
   // If the cord was already flat, the contents are not modified.
@@ -630,7 +635,7 @@ class Cord {
   // Helper for MemoryUsage()
   static size_t MemoryUsageAux(const absl::cord_internal::CordRep* rep);
 
-  // Helper for GetFlat()
+  // Helper for GetFlat() and TryFlat()
   static bool GetFlatAux(absl::cord_internal::CordRep* rep,
                          absl::string_view* fragment);
 
@@ -942,6 +947,18 @@ inline size_t Cord::EstimatedMemoryUsage() const {
   return result;
 }
 
+inline absl::optional<absl::string_view> Cord::TryFlat() const {
+  absl::cord_internal::CordRep* rep = contents_.tree();
+  if (rep == nullptr) {
+    return absl::string_view(contents_.data(), contents_.size());
+  }
+  absl::string_view fragment;
+  if (GetFlatAux(rep, &fragment)) {
+    return fragment;
+  }
+  return absl::nullopt;
+}
+
 inline absl::string_view Cord::Flatten() {
   absl::cord_internal::CordRep* rep = contents_.tree();
   if (rep == nullptr) {
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc
index 68515cbf15b9..a683cc4b68d7 100644
--- a/absl/strings/cord_test.cc
+++ b/absl/strings/cord_test.cc
@@ -70,9 +70,8 @@ static std::string RandomLowercaseString(RandomEngine* rng) {
 static std::string RandomLowercaseString(RandomEngine* rng, size_t length) {
   std::string result(length, '\0');
   std::uniform_int_distribution<int> chars('a', 'z');
-  std::generate(result.begin(), result.end(), [&]() {
-    return static_cast<char>(chars(*rng));
-  });
+  std::generate(result.begin(), result.end(),
+                [&]() { return static_cast<char>(chars(*rng)); });
   return result;
 }
 
@@ -424,6 +423,50 @@ TEST(Cord, CopyToString) {
                                 "copying ", "to ", "a ", "string."}));
 }
 
+TEST(TryFlat, Empty) {
+  absl::Cord c;
+  EXPECT_EQ(c.TryFlat(), "");
+}
+
+TEST(TryFlat, Flat) {
+  absl::Cord c("hello");
+  EXPECT_EQ(c.TryFlat(), "hello");
+}
+
+TEST(TryFlat, SubstrInlined) {
+  absl::Cord c("hello");
+  c.RemovePrefix(1);
+  EXPECT_EQ(c.TryFlat(), "ello");
+}
+
+TEST(TryFlat, SubstrFlat) {
+  absl::Cord c("longer than 15 bytes");
+  c.RemovePrefix(1);
+  EXPECT_EQ(c.TryFlat(), "onger than 15 bytes");
+}
+
+TEST(TryFlat, Concat) {
+  absl::Cord c = absl::MakeFragmentedCord({"hel", "lo"});
+  EXPECT_EQ(c.TryFlat(), absl::nullopt);
+}
+
+TEST(TryFlat, External) {
+  absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {});
+  EXPECT_EQ(c.TryFlat(), "hell");
+}
+
+TEST(TryFlat, SubstrExternal) {
+  absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {});
+  c.RemovePrefix(1);
+  EXPECT_EQ(c.TryFlat(), "ell");
+}
+
+TEST(TryFlat, SubstrConcat) {
+  absl::Cord c = absl::MakeFragmentedCord({"hello", " world"});
+  c.RemovePrefix(1);
+  EXPECT_EQ(c.TryFlat(), absl::nullopt);
+}
+
 static bool IsFlat(const absl::Cord& c) {
   return c.chunk_begin() == c.chunk_end() || ++c.chunk_begin() == c.chunk_end();
 }
@@ -511,24 +554,24 @@ TEST(Cord, MultipleLengths) {
   for (size_t i = 0; i < d.size(); i++) {
     std::string a = d.data(i);
 
-    { // Construct from Cord
+    {  // Construct from Cord
       absl::Cord tmp(a);
       absl::Cord x(tmp);
       EXPECT_EQ(a, std::string(x)) << "'" << a << "'";
     }
 
-    { // Construct from absl::string_view
+    {  // Construct from absl::string_view
       absl::Cord x(a);
       EXPECT_EQ(a, std::string(x)) << "'" << a << "'";
     }
 
-    { // Append cord to self
+    {  // Append cord to self
       absl::Cord self(a);
       self.Append(self);
       EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'";
     }
 
-    { // Prepend cord to self
+    {  // Prepend cord to self
       absl::Cord self(a);
       self.Prepend(self);
       EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'";
@@ -538,40 +581,40 @@ TEST(Cord, MultipleLengths) {
     for (size_t j = 0; j < d.size(); j++) {
       std::string b = d.data(j);
 
-      { // CopyFrom Cord
+      {  // CopyFrom Cord
         absl::Cord x(a);
         absl::Cord y(b);
         x = y;
         EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'";
       }
 
-      { // CopyFrom absl::string_view
+      {  // CopyFrom absl::string_view
         absl::Cord x(a);
         x = b;
         EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'";
       }
 
-      { // Cord::Append(Cord)
+      {  // Cord::Append(Cord)
         absl::Cord x(a);
         absl::Cord y(b);
         x.Append(y);
         EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'";
       }
 
-      { // Cord::Append(absl::string_view)
+      {  // Cord::Append(absl::string_view)
         absl::Cord x(a);
         x.Append(b);
         EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'";
       }
 
-      { // Cord::Prepend(Cord)
+      {  // Cord::Prepend(Cord)
         absl::Cord x(a);
         absl::Cord y(b);
         x.Prepend(y);
         EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'";
       }
 
-      { // Cord::Prepend(absl::string_view)
+      {  // Cord::Prepend(absl::string_view)
         absl::Cord x(a);
         x.Prepend(b);
         EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'";
@@ -1089,7 +1132,7 @@ TEST(ConstructFromExternal, ReferenceQualifierOverloads) {
 }
 
 TEST(ExternalMemory, BasicUsage) {
-  static const char* strings[] = { "", "hello", "there" };
+  static const char* strings[] = {"", "hello", "there"};
   for (const char* str : strings) {
     absl::Cord dst("(prefix)");
     AddExternalMemory(str, &dst);
diff --git a/absl/types/span.h b/absl/types/span.h
index 25347c63fe8c..21cda34b1f22 100644
--- a/absl/types/span.h
+++ b/absl/types/span.h
@@ -292,60 +292,74 @@ class Span {
 
   // Span::front()
   //
-  // Returns a reference to the first element of this span.
+  // Returns a reference to the first element of this span. The span must not
+  // be empty.
   constexpr reference front() const noexcept {
     return ABSL_ASSERT(size() > 0), *data();
   }
 
   // Span::back()
   //
-  // Returns a reference to the last element of this span.
+  // Returns a reference to the last element of this span. The span must not
+  // be empty.
   constexpr reference back() const noexcept {
     return ABSL_ASSERT(size() > 0), *(data() + size() - 1);
   }
 
   // Span::begin()
   //
-  // Returns an iterator to the first element of this span.
+  // Returns an iterator pointing to the first element of this span, or `end()`
+  // if the span is empty.
   constexpr iterator begin() const noexcept { return data(); }
 
   // Span::cbegin()
   //
-  // Returns a const iterator to the first element of this span.
+  // Returns a const iterator pointing to the first element of this span, or
+  // `end()` if the span is empty.
   constexpr const_iterator cbegin() const noexcept { return begin(); }
 
   // Span::end()
   //
-  // Returns an iterator to the last element of this span.
+  // Returns an iterator pointing just beyond the last element at the
+  // end of this span. This iterator acts as a placeholder; attempting to
+  // access it results in undefined behavior.
   constexpr iterator end() const noexcept { return data() + size(); }
 
   // Span::cend()
   //
-  // Returns a const iterator to the last element of this span.
+  // Returns a const iterator pointing just beyond the last element at the
+  // end of this span. This iterator acts as a placeholder; attempting to
+  // access it results in undefined behavior.
   constexpr const_iterator cend() const noexcept { return end(); }
 
   // Span::rbegin()
   //
-  // Returns a reverse iterator starting at the last element of this span.
+  // Returns a reverse iterator pointing to the last element at the end of this
+  // span, or `rend()` if the span is empty.
   constexpr reverse_iterator rbegin() const noexcept {
     return reverse_iterator(end());
   }
 
   // Span::crbegin()
   //
-  // Returns a reverse const iterator starting at the last element of this span.
+  // Returns a const reverse iterator pointing to the last element at the end of
+  // this span, or `crend()` if the span is empty.
   constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
 
   // Span::rend()
   //
-  // Returns a reverse iterator starting at the first element of this span.
+  // Returns a reverse iterator pointing just before the first element
+  // at the beginning of this span. This pointer acts as a placeholder;
+  // attempting to access its element results in undefined behavior.
   constexpr reverse_iterator rend() const noexcept {
     return reverse_iterator(begin());
   }
 
   // Span::crend()
   //
-  // Returns a reverse iterator starting at the first element of this span.
+  // Returns a reverse const iterator pointing just before the first element
+  // at the beginning of this span. This pointer acts as a placeholder;
+  // attempting to access its element results in undefined behavior.
   constexpr const_reverse_iterator crend() const noexcept { return rend(); }
 
   // Span mutations