| // 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. | 
 |  | 
 | #ifndef QUICHE_QUIC_CORE_QUIC_CIRCULAR_DEQUE_H_ | 
 | #define QUICHE_QUIC_CORE_QUIC_CIRCULAR_DEQUE_H_ | 
 |  | 
 | #include <algorithm> | 
 | #include <cstddef> | 
 | #include <cstring> | 
 | #include <iterator> | 
 | #include <memory> | 
 | #include <ostream> | 
 | #include <type_traits> | 
 |  | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" | 
 |  | 
 | namespace quic { | 
 |  | 
 | // QuicCircularDeque is a STL-style container that is similar to std deque in | 
 | // API and std::vector in capacity management. The goal is to optimize a common | 
 | // QUIC use case where we keep adding new elements to the end and removing old | 
 | // elements from the beginning, under such scenarios, if the container's size() | 
 | // remain relatively stable, QuicCircularDeque requires little to no memory | 
 | // allocations or deallocations. | 
 | // | 
 | // The implementation, as the name suggests, uses a flat circular buffer to hold | 
 | // all elements. At any point in time, either | 
 | // a) All elements are placed in a contiguous portion of this buffer, like a | 
 | //    c-array, or | 
 | // b) Elements are phycially divided into two parts: the first part occupies the | 
 | //    end of the buffer and the second part occupies the beginning of the | 
 | //    buffer. | 
 | // | 
 | // Currently, elements can only be pushed or poped from either ends, it can't be | 
 | // inserted or erased in the middle. | 
 | // | 
 | // TODO(wub): Make memory grow/shrink strategies customizable. | 
 | template <typename T, | 
 |           size_t MinCapacityIncrement = 3, | 
 |           typename Allocator = std::allocator<T>> | 
 | class QUIC_NO_EXPORT QuicCircularDeque { | 
 |   using AllocatorTraits = std::allocator_traits<Allocator>; | 
 |  | 
 |   // Pointee is either T or const T. | 
 |   template <typename Pointee> | 
 |   class QUIC_NO_EXPORT basic_iterator { | 
 |     using size_type = typename AllocatorTraits::size_type; | 
 |  | 
 |    public: | 
 |     using iterator_category = std::random_access_iterator_tag; | 
 |     using value_type = typename AllocatorTraits::value_type; | 
 |     using difference_type = typename AllocatorTraits::difference_type; | 
 |     using pointer = Pointee*; | 
 |     using reference = Pointee&; | 
 |  | 
 |     basic_iterator() = default; | 
 |  | 
 |     // A copy constructor if Pointee is T. | 
 |     // A conversion from iterator to const_iterator if Pointee is const T. | 
 |     basic_iterator( | 
 |         const basic_iterator<value_type>& it)  // NOLINT(runtime/explicit) | 
 |         : deque_(it.deque_), index_(it.index_) {} | 
 |  | 
 |     // A copy assignment if Pointee is T. | 
 |     // A assignment from iterator to const_iterator if Pointee is const T. | 
 |     basic_iterator& operator=(const basic_iterator<value_type>& it) { | 
 |       if (this != &it) { | 
 |         deque_ = it.deque_; | 
 |         index_ = it.index_; | 
 |       } | 
 |       return *this; | 
 |     } | 
 |  | 
 |     reference operator*() const { return *deque_->index_to_address(index_); } | 
 |     pointer operator->() const { return deque_->index_to_address(index_); } | 
 |     reference operator[](difference_type i) { return *(*this + i); } | 
 |  | 
 |     basic_iterator& operator++() { | 
 |       Increment(); | 
 |       return *this; | 
 |     } | 
 |  | 
 |     basic_iterator operator++(int) { | 
 |       basic_iterator result = *this; | 
 |       Increment(); | 
 |       return result; | 
 |     } | 
 |  | 
 |     basic_iterator operator--() { | 
 |       Decrement(); | 
 |       return *this; | 
 |     } | 
 |  | 
 |     basic_iterator operator--(int) { | 
 |       basic_iterator result = *this; | 
 |       Decrement(); | 
 |       return result; | 
 |     } | 
 |  | 
 |     friend basic_iterator operator+(const basic_iterator& it, | 
 |                                     difference_type delta) { | 
 |       basic_iterator result = it; | 
 |       result.IncrementBy(delta); | 
 |       return result; | 
 |     } | 
 |  | 
 |     basic_iterator& operator+=(difference_type delta) { | 
 |       IncrementBy(delta); | 
 |       return *this; | 
 |     } | 
 |  | 
 |     friend basic_iterator operator-(const basic_iterator& it, | 
 |                                     difference_type delta) { | 
 |       basic_iterator result = it; | 
 |       result.IncrementBy(-delta); | 
 |       return result; | 
 |     } | 
 |  | 
 |     basic_iterator& operator-=(difference_type delta) { | 
 |       IncrementBy(-delta); | 
 |       return *this; | 
 |     } | 
 |  | 
 |     friend difference_type operator-(const basic_iterator& lhs, | 
 |                                      const basic_iterator& rhs) { | 
 |       return lhs.ExternalPosition() - rhs.ExternalPosition(); | 
 |     } | 
 |  | 
 |     friend bool operator==(const basic_iterator& lhs, | 
 |                            const basic_iterator& rhs) { | 
 |       return lhs.index_ == rhs.index_; | 
 |     } | 
 |  | 
 |     friend bool operator!=(const basic_iterator& lhs, | 
 |                            const basic_iterator& rhs) { | 
 |       return !(lhs == rhs); | 
 |     } | 
 |  | 
 |     friend bool operator<(const basic_iterator& lhs, | 
 |                           const basic_iterator& rhs) { | 
 |       return lhs.ExternalPosition() < rhs.ExternalPosition(); | 
 |     } | 
 |  | 
 |     friend bool operator<=(const basic_iterator& lhs, | 
 |                            const basic_iterator& rhs) { | 
 |       return !(lhs > rhs); | 
 |     } | 
 |  | 
 |     friend bool operator>(const basic_iterator& lhs, | 
 |                           const basic_iterator& rhs) { | 
 |       return lhs.ExternalPosition() > rhs.ExternalPosition(); | 
 |     } | 
 |  | 
 |     friend bool operator>=(const basic_iterator& lhs, | 
 |                            const basic_iterator& rhs) { | 
 |       return !(lhs < rhs); | 
 |     } | 
 |  | 
 |    private: | 
 |     basic_iterator(const QuicCircularDeque* deque, size_type index) | 
 |         : deque_(deque), index_(index) {} | 
 |  | 
 |     void Increment() { | 
 |       DCHECK_LE(ExternalPosition() + 1, deque_->size()); | 
 |       index_ = deque_->index_next(index_); | 
 |     } | 
 |  | 
 |     void Decrement() { | 
 |       DCHECK_GE(ExternalPosition(), 1u); | 
 |       index_ = deque_->index_prev(index_); | 
 |     } | 
 |  | 
 |     void IncrementBy(difference_type delta) { | 
 |       if (delta >= 0) { | 
 |         // After increment we are before or at end(). | 
 |         DCHECK_LE(static_cast<size_type>(ExternalPosition() + delta), | 
 |                   deque_->size()); | 
 |       } else { | 
 |         // After decrement we are after or at begin(). | 
 |         DCHECK_GE(ExternalPosition(), static_cast<size_type>(-delta)); | 
 |       } | 
 |       index_ = deque_->index_increment_by(index_, delta); | 
 |     } | 
 |  | 
 |     size_type ExternalPosition() const { | 
 |       if (index_ >= deque_->begin_) { | 
 |         return index_ - deque_->begin_; | 
 |       } | 
 |       return index_ + deque_->data_capacity() - deque_->begin_; | 
 |     } | 
 |  | 
 |     friend class QuicCircularDeque; | 
 |     const QuicCircularDeque* deque_ = nullptr; | 
 |     size_type index_ = 0; | 
 |   }; | 
 |  | 
 |  public: | 
 |   using allocator_type = typename AllocatorTraits::allocator_type; | 
 |   using value_type = typename AllocatorTraits::value_type; | 
 |   using size_type = typename AllocatorTraits::size_type; | 
 |   using difference_type = typename AllocatorTraits::difference_type; | 
 |   using reference = value_type&; | 
 |   using const_reference = const value_type&; | 
 |   using pointer = typename AllocatorTraits::pointer; | 
 |   using const_pointer = typename AllocatorTraits::const_pointer; | 
 |   using iterator = basic_iterator<T>; | 
 |   using const_iterator = basic_iterator<const T>; | 
 |   using reverse_iterator = std::reverse_iterator<iterator>; | 
 |   using const_reverse_iterator = std::reverse_iterator<const_iterator>; | 
 |  | 
 |   QuicCircularDeque() : QuicCircularDeque(allocator_type()) {} | 
 |   explicit QuicCircularDeque(const allocator_type& alloc) | 
 |       : allocator_and_data_(alloc) {} | 
 |  | 
 |   QuicCircularDeque(size_type count, | 
 |                     const T& value, | 
 |                     const Allocator& alloc = allocator_type()) | 
 |       : allocator_and_data_(alloc) { | 
 |     resize(count, value); | 
 |   } | 
 |  | 
 |   explicit QuicCircularDeque(size_type count, | 
 |                              const Allocator& alloc = allocator_type()) | 
 |       : allocator_and_data_(alloc) { | 
 |     resize(count); | 
 |   } | 
 |  | 
 |   template < | 
 |       class InputIt, | 
 |       typename = std::enable_if_t<std::is_base_of< | 
 |           std::input_iterator_tag, | 
 |           typename std::iterator_traits<InputIt>::iterator_category>::value>> | 
 |   QuicCircularDeque(InputIt first, | 
 |                     InputIt last, | 
 |                     const Allocator& alloc = allocator_type()) | 
 |       : allocator_and_data_(alloc) { | 
 |     AssignRange(first, last); | 
 |   } | 
 |  | 
 |   QuicCircularDeque(const QuicCircularDeque& other) | 
 |       : QuicCircularDeque( | 
 |             other, | 
 |             AllocatorTraits::select_on_container_copy_construction( | 
 |                 other.allocator_and_data_.allocator())) {} | 
 |  | 
 |   QuicCircularDeque(const QuicCircularDeque& other, const allocator_type& alloc) | 
 |       : allocator_and_data_(alloc) { | 
 |     assign(other.begin(), other.end()); | 
 |   } | 
 |  | 
 |   QuicCircularDeque(QuicCircularDeque&& other) | 
 |       : begin_(other.begin_), | 
 |         end_(other.end_), | 
 |         allocator_and_data_(std::move(other.allocator_and_data_)) { | 
 |     other.begin_ = other.end_ = 0; | 
 |     other.allocator_and_data_.data = nullptr; | 
 |     other.allocator_and_data_.data_capacity = 0; | 
 |   } | 
 |  | 
 |   QuicCircularDeque(QuicCircularDeque&& other, const allocator_type& alloc) | 
 |       : allocator_and_data_(alloc) { | 
 |     MoveRetainAllocator(std::move(other)); | 
 |   } | 
 |  | 
 |   QuicCircularDeque(std::initializer_list<T> init, | 
 |                     const allocator_type& alloc = allocator_type()) | 
 |       : QuicCircularDeque(init.begin(), init.end(), alloc) {} | 
 |  | 
 |   QuicCircularDeque& operator=(const QuicCircularDeque& other) { | 
 |     if (this == &other) { | 
 |       return *this; | 
 |     } | 
 |     if (AllocatorTraits::propagate_on_container_copy_assignment::value && | 
 |         (allocator_and_data_.allocator() != | 
 |          other.allocator_and_data_.allocator())) { | 
 |       // Destroy all current elements and blocks with the current allocator, | 
 |       // before switching this to use the allocator propagated from "other". | 
 |       DestroyAndDeallocateAll(); | 
 |       begin_ = end_ = 0; | 
 |       allocator_and_data_ = | 
 |           AllocatorAndData(other.allocator_and_data_.allocator()); | 
 |     } | 
 |     assign(other.begin(), other.end()); | 
 |     return *this; | 
 |   } | 
 |  | 
 |   QuicCircularDeque& operator=(QuicCircularDeque&& other) { | 
 |     if (this == &other) { | 
 |       return *this; | 
 |     } | 
 |     if (AllocatorTraits::propagate_on_container_move_assignment::value) { | 
 |       // Take over the storage of "other", along with its allocator. | 
 |       this->~QuicCircularDeque(); | 
 |       new (this) QuicCircularDeque(std::move(other)); | 
 |     } else { | 
 |       MoveRetainAllocator(std::move(other)); | 
 |     } | 
 |     return *this; | 
 |   } | 
 |  | 
 |   ~QuicCircularDeque() { DestroyAndDeallocateAll(); } | 
 |  | 
 |   void assign(size_type count, const T& value) { | 
 |     ClearRetainCapacity(); | 
 |     reserve(count); | 
 |     for (size_t i = 0; i < count; ++i) { | 
 |       emplace_back(value); | 
 |     } | 
 |   } | 
 |  | 
 |   template < | 
 |       class InputIt, | 
 |       typename = std::enable_if_t<std::is_base_of< | 
 |           std::input_iterator_tag, | 
 |           typename std::iterator_traits<InputIt>::iterator_category>::value>> | 
 |   void assign(InputIt first, InputIt last) { | 
 |     AssignRange(first, last); | 
 |   } | 
 |  | 
 |   void assign(std::initializer_list<T> ilist) { | 
 |     assign(ilist.begin(), ilist.end()); | 
 |   } | 
 |  | 
 |   reference at(size_type pos) { | 
 |     DCHECK(pos < size()) << "pos:" << pos << ", size():" << size(); | 
 |     size_type index = begin_ + pos; | 
 |     if (index < data_capacity()) { | 
 |       return *index_to_address(index); | 
 |     } | 
 |     return *index_to_address(index - data_capacity()); | 
 |   } | 
 |  | 
 |   const_reference at(size_type pos) const { | 
 |     return const_cast<QuicCircularDeque*>(this)->at(pos); | 
 |   } | 
 |  | 
 |   reference operator[](size_type pos) { return at(pos); } | 
 |  | 
 |   const_reference operator[](size_type pos) const { return at(pos); } | 
 |  | 
 |   reference front() { | 
 |     DCHECK(!empty()); | 
 |     return *index_to_address(begin_); | 
 |   } | 
 |  | 
 |   const_reference front() const { | 
 |     return const_cast<QuicCircularDeque*>(this)->front(); | 
 |   } | 
 |  | 
 |   reference back() { | 
 |     DCHECK(!empty()); | 
 |     return *(index_to_address(end_ == 0 ? data_capacity() - 1 : end_ - 1)); | 
 |   } | 
 |  | 
 |   const_reference back() const { | 
 |     return const_cast<QuicCircularDeque*>(this)->back(); | 
 |   } | 
 |  | 
 |   iterator begin() { return iterator(this, begin_); } | 
 |   const_iterator begin() const { return const_iterator(this, begin_); } | 
 |   const_iterator cbegin() const { return const_iterator(this, begin_); } | 
 |  | 
 |   iterator end() { return iterator(this, end_); } | 
 |   const_iterator end() const { return const_iterator(this, end_); } | 
 |   const_iterator cend() const { return const_iterator(this, end_); } | 
 |  | 
 |   reverse_iterator rbegin() { return reverse_iterator(end()); } | 
 |   const_reverse_iterator rbegin() const { | 
 |     return const_reverse_iterator(end()); | 
 |   } | 
 |   const_reverse_iterator crbegin() const { return rbegin(); } | 
 |  | 
 |   reverse_iterator rend() { return reverse_iterator(begin()); } | 
 |   const_reverse_iterator rend() const { | 
 |     return const_reverse_iterator(begin()); | 
 |   } | 
 |   const_reverse_iterator crend() const { return rend(); } | 
 |  | 
 |   size_type capacity() const { | 
 |     return data_capacity() == 0 ? 0 : data_capacity() - 1; | 
 |   } | 
 |  | 
 |   void reserve(size_type new_cap) { | 
 |     if (new_cap > capacity()) { | 
 |       Relocate(new_cap); | 
 |     } | 
 |   } | 
 |  | 
 |   // Remove all elements. Leave capacity unchanged. | 
 |   void clear() { ClearRetainCapacity(); } | 
 |  | 
 |   bool empty() const { return begin_ == end_; } | 
 |  | 
 |   size_type size() const { | 
 |     if (begin_ <= end_) { | 
 |       return end_ - begin_; | 
 |     } | 
 |     return data_capacity() + end_ - begin_; | 
 |   } | 
 |  | 
 |   void resize(size_type count) { ResizeInternal(count); } | 
 |  | 
 |   void resize(size_type count, const value_type& value) { | 
 |     ResizeInternal(count, value); | 
 |   } | 
 |  | 
 |   void push_front(const T& value) { emplace_front(value); } | 
 |   void push_front(T&& value) { emplace_front(std::move(value)); } | 
 |  | 
 |   template <class... Args> | 
 |   reference emplace_front(Args&&... args) { | 
 |     MaybeExpandCapacity(1); | 
 |     begin_ = index_prev(begin_); | 
 |     new (index_to_address(begin_)) T(std::forward<Args>(args)...); | 
 |     return front(); | 
 |   } | 
 |  | 
 |   void push_back(const T& value) { emplace_back(value); } | 
 |   void push_back(T&& value) { emplace_back(std::move(value)); } | 
 |  | 
 |   template <class... Args> | 
 |   reference emplace_back(Args&&... args) { | 
 |     MaybeExpandCapacity(1); | 
 |     new (index_to_address(end_)) T(std::forward<Args>(args)...); | 
 |     end_ = index_next(end_); | 
 |     return back(); | 
 |   } | 
 |  | 
 |   void pop_front() { | 
 |     DCHECK(!empty()); | 
 |     DestroyByIndex(begin_); | 
 |     begin_ = index_next(begin_); | 
 |     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_); | 
 |     DestroyByIndex(end_); | 
 |     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_); | 
 |     swap(end_, other.end_); | 
 |  | 
 |     if (AllocatorTraits::propagate_on_container_swap::value) { | 
 |       swap(allocator_and_data_, other.allocator_and_data_); | 
 |     } else { | 
 |       // When propagate_on_container_swap is false, it is undefined behavior, by | 
 |       // c++ standard, to swap between two AllocatorAwareContainer(s) with | 
 |       // unequal allocators. | 
 |       DCHECK(get_allocator() == other.get_allocator()) | 
 |           << "Undefined swap behavior"; | 
 |       swap(allocator_and_data_.data, other.allocator_and_data_.data); | 
 |       swap(allocator_and_data_.data_capacity, | 
 |            other.allocator_and_data_.data_capacity); | 
 |     } | 
 |   } | 
 |  | 
 |   friend void swap(QuicCircularDeque& lhs, QuicCircularDeque& rhs) { | 
 |     lhs.swap(rhs); | 
 |   } | 
 |  | 
 |   allocator_type get_allocator() const { | 
 |     return allocator_and_data_.allocator(); | 
 |   } | 
 |  | 
 |   friend bool operator==(const QuicCircularDeque& lhs, | 
 |                          const QuicCircularDeque& rhs) { | 
 |     return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); | 
 |   } | 
 |  | 
 |   friend bool operator!=(const QuicCircularDeque& lhs, | 
 |                          const QuicCircularDeque& rhs) { | 
 |     return !(lhs == rhs); | 
 |   } | 
 |  | 
 |   friend QUIC_NO_EXPORT std::ostream& operator<<(std::ostream& os, | 
 |                                                  const QuicCircularDeque& dq) { | 
 |     os << "{"; | 
 |     for (size_type pos = 0; pos != dq.size(); ++pos) { | 
 |       if (pos != 0) { | 
 |         os << ","; | 
 |       } | 
 |       os << " " << dq[pos]; | 
 |     } | 
 |     os << " }"; | 
 |     return os; | 
 |   } | 
 |  | 
 |  private: | 
 |   void MoveRetainAllocator(QuicCircularDeque&& other) { | 
 |     if (get_allocator() == other.get_allocator()) { | 
 |       // Take over the storage of "other", with which we share an allocator. | 
 |       DestroyAndDeallocateAll(); | 
 |  | 
 |       begin_ = other.begin_; | 
 |       end_ = other.end_; | 
 |       allocator_and_data_.data = other.allocator_and_data_.data; | 
 |       allocator_and_data_.data_capacity = | 
 |           other.allocator_and_data_.data_capacity; | 
 |  | 
 |       other.begin_ = other.end_ = 0; | 
 |       other.allocator_and_data_.data = nullptr; | 
 |       other.allocator_and_data_.data_capacity = 0; | 
 |     } else { | 
 |       // We cannot take over of the storage from "other", since it has a | 
 |       // different allocator; we're stuck move-assigning elements individually. | 
 |       ClearRetainCapacity(); | 
 |       for (auto& elem : other) { | 
 |         push_back(std::move(elem)); | 
 |       } | 
 |       other.clear(); | 
 |     } | 
 |   } | 
 |  | 
 |   template < | 
 |       typename InputIt, | 
 |       typename = std::enable_if_t<std::is_base_of< | 
 |           std::input_iterator_tag, | 
 |           typename std::iterator_traits<InputIt>::iterator_category>::value>> | 
 |   void AssignRange(InputIt first, InputIt last) { | 
 |     ClearRetainCapacity(); | 
 |     if (std::is_base_of< | 
 |             std::random_access_iterator_tag, | 
 |             typename std::iterator_traits<InputIt>::iterator_category>::value) { | 
 |       reserve(std::distance(first, last)); | 
 |     } | 
 |     for (; first != last; ++first) { | 
 |       emplace_back(*first); | 
 |     } | 
 |   } | 
 |  | 
 |   // WARNING: begin_, end_ and allocator_and_data_ are not modified. | 
 |   void DestroyAndDeallocateAll() { | 
 |     DestroyRange(begin_, end_); | 
 |  | 
 |     if (data_capacity() > 0) { | 
 |       DCHECK_NE(nullptr, allocator_and_data_.data); | 
 |       AllocatorTraits::deallocate(allocator_and_data_.allocator(), | 
 |                                   allocator_and_data_.data, data_capacity()); | 
 |     } | 
 |   } | 
 |  | 
 |   void ClearRetainCapacity() { | 
 |     DestroyRange(begin_, end_); | 
 |     begin_ = end_ = 0; | 
 |   } | 
 |  | 
 |   void MaybeShrinkCapacity() { | 
 |     // TODO(wub): Implement a storage policy that actually shrinks. | 
 |   } | 
 |  | 
 |   void MaybeExpandCapacity(size_t num_additional_elements) { | 
 |     size_t new_size = size() + num_additional_elements; | 
 |     if (capacity() >= new_size) { | 
 |       return; | 
 |     } | 
 |  | 
 |     // The minimum amount of additional capacity to grow. | 
 |     size_t min_additional_capacity = | 
 |         std::max(MinCapacityIncrement, capacity() / 4); | 
 |     size_t new_capacity = | 
 |         std::max(new_size, capacity() + min_additional_capacity); | 
 |  | 
 |     Relocate(new_capacity); | 
 |   } | 
 |  | 
 |   void Relocate(size_t new_capacity) { | 
 |     const size_t num_elements = size(); | 
 |     DCHECK_GT(new_capacity, num_elements) | 
 |         << "new_capacity:" << new_capacity << ", num_elements:" << num_elements; | 
 |  | 
 |     size_t new_data_capacity = new_capacity + 1; | 
 |     pointer new_data = AllocatorTraits::allocate( | 
 |         allocator_and_data_.allocator(), new_data_capacity); | 
 |  | 
 |     if (begin_ < end_) { | 
 |       // Not wrapped. | 
 |       RelocateUnwrappedRange(begin_, end_, new_data); | 
 |     } else if (begin_ > end_) { | 
 |       // Wrapped. | 
 |       const size_t num_elements_before_wrap = data_capacity() - begin_; | 
 |       RelocateUnwrappedRange(begin_, data_capacity(), new_data); | 
 |       RelocateUnwrappedRange(0, end_, new_data + num_elements_before_wrap); | 
 |     } | 
 |  | 
 |     if (data_capacity()) { | 
 |       AllocatorTraits::deallocate(allocator_and_data_.allocator(), | 
 |                                   allocator_and_data_.data, data_capacity()); | 
 |     } | 
 |  | 
 |     allocator_and_data_.data = new_data; | 
 |     allocator_and_data_.data_capacity = new_data_capacity; | 
 |     begin_ = 0; | 
 |     end_ = num_elements; | 
 |   } | 
 |  | 
 |   template <typename T_ = T> | 
 |   typename std::enable_if<std::is_trivially_copyable<T_>::value, void>::type | 
 |   RelocateUnwrappedRange(size_type begin, size_type end, pointer dest) const { | 
 |     DCHECK_LE(begin, end) << "begin:" << begin << ", end:" << end; | 
 |     pointer src = index_to_address(begin); | 
 |     DCHECK_NE(src, nullptr); | 
 |     memcpy(dest, src, sizeof(T) * (end - begin)); | 
 |     DestroyRange(begin, end); | 
 |   } | 
 |  | 
 |   template <typename T_ = T> | 
 |   typename std::enable_if<!std::is_trivially_copyable<T_>::value && | 
 |                               std::is_move_constructible<T_>::value, | 
 |                           void>::type | 
 |   RelocateUnwrappedRange(size_type begin, size_type end, pointer dest) const { | 
 |     DCHECK_LE(begin, end) << "begin:" << begin << ", end:" << end; | 
 |     pointer src = index_to_address(begin); | 
 |     pointer src_end = index_to_address(end); | 
 |     while (src != src_end) { | 
 |       new (dest) T(std::move(*src)); | 
 |       DestroyByAddress(src); | 
 |       ++dest; | 
 |       ++src; | 
 |     } | 
 |   } | 
 |  | 
 |   template <typename T_ = T> | 
 |   typename std::enable_if<!std::is_trivially_copyable<T_>::value && | 
 |                               !std::is_move_constructible<T_>::value, | 
 |                           void>::type | 
 |   RelocateUnwrappedRange(size_type begin, size_type end, pointer dest) const { | 
 |     DCHECK_LE(begin, end) << "begin:" << begin << ", end:" << end; | 
 |     pointer src = index_to_address(begin); | 
 |     pointer src_end = index_to_address(end); | 
 |     while (src != src_end) { | 
 |       new (dest) T(*src); | 
 |       DestroyByAddress(src); | 
 |       ++dest; | 
 |       ++src; | 
 |     } | 
 |   } | 
 |  | 
 |   template <class... U> | 
 |   void ResizeInternal(size_type count, U&&... u) { | 
 |     if (count > size()) { | 
 |       // Expanding. | 
 |       MaybeExpandCapacity(count - size()); | 
 |       while (size() < count) { | 
 |         emplace_back(std::forward<U>(u)...); | 
 |       } | 
 |     } else { | 
 |       // Most likely shrinking. No-op if count == size(). | 
 |       size_type new_end = (begin_ + count) % data_capacity(); | 
 |       DestroyRange(new_end, end_); | 
 |       end_ = new_end; | 
 |  | 
 |       MaybeShrinkCapacity(); | 
 |     } | 
 |   } | 
 |  | 
 |   void DestroyRange(size_type begin, size_type end) const { | 
 |     if (std::is_trivially_destructible<T>::value) { | 
 |       return; | 
 |     } | 
 |     if (end >= begin) { | 
 |       DestroyUnwrappedRange(begin, end); | 
 |     } else { | 
 |       DestroyUnwrappedRange(begin, data_capacity()); | 
 |       DestroyUnwrappedRange(0, end); | 
 |     } | 
 |   } | 
 |  | 
 |   // Should only be called from DestroyRange. | 
 |   void DestroyUnwrappedRange(size_type begin, size_type end) const { | 
 |     DCHECK_LE(begin, end) << "begin:" << begin << ", end:" << end; | 
 |     for (; begin != end; ++begin) { | 
 |       DestroyByIndex(begin); | 
 |     } | 
 |   } | 
 |  | 
 |   void DestroyByIndex(size_type index) const { | 
 |     DestroyByAddress(index_to_address(index)); | 
 |   } | 
 |  | 
 |   void DestroyByAddress(pointer address) const { | 
 |     if (std::is_trivially_destructible<T>::value) { | 
 |       return; | 
 |     } | 
 |     address->~T(); | 
 |   } | 
 |  | 
 |   size_type data_capacity() const { return allocator_and_data_.data_capacity; } | 
 |  | 
 |   pointer index_to_address(size_type index) const { | 
 |     return allocator_and_data_.data + index; | 
 |   } | 
 |  | 
 |   size_type index_prev(size_type index) const { | 
 |     return index == 0 ? data_capacity() - 1 : index - 1; | 
 |   } | 
 |  | 
 |   size_type index_next(size_type index) const { | 
 |     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(static_cast<size_type>(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 | 
 |   // data members. | 
 |   struct AllocatorAndData : private allocator_type { | 
 |     explicit AllocatorAndData(const allocator_type& alloc) | 
 |         : allocator_type(alloc) {} | 
 |  | 
 |     const allocator_type& allocator() const { return *this; } | 
 |     allocator_type& allocator() { return *this; } | 
 |  | 
 |     pointer data = nullptr; | 
 |     size_type data_capacity = 0; | 
 |   }; | 
 |  | 
 |   size_type begin_ = 0; | 
 |   size_type end_ = 0; | 
 |   AllocatorAndData allocator_and_data_; | 
 | }; | 
 |  | 
 | }  // namespace quic | 
 |  | 
 | #endif  // QUICHE_QUIC_CORE_QUIC_CIRCULAR_DEQUE_H_ |