Overte C++ Documentation
RingBufferHistory.h
1 //
2 // RingBufferHistory.h
3 // libraries/shared/src
4 //
5 // Created by Yixin Wang on 7/9/2014
6 // Copyright 2014 High Fidelity, Inc.
7 //
8 // Distributed under the Apache License, Version 2.0.
9 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10 //
11 
12 #ifndef hifi_RingBufferHistory_h
13 #define hifi_RingBufferHistory_h
14 
15 #include <stdlib.h>
16 #include <iterator>
17 
18 #include <qvector.h>
19 
20 template <typename T>
21 class RingBufferHistory {
22 
23 public:
24 
25  RingBufferHistory(int capacity = 10)
26  : _size(capacity + 1),
27  _capacity(capacity),
28  _newestEntryAtIndex(0),
29  _numEntries(0),
30  _buffer(capacity + 1)
31  {
32  }
33 
34  void clear() {
35  _numEntries = 0;
36  }
37 
38  void setCapacity(int capacity) {
39  _size = capacity + 1;
40  _capacity = capacity;
41  _newestEntryAtIndex = 0;
42  _numEntries = 0;
43  _buffer.resize(_size);
44  }
45 
46  void insert(const T& entry) {
47  // increment newest entry index cyclically
48  _newestEntryAtIndex = (_newestEntryAtIndex + 1) % _size;
49 
50  // insert new entry
51  _buffer[_newestEntryAtIndex] = entry;
52  if (_numEntries < _capacity) {
53  _numEntries++;
54  }
55  }
56 
57  // std::unique_ptr need to be passed as an rvalue ref and moved into the vector
58  void insert(T&& entry) {
59  // increment newest entry index cyclically
60  _newestEntryAtIndex = (_newestEntryAtIndex + 1) % _size;
61 
62  // insert new entry
63  _buffer[_newestEntryAtIndex] = std::move(entry);
64  if (_numEntries < _capacity) {
65  _numEntries++;
66  }
67  }
68 
69  // 0 retrieves the most recent entry, _numEntries - 1 retrieves the oldest.
70  // returns NULL if entryAge not within [0, _numEntries-1]
71  const T* get(int entryAge) const {
72  if (!(entryAge >= 0 && entryAge < _numEntries)) {
73  return NULL;
74  }
75  int entryAt = _newestEntryAtIndex - entryAge;
76  if (entryAt < 0) {
77  entryAt += _size;
78  }
79  return &_buffer[entryAt];
80  }
81 
82  T* get(int entryAge) {
83  return const_cast<T*>((static_cast<const RingBufferHistory*>(this))->get(entryAge));
84  }
85 
86  const T* getNewestEntry() const {
87  return _numEntries == 0 ? NULL : &_buffer[_newestEntryAtIndex];
88  }
89 
90  T* getNewestEntry() {
91  return _numEntries == 0 ? NULL : &_buffer[_newestEntryAtIndex];
92  }
93 
94  int getCapacity() const { return _capacity; }
95  int getNumEntries() const { return _numEntries; }
96  bool isFilled() const { return _numEntries == _capacity; }
97 
98 private:
99  int _size;
100  int _capacity;
101  int _newestEntryAtIndex;
102  int _numEntries;
103  std::vector<T> _buffer;
104 
105 public:
106  class Iterator {
107  public:
108  using iterator_category = std::random_access_iterator_tag;
109  using value_type = T;
110  using difference_type = ptrdiff_t;
111  using pointer = T*;
112  using reference = T&;
113 
114  Iterator(T* bufferFirst, T* bufferLast, T* newestAt, T* at)
115  : _bufferFirst(bufferFirst),
116  _bufferLast(bufferLast),
117  _bufferLength(bufferLast - bufferFirst + 1),
118  _newestAt(newestAt),
119  _at(at) {}
120 
121  bool operator==(const Iterator& rhs) { return _at == rhs._at; }
122  bool operator!=(const Iterator& rhs) { return _at != rhs._at; }
123  T& operator*() { return *_at; }
124  T* operator->() { return _at; }
125 
126  Iterator& operator++() {
127  _at = (_at == _bufferFirst) ? _bufferLast : _at - 1;
128  return *this;
129  }
130 
131  Iterator operator++(int) {
132  Iterator tmp(*this);
133  ++(*this);
134  return tmp;
135  }
136 
137  Iterator& operator--() {
138  _at = (_at == _bufferLast) ? _bufferFirst : _at + 1;
139  return *this;
140  }
141 
142  Iterator operator--(int) {
143  Iterator tmp(*this);
144  --(*this);
145  return tmp;
146  }
147 
148  Iterator operator+(int add) {
149  Iterator sum(*this);
150  sum._at = atShiftedBy(add);
151  return sum;
152  }
153 
154  Iterator operator-(int sub) {
155  Iterator sum(*this);
156  sum._at = atShiftedBy(-sub);
157  return sum;
158  }
159 
160  Iterator& operator+=(int add) {
161  _at = atShiftedBy(add);
162  return *this;
163  }
164 
165  Iterator& operator-=(int sub) {
166  _at = atShiftedBy(-sub);
167  return *this;
168  }
169 
170  T& operator[](int i) {
171  return *(atShiftedBy(i));
172  }
173 
174  bool operator<(const Iterator& rhs) {
175  return age() < rhs.age();
176  }
177 
178  bool operator>(const Iterator& rhs) {
179  return age() > rhs.age();
180  }
181 
182  bool operator<=(const Iterator& rhs) {
183  return age() <= rhs.age();
184  }
185 
186  bool operator>=(const Iterator& rhs) {
187  return age() >= rhs.age();
188  }
189 
190  int operator-(const Iterator& rhs) {
191  return age() - rhs.age();
192  }
193 
194  private:
195  T* atShiftedBy(int i) { // shifts i places towards _bufferFirst (towards older entries)
196  i = (_at - _bufferFirst - i) % _bufferLength;
197  if (i < 0) {
198  i += _bufferLength;
199  }
200  return _bufferFirst + i;
201  }
202 
203  int age() {
204  int age = _newestAt - _at;
205  if (age < 0) {
206  age += _bufferLength;
207  }
208  return age;
209  }
210 
211  T* _bufferFirst;
212  T* _bufferLast;
213  int _bufferLength;
214  T* _newestAt;
215  T* _at;
216  };
217 
218  Iterator begin() { return Iterator(&_buffer.front(), &_buffer.back(), &_buffer[_newestEntryAtIndex], &_buffer[_newestEntryAtIndex]); }
219 
220  Iterator end() {
221  int endAtIndex = _newestEntryAtIndex - _numEntries;
222  if (endAtIndex < 0) {
223  endAtIndex += _size;
224  }
225  return Iterator(&_buffer.front(), &_buffer.back(), &_buffer[_newestEntryAtIndex], &_buffer[endAtIndex]);
226  }
227 };
228 
229 #endif // hifi_RingBufferHistory_h