Optimize `resize()` shrinking and simplify `assign()` in `absl::chunked_queue`.

Now that `ChunkedQueueBlock` is doubly-linked (`prev()`), use backward block traversal to optimize and simplify erase operations:

1. Replace `iterator_common::IncrBy()` with `DecrBy()` and update `resize(size_t new_size)` when shrinking (`new_size < size()`) to walk backward from `end()` via `DecrBy(size() - new_size)` instead of walking forward from `begin()` via `IncrBy(new_size)`. This avoids traversing the kept prefix blocks and reduces shrinking time complexity from O(B_kept + N_removed) to O(N_removed).

2. Move the block-boundary adjustment (`i.ptr == i.block->start() && i.block->prev() != nullptr`) directly into `EraseAllFrom(iterator i)`, eliminating the need to track `prev_block` on every element iteration and special-case block boundaries in `assign(Iter first, Iter last)`.

PiperOrigin-RevId: 1007269347
diff --git a/absl/container/chunked_queue.h b/absl/container/chunked_queue.h
index 7dba5dc..a027291 100644
--- a/absl/container/chunked_queue.h
+++ b/absl/container/chunked_queue.h
@@ -172,12 +172,13 @@
       --ptr;
     }
 
-    void IncrBy(size_t n) {
-      while (n > static_cast<size_t>(block->limit() - ptr)) {
-        n -= static_cast<size_t>(block->limit() - ptr);
-        *this = iterator_common(block->next());
+    void DecrBy(size_t n) {
+      while (n > static_cast<size_t>(ptr - block->start())) {
+        n -= static_cast<size_t>(ptr - block->start());
+        block = block->prev();
+        ptr = block->limit();
       }
-      ptr += n;
+      ptr -= n;
     }
 
     Block* block = nullptr;
@@ -379,41 +380,15 @@
                 base_internal::IsAtLeastInputIterator<Iter>::value>>
   void assign(Iter first, Iter last) {
     auto out = begin();
-    Block* prev_block = nullptr;
 
     // Overwrite existing elements.
-    for (; out != end() && first != last; ++first) {
-      // Track the previous block so we can correctly update tail_ if we stop
-      // exactly at a block boundary.
-      if (out.ptr + 1 == out.block->limit()) {
-        prev_block = out.block;
-      }
+    for (; out != end() && first != last; ++out, ++first) {
       *out = *first;
-      ++out;
     }
 
-    // If we stopped exactly at the start of a block (meaning the previous block
-    // was full), we must ensure tail_ points to the end of the previous block,
-    // not the start of the current (now empty and to be deleted) block.
-    // This maintains the invariant required by back() which assumes tail_
-    // never points to the start of a block (unless it's the only block).
-    if (!empty() && out.block != nullptr && out.ptr == out.block->start() &&
-        prev_block != nullptr) {
-      // Delete the current block and all subsequent blocks.
-      //
-      // NOTE: Calling EraseAllFrom on an iterator that points to the limit of
-      // the previous block will not delete any element from the previous block.
-      iterator prev_block_end(prev_block);
-      prev_block_end.ptr = prev_block->limit();
-      EraseAllFrom(prev_block_end);
-
-      // Update tail_ to point to the end of the previous block.
-      tail_ = prev_block_end;
-      prev_block->set_next(nullptr);
-    } else {
-      // Standard erase from the current position to the end.
-      EraseAllFrom(out);
-    }
+    // Erase any remaining existing elements from the current position to the
+    // end.
+    EraseAllFrom(out);
 
     // Append any remaining new elements.
     for (; first != last; ++first) {
@@ -713,8 +688,8 @@
     return;
   }
   ABSL_ASSERT(new_size < size());
-  auto new_end = begin();
-  new_end.IncrBy(new_size);
+  auto new_end = end();
+  new_end.DecrBy(size() - new_size);
   ABSL_ASSERT(new_end != end());
   EraseAllFrom(new_end);
 }
@@ -809,6 +784,15 @@
   if (!i.block) {
     return;
   }
+  // If `i` points to the start of a non-head block, all elements in `i.block`
+  // are being erased. Repoint `i` to the end of the previous block so that
+  // `i.block` is also deleted below and `tail_` points to `prev->limit()`
+  // rather than the start of an empty block (preserving the invariant required
+  // by `back()`, `pop_back()`, and `Incr()`).
+  if (i.ptr == i.block->start() && i.block->prev() != nullptr) {
+    i.block = i.block->prev();
+    i.ptr = i.block->limit();
+  }
   ABSL_ASSERT(i.ptr);
   ABSL_ASSERT(i.block->limit());
   alloc_and_size_.size -= Destroy(i.ptr, block_end(i.block));