OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2017 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 NET_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_ |
| 6 #define NET_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_ |
| 7 |
| 8 #include <deque> |
| 9 |
| 10 #include "base/logging.h" |
| 11 #include "net/quic/core/quic_types.h" |
| 12 |
| 13 namespace net { |
| 14 |
| 15 // PacketNumberIndexedQueue is a queue of mostly continuous numbered entries |
| 16 // which supports the following operations: |
| 17 // - adding elements to the end of the queue, or at some point past the end |
| 18 // - removing elements in any order |
| 19 // - retrieving elements |
| 20 // If all elements are inserted in order, all of the operations above are |
| 21 // amortized O(1) time. |
| 22 // |
| 23 // Internally, the data structure is a deque where each element is marked as |
| 24 // present or not. The deque starts at the lowest present index. Whenever an |
| 25 // element is removed, it's marked as not present, and the front of the deque is |
| 26 // cleared of elements that are not present. |
| 27 // |
| 28 // The tail of the queue is not cleared due to the assumption of entries being |
| 29 // inserted in order, though removing all elements of the queue will return it |
| 30 // to its initial state. |
| 31 // |
| 32 // Note that this data structure is inherently hazardous, since an addition of |
| 33 // just two entries will cause it to consume all of the memory available. |
| 34 // Because of that, it is not a general-purpose container and should not be used |
| 35 // as one. |
| 36 template <typename T> |
| 37 class PacketNumberIndexedQueue { |
| 38 public: |
| 39 PacketNumberIndexedQueue() |
| 40 : number_of_present_entries_(0), first_packet_(0) {} |
| 41 |
| 42 // Retrieve the entry associated with the packet number. Returns the pointer |
| 43 // to the entry in case of success, or nullptr if the entry does not exist. |
| 44 T* GetEntry(QuicPacketNumber packet_number); |
| 45 |
| 46 // Inserts data associated |packet_number| into (or past) the end of the |
| 47 // queue, filling up the missing intermediate entries as necessary. Returns |
| 48 // true if the element has been inserted successfully, false if it was already |
| 49 // in the queue or inserted out of order. |
| 50 template <typename... Args> |
| 51 bool Emplace(QuicPacketNumber packet_number, Args&&... args); |
| 52 |
| 53 // Removes data associated with |packet_number| and frees the slots in the |
| 54 // queue as necessary. |
| 55 bool Remove(QuicPacketNumber packet_number); |
| 56 |
| 57 bool IsEmpty() const { return number_of_present_entries_ == 0; } |
| 58 |
| 59 // Returns the number of entries in the queue. |
| 60 size_t number_of_present_entries() const { |
| 61 return number_of_present_entries_; |
| 62 } |
| 63 |
| 64 // Returns the number of entries allocated in the underlying deque. This is |
| 65 // proportional to the memory usage of the queue. |
| 66 size_t entry_slots_used() const { return entries_.size(); } |
| 67 |
| 68 // Packet number of the first entry in the queue. Zero if the queue is empty. |
| 69 size_t first_packet() const { return first_packet_; } |
| 70 |
| 71 // Packet number of the last entry ever inserted in the queue. Note that the |
| 72 // entry in question may have already been removed. Zero if the queue is |
| 73 // empty. |
| 74 size_t last_packet() const { |
| 75 if (IsEmpty()) { |
| 76 return 0; |
| 77 } |
| 78 return first_packet_ + entries_.size() - 1; |
| 79 } |
| 80 |
| 81 private: |
| 82 // Wrapper around T used to mark whether the entry is actually in the map. |
| 83 struct EntryWrapper { |
| 84 T data; |
| 85 bool present; |
| 86 |
| 87 EntryWrapper() : data(), present(false) {} |
| 88 |
| 89 template <typename... Args> |
| 90 explicit EntryWrapper(Args&&... args) |
| 91 : data(std::forward<Args>(args)...), present(true) {} |
| 92 }; |
| 93 |
| 94 // Cleans up unused slots in the front after removing an element. |
| 95 void Cleanup(); |
| 96 |
| 97 EntryWrapper* GetEntryWrapper(QuicPacketNumber offset); |
| 98 |
| 99 std::deque<EntryWrapper> entries_; |
| 100 size_t number_of_present_entries_; |
| 101 QuicPacketNumber first_packet_; |
| 102 }; |
| 103 |
| 104 template <typename T> |
| 105 T* PacketNumberIndexedQueue<T>::GetEntry(QuicPacketNumber packet_number) { |
| 106 EntryWrapper* entry = GetEntryWrapper(packet_number); |
| 107 if (entry == nullptr) { |
| 108 return nullptr; |
| 109 } |
| 110 return &entry->data; |
| 111 } |
| 112 |
| 113 template <typename T> |
| 114 template <typename... Args> |
| 115 bool PacketNumberIndexedQueue<T>::Emplace(QuicPacketNumber packet_number, |
| 116 Args&&... args) { |
| 117 if (IsEmpty()) { |
| 118 DCHECK(entries_.empty()); |
| 119 DCHECK_EQ(0u, first_packet_); |
| 120 |
| 121 entries_.emplace_back(std::forward<Args>(args)...); |
| 122 number_of_present_entries_ = 1; |
| 123 first_packet_ = packet_number; |
| 124 return true; |
| 125 } |
| 126 |
| 127 // Do not allow insertion out-of-order. |
| 128 if (packet_number <= last_packet()) { |
| 129 return false; |
| 130 } |
| 131 |
| 132 // Handle potentially missing elements. |
| 133 size_t offset = packet_number - first_packet_; |
| 134 if (offset > entries_.size()) { |
| 135 entries_.resize(offset); |
| 136 } |
| 137 |
| 138 number_of_present_entries_++; |
| 139 entries_.emplace_back(std::forward<Args>(args)...); |
| 140 DCHECK_EQ(packet_number, last_packet()); |
| 141 return true; |
| 142 } |
| 143 |
| 144 template <typename T> |
| 145 bool PacketNumberIndexedQueue<T>::Remove(QuicPacketNumber packet_number) { |
| 146 EntryWrapper* entry = GetEntryWrapper(packet_number); |
| 147 if (entry == nullptr) { |
| 148 return false; |
| 149 } |
| 150 entry->present = false; |
| 151 number_of_present_entries_--; |
| 152 |
| 153 if (packet_number == first_packet()) { |
| 154 Cleanup(); |
| 155 } |
| 156 return true; |
| 157 } |
| 158 |
| 159 template <typename T> |
| 160 void PacketNumberIndexedQueue<T>::Cleanup() { |
| 161 while (!entries_.empty() && !entries_.front().present) { |
| 162 entries_.pop_front(); |
| 163 first_packet_++; |
| 164 } |
| 165 if (entries_.empty()) { |
| 166 first_packet_ = 0; |
| 167 } |
| 168 } |
| 169 |
| 170 template <typename T> |
| 171 auto PacketNumberIndexedQueue<T>::GetEntryWrapper(QuicPacketNumber offset) |
| 172 -> EntryWrapper* { |
| 173 if (offset < first_packet_) { |
| 174 return nullptr; |
| 175 } |
| 176 |
| 177 offset -= first_packet_; |
| 178 if (offset >= entries_.size()) { |
| 179 return nullptr; |
| 180 } |
| 181 |
| 182 EntryWrapper* entry = &entries_[offset]; |
| 183 if (!entry->present) { |
| 184 return nullptr; |
| 185 } |
| 186 |
| 187 return entry; |
| 188 } |
| 189 |
| 190 } // namespace net |
| 191 |
| 192 #endif // NET_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_ |
OLD | NEW |