Simpatico  v1.10
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 
14 namespace Util
15 {
16 
25  template <class Data>
26  class RingBuffer
27  {
28 
29  public:
30 
34  RingBuffer();
35 
41  RingBuffer(const RingBuffer<Data>& other);
42 
49 
53  virtual ~RingBuffer();
54 
63  void allocate(int capacity);
64 
68  void clear();
69 
75  void append(Data value);
76 
80  int size() const;
81 
85  int capacity() const;
86 
90  bool isAllocated() const;
91 
95  bool isFull() const;
96 
102  const Data& operator [] (int offset) const;
103 
109  Data& operator [] (int offset);
110 
117  template <class Archive>
118  void serialize(Archive& ar, const unsigned int version);
119 
120  private:
121 
123  Data* data_;
124 
126  int capacity_;
127 
129  int size_;
130 
132  int last_;
133 
134  };
135 
136  /*
137  * Default contructor
138  */
139  template <class Data>
141  : data_(0),
142  capacity_(0),
143  size_(0),
144  last_(0)
145  {}
146 
147  /*
148  * Copy constructor.
149  *
150  * Allocates new memory and copies all elements by value.
151  *
152  *\param other the RingBuffer to be copied.
153  */
154  template <class Data>
156  : data_(0),
157  capacity_(0),
158  size_(0),
159  last_(0)
160  {
161  if (other.capacity_ > 0) {
162  assert(other.data_ != 0);
163  Memory::allocate<Data>(data_, other.capacity_);
164  capacity_ = other.capacity_;
165  size_ = other.size_;
166  last_ = other.last_;
167  for (int i = 0; i < capacity_; ++i) {
168  data_[i] = other.data_[i];
169  }
170  }
171  }
172 
173  /*
174  * Assignment, element-by-element.
175  *
176  * Assignment is allowed only for allocated RingBuffer objects of equal
177  * capacity. This operator will allocate this RingBuffer if it was not
178  * allocated previously.
179  */
180  template <class Data>
183  {
184 
185  // Check for self assignment
186  if (this == &other) return *this;
187 
188  if (other.isAllocated()) {
189 
190  assert(other.capacity_ > 0);
191 
192  if (!isAllocated()) {
193 
194  Memory::allocate<Data>(data_, other.capacity_);
195  capacity_ = other.capacity_;
196 
197  } else if (capacity_ != other.capacity_) {
198 
199  UTIL_THROW("Cannot assign RingBuffers of unequal capacity");
200 
201  }
202 
203  size_ = other.size_;
204  last_ = other.last_;
205 
206  for (int i = 0; i < capacity_; ++i) {
207  data_[i] = other.data_[i];
208  }
209 
210  } else if (isAllocated()) {
211  UTIL_THROW("Cannot assign unallocated array to allocated array");
212  }
213 
214  return *this;
215  }
216 
218  template <class Data>
220  {
221  if (data_) {
222  Memory::deallocate<Data>(data_, capacity_);
223  }
224  }
225 
227  template <class Data>
229  {
230  if (data_ == 0) {
231  Memory::allocate<Data>(data_, capacity);
232  capacity_ = capacity;
233  } else {
234  UTIL_THROW("Error: Attempt to re-allocate a RingBuffer");
235  }
236  last_ = capacity_ - 1;
237  size_ = 0; // No values in buffer
238  }
239 
240  /*
241  * Set buffer to empty state, by resetting counters.
242  */
243  template <class Data>
244  inline
246  {
247  last_ = capacity_ - 1;
248  size_ = 0;
249  }
250 
251  /*
252  * Append a new value to the end of the buffer
253  */
254  template <class Data>
255  inline
256  void RingBuffer<Data>::append(Data data)
257  {
258  assert(last_ < capacity_);
259  ++last_;
260  if (last_ == capacity_) {
261  last_ = 0; // wrap around
262  };
263  data_[last_] = data;
264 
265  // If buffer is not yet full, update size_
266  if (size_ < capacity_) {
267  ++size_;
268  };
269 
270  }
271 
272  /*
273  * Return number of values in buffer.
274  */
275  template <class Data>
276  inline
278  { return size_; }
279 
280  /*
281  * Return the capacity of the buffer.
282  */
283  template <class Data>
284  inline
286  { return capacity_; }
287 
288  /*
289  * Return true if the RingBuffer has been allocated, false otherwise.
290  */
291  template <class Data>
292  inline
294  { return (data_ != 0); }
295 
296  /*
297  * Return true if the RingBuffer is full.
298  */
299  template <class Data>
300  inline
302  { return (size_ == capacity_); }
303 
304  /*
305  * Retrive a value by const reference, index backwards from 0 (current)
306  */
307  template <class Data>
308  inline
309  const Data& RingBuffer<Data>::operator [] (int offset) const
310  {
311  if ( offset >= size_ ) {
312  UTIL_THROW("offset >= size_");
313  }
314  int i;
315  i = last_ - offset;
316  if (i < 0) {
317  i = i + capacity_;
318  }
319  return data_[i];
320  }
321 
322  /*
323  * Retrive a value by reference, index backwards from 0 (current)
324  */
325  template <class Data>
326  inline
328  {
329  if ( offset >= size_ ) {
330  UTIL_THROW("offset >= size_");
331  }
332  int i;
333  i = last_ - offset;
334  if (i < 0) {
335  i = i + capacity_;
336  }
337  return data_[i];
338  }
339 
340  /*
341  * Serialize a RingBuffer to/from an Archive.
342  */
343  template <class Data>
344  template <class Archive>
345  void RingBuffer<Data>::serialize(Archive& ar, const unsigned int version)
346  {
347  int capacity;
348  if (Archive::is_saving()) {
349  capacity = capacity_;
350  }
351  ar & capacity;
352  if (Archive::is_loading()) {
353  if (!isAllocated()) {
354  if (capacity > 0) {
355  allocate(capacity);
356  } else {
357  capacity_ = 0;
358  }
359  } else {
360  if (capacity != capacity_) {
361  UTIL_THROW("Inconsistent RingBuffer capacities");
362  }
363  }
364  }
365  ar & size_;
366  ar & last_;
367  for (int i = 0; i < capacity_; ++i) {
368  ar & data_[i];
369  }
370  }
371 
372 }
373 #endif
void allocate(int capacity)
Allocate a new empty buffer.
Definition: RingBuffer.h:228
Class for storing history of previous values in an array.
Definition: RingBuffer.h:26
void append(Data value)
Add a new value to the buffer.
Definition: RingBuffer.h:256
virtual ~RingBuffer()
Destructor.
Definition: RingBuffer.h:219
File containing preprocessor macros for error handling.
RingBuffer()
Contructor.
Definition: RingBuffer.h:140
int capacity() const
Return the capacity of the buffer.
Definition: RingBuffer.h:285
bool isFull() const
Return true if full (if size == capacity), false otherwise.
Definition: RingBuffer.h:301
RingBuffer & operator=(const RingBuffer< Data > &other)
Assignment.
Definition: RingBuffer.h:182
#define UTIL_THROW(msg)
Macro for throwing an Exception, reporting function, file and line number.
Definition: global.h:51
const Data & operator[](int offset) const
Retrieve a const value, a specified number of time steps ago.
Definition: RingBuffer.h:309
Utility classes for scientific computation.
Definition: accumulators.mod:1
void serialize(Archive &ar, const unsigned int version)
Serialize a RingBuffer to/from an Archive.
Definition: RingBuffer.h:345
bool isAllocated() const
Return true if the RingBuffer has been allocated, false otherwise.
Definition: RingBuffer.h:293
int size() const
Return number of values currently in the buffer.
Definition: RingBuffer.h:277
void clear()
Set previously allocated buffer to empty state.
Definition: RingBuffer.h:245