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) {