Files
pvAccess/pvAccessApp/utils/growingCircularBuffer.h
Matej Sekoranja 441532ff66 baseException fix
2011-01-28 19:52:46 +01:00

282 lines
8.2 KiB
C++

/*
* growingCircularBuffer.h
*
* Created on: Nov 11, 2010
* Author: Miha Vitorovic
*/
#ifndef GROWINGCIRCULARBUFFER_H_
#define GROWINGCIRCULARBUFFER_H_
// TODO remove exception throwing !!!
#include <epicsException.h>
namespace epics {
namespace pvAccess {
/**
* Implementation of circular FIFO unbouded buffer.
* Instance is not thread safe.
* @author Miha Vitorovic
*/
template<class T>
class GrowingCircularBuffer {
public:
/**
* Create a GrowingCircularBuffer with the given capacity.
**/
GrowingCircularBuffer(size_t capacity = 16) :
_elements(new T[capacity]), _takePointer(0), _putPointer(0),
_count(0), _size(capacity) {
}
~GrowingCircularBuffer() {
delete[] _elements;
}
/**
* Get number of elements in the buffer.
* @return number of elements in the buffer.
*/
inline size_t size() {
return _count;
}
/**
* Get current buffer capacity.
* @return buffer current capacity.
*/
inline size_t capacity() {
return _size;
}
/**
* Insert a new element in to the buffer.
* If buffer full the buffer is doubled.
*
* @param x element to insert.
* @return <code>true</code> if first element.
*/
bool insert(const T x);
/**
* Extract the oldest element from the buffer.
* @return the oldest element from the buffer.
*/
T extract();
private:
/**
* Array (circular buffer) of elements.
*/
T* _elements;
/**
* Take (read) pointer.
*/
size_t _takePointer;
/**
* Put (write) pointer.
*/
size_t _putPointer;
/**
* Number of elements in the buffer.
*/
size_t _count;
size_t _size;
void arraycopy(T* src, size_t srcPos, T* dest, size_t destPos,
size_t length);
};
template<class T>
void GrowingCircularBuffer<T>::arraycopy(T* src, size_t srcPos,
T* dest, size_t destPos, size_t length) {
if(srcPos<destPos) // this takes care of same-buffer copy
for(int i = length-1; i>=0; i--)
dest[destPos+i] = src[srcPos+i];
else
for(size_t i = 0; i<length; i++)
dest[destPos++] = src[srcPos++];
}
template<class T>
bool GrowingCircularBuffer<T>::insert(const T x) {
if(_count==_size) {
// we are full, grow by factor 2
T* newElements = new T[_size*2];
// invariant: _takePointer < _size
size_t split = _size-_takePointer;
if(split>0) arraycopy(_elements, _takePointer, newElements, 0,
split);
if(_takePointer!=0) arraycopy(_elements, 0, newElements, split,
_putPointer);
_takePointer = 0;
_putPointer = _size;
_size *= 2;
delete[] _elements;
_elements = newElements;
}
_count++;
_elements[_putPointer] = x;
if(++_putPointer>=_size) _putPointer = 0;
return _count==1;
}
template<class T>
T GrowingCircularBuffer<T>::extract() {
if(_count==0) THROW_BASE_EXCEPTION("Buffer empty.");
_count--;
T old = _elements[_takePointer];
if(++_takePointer>=_size) _takePointer = 0;
return old;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* Template specialization for pointers.
* Implementation of circular FIFO unbouded buffer.
* Instance is not thread safe.
* @author Miha Vitorovic
*/
template<class T>
class GrowingCircularBuffer<T*> {
public:
/**
* Create a GrowingCircularBuffer with the given capacity.
**/
GrowingCircularBuffer(size_t capacity = 16) :
_elements(new T*[capacity]), _takePointer(0), _putPointer(0),
_count(0), _size(capacity) {
}
~GrowingCircularBuffer() {
delete[] _elements;
}
/**
* Get number of elements in the buffer.
* @return number of elements in the buffer.
*/
inline size_t size() {
return _count;
}
/**
* Get current buffer capacity.
* @return buffer current capacity.
*/
inline size_t capacity() {
return _size;
}
/**
* Insert a new element in to the buffer.
* If buffer full the buffer is doubled.
*
* @param x element to insert.
* @return <code>true</code> if first element.
*/
bool insert(const T* x);
/**
* Extract the oldest element from the buffer.
* @return the oldest element from the buffer.
*/
T* extract();
private:
/**
* Array (circular buffer) of elements.
*/
T** _elements;
/**
* Take (read) pointer.
*/
size_t _takePointer;
/**
* Put (write) pointer.
*/
size_t _putPointer;
/**
* Number of elements in the buffer.
*/
size_t _count;
size_t _size;
void arraycopy(T** src, size_t srcPos, T** dest, size_t destPos,
size_t length);
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* g++ requires template definition inside a header file.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
template<class T>
void GrowingCircularBuffer<T*>::arraycopy(T** src, size_t srcPos,
T** dest, size_t destPos, size_t length) {
if(srcPos<destPos) // this takes care of same-buffer copy
for(int i = length-1; i>=0; i--)
dest[destPos+i] = src[srcPos+i];
else
for(size_t i = 0; i<length; i++)
dest[destPos++] = src[srcPos++];
}
template<class T>
bool GrowingCircularBuffer<T*>::insert(const T* x) {
if(_count==_size) {
// we are full, grow by factor 2
T** newElements = new T*[_size*2];
// invariant: _takePointer < _size
size_t split = _size-_takePointer;
if(split>0) arraycopy(_elements, _takePointer, newElements, 0,
split);
if(_takePointer!=0) arraycopy(_elements, 0, newElements, split,
_putPointer);
_takePointer = 0;
_putPointer = _size;
_size *= 2;
delete[] _elements;
_elements = newElements;
}
_count++;
_elements[_putPointer] = const_cast<T*>(x);
if(++_putPointer>=_size) _putPointer = 0;
return _count==1;
}
template<class T>
T* GrowingCircularBuffer<T*>::extract() {
if(_count==0) return NULL;
_count--;
T* old = _elements[_takePointer];
if(++_takePointer>=_size) _takePointer = 0;
return old;
}
}
}
#endif /* GROWINGCIRCULARBUFFER_H_ */