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_QUIC_CORE_QUIC_INTERVAL_DEQUE_H_
6 #define QUICHE_QUIC_CORE_QUIC_INTERVAL_DEQUE_H_
7
8 #include <algorithm>
9 #include <optional>
10
11 #include "quiche/quic/core/quic_interval.h"
12 #include "quiche/quic/core/quic_types.h"
13 #include "quiche/quic/platform/api/quic_bug_tracker.h"
14 #include "quiche/quic/platform/api/quic_export.h"
15 #include "quiche/quic/platform/api/quic_logging.h"
16 #include "quiche/common/quiche_circular_deque.h"
17
18 namespace quic {
19
20 namespace test {
21 class QuicIntervalDequePeer;
22 } // namespace test
23
24 // QuicIntervalDeque<T, C> is a templated wrapper container, wrapping random
25 // access data structures. The wrapper allows items to be added to the
26 // underlying container with intervals associated with each item. The intervals
27 // _should_ be added already sorted and represent searchable indices. The search
28 // is optimized for sequential usage.
29 //
30 // As the intervals are to be searched sequentially the search for the next
31 // interval can be achieved in O(1), by simply remembering the last interval
32 // consumed. The structure also checks for an "off-by-one" use case wherein the
33 // |cached_index_| is off by one index as the caller didn't call operator |++|
34 // to increment the index. Other intervals can be found in O(log(n)) as they are
35 // binary searchable. A use case for this structure is packet buffering: Packets
36 // are sent sequentially but can sometimes needed for retransmission. The
37 // packets and their payloads are added with associated intervals representing
38 // data ranges they carry. When a user asks for a particular interval it's very
39 // likely they are requesting the next sequential interval, receiving it in O(1)
40 // time. Updating the sequential index is done automatically through the
41 // |DataAt| method and its iterator operator |++|.
42 //
43 // The intervals are represented using the usual C++ STL convention, namely as
44 // the half-open QuicInterval [min, max). A point p is considered to be
45 // contained in the QuicInterval iff p >= min && p < max. One consequence of
46 // this definition is that for any non-empty QuicInterval, min is contained in
47 // the QuicInterval but max is not. There is no canonical representation for the
48 // empty QuicInterval; and empty intervals are forbidden from being added to
49 // this container as they would be unsearchable.
50 //
51 // The type T is required to be copy-constructable or move-constructable. The
52 // type T is also expected to have an |interval()| method returning a
53 // QuicInterval<std::size> for the particular value. The type C is required to
54 // be a random access container supporting the methods |pop_front|, |push_back|,
55 // |operator[]|, |size|, and iterator support for |std::lower_bound| eg. a
56 // |deque| or |vector|.
57 //
58 // The QuicIntervalDeque<T, C>, like other C++ STL random access containers,
59 // doesn't have any explicit support for any equality operators.
60 //
61 //
62 // Examples with internal state:
63 //
64 // // An example class to be stored inside the Interval Deque.
65 // struct IntervalVal {
66 // const int32_t val;
67 // const size_t interval_begin, interval_end;
68 // QuicInterval<size_t> interval();
69 // };
70 // typedef IntervalVal IV;
71 // QuicIntervialDeque<IntervalValue> deque;
72 //
73 // // State:
74 // // cached_index -> None
75 // // container -> {}
76 //
77 // // Add interval items
78 // deque.PushBack(IV(val: 0, interval_begin: 0, interval_end: 10));
79 // deque.PushBack(IV(val: 1, interval_begin: 20, interval_end: 25));
80 // deque.PushBack(IV(val: 2, interval_begin: 25, interval_end: 30));
81 //
82 // // State:
83 // // cached_index -> 0
84 // // container -> {{0, [0, 10)}, {1, [20, 25)}, {2, [25, 30)}}
85 //
86 // // Look for 0 and return [0, 10). Time: O(1)
87 // auto it = deque.DataAt(0);
88 // assert(it->val == 0);
89 // it++; // Increment and move the |cached_index_| over [0, 10) to [20, 25).
90 // assert(it->val == 1);
91 //
92 // // State:
93 // // cached_index -> 1
94 // // container -> {{0, [0, 10)}, {1, [20, 25)}, {2, [25, 30)}}
95 //
96 // // Look for 20 and return [20, 25). Time: O(1)
97 // auto it = deque.DataAt(20); // |cached_index_| remains unchanged.
98 // assert(it->val == 1);
99 //
100 // // State:
101 // // cached_index -> 1
102 // // container -> {{0, [0, 10)}, {1, [20, 25)}, {2, [25, 30)}}
103 //
104 // // Look for 15 and return deque.DataEnd(). Time: O(log(n))
105 // auto it = deque.DataAt(15); // |cached_index_| remains unchanged.
106 // assert(it == deque.DataEnd());
107 //
108 // // Look for 25 and return [25, 30). Time: O(1) with off-by-one.
109 // auto it = deque.DataAt(25); // |cached_index_| is updated to 2.
110 // assert(it->val == 2);
111 // it++; // |cached_index_| is set to |None| as all data has been iterated.
112 //
113 //
114 // // State:
115 // // cached_index -> None
116 // // container -> {{0, [0, 10)}, {1, [20, 25)}, {2, [25, 30)}}
117 //
118 // // Look again for 0 and return [0, 10). Time: O(log(n))
119 // auto it = deque.DataAt(0);
120 //
121 //
122 // deque.PopFront(); // Pop -> {0, [0, 10)}
123 //
124 // // State:
125 // // cached_index -> None
126 // // container -> {{1, [20, 25)}, {2, [25, 30)}}
127 //
128 // deque.PopFront(); // Pop -> {1, [20, 25)}
129 //
130 // // State:
131 // // cached_index -> None
132 // // container -> {{2, [25, 30)}}
133 //
134 // deque.PushBack(IV(val: 3, interval_begin: 35, interval_end: 50));
135 //
136 // // State:
137 // // cached_index -> 1
138 // // container -> {{2, [25, 30)}, {3, [35, 50)}}
139
140 template <class T, class C = quiche::QuicheCircularDeque<T>>
141 class QUICHE_NO_EXPORT QuicIntervalDeque {
142 public:
143 class QUICHE_NO_EXPORT Iterator {
144 public:
145 // Used by |std::lower_bound|
146 using iterator_category = std::forward_iterator_tag;
147 using value_type = T;
148 using difference_type = std::ptrdiff_t;
149 using pointer = T*;
150 using reference = T&;
151
152 // Every increment of the iterator will increment the |cached_index_| if
153 // |index_| is larger than the current |cached_index_|. |index_| is bounded
154 // at |deque_.size()| and any attempt to increment above that will be
155 // ignored. Once an iterator has iterated all elements the |cached_index_|
156 // will be reset.
Iterator(std::size_t index,QuicIntervalDeque * deque)157 Iterator(std::size_t index, QuicIntervalDeque* deque)
158 : index_(index), deque_(deque) {}
159 // Only the ++ operator attempts to update the cached index. Other operators
160 // are used by |lower_bound| to binary search and are thus private.
161 Iterator& operator++() {
162 // Don't increment when we are at the end.
163 const std::size_t container_size = deque_->container_.size();
164 if (index_ >= container_size) {
165 QUIC_BUG(quic_bug_10862_1) << "Iterator out of bounds.";
166 return *this;
167 }
168 index_++;
169 if (deque_->cached_index_.has_value()) {
170 const std::size_t cached_index = *deque_->cached_index_;
171 // If all items are iterated then reset the |cached_index_|
172 if (index_ == container_size) {
173 deque_->cached_index_.reset();
174 } else {
175 // Otherwise the new |cached_index_| is the max of itself and |index_|
176 if (cached_index < index_) {
177 deque_->cached_index_ = index_;
178 }
179 }
180 }
181 return *this;
182 }
183 Iterator operator++(int) {
184 Iterator copy = *this;
185 ++(*this);
186 return copy;
187 }
188 reference operator*() { return deque_->container_[index_]; }
189 reference operator*() const { return deque_->container_[index_]; }
190 pointer operator->() { return &deque_->container_[index_]; }
191 bool operator==(const Iterator& rhs) const {
192 return index_ == rhs.index_ && deque_ == rhs.deque_;
193 }
194 bool operator!=(const Iterator& rhs) const { return !(*this == rhs); }
195
196 private:
197 // A set of private operators for |std::lower_bound|
198 Iterator operator+(difference_type amount) const {
199 Iterator copy = *this;
200 copy.index_ += amount;
201 QUICHE_DCHECK(copy.index_ < copy.deque_->Size());
202 return copy;
203 }
204 Iterator& operator+=(difference_type amount) {
205 index_ += amount;
206 QUICHE_DCHECK(index_ < deque_->Size());
207 return *this;
208 }
209 difference_type operator-(const Iterator& rhs) const {
210 return static_cast<difference_type>(index_) -
211 static_cast<difference_type>(rhs.index_);
212 }
213
214 // |index_| is the index of the item in |*deque_|.
215 std::size_t index_;
216 // |deque_| is a pointer to the container the iterator came from.
217 QuicIntervalDeque* deque_;
218
219 friend class QuicIntervalDeque;
220 };
221
222 QuicIntervalDeque();
223
224 // Adds an item to the underlying container. The |item|'s interval _should_ be
225 // strictly greater than the last interval added.
226 void PushBack(T&& item);
227 void PushBack(const T& item);
228 // Removes the front/top of the underlying container and the associated
229 // interval.
230 void PopFront();
231 // Returns an iterator to the beginning of the data. The iterator will move
232 // the |cached_index_| as the iterator moves.
233 Iterator DataBegin();
234 // Returns an iterator to the end of the data.
235 Iterator DataEnd();
236 // Returns an iterator pointing to the item in |interval_begin|. The iterator
237 // will move the |cached_index_| as the iterator moves.
238 Iterator DataAt(const std::size_t interval_begin);
239
240 // Returns the number of items contained inside the structure.
241 std::size_t Size() const;
242 // Returns whether the structure is empty.
243 bool Empty() const;
244
245 private:
246 struct QUICHE_NO_EXPORT IntervalCompare {
operatorIntervalCompare247 bool operator()(const T& item, std::size_t interval_begin) const {
248 return item.interval().max() <= interval_begin;
249 }
250 };
251
252 template <class U>
253 void PushBackUniversal(U&& item);
254
255 Iterator Search(const std::size_t interval_begin,
256 const std::size_t begin_index, const std::size_t end_index);
257
258 // For accessing the |cached_index_|
259 friend class test::QuicIntervalDequePeer;
260
261 C container_;
262 std::optional<std::size_t> cached_index_;
263 };
264
265 template <class T, class C>
QuicIntervalDeque()266 QuicIntervalDeque<T, C>::QuicIntervalDeque() {}
267
268 template <class T, class C>
PushBack(T && item)269 void QuicIntervalDeque<T, C>::PushBack(T&& item) {
270 PushBackUniversal(std::move(item));
271 }
272
273 template <class T, class C>
PushBack(const T & item)274 void QuicIntervalDeque<T, C>::PushBack(const T& item) {
275 PushBackUniversal(item);
276 }
277
278 template <class T, class C>
PopFront()279 void QuicIntervalDeque<T, C>::PopFront() {
280 if (container_.size() == 0) {
281 QUIC_BUG(quic_bug_10862_2) << "Trying to pop from an empty container.";
282 return;
283 }
284 container_.pop_front();
285 if (container_.size() == 0) {
286 cached_index_.reset();
287 }
288 if (cached_index_.value_or(0) > 0) {
289 cached_index_ = *cached_index_ - 1;
290 }
291 }
292
293 template <class T, class C>
294 typename QuicIntervalDeque<T, C>::Iterator
DataBegin()295 QuicIntervalDeque<T, C>::DataBegin() {
296 return Iterator(0, this);
297 }
298
299 template <class T, class C>
DataEnd()300 typename QuicIntervalDeque<T, C>::Iterator QuicIntervalDeque<T, C>::DataEnd() {
301 return Iterator(container_.size(), this);
302 }
303
304 template <class T, class C>
DataAt(const std::size_t interval_begin)305 typename QuicIntervalDeque<T, C>::Iterator QuicIntervalDeque<T, C>::DataAt(
306 const std::size_t interval_begin) {
307 // No |cached_index_| value means all items can be searched.
308 if (!cached_index_.has_value()) {
309 return Search(interval_begin, 0, container_.size());
310 }
311
312 const std::size_t cached_index = *cached_index_;
313 QUICHE_DCHECK(cached_index < container_.size());
314
315 const QuicInterval<size_t> cached_interval =
316 container_[cached_index].interval();
317 // Does our cached index point directly to what we want?
318 if (cached_interval.Contains(interval_begin)) {
319 return Iterator(cached_index, this);
320 }
321
322 // Are we off-by-one?
323 const std::size_t next_index = cached_index + 1;
324 if (next_index < container_.size()) {
325 if (container_[next_index].interval().Contains(interval_begin)) {
326 cached_index_ = next_index;
327 return Iterator(next_index, this);
328 }
329 }
330
331 // Small optimization:
332 // Determine if we should binary search above or below the cached interval.
333 const std::size_t cached_begin = cached_interval.min();
334 bool looking_below = interval_begin < cached_begin;
335 const std::size_t lower = looking_below ? 0 : cached_index + 1;
336 const std::size_t upper = looking_below ? cached_index : container_.size();
337 Iterator ret = Search(interval_begin, lower, upper);
338 if (ret == DataEnd()) {
339 return ret;
340 }
341 // Update the |cached_index_| to point to the higher index.
342 if (!looking_below) {
343 cached_index_ = ret.index_;
344 }
345 return ret;
346 }
347
348 template <class T, class C>
Size()349 std::size_t QuicIntervalDeque<T, C>::Size() const {
350 return container_.size();
351 }
352
353 template <class T, class C>
Empty()354 bool QuicIntervalDeque<T, C>::Empty() const {
355 return container_.size() == 0;
356 }
357
358 template <class T, class C>
359 template <class U>
PushBackUniversal(U && item)360 void QuicIntervalDeque<T, C>::PushBackUniversal(U&& item) {
361 QuicInterval<std::size_t> interval = item.interval();
362 // Adding an empty interval is a bug.
363 if (interval.Empty()) {
364 QUIC_BUG(quic_bug_10862_3)
365 << "Trying to save empty interval to quiche::QuicheCircularDeque.";
366 return;
367 }
368 container_.push_back(std::forward<U>(item));
369 if (!cached_index_.has_value()) {
370 cached_index_ = container_.size() - 1;
371 }
372 }
373
374 template <class T, class C>
Search(const std::size_t interval_begin,const std::size_t begin_index,const std::size_t end_index)375 typename QuicIntervalDeque<T, C>::Iterator QuicIntervalDeque<T, C>::Search(
376 const std::size_t interval_begin, const std::size_t begin_index,
377 const std::size_t end_index) {
378 auto begin = container_.begin() + begin_index;
379 auto end = container_.begin() + end_index;
380 auto res = std::lower_bound(begin, end, interval_begin, IntervalCompare());
381 // Just because we run |lower_bound| and it didn't return |container_.end()|
382 // doesn't mean we found our desired interval.
383 if (res != end && res->interval().Contains(interval_begin)) {
384 return Iterator(std::distance(begin, res) + begin_index, this);
385 }
386 return DataEnd();
387 }
388
389 } // namespace quic
390
391 #endif // QUICHE_QUIC_CORE_QUIC_INTERVAL_DEQUE_H_
392