Add front(), back(), and pop_back() to internal containers

Change-Id: Ia0ba822c24af684663373fd5d94cf79b3973490c
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/78889
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
diff --git a/crypto/mem_internal.h b/crypto/mem_internal.h
index c4f51d5..80d61bb 100644
--- a/crypto/mem_internal.h
+++ b/crypto/mem_internal.h
@@ -114,6 +114,23 @@
     return data_[i];
   }
 
+  T &front() {
+    BSSL_CHECK(size_ != 0);
+    return data_[0];
+  }
+  const T &front() const {
+    BSSL_CHECK(size_ != 0);
+    return data_[0];
+  }
+  T &back() {
+    BSSL_CHECK(size_ != 0);
+    return data_[size_ - 1];
+  }
+  const T &back() const {
+    BSSL_CHECK(size_ != 0);
+    return data_[size_ - 1];
+  }
+
   T *begin() { return data_; }
   const T *begin() const { return data_; }
   T *end() { return data_ + size_; }
@@ -240,6 +257,23 @@
     return data_[i];
   }
 
+  T &front() {
+    BSSL_CHECK(size_ != 0);
+    return data_[0];
+  }
+  const T &front() const {
+    BSSL_CHECK(size_ != 0);
+    return data_[0];
+  }
+  T &back() {
+    BSSL_CHECK(size_ != 0);
+    return data_[size_ - 1];
+  }
+  const T &back() const {
+    BSSL_CHECK(size_ != 0);
+    return data_[size_ - 1];
+  }
+
   T *begin() { return data_; }
   const T *begin() const { return data_; }
   T *end() { return data_ + size_; }
@@ -253,6 +287,12 @@
     capacity_ = 0;
   }
 
+  void pop_back() {
+    BSSL_CHECK(size_ != 0);
+    std::destroy_at(&data_[size_ - 1]);
+    size_--;
+  }
+
   // Push adds |elem| at the end of the internal array, growing if necessary. It
   // returns false when allocation fails.
   [[nodiscard]] bool Push(T elem) {
@@ -369,6 +409,23 @@
     return data()[i];
   }
 
+  T &front() {
+    BSSL_CHECK(size_ != 0);
+    return data()[0];
+  }
+  const T &front() const {
+    BSSL_CHECK(size_ != 0);
+    return data()[0];
+  }
+  T &back() {
+    BSSL_CHECK(size_ != 0);
+    return data()[size_ - 1];
+  }
+  const T &back() const {
+    BSSL_CHECK(size_ != 0);
+    return data()[size_ - 1];
+  }
+
   T *begin() { return data(); }
   const T *begin() const { return data(); }
   T *end() { return data() + size_; }
@@ -376,6 +433,11 @@
 
   void clear() { Shrink(0); }
 
+  void pop_back() {
+    BSSL_CHECK(size_ != 0);
+    Shrink(size_ - 1);
+  }
+
   // Shrink resizes the vector to |new_size|, which must not be larger than the
   // current size. Unlike |Resize|, this can be called when |T| is not
   // default-constructible.
diff --git a/crypto/mem_test.cc b/crypto/mem_test.cc
index 2d923a8..70b283c 100644
--- a/crypto/mem_test.cc
+++ b/crypto/mem_test.cc
@@ -21,6 +21,22 @@
 BSSL_NAMESPACE_BEGIN
 namespace {
 
+TEST(ArrayTest, Basic) {
+  Array<int> array;
+  EXPECT_TRUE(array.empty());
+  EXPECT_EQ(array.size(), 0u);
+  const int v[] = {1, 2, 3, 4};
+  ASSERT_TRUE(array.CopyFrom(v));
+  EXPECT_FALSE(array.empty());
+  EXPECT_EQ(array.size(), 4u);
+  EXPECT_EQ(array[0], 1);
+  EXPECT_EQ(array[1], 2);
+  EXPECT_EQ(array[2], 3);
+  EXPECT_EQ(array[3], 4);
+  EXPECT_EQ(array.front(), 1);
+  EXPECT_EQ(array.back(), 4);
+}
+
 TEST(ArrayTest, InitValueConstructs) {
   Array<uint8_t> array;
   ASSERT_TRUE(array.Init(10));
@@ -32,6 +48,8 @@
 
 TEST(ArrayDeathTest, BoundsChecks) {
   Array<int> array;
+  EXPECT_DEATH_IF_SUPPORTED(array.front(), "");
+  EXPECT_DEATH_IF_SUPPORTED(array.back(), "");
   const int v[] = {1, 2, 3, 4};
   ASSERT_TRUE(array.CopyFrom(v));
   EXPECT_DEATH_IF_SUPPORTED(array[4], "");
@@ -57,6 +75,8 @@
   for (size_t i = 0; i < vec.size(); i++) {
     EXPECT_EQ(vec[i], i == 0 ? 42 : i);
   }
+  EXPECT_EQ(vec.front(), 42u);
+  EXPECT_EQ(vec.back(), 16u);
 
   // Clearing the vector should give an empty one.
   vec.clear();
@@ -67,6 +87,8 @@
   ASSERT_TRUE(!vec.empty());
   EXPECT_EQ(vec.size(), 1u);
   EXPECT_EQ(vec[0], 42u);
+  EXPECT_EQ(vec.front(), 42u);
+  EXPECT_EQ(vec.back(), 42u);
 }
 
 TEST(VectorTest, MoveConstructor) {
@@ -97,10 +119,19 @@
     }
     ASSERT_TRUE(vec.Push(std::move(elem)));
   }
-  EXPECT_EQ(vec.size(), static_cast<size_t>(100));
+  EXPECT_EQ(vec.size(), 100u);
+
+  // Add and remove some element.
+  TagAndArray extra;
+  extra.tag = 1234;
+  ASSERT_TRUE(extra.vec.Push(1234));
+  ASSERT_TRUE(vec.Push(std::move(extra)));
+  EXPECT_EQ(vec.size(), 101u);
+  vec.pop_back();
+  EXPECT_EQ(vec.size(), 100u);
 
   Vector<TagAndArray> vec_moved(std::move(vec));
-  EXPECT_EQ(vec_moved.size(), static_cast<size_t>(100));
+  EXPECT_EQ(vec_moved.size(), 100u);
   size_t count = 0;
   for (const TagAndArray &elem : vec_moved) {
     // Test the square bracket operator returns the same value as iteration.
@@ -135,6 +166,9 @@
 
 TEST(VectorDeathTest, BoundsChecks) {
   Vector<int> vec;
+  EXPECT_DEATH_IF_SUPPORTED(vec.front(), "");
+  EXPECT_DEATH_IF_SUPPORTED(vec.back(), "");
+  EXPECT_DEATH_IF_SUPPORTED(vec.pop_back(), "");
   ASSERT_TRUE(vec.Push(1));
   // Within bounds of the capacity, but not the vector.
   EXPECT_DEATH_IF_SUPPORTED(vec[1], "");
@@ -164,6 +198,8 @@
   iter++;
   EXPECT_EQ(iter, vec.end());
   EXPECT_EQ(Span(vec), Span(data3));
+  EXPECT_EQ(vec.front(), 1);
+  EXPECT_EQ(vec.back(), 3);
 
   InplaceVector<int, 4> vec2 = vec;
   EXPECT_EQ(Span(vec), Span(vec2));
@@ -205,6 +241,11 @@
   vec_of_vecs.Resize(2);
   EXPECT_EQ(Span(vec_of_vecs), Span(data, 2));
 
+  vec_of_vecs.PushBack({42});
+  EXPECT_EQ(3u, vec_of_vecs.size());
+  vec_of_vecs.pop_back();
+  EXPECT_EQ(2u, vec_of_vecs.size());
+
   vec_of_vecs.Resize(4);
   EXPECT_EQ(4u, vec_of_vecs.size());
   EXPECT_EQ(vec_of_vecs[0], data[0]);
@@ -344,6 +385,9 @@
   InplaceVector<int, 4> vec;
   // The vector is currently empty.
   EXPECT_DEATH_IF_SUPPORTED(vec[0], "");
+  EXPECT_DEATH_IF_SUPPORTED(vec.front(), "");
+  EXPECT_DEATH_IF_SUPPORTED(vec.back(), "");
+  EXPECT_DEATH_IF_SUPPORTED(vec.pop_back(), "");
   int data[] = {1, 2, 3};
   vec.CopyFrom(data);
   // Some more out-of-bounds elements.