PSCF v1.1
RingBuffer.h
1#ifndef UTIL_RING_BUFFER_H
2#define UTIL_RING_BUFFER_H
3
4/*
5* Util Package - C++ Utilities for Scientific Computation
6*
7* Copyright 2010 - 2017, The Regents of the University of Minnesota
8* Distributed under the terms of the GNU General Public License.
9*/
10
11#include <util/misc/Memory.h>
12#include <util/global.h>
13
14namespace Util
15{
16
25 template <class Data>
27 {
28
29 public:
30
35
42
49
53 virtual ~RingBuffer();
54
63 void allocate(int capacity);
64
68 void clear();
69
79 void append(Data const & value);
80
104 void advance();
105
109 int size() const;
110
114 int capacity() const;
115
119 bool isAllocated() const;
120
124 bool isFull() const;
125
131 Data const & operator [] (int offset) const;
132
138 Data& operator [] (int offset);
139
146 template <class Archive>
147 void serialize(Archive& ar, const unsigned int version);
148
149 private:
150
152 Data* data_;
153
155 int capacity_;
156
158 int size_;
159
161 int last_;
162
163 };
164
165 /*
166 * Default contructor
167 */
168 template <class Data>
170 : data_(0),
171 capacity_(0),
172 size_(0),
173 last_(0)
174 {}
175
176 /*
177 * Copy constructor.
178 *
179 * Allocates new memory and copies all elements by value.
180 *
181 *\param other the RingBuffer to be copied.
182 */
183 template <class Data>
185 : data_(0),
186 capacity_(0),
187 size_(0),
188 last_(0)
189 {
190 if (other.capacity_ > 0) {
191 assert(other.data_ != 0);
192 Memory::allocate<Data>(data_, other.capacity_);
193 capacity_ = other.capacity_;
194 size_ = other.size_;
195 last_ = other.last_;
196 for (int i = 0; i < capacity_; ++i) {
197 data_[i] = other.data_[i];
198 }
199 }
200 }
201
202 /*
203 * Assignment, element-by-element.
204 *
205 * Assignment is allowed only for allocated RingBuffer objects of equal
206 * capacity. This operator will allocate this RingBuffer if it was not
207 * allocated previously.
208 */
209 template <class Data>
212 {
213
214 // Check for self assignment
215 if (this == &other) return *this;
216
217 if (other.isAllocated()) {
218
219 assert(other.capacity_ > 0);
220
221 if (!isAllocated()) {
222
223 Memory::allocate<Data>(data_, other.capacity_);
224 capacity_ = other.capacity_;
225
226 } else if (capacity_ != other.capacity_) {
227
228 UTIL_THROW("Cannot assign RingBuffers of unequal capacity");
229
230 }
231
232 size_ = other.size_;
233 last_ = other.last_;
234
235 for (int i = 0; i < capacity_; ++i) {
236 data_[i] = other.data_[i];
237 }
238
239 } else if (isAllocated()) {
240 UTIL_THROW("Cannot assign unallocated array to allocated array");
241 }
242
243 return *this;
244 }
245
247 template <class Data>
249 {
250 if (data_) {
251 Memory::deallocate<Data>(data_, capacity_);
252 }
253 }
254
256 template <class Data>
257 void RingBuffer<Data>::allocate(int capacity)
258 {
259 if (data_ == 0) {
260 Memory::allocate<Data>(data_, capacity);
261 capacity_ = capacity;
262 } else {
263 UTIL_THROW("Error: Attempt to re-allocate a RingBuffer");
264 }
265 last_ = capacity_ - 1;
266 size_ = 0; // No values in buffer
267 }
268
269 /*
270 * Set buffer to empty state, by resetting counters.
271 */
272 template <class Data>
273 inline
275 {
276 last_ = capacity_ - 1;
277 size_ = 0;
278 }
279
280 /*
281 * Append a new value to the end of the buffer
282 */
283 template <class Data>
284 inline
285 void RingBuffer<Data>::append(Data const & data)
286 {
287 assert(last_ < capacity_);
288 ++last_;
289 if (last_ == capacity_) {
290 last_ = 0; // wrap around
291 };
292 data_[last_] = data;
293
294 // If buffer is not yet full, update size_
295 if (size_ < capacity_) {
296 ++size_;
297 };
298
299 }
300
301 /*
302 * Advance the last_ pointer and size_ without assigning a value.
303 */
304 template <class Data>
305 inline
307 {
308 assert(last_ < capacity_);
309 ++last_;
310 if (last_ == capacity_) {
311 last_ = 0; // wrap around
312 };
313
314 // If buffer is not yet full, update size_
315 if (size_ < capacity_) {
316 ++size_;
317 };
318 }
319
320 /*
321 * Return number of values in buffer.
322 */
323 template <class Data>
324 inline
326 { return size_; }
327
328 /*
329 * Return the capacity of the buffer.
330 */
331 template <class Data>
332 inline
334 { return capacity_; }
335
336 /*
337 * Return true if the RingBuffer has been allocated, false otherwise.
338 */
339 template <class Data>
340 inline
342 { return (data_ != 0); }
343
344 /*
345 * Return true if the RingBuffer is full.
346 */
347 template <class Data>
348 inline
350 { return (size_ == capacity_); }
351
352 /*
353 * Retrive a value by const reference, index backwards from 0 (current)
354 */
355 template <class Data>
356 inline
357 Data const & RingBuffer<Data>::operator [] (int offset) const
358 {
359 if ( offset >= size_ ) {
360 UTIL_THROW("offset >= size_");
361 }
362 int i;
363 i = last_ - offset;
364 if (i < 0) {
365 i = i + capacity_;
366 }
367 return data_[i];
368 }
369
370 /*
371 * Retrive a value by reference, index backwards from 0 (current)
372 */
373 template <class Data>
374 inline
376 {
377 if ( offset >= size_ ) {
378 UTIL_THROW("offset >= size_");
379 }
380 int i;
381 i = last_ - offset;
382 if (i < 0) {
383 i = i + capacity_;
384 }
385 return data_[i];
386 }
387
388 /*
389 * Serialize a RingBuffer to/from an Archive.
390 */
391 template <class Data>
392 template <class Archive>
393 void RingBuffer<Data>::serialize(Archive& ar, const unsigned int version)
394 {
395 int capacity;
396 if (Archive::is_saving()) {
397 capacity = capacity_;
398 }
399 ar & capacity;
400 if (Archive::is_loading()) {
401 if (!isAllocated()) {
402 if (capacity > 0) {
403 allocate(capacity);
404 } else {
405 capacity_ = 0;
406 }
407 } else {
408 if (capacity != capacity_) {
409 UTIL_THROW("Inconsistent RingBuffer capacities");
410 }
411 }
412 }
413 ar & size_;
414 ar & last_;
415 for (int i = 0; i < capacity_; ++i) {
416 ar & data_[i];
417 }
418 }
419
420}
421#endif
Class for storing history of previous values in an array.
Definition: RingBuffer.h:27
bool isAllocated() const
Return true if the RingBuffer has been allocated, false otherwise.
Definition: RingBuffer.h:341
bool isFull() const
Return true if full (if size == capacity), false otherwise.
Definition: RingBuffer.h:349
RingBuffer & operator=(RingBuffer< Data > const &other)
Assignment.
Definition: RingBuffer.h:211
void serialize(Archive &ar, const unsigned int version)
Serialize a RingBuffer to/from an Archive.
Definition: RingBuffer.h:393
RingBuffer(RingBuffer< Data > const &other)
Copy contructor.
Definition: RingBuffer.h:184
void allocate(int capacity)
Allocate a new empty buffer.
Definition: RingBuffer.h:257
void clear()
Set previously allocated buffer to empty state.
Definition: RingBuffer.h:274
int capacity() const
Return the capacity of the buffer.
Definition: RingBuffer.h:333
Data const & operator[](int offset) const
Retrieve a const value, a specified number of time steps ago.
Definition: RingBuffer.h:357
void advance()
Advances the pointer to an added element without assigning a value.
Definition: RingBuffer.h:306
virtual ~RingBuffer()
Destructor.
Definition: RingBuffer.h:248
RingBuffer()
Contructor.
Definition: RingBuffer.h:169
int size() const
Return number of values currently in the buffer.
Definition: RingBuffer.h:325
void append(Data const &value)
Add a new value to the buffer.
Definition: RingBuffer.h:285
File containing preprocessor macros for error handling.
#define UTIL_THROW(msg)
Macro for throwing an Exception, reporting function, file and line number.
Definition: global.h:51
Utility classes for scientific computation.
Definition: accumulators.mod:1