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