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 : public std::iterator < std::random_access_iterator_tag, T > {
107  public:
108  Iterator(T* bufferFirst, T* bufferLast, T* newestAt, T* at)
109  : _bufferFirst(bufferFirst),
110  _bufferLast(bufferLast),
111  _bufferLength(bufferLast - bufferFirst + 1),
112  _newestAt(newestAt),
113  _at(at) {}
114 
115  bool operator==(const Iterator& rhs) { return _at == rhs._at; }
116  bool operator!=(const Iterator& rhs) { return _at != rhs._at; }
117  T& operator*() { return *_at; }
118  T* operator->() { return _at; }
119 
120  Iterator& operator++() {
121  _at = (_at == _bufferFirst) ? _bufferLast : _at - 1;
122  return *this;
123  }
124 
125  Iterator operator++(int) {
126  Iterator tmp(*this);
127  ++(*this);
128  return tmp;
129  }
130 
131  Iterator& operator--() {
132  _at = (_at == _bufferLast) ? _bufferFirst : _at + 1;
133  return *this;
134  }
135 
136  Iterator operator--(int) {
137  Iterator tmp(*this);
138  --(*this);
139  return tmp;
140  }
141 
142  Iterator operator+(int add) {
143  Iterator sum(*this);
144  sum._at = atShiftedBy(add);
145  return sum;
146  }
147 
148  Iterator operator-(int sub) {
149  Iterator sum(*this);
150  sum._at = atShiftedBy(-sub);
151  return sum;
152  }
153 
154  Iterator& operator+=(int add) {
155  _at = atShiftedBy(add);
156  return *this;
157  }
158 
159  Iterator& operator-=(int sub) {
160  _at = atShiftedBy(-sub);
161  return *this;
162  }
163 
164  T& operator[](int i) {
165  return *(atShiftedBy(i));
166  }
167 
168  bool operator<(const Iterator& rhs) {
169  return age() < rhs.age();
170  }
171 
172  bool operator>(const Iterator& rhs) {
173  return age() > rhs.age();
174  }
175 
176  bool operator<=(const Iterator& rhs) {
177  return age() <= rhs.age();
178  }
179 
180  bool operator>=(const Iterator& rhs) {
181  return age() >= rhs.age();
182  }
183 
184  int operator-(const Iterator& rhs) {
185  return age() - rhs.age();
186  }
187 
188  private:
189  T* atShiftedBy(int i) { // shifts i places towards _bufferFirst (towards older entries)
190  i = (_at - _bufferFirst - i) % _bufferLength;
191  if (i < 0) {
192  i += _bufferLength;
193  }
194  return _bufferFirst + i;
195  }
196 
197  int age() {
198  int age = _newestAt - _at;
199  if (age < 0) {
200  age += _bufferLength;
201  }
202  return age;
203  }
204 
205  T* _bufferFirst;
206  T* _bufferLast;
207  int _bufferLength;
208  T* _newestAt;
209  T* _at;
210  };
211 
212  Iterator begin() { return Iterator(&_buffer.front(), &_buffer.back(), &_buffer[_newestEntryAtIndex], &_buffer[_newestEntryAtIndex]); }
213 
214  Iterator end() {
215  int endAtIndex = _newestEntryAtIndex - _numEntries;
216  if (endAtIndex < 0) {
217  endAtIndex += _size;
218  }
219  return Iterator(&_buffer.front(), &_buffer.back(), &_buffer[_newestEntryAtIndex], &_buffer[endAtIndex]);
220  }
221 };
222 
223 #endif // hifi_RingBufferHistory_h