diff --git a/.gitignore b/.gitignore index a6ef824..4406fad 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /bazel-* +.idea diff --git a/src/collections/vector.h b/src/collections/vector.h index a58c60e..1465363 100644 --- a/src/collections/vector.h +++ b/src/collections/vector.h @@ -3,21 +3,32 @@ #include #include +#include template class Vec { public: Vec(); + Vec(const Vec& v); virtual ~Vec(); private: T* buf; size_t len; + size_t _capacity; + void reallocate(size_t resize); public: // Get the length of the vector size_t size(); + // Get the size of underneath array + size_t capacity(); + + // Set the size of underneath array to at least len + additional + // Do nothing if capacity already greater than len + additional + void reserve(size_t additional); + // Examine if the vector is empty. bool is_empty(); @@ -25,7 +36,8 @@ class Vec { void push(T data); // Pop out an element from the back of vector - std::optional pop(); + // this function is no exception safe + std::optional pop() noexcept; // Overloading index operator T operator[](size_t index); @@ -34,7 +46,30 @@ class Vec { template inline Vec::Vec() { this->buf = nullptr; - len = 0; + this->len = 0; + this->_capacity = 0; +} + +template +Vec::Vec(const Vec& v) { + this->buf = new T[v._capacity]; + std::copy(v.buf, v.buf + v.len, this->buf); + this->len = v.len; + this->_capacity = v._capacity; +} + +// Set this->_capacity to resize and reallocate to a new buffer +template +void Vec::reallocate(size_t resize) { + this->_capacity = resize; + if (this->_capacity == 0) { + delete[] buf; + buf = nullptr; + } + T* new_buf = new T[this->_capacity]; + std::copy(this->buf, this->buf + this->len, new_buf); + delete[] buf; + buf = new_buf; } template @@ -42,9 +77,22 @@ inline size_t Vec::size() { return this->len; } +template +inline size_t Vec::capacity() { + return this->_capacity; +} + +template +void Vec::reserve(size_t additional) { + size_t new_capacity = this->len + additional; + if (this->_capacity < new_capacity) { + reallocate(new_capacity); + } +} + template inline Vec::~Vec() { - if (this->len == 0) delete[] this->buf; + if (this->buf != nullptr) delete[] this->buf; } template @@ -54,19 +102,30 @@ inline bool Vec::is_empty() { template void Vec::push(T data) { - T* new_buf = new T[this->len + 1]; - std::copy(this->buf, this->buf + this->len, new_buf); - new_buf[this->len] = data; - - delete[] this->buf; - - this->buf = new_buf; + size_t total = this->len + 1; + if (total > this->_capacity) { + if (this->_capacity == 0) { + this->_capacity = 1; + } else { + this->_capacity = this->_capacity * 2; + } + reallocate(this->_capacity); + } + buf[this->len] = data; this->len++; } template -std::optional Vec::pop() { - if (this->len > 0) return this->buf[this->len-- - 1]; +std::optional Vec::pop() noexcept { + if (this->len > 0) { + T ret = this->buf[this->len-- - 1]; + if (this->len == 0) { + reallocate(0); + } else if (this->_capacity > (4 * this->len)) { + reallocate(this->_capacity >> 1); + } + return ret; + } return {}; } diff --git a/tests/test-vector.cc b/tests/test-vector.cc index c82691a..3998c55 100644 --- a/tests/test-vector.cc +++ b/tests/test-vector.cc @@ -38,3 +38,37 @@ TEST(TestVec, TestPop) { EXPECT_EQ(233, vec.pop().value()); EXPECT_EQ(std::nullopt, vec.pop()); } + +TEST(TestVec, TestCapacity) { + int capacities[] = {0, 1, 2, 4}; + Vec vec; + for (auto capacity : capacities) { + EXPECT_EQ(capacity, vec.capacity()); + vec.push(capacity); + } +} + +TEST(TestVec, TestReserve) { + Vec vec; + int additional = 5; + int cases[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; + for (auto data : cases) { + vec.push(data); + vec.reserve(additional); + EXPECT_GE(vec.capacity(), vec.size() + additional); + } +} + +TEST(TestVec, TestCopyCtor) { + int cases[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + Vec vec; + for (auto data : cases) { + vec.push(data); + } + Vec vec_copy = Vec(vec); + EXPECT_EQ(vec.size(), vec_copy.size()); + EXPECT_EQ(vec.capacity(), vec_copy.capacity()); + for (auto idx : cases) { + EXPECT_EQ(vec[idx], vec_copy[idx]); + } +}