blob: 695ff7d91ecd5604e9a13c3573ff1de6cebe18a5 [file] [log] [blame]
QUICHE teama6ef0a62019-03-07 20:34:33 -05001// 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 QUICHE_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_
6#define QUICHE_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_
7
8#include "net/third_party/quiche/src/quic/core/quic_constants.h"
wub254545c2019-04-04 13:56:52 -07009#include "net/third_party/quiche/src/quic/core/quic_packet_number.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050010#include "net/third_party/quiche/src/quic/core/quic_types.h"
11#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
12#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
13
14namespace quic {
15
16// PacketNumberIndexedQueue is a queue of mostly continuous numbered entries
17// which supports the following operations:
18// - adding elements to the end of the queue, or at some point past the end
19// - removing elements in any order
20// - retrieving elements
21// If all elements are inserted in order, all of the operations above are
22// amortized O(1) time.
23//
24// Internally, the data structure is a deque where each element is marked as
25// present or not. The deque starts at the lowest present index. Whenever an
26// element is removed, it's marked as not present, and the front of the deque is
27// cleared of elements that are not present.
28//
29// The tail of the queue is not cleared due to the assumption of entries being
30// inserted in order, though removing all elements of the queue will return it
31// to its initial state.
32//
33// Note that this data structure is inherently hazardous, since an addition of
34// just two entries will cause it to consume all of the memory available.
35// Because of that, it is not a general-purpose container and should not be used
36// as one.
37template <typename T>
38class PacketNumberIndexedQueue {
39 public:
40 PacketNumberIndexedQueue() : number_of_present_entries_(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 const T* GetEntry(QuicPacketNumber packet_number) const;
46
47 // Inserts data associated |packet_number| into (or past) the end of the
48 // queue, filling up the missing intermediate entries as necessary. Returns
49 // true if the element has been inserted successfully, false if it was already
50 // in the queue or inserted out of order.
51 template <typename... Args>
52 bool Emplace(QuicPacketNumber packet_number, Args&&... args);
53
54 // Removes data associated with |packet_number| and frees the slots in the
55 // queue as necessary.
56 bool Remove(QuicPacketNumber packet_number);
57
wub254545c2019-04-04 13:56:52 -070058 // Same as above, but if an entry is present in the queue, also call f(entry)
59 // before removing it.
60 template <typename Function>
61 bool Remove(QuicPacketNumber packet_number, Function f);
62
QUICHE teama6ef0a62019-03-07 20:34:33 -050063 bool IsEmpty() const { return number_of_present_entries_ == 0; }
64
65 // Returns the number of entries in the queue.
66 size_t number_of_present_entries() const {
67 return number_of_present_entries_;
68 }
69
70 // Returns the number of entries allocated in the underlying deque. This is
71 // proportional to the memory usage of the queue.
72 size_t entry_slots_used() const { return entries_.size(); }
73
74 // Packet number of the first entry in the queue.
75 QuicPacketNumber first_packet() const { return first_packet_; }
76
77 // Packet number of the last entry ever inserted in the queue. Note that the
78 // entry in question may have already been removed. Zero if the queue is
79 // empty.
80 QuicPacketNumber last_packet() const {
81 if (IsEmpty()) {
82 return QuicPacketNumber();
83 }
84 return first_packet_ + entries_.size() - 1;
85 }
86
87 private:
88 // Wrapper around T used to mark whether the entry is actually in the map.
89 struct EntryWrapper : T {
90 bool present;
91
92 EntryWrapper() : present(false) {}
93
94 template <typename... Args>
95 explicit EntryWrapper(Args&&... args)
96 : T(std::forward<Args>(args)...), present(true) {}
97 };
98
99 // Cleans up unused slots in the front after removing an element.
100 void Cleanup();
101
102 const EntryWrapper* GetEntryWrapper(QuicPacketNumber offset) const;
103 EntryWrapper* GetEntryWrapper(QuicPacketNumber offset) {
104 const auto* const_this = this;
105 return const_cast<EntryWrapper*>(const_this->GetEntryWrapper(offset));
106 }
107
108 QuicDeque<EntryWrapper> entries_;
109 size_t number_of_present_entries_;
110 QuicPacketNumber first_packet_;
111};
112
113template <typename T>
114T* PacketNumberIndexedQueue<T>::GetEntry(QuicPacketNumber packet_number) {
115 EntryWrapper* entry = GetEntryWrapper(packet_number);
116 if (entry == nullptr) {
117 return nullptr;
118 }
119 return entry;
120}
121
122template <typename T>
123const T* PacketNumberIndexedQueue<T>::GetEntry(
124 QuicPacketNumber packet_number) const {
125 const EntryWrapper* entry = GetEntryWrapper(packet_number);
126 if (entry == nullptr) {
127 return nullptr;
128 }
129 return entry;
130}
131
132template <typename T>
133template <typename... Args>
134bool PacketNumberIndexedQueue<T>::Emplace(QuicPacketNumber packet_number,
135 Args&&... args) {
136 if (!packet_number.IsInitialized()) {
137 QUIC_BUG << "Try to insert an uninitialized packet number";
138 return false;
139 }
140
141 if (IsEmpty()) {
142 DCHECK(entries_.empty());
143 DCHECK(!first_packet_.IsInitialized());
144
145 entries_.emplace_back(std::forward<Args>(args)...);
146 number_of_present_entries_ = 1;
147 first_packet_ = packet_number;
148 return true;
149 }
150
151 // Do not allow insertion out-of-order.
152 if (packet_number <= last_packet()) {
153 return false;
154 }
155
156 // Handle potentially missing elements.
157 size_t offset = packet_number - first_packet_;
158 if (offset > entries_.size()) {
159 entries_.resize(offset);
160 }
161
162 number_of_present_entries_++;
163 entries_.emplace_back(std::forward<Args>(args)...);
164 DCHECK_EQ(packet_number, last_packet());
165 return true;
166}
167
168template <typename T>
169bool PacketNumberIndexedQueue<T>::Remove(QuicPacketNumber packet_number) {
wub254545c2019-04-04 13:56:52 -0700170 return Remove(packet_number, [](const T&) {});
171}
172
173template <typename T>
174template <typename Function>
175bool PacketNumberIndexedQueue<T>::Remove(QuicPacketNumber packet_number,
176 Function f) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500177 EntryWrapper* entry = GetEntryWrapper(packet_number);
178 if (entry == nullptr) {
179 return false;
180 }
wub254545c2019-04-04 13:56:52 -0700181 f(*static_cast<const T*>(entry));
QUICHE teama6ef0a62019-03-07 20:34:33 -0500182 entry->present = false;
183 number_of_present_entries_--;
184
185 if (packet_number == first_packet()) {
186 Cleanup();
187 }
188 return true;
189}
190
191template <typename T>
192void PacketNumberIndexedQueue<T>::Cleanup() {
193 while (!entries_.empty() && !entries_.front().present) {
194 entries_.pop_front();
195 first_packet_++;
196 }
197 if (entries_.empty()) {
198 first_packet_.Clear();
199 }
200}
201
202template <typename T>
203auto PacketNumberIndexedQueue<T>::GetEntryWrapper(
204 QuicPacketNumber packet_number) const -> const EntryWrapper* {
205 if (!packet_number.IsInitialized() || IsEmpty() ||
206 packet_number < first_packet_) {
207 return nullptr;
208 }
209
210 uint64_t offset = packet_number - first_packet_;
211 if (offset >= entries_.size()) {
212 return nullptr;
213 }
214
215 const EntryWrapper* entry = &entries_[offset];
216 if (!entry->present) {
217 return nullptr;
218 }
219
220 return entry;
221}
222
223} // namespace quic
224
225#endif // QUICHE_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_