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.