gfe-relnote: (n/a) Add pop_front_n and pop_back_n methods to QuicCircularDeque. Code not used yet. PiperOrigin-RevId: 279127905 Change-Id: I3a5ee539cb53d1cb7fac68ba30f230135f1023fd
diff --git a/quic/core/quic_circular_deque.h b/quic/core/quic_circular_deque.h index 54043fd..3a34626 100644 --- a/quic/core/quic_circular_deque.h +++ b/quic/core/quic_circular_deque.h
@@ -151,17 +151,25 @@ basic_iterator(const QuicCircularDeque* deque, size_type index) : deque_(deque), index_(index) {} - void Increment() { index_ = deque_->index_next(index_); } + void Increment() { + DCHECK_LE(ExternalPosition() + 1, deque_->size()); + index_ = deque_->index_next(index_); + } - void Decrement() { index_ = deque_->index_prev(index_); } + void Decrement() { + DCHECK_GE(ExternalPosition(), 1); + index_ = deque_->index_prev(index_); + } void IncrementBy(difference_type delta) { - if (delta == 0) { - return; + if (delta >= 0) { + // After increment we are before or at end(). + DCHECK_LE(ExternalPosition() + delta, deque_->size()); + } else { + // After decrement we are after or at begin(). + DCHECK_GE(ExternalPosition(), -delta); } - - index_ = (deque_->begin_ + ExternalPosition() + delta) % - deque_->data_capacity(); + index_ = deque_->index_increment_by(index_, delta); } size_type ExternalPosition() const { @@ -415,6 +423,15 @@ MaybeShrinkCapacity(); } + size_type pop_front_n(size_type count) { + size_type num_elements_to_pop = std::min(count, size()); + size_type new_begin = index_increment_by(begin_, num_elements_to_pop); + DestroyRange(begin_, new_begin); + begin_ = new_begin; + MaybeShrinkCapacity(); + return num_elements_to_pop; + } + void pop_back() { DCHECK(!empty()); end_ = index_prev(end_); @@ -422,6 +439,15 @@ MaybeShrinkCapacity(); } + size_type pop_back_n(size_type count) { + size_type num_elements_to_pop = std::min(count, size()); + size_type new_end = index_increment_by(end_, -num_elements_to_pop); + DestroyRange(new_end, end_); + end_ = new_end; + MaybeShrinkCapacity(); + return num_elements_to_pop; + } + void swap(QuicCircularDeque& other) { using std::swap; swap(begin_, other.begin_); @@ -683,6 +709,15 @@ return index == data_capacity() - 1 ? 0 : index + 1; } + size_type index_increment_by(size_type index, difference_type delta) const { + if (delta == 0) { + return index; + } + + DCHECK_LT(std::abs(delta), data_capacity()); + return (index + data_capacity() + delta) % data_capacity(); + } + // Empty base-class optimization: bundle storage for our allocator together // with the fields we had to store anyway, via inheriting from the allocator, // so this allocator instance doesn't consume any storage when its type has no
diff --git a/quic/core/quic_circular_deque_test.cc b/quic/core/quic_circular_deque_test.cc index f9774ee..8844d92 100644 --- a/quic/core/quic_circular_deque_test.cc +++ b/quic/core/quic_circular_deque_test.cc
@@ -660,6 +660,41 @@ ASSERT_GT(&dq1.front(), &dq1.back()); // dq1 destructs with wrapped data. } + + { // Pop n elements from front. + QuicCircularDeque<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. + QuicCircularDeque<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(QuicCircularDeque, Allocation) {