xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/quic_interval_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_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