xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/common/quiche_circular_deque.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef QUICHE_COMMON_QUICHE_CIRCULAR_DEQUE_H_
6 #define QUICHE_COMMON_QUICHE_CIRCULAR_DEQUE_H_
7 
8 #include <algorithm>
9 #include <cstddef>
10 #include <cstring>
11 #include <iterator>
12 #include <memory>
13 #include <ostream>
14 #include <type_traits>
15 
16 #include "quiche/common/platform/api/quiche_export.h"
17 #include "quiche/common/platform/api/quiche_logging.h"
18 
19 namespace quiche {
20 
21 // QuicheCircularDeque is a STL-style container that is similar to std::deque in
22 // API and std::vector in capacity management. The goal is to optimize a common
23 // QUIC use case where we keep adding new elements to the end and removing old
24 // elements from the beginning, under such scenarios, if the container's size()
25 // remain relatively stable, QuicheCircularDeque requires little to no memory
26 // allocations or deallocations.
27 //
28 // The implementation, as the name suggests, uses a flat circular buffer to hold
29 // all elements. At any point in time, either
30 // a) All elements are placed in a contiguous portion of this buffer, like a
31 //    c-array, or
32 // b) Elements are phycially divided into two parts: the first part occupies the
33 //    end of the buffer and the second part occupies the beginning of the
34 //    buffer.
35 //
36 // Currently, elements can only be pushed or poped from either ends, it can't be
37 // inserted or erased in the middle.
38 //
39 // TODO(wub): Make memory grow/shrink strategies customizable.
40 template <typename T, size_t MinCapacityIncrement = 3,
41           typename Allocator = std::allocator<T>>
42 class QUICHE_NO_EXPORT QuicheCircularDeque {
43   using AllocatorTraits = std::allocator_traits<Allocator>;
44 
45   // Pointee is either T or const T.
46   template <typename Pointee>
47   class QUICHE_NO_EXPORT basic_iterator {
48     using size_type = typename AllocatorTraits::size_type;
49 
50    public:
51     using iterator_category = std::random_access_iterator_tag;
52     using value_type = typename AllocatorTraits::value_type;
53     using difference_type = typename AllocatorTraits::difference_type;
54     using pointer = Pointee*;
55     using reference = Pointee&;
56 
57     basic_iterator() = default;
58 
59     // A copy constructor if Pointee is T.
60     // A conversion from iterator to const_iterator if Pointee is const T.
basic_iterator(const basic_iterator<value_type> & it)61     basic_iterator(
62         const basic_iterator<value_type>& it)  // NOLINT(runtime/explicit)
63         : deque_(it.deque_), index_(it.index_) {}
64 
65     // A copy assignment if Pointee is T.
66     // A assignment from iterator to const_iterator if Pointee is const T.
67     basic_iterator& operator=(const basic_iterator<value_type>& it) {
68       if (this != &it) {
69         deque_ = it.deque_;
70         index_ = it.index_;
71       }
72       return *this;
73     }
74 
75     reference operator*() const { return *deque_->index_to_address(index_); }
76     pointer operator->() const { return deque_->index_to_address(index_); }
77     reference operator[](difference_type i) { return *(*this + i); }
78 
79     basic_iterator& operator++() {
80       Increment();
81       return *this;
82     }
83 
84     basic_iterator operator++(int) {
85       basic_iterator result = *this;
86       Increment();
87       return result;
88     }
89 
90     basic_iterator operator--() {
91       Decrement();
92       return *this;
93     }
94 
95     basic_iterator operator--(int) {
96       basic_iterator result = *this;
97       Decrement();
98       return result;
99     }
100 
101     friend basic_iterator operator+(const basic_iterator& it,
102                                     difference_type delta) {
103       basic_iterator result = it;
104       result.IncrementBy(delta);
105       return result;
106     }
107 
108     basic_iterator& operator+=(difference_type delta) {
109       IncrementBy(delta);
110       return *this;
111     }
112 
113     friend basic_iterator operator-(const basic_iterator& it,
114                                     difference_type delta) {
115       basic_iterator result = it;
116       result.IncrementBy(-delta);
117       return result;
118     }
119 
120     basic_iterator& operator-=(difference_type delta) {
121       IncrementBy(-delta);
122       return *this;
123     }
124 
125     friend difference_type operator-(const basic_iterator& lhs,
126                                      const basic_iterator& rhs) {
127       return lhs.ExternalPosition() - rhs.ExternalPosition();
128     }
129 
130     friend bool operator==(const basic_iterator& lhs,
131                            const basic_iterator& rhs) {
132       return lhs.index_ == rhs.index_;
133     }
134 
135     friend bool operator!=(const basic_iterator& lhs,
136                            const basic_iterator& rhs) {
137       return !(lhs == rhs);
138     }
139 
140     friend bool operator<(const basic_iterator& lhs,
141                           const basic_iterator& rhs) {
142       return lhs.ExternalPosition() < rhs.ExternalPosition();
143     }
144 
145     friend bool operator<=(const basic_iterator& lhs,
146                            const basic_iterator& rhs) {
147       return !(lhs > rhs);
148     }
149 
150     friend bool operator>(const basic_iterator& lhs,
151                           const basic_iterator& rhs) {
152       return lhs.ExternalPosition() > rhs.ExternalPosition();
153     }
154 
155     friend bool operator>=(const basic_iterator& lhs,
156                            const basic_iterator& rhs) {
157       return !(lhs < rhs);
158     }
159 
160    private:
basic_iterator(const QuicheCircularDeque * deque,size_type index)161     basic_iterator(const QuicheCircularDeque* deque, size_type index)
162         : deque_(deque), index_(index) {}
163 
Increment()164     void Increment() {
165       QUICHE_DCHECK_LE(ExternalPosition() + 1, deque_->size());
166       index_ = deque_->index_next(index_);
167     }
168 
Decrement()169     void Decrement() {
170       QUICHE_DCHECK_GE(ExternalPosition(), 1u);
171       index_ = deque_->index_prev(index_);
172     }
173 
IncrementBy(difference_type delta)174     void IncrementBy(difference_type delta) {
175       if (delta >= 0) {
176         // After increment we are before or at end().
177         QUICHE_DCHECK_LE(static_cast<size_type>(ExternalPosition() + delta),
178                          deque_->size());
179       } else {
180         // After decrement we are after or at begin().
181         QUICHE_DCHECK_GE(ExternalPosition(), static_cast<size_type>(-delta));
182       }
183       index_ = deque_->index_increment_by(index_, delta);
184     }
185 
ExternalPosition()186     size_type ExternalPosition() const {
187       if (index_ >= deque_->begin_) {
188         return index_ - deque_->begin_;
189       }
190       return index_ + deque_->data_capacity() - deque_->begin_;
191     }
192 
193     friend class QuicheCircularDeque;
194     const QuicheCircularDeque* deque_ = nullptr;
195     size_type index_ = 0;
196   };
197 
198  public:
199   using allocator_type = typename AllocatorTraits::allocator_type;
200   using value_type = typename AllocatorTraits::value_type;
201   using size_type = typename AllocatorTraits::size_type;
202   using difference_type = typename AllocatorTraits::difference_type;
203   using reference = value_type&;
204   using const_reference = const value_type&;
205   using pointer = typename AllocatorTraits::pointer;
206   using const_pointer = typename AllocatorTraits::const_pointer;
207   using iterator = basic_iterator<T>;
208   using const_iterator = basic_iterator<const T>;
209   using reverse_iterator = std::reverse_iterator<iterator>;
210   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
211 
QuicheCircularDeque()212   QuicheCircularDeque() : QuicheCircularDeque(allocator_type()) {}
QuicheCircularDeque(const allocator_type & alloc)213   explicit QuicheCircularDeque(const allocator_type& alloc)
214       : allocator_and_data_(alloc) {}
215 
216   QuicheCircularDeque(size_type count, const T& value,
217                       const Allocator& alloc = allocator_type())
allocator_and_data_(alloc)218       : allocator_and_data_(alloc) {
219     resize(count, value);
220   }
221 
222   explicit QuicheCircularDeque(size_type count,
223                                const Allocator& alloc = allocator_type())
allocator_and_data_(alloc)224       : allocator_and_data_(alloc) {
225     resize(count);
226   }
227 
228   template <
229       class InputIt,
230       typename = std::enable_if_t<std::is_base_of<
231           std::input_iterator_tag,
232           typename std::iterator_traits<InputIt>::iterator_category>::value>>
233   QuicheCircularDeque(InputIt first, InputIt last,
234                       const Allocator& alloc = allocator_type())
allocator_and_data_(alloc)235       : allocator_and_data_(alloc) {
236     AssignRange(first, last);
237   }
238 
QuicheCircularDeque(const QuicheCircularDeque & other)239   QuicheCircularDeque(const QuicheCircularDeque& other)
240       : QuicheCircularDeque(
241             other, AllocatorTraits::select_on_container_copy_construction(
242                        other.allocator_and_data_.allocator())) {}
243 
QuicheCircularDeque(const QuicheCircularDeque & other,const allocator_type & alloc)244   QuicheCircularDeque(const QuicheCircularDeque& other,
245                       const allocator_type& alloc)
246       : allocator_and_data_(alloc) {
247     assign(other.begin(), other.end());
248   }
249 
QuicheCircularDeque(QuicheCircularDeque && other)250   QuicheCircularDeque(QuicheCircularDeque&& other)
251       : begin_(other.begin_),
252         end_(other.end_),
253         allocator_and_data_(std::move(other.allocator_and_data_)) {
254     other.begin_ = other.end_ = 0;
255     other.allocator_and_data_.data = nullptr;
256     other.allocator_and_data_.data_capacity = 0;
257   }
258 
QuicheCircularDeque(QuicheCircularDeque && other,const allocator_type & alloc)259   QuicheCircularDeque(QuicheCircularDeque&& other, const allocator_type& alloc)
260       : allocator_and_data_(alloc) {
261     MoveRetainAllocator(std::move(other));
262   }
263 
264   QuicheCircularDeque(std::initializer_list<T> init,
265                       const allocator_type& alloc = allocator_type())
266       : QuicheCircularDeque(init.begin(), init.end(), alloc) {}
267 
268   QuicheCircularDeque& operator=(const QuicheCircularDeque& other) {
269     if (this == &other) {
270       return *this;
271     }
272     if (AllocatorTraits::propagate_on_container_copy_assignment::value &&
273         (allocator_and_data_.allocator() !=
274          other.allocator_and_data_.allocator())) {
275       // Destroy all current elements and blocks with the current allocator,
276       // before switching this to use the allocator propagated from "other".
277       DestroyAndDeallocateAll();
278       begin_ = end_ = 0;
279       allocator_and_data_ =
280           AllocatorAndData(other.allocator_and_data_.allocator());
281     }
282     assign(other.begin(), other.end());
283     return *this;
284   }
285 
286   QuicheCircularDeque& operator=(QuicheCircularDeque&& other) {
287     if (this == &other) {
288       return *this;
289     }
290     if (AllocatorTraits::propagate_on_container_move_assignment::value) {
291       // Take over the storage of "other", along with its allocator.
292       this->~QuicheCircularDeque();
293       new (this) QuicheCircularDeque(std::move(other));
294     } else {
295       MoveRetainAllocator(std::move(other));
296     }
297     return *this;
298   }
299 
~QuicheCircularDeque()300   ~QuicheCircularDeque() { DestroyAndDeallocateAll(); }
301 
assign(size_type count,const T & value)302   void assign(size_type count, const T& value) {
303     ClearRetainCapacity();
304     reserve(count);
305     for (size_t i = 0; i < count; ++i) {
306       emplace_back(value);
307     }
308   }
309 
310   template <
311       class InputIt,
312       typename = std::enable_if_t<std::is_base_of<
313           std::input_iterator_tag,
314           typename std::iterator_traits<InputIt>::iterator_category>::value>>
assign(InputIt first,InputIt last)315   void assign(InputIt first, InputIt last) {
316     AssignRange(first, last);
317   }
318 
assign(std::initializer_list<T> ilist)319   void assign(std::initializer_list<T> ilist) {
320     assign(ilist.begin(), ilist.end());
321   }
322 
at(size_type pos)323   reference at(size_type pos) {
324     QUICHE_DCHECK(pos < size()) << "pos:" << pos << ", size():" << size();
325     size_type index = begin_ + pos;
326     if (index < data_capacity()) {
327       return *index_to_address(index);
328     }
329     return *index_to_address(index - data_capacity());
330   }
331 
at(size_type pos)332   const_reference at(size_type pos) const {
333     return const_cast<QuicheCircularDeque*>(this)->at(pos);
334   }
335 
336   reference operator[](size_type pos) { return at(pos); }
337 
338   const_reference operator[](size_type pos) const { return at(pos); }
339 
front()340   reference front() {
341     QUICHE_DCHECK(!empty());
342     return *index_to_address(begin_);
343   }
344 
front()345   const_reference front() const {
346     return const_cast<QuicheCircularDeque*>(this)->front();
347   }
348 
back()349   reference back() {
350     QUICHE_DCHECK(!empty());
351     return *(index_to_address(end_ == 0 ? data_capacity() - 1 : end_ - 1));
352   }
353 
back()354   const_reference back() const {
355     return const_cast<QuicheCircularDeque*>(this)->back();
356   }
357 
begin()358   iterator begin() { return iterator(this, begin_); }
begin()359   const_iterator begin() const { return const_iterator(this, begin_); }
cbegin()360   const_iterator cbegin() const { return const_iterator(this, begin_); }
361 
end()362   iterator end() { return iterator(this, end_); }
end()363   const_iterator end() const { return const_iterator(this, end_); }
cend()364   const_iterator cend() const { return const_iterator(this, end_); }
365 
rbegin()366   reverse_iterator rbegin() { return reverse_iterator(end()); }
rbegin()367   const_reverse_iterator rbegin() const {
368     return const_reverse_iterator(end());
369   }
crbegin()370   const_reverse_iterator crbegin() const { return rbegin(); }
371 
rend()372   reverse_iterator rend() { return reverse_iterator(begin()); }
rend()373   const_reverse_iterator rend() const {
374     return const_reverse_iterator(begin());
375   }
crend()376   const_reverse_iterator crend() const { return rend(); }
377 
capacity()378   size_type capacity() const {
379     return data_capacity() == 0 ? 0 : data_capacity() - 1;
380   }
381 
reserve(size_type new_cap)382   void reserve(size_type new_cap) {
383     if (new_cap > capacity()) {
384       Relocate(new_cap);
385     }
386   }
387 
388   // Remove all elements. Leave capacity unchanged.
clear()389   void clear() { ClearRetainCapacity(); }
390 
empty()391   bool empty() const { return begin_ == end_; }
392 
size()393   size_type size() const {
394     if (begin_ <= end_) {
395       return end_ - begin_;
396     }
397     return data_capacity() + end_ - begin_;
398   }
399 
resize(size_type count)400   void resize(size_type count) { ResizeInternal(count); }
401 
resize(size_type count,const value_type & value)402   void resize(size_type count, const value_type& value) {
403     ResizeInternal(count, value);
404   }
405 
push_front(const T & value)406   void push_front(const T& value) { emplace_front(value); }
push_front(T && value)407   void push_front(T&& value) { emplace_front(std::move(value)); }
408 
409   template <class... Args>
emplace_front(Args &&...args)410   reference emplace_front(Args&&... args) {
411     MaybeExpandCapacity(1);
412     begin_ = index_prev(begin_);
413     new (index_to_address(begin_)) T(std::forward<Args>(args)...);
414     return front();
415   }
416 
push_back(const T & value)417   void push_back(const T& value) { emplace_back(value); }
push_back(T && value)418   void push_back(T&& value) { emplace_back(std::move(value)); }
419 
420   template <class... Args>
emplace_back(Args &&...args)421   reference emplace_back(Args&&... args) {
422     MaybeExpandCapacity(1);
423     new (index_to_address(end_)) T(std::forward<Args>(args)...);
424     end_ = index_next(end_);
425     return back();
426   }
427 
pop_front()428   void pop_front() {
429     QUICHE_DCHECK(!empty());
430     DestroyByIndex(begin_);
431     begin_ = index_next(begin_);
432     MaybeShrinkCapacity();
433   }
434 
pop_front_n(size_type count)435   size_type pop_front_n(size_type count) {
436     size_type num_elements_to_pop = std::min(count, size());
437     size_type new_begin = index_increment_by(begin_, num_elements_to_pop);
438     DestroyRange(begin_, new_begin);
439     begin_ = new_begin;
440     MaybeShrinkCapacity();
441     return num_elements_to_pop;
442   }
443 
pop_back()444   void pop_back() {
445     QUICHE_DCHECK(!empty());
446     end_ = index_prev(end_);
447     DestroyByIndex(end_);
448     MaybeShrinkCapacity();
449   }
450 
pop_back_n(size_type count)451   size_type pop_back_n(size_type count) {
452     size_type num_elements_to_pop = std::min(count, size());
453     size_type new_end = index_increment_by(end_, -num_elements_to_pop);
454     DestroyRange(new_end, end_);
455     end_ = new_end;
456     MaybeShrinkCapacity();
457     return num_elements_to_pop;
458   }
459 
swap(QuicheCircularDeque & other)460   void swap(QuicheCircularDeque& other) {
461     using std::swap;
462     swap(begin_, other.begin_);
463     swap(end_, other.end_);
464 
465     if (AllocatorTraits::propagate_on_container_swap::value) {
466       swap(allocator_and_data_, other.allocator_and_data_);
467     } else {
468       // When propagate_on_container_swap is false, it is undefined behavior, by
469       // c++ standard, to swap between two AllocatorAwareContainer(s) with
470       // unequal allocators.
471       QUICHE_DCHECK(get_allocator() == other.get_allocator())
472           << "Undefined swap behavior";
473       swap(allocator_and_data_.data, other.allocator_and_data_.data);
474       swap(allocator_and_data_.data_capacity,
475            other.allocator_and_data_.data_capacity);
476     }
477   }
478 
swap(QuicheCircularDeque & lhs,QuicheCircularDeque & rhs)479   friend void swap(QuicheCircularDeque& lhs, QuicheCircularDeque& rhs) {
480     lhs.swap(rhs);
481   }
482 
get_allocator()483   allocator_type get_allocator() const {
484     return allocator_and_data_.allocator();
485   }
486 
487   friend bool operator==(const QuicheCircularDeque& lhs,
488                          const QuicheCircularDeque& rhs) {
489     return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
490   }
491 
492   friend bool operator!=(const QuicheCircularDeque& lhs,
493                          const QuicheCircularDeque& rhs) {
494     return !(lhs == rhs);
495   }
496 
497   friend QUICHE_NO_EXPORT std::ostream& operator<<(
498       std::ostream& os, const QuicheCircularDeque& dq) {
499     os << "{";
500     for (size_type pos = 0; pos != dq.size(); ++pos) {
501       if (pos != 0) {
502         os << ",";
503       }
504       os << " " << dq[pos];
505     }
506     os << " }";
507     return os;
508   }
509 
510  private:
MoveRetainAllocator(QuicheCircularDeque && other)511   void MoveRetainAllocator(QuicheCircularDeque&& other) {
512     if (get_allocator() == other.get_allocator()) {
513       // Take over the storage of "other", with which we share an allocator.
514       DestroyAndDeallocateAll();
515 
516       begin_ = other.begin_;
517       end_ = other.end_;
518       allocator_and_data_.data = other.allocator_and_data_.data;
519       allocator_and_data_.data_capacity =
520           other.allocator_and_data_.data_capacity;
521 
522       other.begin_ = other.end_ = 0;
523       other.allocator_and_data_.data = nullptr;
524       other.allocator_and_data_.data_capacity = 0;
525     } else {
526       // We cannot take over of the storage from "other", since it has a
527       // different allocator; we're stuck move-assigning elements individually.
528       ClearRetainCapacity();
529       for (auto& elem : other) {
530         push_back(std::move(elem));
531       }
532       other.clear();
533     }
534   }
535 
536   template <
537       typename InputIt,
538       typename = std::enable_if_t<std::is_base_of<
539           std::input_iterator_tag,
540           typename std::iterator_traits<InputIt>::iterator_category>::value>>
AssignRange(InputIt first,InputIt last)541   void AssignRange(InputIt first, InputIt last) {
542     ClearRetainCapacity();
543     if (std::is_base_of<
544             std::random_access_iterator_tag,
545             typename std::iterator_traits<InputIt>::iterator_category>::value) {
546       reserve(std::distance(first, last));
547     }
548     for (; first != last; ++first) {
549       emplace_back(*first);
550     }
551   }
552 
553   // WARNING: begin_, end_ and allocator_and_data_ are not modified.
DestroyAndDeallocateAll()554   void DestroyAndDeallocateAll() {
555     DestroyRange(begin_, end_);
556 
557     if (data_capacity() > 0) {
558       QUICHE_DCHECK_NE(nullptr, allocator_and_data_.data);
559       AllocatorTraits::deallocate(allocator_and_data_.allocator(),
560                                   allocator_and_data_.data, data_capacity());
561     }
562   }
563 
ClearRetainCapacity()564   void ClearRetainCapacity() {
565     DestroyRange(begin_, end_);
566     begin_ = end_ = 0;
567   }
568 
MaybeShrinkCapacity()569   void MaybeShrinkCapacity() {
570     // TODO(wub): Implement a storage policy that actually shrinks.
571   }
572 
MaybeExpandCapacity(size_t num_additional_elements)573   void MaybeExpandCapacity(size_t num_additional_elements) {
574     size_t new_size = size() + num_additional_elements;
575     if (capacity() >= new_size) {
576       return;
577     }
578 
579     // The minimum amount of additional capacity to grow.
580     size_t min_additional_capacity =
581         std::max(MinCapacityIncrement, capacity() / 4);
582     size_t new_capacity =
583         std::max(new_size, capacity() + min_additional_capacity);
584 
585     Relocate(new_capacity);
586   }
587 
Relocate(size_t new_capacity)588   void Relocate(size_t new_capacity) {
589     const size_t num_elements = size();
590     QUICHE_DCHECK_GT(new_capacity, num_elements)
591         << "new_capacity:" << new_capacity << ", num_elements:" << num_elements;
592 
593     size_t new_data_capacity = new_capacity + 1;
594     pointer new_data = AllocatorTraits::allocate(
595         allocator_and_data_.allocator(), new_data_capacity);
596 
597     if (begin_ < end_) {
598       // Not wrapped.
599       RelocateUnwrappedRange(begin_, end_, new_data);
600     } else if (begin_ > end_) {
601       // Wrapped.
602       const size_t num_elements_before_wrap = data_capacity() - begin_;
603       RelocateUnwrappedRange(begin_, data_capacity(), new_data);
604       RelocateUnwrappedRange(0, end_, new_data + num_elements_before_wrap);
605     }
606 
607     if (data_capacity()) {
608       AllocatorTraits::deallocate(allocator_and_data_.allocator(),
609                                   allocator_and_data_.data, data_capacity());
610     }
611 
612     allocator_and_data_.data = new_data;
613     allocator_and_data_.data_capacity = new_data_capacity;
614     begin_ = 0;
615     end_ = num_elements;
616   }
617 
618   template <typename T_ = T>
619   typename std::enable_if<std::is_trivially_copyable<T_>::value, void>::type
RelocateUnwrappedRange(size_type begin,size_type end,pointer dest)620   RelocateUnwrappedRange(size_type begin, size_type end, pointer dest) const {
621     QUICHE_DCHECK_LE(begin, end) << "begin:" << begin << ", end:" << end;
622     pointer src = index_to_address(begin);
623     QUICHE_DCHECK_NE(src, nullptr);
624     memcpy(dest, src, sizeof(T) * (end - begin));
625     DestroyRange(begin, end);
626   }
627 
628   template <typename T_ = T>
629   typename std::enable_if<!std::is_trivially_copyable<T_>::value &&
630                               std::is_move_constructible<T_>::value,
631                           void>::type
RelocateUnwrappedRange(size_type begin,size_type end,pointer dest)632   RelocateUnwrappedRange(size_type begin, size_type end, pointer dest) const {
633     QUICHE_DCHECK_LE(begin, end) << "begin:" << begin << ", end:" << end;
634     pointer src = index_to_address(begin);
635     pointer src_end = index_to_address(end);
636     while (src != src_end) {
637       new (dest) T(std::move(*src));
638       DestroyByAddress(src);
639       ++dest;
640       ++src;
641     }
642   }
643 
644   template <typename T_ = T>
645   typename std::enable_if<!std::is_trivially_copyable<T_>::value &&
646                               !std::is_move_constructible<T_>::value,
647                           void>::type
RelocateUnwrappedRange(size_type begin,size_type end,pointer dest)648   RelocateUnwrappedRange(size_type begin, size_type end, pointer dest) const {
649     QUICHE_DCHECK_LE(begin, end) << "begin:" << begin << ", end:" << end;
650     pointer src = index_to_address(begin);
651     pointer src_end = index_to_address(end);
652     while (src != src_end) {
653       new (dest) T(*src);
654       DestroyByAddress(src);
655       ++dest;
656       ++src;
657     }
658   }
659 
660   template <class... U>
ResizeInternal(size_type count,U &&...u)661   void ResizeInternal(size_type count, U&&... u) {
662     if (count > size()) {
663       // Expanding.
664       MaybeExpandCapacity(count - size());
665       while (size() < count) {
666         emplace_back(std::forward<U>(u)...);
667       }
668     } else {
669       // Most likely shrinking. No-op if count == size().
670       size_type new_end = (begin_ + count) % data_capacity();
671       DestroyRange(new_end, end_);
672       end_ = new_end;
673 
674       MaybeShrinkCapacity();
675     }
676   }
677 
DestroyRange(size_type begin,size_type end)678   void DestroyRange(size_type begin, size_type end) const {
679     if (std::is_trivially_destructible<T>::value) {
680       return;
681     }
682     if (end >= begin) {
683       DestroyUnwrappedRange(begin, end);
684     } else {
685       DestroyUnwrappedRange(begin, data_capacity());
686       DestroyUnwrappedRange(0, end);
687     }
688   }
689 
690   // Should only be called from DestroyRange.
DestroyUnwrappedRange(size_type begin,size_type end)691   void DestroyUnwrappedRange(size_type begin, size_type end) const {
692     QUICHE_DCHECK_LE(begin, end) << "begin:" << begin << ", end:" << end;
693     for (; begin != end; ++begin) {
694       DestroyByIndex(begin);
695     }
696   }
697 
DestroyByIndex(size_type index)698   void DestroyByIndex(size_type index) const {
699     DestroyByAddress(index_to_address(index));
700   }
701 
DestroyByAddress(pointer address)702   void DestroyByAddress(pointer address) const {
703     if (std::is_trivially_destructible<T>::value) {
704       return;
705     }
706     address->~T();
707   }
708 
data_capacity()709   size_type data_capacity() const { return allocator_and_data_.data_capacity; }
710 
index_to_address(size_type index)711   pointer index_to_address(size_type index) const {
712     return allocator_and_data_.data + index;
713   }
714 
index_prev(size_type index)715   size_type index_prev(size_type index) const {
716     return index == 0 ? data_capacity() - 1 : index - 1;
717   }
718 
index_next(size_type index)719   size_type index_next(size_type index) const {
720     return index == data_capacity() - 1 ? 0 : index + 1;
721   }
722 
index_increment_by(size_type index,difference_type delta)723   size_type index_increment_by(size_type index, difference_type delta) const {
724     if (delta == 0) {
725       return index;
726     }
727 
728     QUICHE_DCHECK_LT(static_cast<size_type>(std::abs(delta)), data_capacity());
729     return (index + data_capacity() + delta) % data_capacity();
730   }
731 
732   // Empty base-class optimization: bundle storage for our allocator together
733   // with the fields we had to store anyway, via inheriting from the allocator,
734   // so this allocator instance doesn't consume any storage when its type has no
735   // data members.
736   struct QUICHE_NO_EXPORT AllocatorAndData : private allocator_type {
AllocatorAndDataAllocatorAndData737     explicit AllocatorAndData(const allocator_type& alloc)
738         : allocator_type(alloc) {}
739 
allocatorAllocatorAndData740     const allocator_type& allocator() const { return *this; }
allocatorAllocatorAndData741     allocator_type& allocator() { return *this; }
742 
743     pointer data = nullptr;
744     size_type data_capacity = 0;
745   };
746 
747   size_type begin_ = 0;
748   size_type end_ = 0;
749   AllocatorAndData allocator_and_data_;
750 };
751 
752 }  // namespace quiche
753 
754 #endif  // QUICHE_COMMON_QUICHE_CIRCULAR_DEQUE_H_
755