|  | // Copyright (c) 2019 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "common/quiche_circular_deque.h" | 
|  |  | 
|  | #include <cstddef> | 
|  | #include <cstdint> | 
|  | #include <list> | 
|  | #include <memory> | 
|  | #include <type_traits> | 
|  |  | 
|  | #include "common/platform/api/quiche_logging.h" | 
|  | #include "common/platform/api/quiche_test.h" | 
|  |  | 
|  | using testing::ElementsAre; | 
|  |  | 
|  | namespace quiche { | 
|  | namespace test { | 
|  | namespace { | 
|  |  | 
|  | template <typename T, template <typename> class BaseAllocator = std::allocator> | 
|  | class CountingAllocator : public BaseAllocator<T> { | 
|  | using BaseType = BaseAllocator<T>; | 
|  |  | 
|  | public: | 
|  | using propagate_on_container_copy_assignment = std::true_type; | 
|  | using propagate_on_container_move_assignment = std::true_type; | 
|  | using propagate_on_container_swap = std::true_type; | 
|  |  | 
|  | T* allocate(std::size_t n) { | 
|  | ++shared_counts_->allocate_count; | 
|  | return BaseType::allocate(n); | 
|  | } | 
|  |  | 
|  | void deallocate(T* ptr, std::size_t n) { | 
|  | ++shared_counts_->deallocate_count; | 
|  | return BaseType::deallocate(ptr, n); | 
|  | } | 
|  |  | 
|  | size_t allocate_count() const { return shared_counts_->allocate_count; } | 
|  |  | 
|  | size_t deallocate_count() const { return shared_counts_->deallocate_count; } | 
|  |  | 
|  | friend bool operator==(const CountingAllocator& lhs, | 
|  | const CountingAllocator& rhs) { | 
|  | return lhs.shared_counts_ == rhs.shared_counts_; | 
|  | } | 
|  |  | 
|  | friend bool operator!=(const CountingAllocator& lhs, | 
|  | const CountingAllocator& rhs) { | 
|  | return !(lhs == rhs); | 
|  | } | 
|  |  | 
|  | private: | 
|  | struct Counts { | 
|  | size_t allocate_count = 0; | 
|  | size_t deallocate_count = 0; | 
|  | }; | 
|  |  | 
|  | std::shared_ptr<Counts> shared_counts_ = std::make_shared<Counts>(); | 
|  | }; | 
|  |  | 
|  | template <typename T, | 
|  | typename propagate_on_copy_assignment, | 
|  | typename propagate_on_move_assignment, | 
|  | typename propagate_on_swap, | 
|  | bool equality_result, | 
|  | template <typename> class BaseAllocator = std::allocator> | 
|  | struct ConfigurableAllocator : public BaseAllocator<T> { | 
|  | using propagate_on_container_copy_assignment = propagate_on_copy_assignment; | 
|  | using propagate_on_container_move_assignment = propagate_on_move_assignment; | 
|  | using propagate_on_container_swap = propagate_on_swap; | 
|  |  | 
|  | friend bool operator==(const ConfigurableAllocator& /*lhs*/, | 
|  | const ConfigurableAllocator& /*rhs*/) { | 
|  | return equality_result; | 
|  | } | 
|  |  | 
|  | friend bool operator!=(const ConfigurableAllocator& lhs, | 
|  | const ConfigurableAllocator& rhs) { | 
|  | return !(lhs == rhs); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // [1, 2, 3, 4] ==> [4, 1, 2, 3] | 
|  | template <typename Deque> | 
|  | void ShiftRight(Deque* dq, bool emplace) { | 
|  | auto back = *(&dq->back()); | 
|  | dq->pop_back(); | 
|  | if (emplace) { | 
|  | dq->emplace_front(back); | 
|  | } else { | 
|  | dq->push_front(back); | 
|  | } | 
|  | } | 
|  |  | 
|  | // [1, 2, 3, 4] ==> [2, 3, 4, 1] | 
|  | template <typename Deque> | 
|  | void ShiftLeft(Deque* dq, bool emplace) { | 
|  | auto front = *(&dq->front()); | 
|  | dq->pop_front(); | 
|  | if (emplace) { | 
|  | dq->emplace_back(front); | 
|  | } else { | 
|  | dq->push_back(front); | 
|  | } | 
|  | } | 
|  |  | 
|  | class QuicheCircularDequeTest : public QuicheTest {}; | 
|  |  | 
|  | TEST_F(QuicheCircularDequeTest, Empty) { | 
|  | QuicheCircularDeque<int> dq; | 
|  | EXPECT_TRUE(dq.empty()); | 
|  | EXPECT_EQ(0u, dq.size()); | 
|  | dq.clear(); | 
|  | dq.push_back(10); | 
|  | EXPECT_FALSE(dq.empty()); | 
|  | EXPECT_EQ(1u, dq.size()); | 
|  | EXPECT_EQ(10, dq.front()); | 
|  | EXPECT_EQ(10, dq.back()); | 
|  | dq.pop_front(); | 
|  | EXPECT_TRUE(dq.empty()); | 
|  | EXPECT_EQ(0u, dq.size()); | 
|  |  | 
|  | EXPECT_QUICHE_DEBUG_DEATH(dq.front(), ""); | 
|  | EXPECT_QUICHE_DEBUG_DEATH(dq.back(), ""); | 
|  | EXPECT_QUICHE_DEBUG_DEATH(dq.at(0), ""); | 
|  | EXPECT_QUICHE_DEBUG_DEATH(dq[0], ""); | 
|  | } | 
|  |  | 
|  | TEST_F(QuicheCircularDequeTest, Constructor) { | 
|  | QuicheCircularDeque<int> dq; | 
|  | EXPECT_TRUE(dq.empty()); | 
|  |  | 
|  | std::allocator<int> alloc; | 
|  | QuicheCircularDeque<int> dq1(alloc); | 
|  | EXPECT_TRUE(dq1.empty()); | 
|  |  | 
|  | QuicheCircularDeque<int> dq2(8, 100, alloc); | 
|  | EXPECT_THAT(dq2, ElementsAre(100, 100, 100, 100, 100, 100, 100, 100)); | 
|  |  | 
|  | QuicheCircularDeque<int> dq3(5, alloc); | 
|  | EXPECT_THAT(dq3, ElementsAre(0, 0, 0, 0, 0)); | 
|  |  | 
|  | QuicheCircularDeque<int> dq4_rand_iter(dq3.begin(), dq3.end(), alloc); | 
|  | EXPECT_THAT(dq4_rand_iter, ElementsAre(0, 0, 0, 0, 0)); | 
|  | EXPECT_EQ(dq4_rand_iter, dq3); | 
|  |  | 
|  | std::list<int> dq4_src = {4, 4, 4, 4}; | 
|  | QuicheCircularDeque<int> dq4_bidi_iter(dq4_src.begin(), dq4_src.end()); | 
|  | EXPECT_THAT(dq4_bidi_iter, ElementsAre(4, 4, 4, 4)); | 
|  |  | 
|  | QuicheCircularDeque<int> dq5(dq4_bidi_iter); | 
|  | EXPECT_THAT(dq5, ElementsAre(4, 4, 4, 4)); | 
|  | EXPECT_EQ(dq5, dq4_bidi_iter); | 
|  |  | 
|  | QuicheCircularDeque<int> dq6(dq5, alloc); | 
|  | EXPECT_THAT(dq6, ElementsAre(4, 4, 4, 4)); | 
|  | EXPECT_EQ(dq6, dq5); | 
|  |  | 
|  | QuicheCircularDeque<int> dq7(std::move(*&dq6)); | 
|  | EXPECT_THAT(dq7, ElementsAre(4, 4, 4, 4)); | 
|  | EXPECT_TRUE(dq6.empty()); | 
|  |  | 
|  | QuicheCircularDeque<int> dq8_equal_allocator(std::move(*&dq7), alloc); | 
|  | EXPECT_THAT(dq8_equal_allocator, ElementsAre(4, 4, 4, 4)); | 
|  | EXPECT_TRUE(dq7.empty()); | 
|  |  | 
|  | QuicheCircularDeque<int, 3, CountingAllocator<int>> dq8_temp = {5, 6, 7, 8, | 
|  | 9}; | 
|  | QuicheCircularDeque<int, 3, CountingAllocator<int>> dq8_unequal_allocator( | 
|  | std::move(*&dq8_temp), CountingAllocator<int>()); | 
|  | EXPECT_THAT(dq8_unequal_allocator, ElementsAre(5, 6, 7, 8, 9)); | 
|  | EXPECT_TRUE(dq8_temp.empty()); | 
|  |  | 
|  | QuicheCircularDeque<int> dq9({3, 4, 5, 6, 7}, alloc); | 
|  | EXPECT_THAT(dq9, ElementsAre(3, 4, 5, 6, 7)); | 
|  | } | 
|  |  | 
|  | TEST_F(QuicheCircularDequeTest, Assign) { | 
|  | // assign() | 
|  | QuicheCircularDeque<int, 3, CountingAllocator<int>> dq; | 
|  | dq.assign(7, 1); | 
|  | EXPECT_THAT(dq, ElementsAre(1, 1, 1, 1, 1, 1, 1)); | 
|  | EXPECT_EQ(1u, dq.get_allocator().allocate_count()); | 
|  |  | 
|  | QuicheCircularDeque<int, 3, CountingAllocator<int>> dq2; | 
|  | dq2.assign(dq.begin(), dq.end()); | 
|  | EXPECT_THAT(dq2, ElementsAre(1, 1, 1, 1, 1, 1, 1)); | 
|  | EXPECT_EQ(1u, dq2.get_allocator().allocate_count()); | 
|  | EXPECT_TRUE(std::equal(dq.begin(), dq.end(), dq2.begin(), dq2.end())); | 
|  |  | 
|  | dq2.assign({2, 2, 2, 2, 2, 2}); | 
|  | EXPECT_THAT(dq2, ElementsAre(2, 2, 2, 2, 2, 2)); | 
|  |  | 
|  | // Assign from a non random access iterator. | 
|  | std::list<int> dq3_src = {3, 3, 3, 3, 3}; | 
|  | QuicheCircularDeque<int, 3, CountingAllocator<int>> dq3; | 
|  | dq3.assign(dq3_src.begin(), dq3_src.end()); | 
|  | EXPECT_THAT(dq3, ElementsAre(3, 3, 3, 3, 3)); | 
|  | EXPECT_LT(1u, dq3.get_allocator().allocate_count()); | 
|  |  | 
|  | // Copy assignment | 
|  | dq3 = *&dq3; | 
|  | EXPECT_THAT(dq3, ElementsAre(3, 3, 3, 3, 3)); | 
|  |  | 
|  | QuicheCircularDeque< | 
|  | int, 3, | 
|  | ConfigurableAllocator<int, | 
|  | /*propagate_on_copy_assignment=*/std::true_type, | 
|  | /*propagate_on_move_assignment=*/std::true_type, | 
|  | /*propagate_on_swap=*/std::true_type, | 
|  | /*equality_result=*/false>> | 
|  | dq4, dq5; | 
|  | dq4.assign(dq3.begin(), dq3.end()); | 
|  | dq5 = dq4; | 
|  | EXPECT_THAT(dq5, ElementsAre(3, 3, 3, 3, 3)); | 
|  |  | 
|  | QuicheCircularDeque< | 
|  | int, 3, | 
|  | ConfigurableAllocator<int, | 
|  | /*propagate_on_copy_assignment=*/std::false_type, | 
|  | /*propagate_on_move_assignment=*/std::true_type, | 
|  | /*propagate_on_swap=*/std::true_type, | 
|  | /*equality_result=*/true>> | 
|  | dq6, dq7; | 
|  | dq6.assign(dq3.begin(), dq3.end()); | 
|  | dq7 = dq6; | 
|  | EXPECT_THAT(dq7, ElementsAre(3, 3, 3, 3, 3)); | 
|  |  | 
|  | // Move assignment | 
|  | dq3 = std::move(*&dq3); | 
|  | EXPECT_THAT(dq3, ElementsAre(3, 3, 3, 3, 3)); | 
|  |  | 
|  | ASSERT_TRUE(decltype( | 
|  | dq3.get_allocator())::propagate_on_container_move_assignment::value); | 
|  | decltype(dq3) dq8; | 
|  | dq8 = std::move(*&dq3); | 
|  | EXPECT_THAT(dq8, ElementsAre(3, 3, 3, 3, 3)); | 
|  | EXPECT_TRUE(dq3.empty()); | 
|  |  | 
|  | QuicheCircularDeque< | 
|  | int, 3, | 
|  | ConfigurableAllocator<int, | 
|  | /*propagate_on_copy_assignment=*/std::true_type, | 
|  | /*propagate_on_move_assignment=*/std::false_type, | 
|  | /*propagate_on_swap=*/std::true_type, | 
|  | /*equality_result=*/true>> | 
|  | dq9, dq10; | 
|  | dq9.assign(dq8.begin(), dq8.end()); | 
|  | dq10.assign(dq2.begin(), dq2.end()); | 
|  | dq9 = std::move(*&dq10); | 
|  | EXPECT_THAT(dq9, ElementsAre(2, 2, 2, 2, 2, 2)); | 
|  | EXPECT_TRUE(dq10.empty()); | 
|  |  | 
|  | QuicheCircularDeque< | 
|  | int, 3, | 
|  | ConfigurableAllocator<int, | 
|  | /*propagate_on_copy_assignment=*/std::true_type, | 
|  | /*propagate_on_move_assignment=*/std::false_type, | 
|  | /*propagate_on_swap=*/std::true_type, | 
|  | /*equality_result=*/false>> | 
|  | dq11, dq12; | 
|  | dq11.assign(dq8.begin(), dq8.end()); | 
|  | dq12.assign(dq2.begin(), dq2.end()); | 
|  | dq11 = std::move(*&dq12); | 
|  | EXPECT_THAT(dq11, ElementsAre(2, 2, 2, 2, 2, 2)); | 
|  | EXPECT_TRUE(dq12.empty()); | 
|  | } | 
|  |  | 
|  | TEST_F(QuicheCircularDequeTest, Access) { | 
|  | // at() | 
|  | // operator[] | 
|  | // front() | 
|  | // back() | 
|  |  | 
|  | QuicheCircularDeque<int, 3, CountingAllocator<int>> dq; | 
|  | dq.push_back(10); | 
|  | EXPECT_EQ(dq.front(), 10); | 
|  | EXPECT_EQ(dq.back(), 10); | 
|  | EXPECT_EQ(dq.at(0), 10); | 
|  | EXPECT_EQ(dq[0], 10); | 
|  | dq.front() = 12; | 
|  | EXPECT_EQ(dq.front(), 12); | 
|  | EXPECT_EQ(dq.back(), 12); | 
|  | EXPECT_EQ(dq.at(0), 12); | 
|  | EXPECT_EQ(dq[0], 12); | 
|  |  | 
|  | const auto& dqref = dq; | 
|  | EXPECT_EQ(dqref.front(), 12); | 
|  | EXPECT_EQ(dqref.back(), 12); | 
|  | EXPECT_EQ(dqref.at(0), 12); | 
|  | EXPECT_EQ(dqref[0], 12); | 
|  |  | 
|  | dq.pop_front(); | 
|  | EXPECT_TRUE(dqref.empty()); | 
|  |  | 
|  | // Push to capacity. | 
|  | dq.push_back(15); | 
|  | dq.push_front(5); | 
|  | dq.push_back(25); | 
|  | EXPECT_EQ(dq.size(), dq.capacity()); | 
|  | EXPECT_THAT(dq, ElementsAre(5, 15, 25)); | 
|  | EXPECT_LT(&dq.front(), &dq.back()); | 
|  | EXPECT_EQ(dq.front(), 5); | 
|  | EXPECT_EQ(dq.back(), 25); | 
|  | EXPECT_EQ(dq.at(0), 5); | 
|  | EXPECT_EQ(dq.at(1), 15); | 
|  | EXPECT_EQ(dq.at(2), 25); | 
|  | EXPECT_EQ(dq[0], 5); | 
|  | EXPECT_EQ(dq[1], 15); | 
|  | EXPECT_EQ(dq[2], 25); | 
|  |  | 
|  | // Shift right such that begin=1 and end=0. Data is still not wrapped. | 
|  | dq.pop_front(); | 
|  | dq.push_back(35); | 
|  | EXPECT_THAT(dq, ElementsAre(15, 25, 35)); | 
|  | EXPECT_LT(&dq.front(), &dq.back()); | 
|  | EXPECT_EQ(dq.front(), 15); | 
|  | EXPECT_EQ(dq.back(), 35); | 
|  | EXPECT_EQ(dq.at(0), 15); | 
|  | EXPECT_EQ(dq.at(1), 25); | 
|  | EXPECT_EQ(dq.at(2), 35); | 
|  | EXPECT_EQ(dq[0], 15); | 
|  | EXPECT_EQ(dq[1], 25); | 
|  | EXPECT_EQ(dq[2], 35); | 
|  |  | 
|  | // Shift right such that data is wrapped. | 
|  | dq.pop_front(); | 
|  | dq.push_back(45); | 
|  | EXPECT_THAT(dq, ElementsAre(25, 35, 45)); | 
|  | EXPECT_GT(&dq.front(), &dq.back()); | 
|  | EXPECT_EQ(dq.front(), 25); | 
|  | EXPECT_EQ(dq.back(), 45); | 
|  | EXPECT_EQ(dq.at(0), 25); | 
|  | EXPECT_EQ(dq.at(1), 35); | 
|  | EXPECT_EQ(dq.at(2), 45); | 
|  | EXPECT_EQ(dq[0], 25); | 
|  | EXPECT_EQ(dq[1], 35); | 
|  | EXPECT_EQ(dq[2], 45); | 
|  |  | 
|  | // Shift right again, data is still wrapped. | 
|  | dq.pop_front(); | 
|  | dq.push_back(55); | 
|  | EXPECT_THAT(dq, ElementsAre(35, 45, 55)); | 
|  | EXPECT_GT(&dq.front(), &dq.back()); | 
|  | EXPECT_EQ(dq.front(), 35); | 
|  | EXPECT_EQ(dq.back(), 55); | 
|  | EXPECT_EQ(dq.at(0), 35); | 
|  | EXPECT_EQ(dq.at(1), 45); | 
|  | EXPECT_EQ(dq.at(2), 55); | 
|  | EXPECT_EQ(dq[0], 35); | 
|  | EXPECT_EQ(dq[1], 45); | 
|  | EXPECT_EQ(dq[2], 55); | 
|  |  | 
|  | // Shift right one last time. begin returns to 0. Data is no longer wrapped. | 
|  | dq.pop_front(); | 
|  | dq.push_back(65); | 
|  | EXPECT_THAT(dq, ElementsAre(45, 55, 65)); | 
|  | EXPECT_LT(&dq.front(), &dq.back()); | 
|  | EXPECT_EQ(dq.front(), 45); | 
|  | EXPECT_EQ(dq.back(), 65); | 
|  | EXPECT_EQ(dq.at(0), 45); | 
|  | EXPECT_EQ(dq.at(1), 55); | 
|  | EXPECT_EQ(dq.at(2), 65); | 
|  | EXPECT_EQ(dq[0], 45); | 
|  | EXPECT_EQ(dq[1], 55); | 
|  | EXPECT_EQ(dq[2], 65); | 
|  |  | 
|  | EXPECT_EQ(1u, dq.get_allocator().allocate_count()); | 
|  | } | 
|  |  | 
|  | TEST_F(QuicheCircularDequeTest, Iterate) { | 
|  | QuicheCircularDeque<int> dq; | 
|  | EXPECT_EQ(dq.begin(), dq.end()); | 
|  | EXPECT_EQ(dq.cbegin(), dq.cend()); | 
|  | EXPECT_EQ(dq.rbegin(), dq.rend()); | 
|  | EXPECT_EQ(dq.crbegin(), dq.crend()); | 
|  |  | 
|  | dq.emplace_back(2); | 
|  | QuicheCircularDeque<int>::const_iterator citer = dq.begin(); | 
|  | EXPECT_NE(citer, dq.end()); | 
|  | EXPECT_EQ(*citer, 2); | 
|  | ++citer; | 
|  | EXPECT_EQ(citer, dq.end()); | 
|  |  | 
|  | EXPECT_EQ(*dq.begin(), 2); | 
|  | EXPECT_EQ(*dq.cbegin(), 2); | 
|  | EXPECT_EQ(*dq.rbegin(), 2); | 
|  | EXPECT_EQ(*dq.crbegin(), 2); | 
|  |  | 
|  | dq.emplace_front(1); | 
|  | QuicheCircularDeque<int>::const_reverse_iterator criter = dq.rbegin(); | 
|  | EXPECT_NE(criter, dq.rend()); | 
|  | EXPECT_EQ(*criter, 2); | 
|  | ++criter; | 
|  | EXPECT_NE(criter, dq.rend()); | 
|  | EXPECT_EQ(*criter, 1); | 
|  | ++criter; | 
|  | EXPECT_EQ(criter, dq.rend()); | 
|  |  | 
|  | EXPECT_EQ(*dq.begin(), 1); | 
|  | EXPECT_EQ(*dq.cbegin(), 1); | 
|  | EXPECT_EQ(*dq.rbegin(), 2); | 
|  | EXPECT_EQ(*dq.crbegin(), 2); | 
|  |  | 
|  | dq.push_back(3); | 
|  |  | 
|  | // Forward iterate. | 
|  | int expected_value = 1; | 
|  | for (QuicheCircularDeque<int>::iterator it = dq.begin(); it != dq.end(); | 
|  | ++it) { | 
|  | EXPECT_EQ(expected_value++, *it); | 
|  | } | 
|  |  | 
|  | expected_value = 1; | 
|  | for (QuicheCircularDeque<int>::const_iterator it = dq.cbegin(); | 
|  | it != dq.cend(); ++it) { | 
|  | EXPECT_EQ(expected_value++, *it); | 
|  | } | 
|  |  | 
|  | // Reverse iterate. | 
|  | expected_value = 3; | 
|  | for (QuicheCircularDeque<int>::reverse_iterator it = dq.rbegin(); | 
|  | it != dq.rend(); ++it) { | 
|  | EXPECT_EQ(expected_value--, *it); | 
|  | } | 
|  |  | 
|  | expected_value = 3; | 
|  | for (QuicheCircularDeque<int>::const_reverse_iterator it = dq.crbegin(); | 
|  | it != dq.crend(); ++it) { | 
|  | EXPECT_EQ(expected_value--, *it); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(QuicheCircularDequeTest, Iterator) { | 
|  | // Default constructed iterators of the same type compare equal. | 
|  | EXPECT_EQ(QuicheCircularDeque<int>::iterator(), | 
|  | QuicheCircularDeque<int>::iterator()); | 
|  | EXPECT_EQ(QuicheCircularDeque<int>::const_iterator(), | 
|  | QuicheCircularDeque<int>::const_iterator()); | 
|  | EXPECT_EQ(QuicheCircularDeque<int>::reverse_iterator(), | 
|  | QuicheCircularDeque<int>::reverse_iterator()); | 
|  | EXPECT_EQ(QuicheCircularDeque<int>::const_reverse_iterator(), | 
|  | QuicheCircularDeque<int>::const_reverse_iterator()); | 
|  |  | 
|  | QuicheCircularDeque<QuicheCircularDeque<int>, 3> dqdq = { | 
|  | {1, 2}, {10, 20, 30}, {100, 200, 300, 400}}; | 
|  |  | 
|  | // iter points to {1, 2} | 
|  | decltype(dqdq)::iterator iter = dqdq.begin(); | 
|  | EXPECT_EQ(iter->size(), 2u); | 
|  | EXPECT_THAT(*iter, ElementsAre(1, 2)); | 
|  |  | 
|  | // citer points to {10, 20, 30} | 
|  | decltype(dqdq)::const_iterator citer = dqdq.cbegin() + 1; | 
|  | EXPECT_NE(*iter, *citer); | 
|  | EXPECT_EQ(citer->size(), 3u); | 
|  | int x = 10; | 
|  | for (auto it = citer->begin(); it != citer->end(); ++it) { | 
|  | EXPECT_EQ(*it, x); | 
|  | x += 10; | 
|  | } | 
|  |  | 
|  | EXPECT_LT(iter, citer); | 
|  | EXPECT_LE(iter, iter); | 
|  | EXPECT_GT(citer, iter); | 
|  | EXPECT_GE(citer, citer); | 
|  |  | 
|  | // iter points to {100, 200, 300, 400} | 
|  | iter += 2; | 
|  | EXPECT_NE(*iter, *citer); | 
|  | EXPECT_EQ(iter->size(), 4u); | 
|  | for (int i = 1; i <= 4; ++i) { | 
|  | EXPECT_EQ(iter->begin()[i - 1], i * 100); | 
|  | } | 
|  |  | 
|  | EXPECT_LT(citer, iter); | 
|  | EXPECT_LE(iter, iter); | 
|  | EXPECT_GT(iter, citer); | 
|  | EXPECT_GE(citer, citer); | 
|  |  | 
|  | // iter points to {10, 20, 30}. (same as citer) | 
|  | iter -= 1; | 
|  | EXPECT_EQ(*iter, *citer); | 
|  | EXPECT_EQ(iter->size(), 3u); | 
|  | x = 10; | 
|  | for (auto it = iter->begin(); it != iter->end();) { | 
|  | EXPECT_EQ(*(it++), x); | 
|  | x += 10; | 
|  | } | 
|  | x = 30; | 
|  | for (auto it = iter->begin() + 2; it != iter->begin();) { | 
|  | EXPECT_EQ(*(it--), x); | 
|  | x -= 10; | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(QuicheCircularDequeTest, Resize) { | 
|  | QuicheCircularDeque<int, 3, CountingAllocator<int>> dq; | 
|  | dq.resize(8); | 
|  | EXPECT_THAT(dq, ElementsAre(0, 0, 0, 0, 0, 0, 0, 0)); | 
|  | EXPECT_EQ(1u, dq.get_allocator().allocate_count()); | 
|  |  | 
|  | dq.resize(10, 5); | 
|  | EXPECT_THAT(dq, ElementsAre(0, 0, 0, 0, 0, 0, 0, 0, 5, 5)); | 
|  |  | 
|  | QuicheCircularDeque<int, 3, CountingAllocator<int>> dq2 = dq; | 
|  |  | 
|  | for (size_t new_size = dq.size(); new_size != 0; --new_size) { | 
|  | dq.resize(new_size); | 
|  | EXPECT_TRUE( | 
|  | std::equal(dq.begin(), dq.end(), dq2.begin(), dq2.begin() + new_size)); | 
|  | } | 
|  |  | 
|  | dq.resize(0); | 
|  | EXPECT_TRUE(dq.empty()); | 
|  |  | 
|  | // Resize when data is wrapped. | 
|  | ASSERT_EQ(dq2.size(), dq2.capacity()); | 
|  | while (dq2.size() < dq2.capacity()) { | 
|  | dq2.push_back(5); | 
|  | } | 
|  |  | 
|  | // Shift left once such that data is wrapped. | 
|  | ASSERT_LT(&dq2.front(), &dq2.back()); | 
|  | dq2.pop_back(); | 
|  | dq2.push_front(-5); | 
|  | ASSERT_GT(&dq2.front(), &dq2.back()); | 
|  |  | 
|  | EXPECT_EQ(-5, dq2.front()); | 
|  | EXPECT_EQ(5, dq2.back()); | 
|  | dq2.resize(dq2.size() + 1, 10); | 
|  |  | 
|  | // Data should be unwrapped after the resize. | 
|  | ASSERT_LT(&dq2.front(), &dq2.back()); | 
|  | EXPECT_EQ(-5, dq2.front()); | 
|  | EXPECT_EQ(10, dq2.back()); | 
|  | EXPECT_EQ(5, *(dq2.rbegin() + 1)); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | class Foo { | 
|  | public: | 
|  | Foo() : Foo(0xF00) {} | 
|  |  | 
|  | explicit Foo(int i) : i_(new int(i)) {} | 
|  |  | 
|  | ~Foo() { | 
|  | if (i_ != nullptr) { | 
|  | delete i_; | 
|  | // Do not set i_ to nullptr such that if the container calls destructor | 
|  | // multiple times, asan can detect it. | 
|  | } | 
|  | } | 
|  |  | 
|  | Foo(const Foo& other) : i_(new int(*other.i_)) {} | 
|  |  | 
|  | Foo(Foo&& other) = delete; | 
|  |  | 
|  | void Set(int i) { *i_ = i; } | 
|  |  | 
|  | int i() const { return *i_; } | 
|  |  | 
|  | friend bool operator==(const Foo& lhs, const Foo& rhs) { | 
|  | return lhs.i() == rhs.i(); | 
|  | } | 
|  |  | 
|  | friend std::ostream& operator<<(std::ostream& os, const Foo& foo) { | 
|  | return os << "Foo(" << foo.i() << ")"; | 
|  | } | 
|  |  | 
|  | private: | 
|  | // By pointing i_ to a dynamically allocated integer, a memory leak will be | 
|  | // reported if the container forget to properly destruct this object. | 
|  | int* i_ = nullptr; | 
|  | }; | 
|  | }  // namespace | 
|  |  | 
|  | TEST_F(QuicheCircularDequeTest, RelocateNonTriviallyCopyable) { | 
|  | // When relocating non-trivially-copyable objects: | 
|  | // - Move constructor is preferred, if available. | 
|  | // - Copy constructor is used otherwise. | 
|  |  | 
|  | { | 
|  | // Move construct in Relocate. | 
|  | using MoveConstructible = std::unique_ptr<Foo>; | 
|  | ASSERT_FALSE(std::is_trivially_copyable<MoveConstructible>::value); | 
|  | ASSERT_TRUE(std::is_move_constructible<MoveConstructible>::value); | 
|  | QuicheCircularDeque<MoveConstructible, 3, | 
|  | CountingAllocator<MoveConstructible>> | 
|  | dq1; | 
|  | dq1.resize(3); | 
|  | EXPECT_EQ(dq1.size(), dq1.capacity()); | 
|  | EXPECT_EQ(1u, dq1.get_allocator().allocate_count()); | 
|  |  | 
|  | dq1.emplace_back(new Foo(0xF1));  // Cause existing elements to relocate. | 
|  | EXPECT_EQ(4u, dq1.size()); | 
|  | EXPECT_EQ(2u, dq1.get_allocator().allocate_count()); | 
|  | EXPECT_EQ(dq1[0], nullptr); | 
|  | EXPECT_EQ(dq1[1], nullptr); | 
|  | EXPECT_EQ(dq1[2], nullptr); | 
|  | EXPECT_EQ(dq1[3]->i(), 0xF1); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Copy construct in Relocate. | 
|  | using NonMoveConstructible = Foo; | 
|  | ASSERT_FALSE(std::is_trivially_copyable<NonMoveConstructible>::value); | 
|  | ASSERT_FALSE(std::is_move_constructible<NonMoveConstructible>::value); | 
|  | QuicheCircularDeque<NonMoveConstructible, 3, | 
|  | CountingAllocator<NonMoveConstructible>> | 
|  | dq2; | 
|  | dq2.resize(3); | 
|  | EXPECT_EQ(dq2.size(), dq2.capacity()); | 
|  | EXPECT_EQ(1u, dq2.get_allocator().allocate_count()); | 
|  |  | 
|  | dq2.emplace_back(0xF1);  // Cause existing elements to relocate. | 
|  | EXPECT_EQ(4u, dq2.size()); | 
|  | EXPECT_EQ(2u, dq2.get_allocator().allocate_count()); | 
|  | EXPECT_EQ(dq2[0].i(), 0xF00); | 
|  | EXPECT_EQ(dq2[1].i(), 0xF00); | 
|  | EXPECT_EQ(dq2[2].i(), 0xF00); | 
|  | EXPECT_EQ(dq2[3].i(), 0xF1); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(QuicheCircularDequeTest, PushPop) { | 
|  | // (push|pop|emplace)_(back|front) | 
|  |  | 
|  | { | 
|  | QuicheCircularDeque<Foo, 4, CountingAllocator<Foo>> dq(4); | 
|  | for (size_t i = 0; i < dq.size(); ++i) { | 
|  | dq[i].Set(i + 1); | 
|  | } | 
|  | QUICHE_LOG(INFO) << "dq initialized to " << dq; | 
|  | EXPECT_THAT(dq, ElementsAre(Foo(1), Foo(2), Foo(3), Foo(4))); | 
|  |  | 
|  | ShiftLeft(&dq, false); | 
|  | QUICHE_LOG(INFO) << "shift left once : " << dq; | 
|  | EXPECT_THAT(dq, ElementsAre(Foo(2), Foo(3), Foo(4), Foo(1))); | 
|  |  | 
|  | ShiftLeft(&dq, true); | 
|  | QUICHE_LOG(INFO) << "shift left twice: " << dq; | 
|  | EXPECT_THAT(dq, ElementsAre(Foo(3), Foo(4), Foo(1), Foo(2))); | 
|  | ASSERT_GT(&dq.front(), &dq.back()); | 
|  | // dq destructs with wrapped data. | 
|  | } | 
|  |  | 
|  | { | 
|  | QuicheCircularDeque<Foo, 4, CountingAllocator<Foo>> dq1(4); | 
|  | for (size_t i = 0; i < dq1.size(); ++i) { | 
|  | dq1[i].Set(i + 1); | 
|  | } | 
|  | QUICHE_LOG(INFO) << "dq1 initialized to " << dq1; | 
|  | EXPECT_THAT(dq1, ElementsAre(Foo(1), Foo(2), Foo(3), Foo(4))); | 
|  |  | 
|  | ShiftRight(&dq1, false); | 
|  | QUICHE_LOG(INFO) << "shift right once : " << dq1; | 
|  | EXPECT_THAT(dq1, ElementsAre(Foo(4), Foo(1), Foo(2), Foo(3))); | 
|  |  | 
|  | ShiftRight(&dq1, true); | 
|  | QUICHE_LOG(INFO) << "shift right twice: " << dq1; | 
|  | EXPECT_THAT(dq1, ElementsAre(Foo(3), Foo(4), Foo(1), Foo(2))); | 
|  | ASSERT_GT(&dq1.front(), &dq1.back()); | 
|  | // dq1 destructs with wrapped data. | 
|  | } | 
|  |  | 
|  | {  // Pop n elements from front. | 
|  | QuicheCircularDeque<Foo, 4, CountingAllocator<Foo>> dq2(5); | 
|  | for (size_t i = 0; i < dq2.size(); ++i) { | 
|  | dq2[i].Set(i + 1); | 
|  | } | 
|  | EXPECT_THAT(dq2, ElementsAre(Foo(1), Foo(2), Foo(3), Foo(4), Foo(5))); | 
|  |  | 
|  | EXPECT_EQ(2u, dq2.pop_front_n(2)); | 
|  | EXPECT_THAT(dq2, ElementsAre(Foo(3), Foo(4), Foo(5))); | 
|  |  | 
|  | EXPECT_EQ(3u, dq2.pop_front_n(100)); | 
|  | EXPECT_TRUE(dq2.empty()); | 
|  | } | 
|  |  | 
|  | {  // Pop n elements from back. | 
|  | QuicheCircularDeque<Foo, 4, CountingAllocator<Foo>> dq3(6); | 
|  | for (size_t i = 0; i < dq3.size(); ++i) { | 
|  | dq3[i].Set(i + 1); | 
|  | } | 
|  | EXPECT_THAT(dq3, | 
|  | ElementsAre(Foo(1), Foo(2), Foo(3), Foo(4), Foo(5), Foo(6))); | 
|  |  | 
|  | ShiftRight(&dq3, true); | 
|  | ShiftRight(&dq3, true); | 
|  | ShiftRight(&dq3, true); | 
|  | EXPECT_THAT(dq3, | 
|  | ElementsAre(Foo(4), Foo(5), Foo(6), Foo(1), Foo(2), Foo(3))); | 
|  |  | 
|  | EXPECT_EQ(2u, dq3.pop_back_n(2)); | 
|  | EXPECT_THAT(dq3, ElementsAre(Foo(4), Foo(5), Foo(6), Foo(1))); | 
|  |  | 
|  | EXPECT_EQ(2u, dq3.pop_back_n(2)); | 
|  | EXPECT_THAT(dq3, ElementsAre(Foo(4), Foo(5))); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(QuicheCircularDequeTest, Allocation) { | 
|  | CountingAllocator<int> alloc; | 
|  |  | 
|  | { | 
|  | QuicheCircularDeque<int, 3, CountingAllocator<int>> dq(alloc); | 
|  | EXPECT_EQ(alloc, dq.get_allocator()); | 
|  | EXPECT_EQ(0u, dq.size()); | 
|  | EXPECT_EQ(0u, dq.capacity()); | 
|  | EXPECT_EQ(0u, alloc.allocate_count()); | 
|  | EXPECT_EQ(0u, alloc.deallocate_count()); | 
|  |  | 
|  | for (int i = 1; i <= 18; ++i) { | 
|  | SCOPED_TRACE(testing::Message() | 
|  | << "i=" << i << ", capacity_b4_push=" << dq.capacity()); | 
|  | dq.push_back(i); | 
|  | EXPECT_EQ(i, static_cast<int>(dq.size())); | 
|  |  | 
|  | const size_t capacity = 3 + (i - 1) / 3 * 3; | 
|  | EXPECT_EQ(capacity, dq.capacity()); | 
|  | EXPECT_EQ(capacity / 3, alloc.allocate_count()); | 
|  | EXPECT_EQ(capacity / 3 - 1, alloc.deallocate_count()); | 
|  | } | 
|  |  | 
|  | dq.push_back(19); | 
|  | EXPECT_EQ(22u, dq.capacity());  // 18 + 18 / 4 | 
|  | EXPECT_EQ(7u, alloc.allocate_count()); | 
|  | EXPECT_EQ(6u, alloc.deallocate_count()); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(7u, alloc.deallocate_count()); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace test | 
|  | }  // namespace quiche | 
|  |  | 
|  | // Use a non-quiche namespace to make sure swap can be used via ADL. | 
|  | namespace { | 
|  |  | 
|  | template <typename T> | 
|  | using SwappableAllocator = quiche::test::ConfigurableAllocator< | 
|  | T, | 
|  | /*propagate_on_copy_assignment=*/std::true_type, | 
|  | /*propagate_on_move_assignment=*/std::true_type, | 
|  | /*propagate_on_swap=*/std::true_type, | 
|  | /*equality_result=*/true>; | 
|  |  | 
|  | template <typename T> | 
|  | using UnswappableEqualAllocator = quiche::test::ConfigurableAllocator< | 
|  | T, | 
|  | /*propagate_on_copy_assignment=*/std::true_type, | 
|  | /*propagate_on_move_assignment=*/std::true_type, | 
|  | /*propagate_on_swap=*/std::false_type, | 
|  | /*equality_result=*/true>; | 
|  |  | 
|  | template <typename T> | 
|  | using UnswappableUnequalAllocator = quiche::test::ConfigurableAllocator< | 
|  | T, | 
|  | /*propagate_on_copy_assignment=*/std::true_type, | 
|  | /*propagate_on_move_assignment=*/std::true_type, | 
|  | /*propagate_on_swap=*/std::false_type, | 
|  | /*equality_result=*/false>; | 
|  |  | 
|  | using quiche::test::QuicheCircularDequeTest; | 
|  |  | 
|  | TEST_F(QuicheCircularDequeTest, Swap) { | 
|  | using std::swap; | 
|  |  | 
|  | quiche::QuicheCircularDeque<int64_t, 3, SwappableAllocator<int64_t>> dq1, dq2; | 
|  | dq1.push_back(10); | 
|  | dq1.push_back(11); | 
|  | dq2.push_back(20); | 
|  | swap(dq1, dq2); | 
|  | EXPECT_THAT(dq1, ElementsAre(20)); | 
|  | EXPECT_THAT(dq2, ElementsAre(10, 11)); | 
|  |  | 
|  | quiche::QuicheCircularDeque<char, 3, UnswappableEqualAllocator<char>> dq3, | 
|  | dq4; | 
|  | dq3 = {1, 2, 3, 4, 5}; | 
|  | dq4 = {6, 7, 8, 9, 0}; | 
|  | swap(dq3, dq4); | 
|  | EXPECT_THAT(dq3, ElementsAre(6, 7, 8, 9, 0)); | 
|  | EXPECT_THAT(dq4, ElementsAre(1, 2, 3, 4, 5)); | 
|  |  | 
|  | quiche::QuicheCircularDeque<int, 3, UnswappableUnequalAllocator<int>> dq5, | 
|  | dq6; | 
|  | dq6.push_front(4); | 
|  |  | 
|  | // Using UnswappableUnequalAllocator is ok as long as swap is not called. | 
|  | dq5.assign(dq6.begin(), dq6.end()); | 
|  | EXPECT_THAT(dq5, ElementsAre(4)); | 
|  |  | 
|  | // Undefined behavior to swap between two containers with unequal allocators. | 
|  | EXPECT_QUICHE_DEBUG_DEATH(swap(dq5, dq6), "Undefined swap behavior"); | 
|  | } | 
|  | }  // namespace |