xref: /aosp_15_r20/external/cronet/base/containers/ring_buffer.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2013 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #ifndef BASE_CONTAINERS_RING_BUFFER_H_
6*6777b538SAndroid Build Coastguard Worker #define BASE_CONTAINERS_RING_BUFFER_H_
7*6777b538SAndroid Build Coastguard Worker 
8*6777b538SAndroid Build Coastguard Worker #include <stddef.h>
9*6777b538SAndroid Build Coastguard Worker 
10*6777b538SAndroid Build Coastguard Worker #include "base/check.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/memory/raw_ref.h"
12*6777b538SAndroid Build Coastguard Worker 
13*6777b538SAndroid Build Coastguard Worker namespace base {
14*6777b538SAndroid Build Coastguard Worker 
15*6777b538SAndroid Build Coastguard Worker // base::RingBuffer uses a fixed-size array, unlike base::circular_deque and
16*6777b538SAndroid Build Coastguard Worker // std::deque, and so, one can access only the last |kSize| elements. Also, you
17*6777b538SAndroid Build Coastguard Worker // can add elements to the front and read/modify random elements, but cannot
18*6777b538SAndroid Build Coastguard Worker // remove elements from the back. Therefore, it does not have a |Size| method,
19*6777b538SAndroid Build Coastguard Worker // only |BufferSize|, which is a constant, and |CurrentIndex|, which is the
20*6777b538SAndroid Build Coastguard Worker // number of elements added so far.
21*6777b538SAndroid Build Coastguard Worker //
22*6777b538SAndroid Build Coastguard Worker // If the above is sufficient for your use case, base::RingBuffer should be more
23*6777b538SAndroid Build Coastguard Worker // efficient than base::circular_deque.
24*6777b538SAndroid Build Coastguard Worker template <typename T, size_t kSize>
25*6777b538SAndroid Build Coastguard Worker class RingBuffer {
26*6777b538SAndroid Build Coastguard Worker  public:
RingBuffer()27*6777b538SAndroid Build Coastguard Worker   RingBuffer() : current_index_(0) {}
28*6777b538SAndroid Build Coastguard Worker   RingBuffer(const RingBuffer&) = delete;
29*6777b538SAndroid Build Coastguard Worker   RingBuffer& operator=(const RingBuffer&) = delete;
30*6777b538SAndroid Build Coastguard Worker 
BufferSize()31*6777b538SAndroid Build Coastguard Worker   size_t BufferSize() const { return kSize; }
32*6777b538SAndroid Build Coastguard Worker 
CurrentIndex()33*6777b538SAndroid Build Coastguard Worker   size_t CurrentIndex() const { return current_index_; }
34*6777b538SAndroid Build Coastguard Worker 
35*6777b538SAndroid Build Coastguard Worker   // Returns true if a value was saved to index |n|.
IsFilledIndex(size_t n)36*6777b538SAndroid Build Coastguard Worker   bool IsFilledIndex(size_t n) const {
37*6777b538SAndroid Build Coastguard Worker     return IsFilledIndexByBufferIndex(BufferIndex(n));
38*6777b538SAndroid Build Coastguard Worker   }
39*6777b538SAndroid Build Coastguard Worker 
40*6777b538SAndroid Build Coastguard Worker   // Returns the element at index |n| (% |kSize|).
41*6777b538SAndroid Build Coastguard Worker   //
42*6777b538SAndroid Build Coastguard Worker   // n = 0 returns the oldest value and
43*6777b538SAndroid Build Coastguard Worker   // n = bufferSize() - 1 returns the most recent value.
ReadBuffer(size_t n)44*6777b538SAndroid Build Coastguard Worker   const T& ReadBuffer(size_t n) const {
45*6777b538SAndroid Build Coastguard Worker     const size_t buffer_index = BufferIndex(n);
46*6777b538SAndroid Build Coastguard Worker     CHECK(IsFilledIndexByBufferIndex(buffer_index));
47*6777b538SAndroid Build Coastguard Worker     return buffer_[buffer_index];
48*6777b538SAndroid Build Coastguard Worker   }
49*6777b538SAndroid Build Coastguard Worker 
MutableReadBuffer(size_t n)50*6777b538SAndroid Build Coastguard Worker   T* MutableReadBuffer(size_t n) {
51*6777b538SAndroid Build Coastguard Worker     const size_t buffer_index = BufferIndex(n);
52*6777b538SAndroid Build Coastguard Worker     CHECK(IsFilledIndexByBufferIndex(buffer_index));
53*6777b538SAndroid Build Coastguard Worker     return &buffer_[buffer_index];
54*6777b538SAndroid Build Coastguard Worker   }
55*6777b538SAndroid Build Coastguard Worker 
SaveToBuffer(const T & value)56*6777b538SAndroid Build Coastguard Worker   void SaveToBuffer(const T& value) {
57*6777b538SAndroid Build Coastguard Worker     buffer_[BufferIndex(0)] = value;
58*6777b538SAndroid Build Coastguard Worker     current_index_++;
59*6777b538SAndroid Build Coastguard Worker   }
60*6777b538SAndroid Build Coastguard Worker 
Clear()61*6777b538SAndroid Build Coastguard Worker   void Clear() { current_index_ = 0; }
62*6777b538SAndroid Build Coastguard Worker 
63*6777b538SAndroid Build Coastguard Worker   // Iterator has const access to the RingBuffer it got retrieved from.
64*6777b538SAndroid Build Coastguard Worker   class Iterator {
65*6777b538SAndroid Build Coastguard Worker    public:
index()66*6777b538SAndroid Build Coastguard Worker     size_t index() const { return index_; }
67*6777b538SAndroid Build Coastguard Worker 
68*6777b538SAndroid Build Coastguard Worker     const T* operator->() const { return &buffer_->ReadBuffer(index_); }
69*6777b538SAndroid Build Coastguard Worker     const T* operator*() const { return &buffer_->ReadBuffer(index_); }
70*6777b538SAndroid Build Coastguard Worker 
71*6777b538SAndroid Build Coastguard Worker     Iterator& operator++() {
72*6777b538SAndroid Build Coastguard Worker       index_++;
73*6777b538SAndroid Build Coastguard Worker       if (index_ == kSize)
74*6777b538SAndroid Build Coastguard Worker         out_of_range_ = true;
75*6777b538SAndroid Build Coastguard Worker       return *this;
76*6777b538SAndroid Build Coastguard Worker     }
77*6777b538SAndroid Build Coastguard Worker 
78*6777b538SAndroid Build Coastguard Worker     Iterator& operator--() {
79*6777b538SAndroid Build Coastguard Worker       if (index_ == 0)
80*6777b538SAndroid Build Coastguard Worker         out_of_range_ = true;
81*6777b538SAndroid Build Coastguard Worker       index_--;
82*6777b538SAndroid Build Coastguard Worker       return *this;
83*6777b538SAndroid Build Coastguard Worker     }
84*6777b538SAndroid Build Coastguard Worker 
85*6777b538SAndroid Build Coastguard Worker     operator bool() const {
86*6777b538SAndroid Build Coastguard Worker       return !out_of_range_ && buffer_->IsFilledIndex(index_);
87*6777b538SAndroid Build Coastguard Worker     }
88*6777b538SAndroid Build Coastguard Worker 
89*6777b538SAndroid Build Coastguard Worker    private:
Iterator(const RingBuffer<T,kSize> & buffer,size_t index)90*6777b538SAndroid Build Coastguard Worker     Iterator(const RingBuffer<T, kSize>& buffer, size_t index)
91*6777b538SAndroid Build Coastguard Worker         : buffer_(buffer), index_(index), out_of_range_(false) {}
92*6777b538SAndroid Build Coastguard Worker 
93*6777b538SAndroid Build Coastguard Worker     const raw_ref<const RingBuffer<T, kSize>> buffer_;
94*6777b538SAndroid Build Coastguard Worker     size_t index_;
95*6777b538SAndroid Build Coastguard Worker     bool out_of_range_;
96*6777b538SAndroid Build Coastguard Worker 
97*6777b538SAndroid Build Coastguard Worker     friend class RingBuffer<T, kSize>;
98*6777b538SAndroid Build Coastguard Worker   };
99*6777b538SAndroid Build Coastguard Worker 
100*6777b538SAndroid Build Coastguard Worker   // Returns an Iterator pointing to the oldest value in the buffer.
101*6777b538SAndroid Build Coastguard Worker   // Example usage (iterate from oldest to newest value):
102*6777b538SAndroid Build Coastguard Worker   //  for (RingBuffer<T, kSize>::Iterator it = ring_buffer.Begin(); it; ++it) {}
Begin()103*6777b538SAndroid Build Coastguard Worker   Iterator Begin() const {
104*6777b538SAndroid Build Coastguard Worker     if (current_index_ < kSize)
105*6777b538SAndroid Build Coastguard Worker       return Iterator(*this, kSize - current_index_);
106*6777b538SAndroid Build Coastguard Worker     return Iterator(*this, 0);
107*6777b538SAndroid Build Coastguard Worker   }
108*6777b538SAndroid Build Coastguard Worker 
109*6777b538SAndroid Build Coastguard Worker   // Returns an Iterator pointing to the newest value in the buffer.
110*6777b538SAndroid Build Coastguard Worker   // Example usage (iterate backwards from newest to oldest value):
111*6777b538SAndroid Build Coastguard Worker   //  for (RingBuffer<T, kSize>::Iterator it = ring_buffer.End(); it; --it) {}
End()112*6777b538SAndroid Build Coastguard Worker   Iterator End() const { return Iterator(*this, kSize - 1); }
113*6777b538SAndroid Build Coastguard Worker 
114*6777b538SAndroid Build Coastguard Worker  private:
BufferIndex(size_t n)115*6777b538SAndroid Build Coastguard Worker   inline size_t BufferIndex(size_t n) const {
116*6777b538SAndroid Build Coastguard Worker     return (current_index_ + n) % kSize;
117*6777b538SAndroid Build Coastguard Worker   }
118*6777b538SAndroid Build Coastguard Worker 
119*6777b538SAndroid Build Coastguard Worker   // This specialization of |IsFilledIndex| is a micro-optimization that enables
120*6777b538SAndroid Build Coastguard Worker   // us to do e.g. `CHECK(IsFilledIndex(n))` without calling |BufferIndex|
121*6777b538SAndroid Build Coastguard Worker   // twice. Since |BufferIndex| involves a % operation, it's not quite free at a
122*6777b538SAndroid Build Coastguard Worker   // micro-scale.
IsFilledIndexByBufferIndex(size_t buffer_index)123*6777b538SAndroid Build Coastguard Worker   inline bool IsFilledIndexByBufferIndex(size_t buffer_index) const {
124*6777b538SAndroid Build Coastguard Worker     return buffer_index < current_index_;
125*6777b538SAndroid Build Coastguard Worker   }
126*6777b538SAndroid Build Coastguard Worker 
127*6777b538SAndroid Build Coastguard Worker   T buffer_[kSize];
128*6777b538SAndroid Build Coastguard Worker   size_t current_index_;
129*6777b538SAndroid Build Coastguard Worker };
130*6777b538SAndroid Build Coastguard Worker 
131*6777b538SAndroid Build Coastguard Worker }  // namespace base
132*6777b538SAndroid Build Coastguard Worker 
133*6777b538SAndroid Build Coastguard Worker #endif  // BASE_CONTAINERS_RING_BUFFER_H_
134