ArrayFIFO template specialization for pointers done.

This commit is contained in:
miha_vitorovic
2011-01-10 15:18:15 +01:00
parent d7852fa666
commit 4e96a20766
2 changed files with 494 additions and 9 deletions

View File

@@ -118,11 +118,11 @@ namespace epics {
#ifdef ARRAY_FIFO_DEBUG
void debugState() {
//size_t mask = _size-1;
std::cout<<"h:"<<_head<<",t:"<<_tail<<",c:"<<_size;
std::cout<<"Simple, h:"<<_head<<",t:"<<_tail<<",c:"<<_size;
std::cout<<",s:"<<size()<<std::endl;
std::cout<<"Content:"<<std::endl;
for (size_t i = 0; i < _size; i++)
std::cout<<"["<<i<<"]: "<<_elements[i]<<std::endl;
std::cout<<"["<<i<<"]: "<<_elements[i]<<std::endl;
}
#endif
@@ -130,7 +130,7 @@ namespace epics {
T* _elements; // array of pointers
size_t _head, _tail, _size;
Mutex _mutex;
static int MIN_INITIAL_CAPACITY;
static const int MIN_INITIAL_CAPACITY = 8;
/**
* Allocate empty array to hold the given number of elements.
@@ -161,11 +161,7 @@ namespace epics {
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* g++ requires template definition inside a header file.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
template<class T>
int ArrayFIFO<T>::MIN_INITIAL_CAPACITY = 8;
/* * * * * * * * * * * template implementation * * * * * * * * * * */
template<class T>
void ArrayFIFO<T>::arraycopy(T* src, size_t srcPos, T* dest,
@@ -374,6 +370,361 @@ namespace epics {
return false;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* Template specialization for pointers
*/
template<class T>
class ArrayFIFO<T*> {
public:
/**
* Constructs an empty array deque with an initial capacity
* sufficient to hold the specified number of elements. Array FIFO
* is designed to hold objects allocated on the heap.
*
* @param[in] numElements lower bound on initial capacity of the
* deque. Default value is 16 elements.
*/
ArrayFIFO(size_t numElements = 16);
~ArrayFIFO();
/**
* Inserts the specified element at the front of this deque.
*
* @param[in] e the element to add.
*/
void addFirst(const T* e);
/**
* Inserts the specified element at the end of this deque.
* @param[in] e the element to add
*/
void addLast(const T* e);
T* pollFirst();
T* pollLast();
T* peekFirst();
T* peekLast();
/**
* Pushes an element onto the stack represented by this deque. In other
* words, inserts the element at the front of this deque.
*
* @param[in] e the element to push
*/
void push(const T* e);
/**
* Pops an element from the stack represented by this deque. In other
* words, removes and returns the first element of this deque.
*
* @return the element at the front of this deque (which is the top
* of the stack represented by this deque), <code>null</code> if no element available.
*/
T* pop();
/**
* Looks at the object at the top of this stack without removing it
* from the stack.
* @return the object at the top of this stack (the last item
* of the <tt>Vector</tt> object).
*/
T* peek();
/**
* Returns the number of elements in this deque.
* @return the number of elements in this deque
*/
size_t size();
/**
* Returns <tt>true</tt> if this deque contains no elements.
*
* @return <tt>true</tt> if this deque contains no elements
*/
bool isEmpty();
/**
* Removes all of the elements from this deque.
* The deque will be empty after this call returns.
*
* @param freeElements tells the methods to automatically free
* the memory of all the elments in the FIFO. Default is {@code true}
*/
void clear();
/**
* Removes the first occurrence of the specified element in this
* deque (when traversing the deque from head to tail).
* If the deque does not contain the element, it is unchanged.
* More formally, removes the first element <tt>e</tt> such that
* <tt>o.equals(e)</tt> (if such an element exists).
* Returns <tt>true</tt> if this deque contained the specified element
* (or equivalently, if this deque changed as a result of the call).
*
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> if the deque contained the specified element
*/
bool remove(const T* e);
#ifdef ARRAY_FIFO_DEBUG
void debugState() {
//size_t mask = _size-1;
std::cout<<"Pointer, h:"<<_head<<",t:"<<_tail<<",c:"<<_size;
std::cout<<",s:"<<size()<<std::endl;
std::cout<<"Content:"<<std::endl;
for (size_t i = 0; i < size(); i++)
std::cout<<"["<<((_head+i)&(_size-1))<<"]: "<<*_elements[(_head+i)&(_size-1)]<<std::endl;
}
#endif
private:
T** _elements; // array of pointers
size_t _head, _tail, _size;
Mutex _mutex;
static const int MIN_INITIAL_CAPACITY = 8;
/**
* Allocate empty array to hold the given number of elements.
* @param[in] numElements the number of elements to hold
*/
void allocateElements(const size_t numElements);
/**
* Double the capacity of this deque. Call only when full, i.e.,
* when head and tail have wrapped around to become equal.
*/
void doubleCapacity();
void arraycopy(T** src, size_t srcPos, T** dest, size_t destPos,
size_t length);
/**
* Removes the element at the specified position in the elements array,
* adjusting head and tail as necessary. This can result in motion of
* elements backwards or forwards in the array.
*
* <p>This method is called delete rather than remove to emphasize
* that its semantics differ from those of {@link List#remove(int)}.
*
* @return true if elements moved backwards
*/
bool del(const size_t i);
};
/* * * * * * * * * * * template implementation * * * * * * * * * * */
template<class T>
void ArrayFIFO<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>
void ArrayFIFO<T*>::allocateElements(size_t numElements) {
_size = MIN_INITIAL_CAPACITY;
// Find the best power of two to hold elements.
// Tests "<=" because arrays aren't kept full.
if(numElements>=_size) {
_size = numElements;
_size |= (_size>>1);
_size |= (_size>>2);
_size |= (_size>>4);
_size |= (_size>>8);
_size |= (_size>>16);
_size++;
}
_elements = new T*[_size];
}
template<class T>
void ArrayFIFO<T*>::doubleCapacity() {
size_t p = _head;
size_t n = _size;
size_t r = n-p; // number of elements to the right of p
size_t newCapacity = n<<1;
T** a = new T*[newCapacity];
arraycopy(_elements, p, a, 0, r);
arraycopy(_elements, 0, a, r, p);
delete[] _elements;
_elements = a;
_size = newCapacity;
_head = 0;
_tail = n;
}
template<class T>
ArrayFIFO<T*>::ArrayFIFO(size_t numElements) :
_head(0), _tail(0), _mutex() {
allocateElements(numElements);
}
template<class T>
ArrayFIFO<T*>::~ArrayFIFO() {
delete[] _elements;
}
template<class T>
void ArrayFIFO<T*>::addFirst(const T* e) {
Lock lock(&_mutex);
_elements[_head = (_head-1)&(_size-1)] = const_cast<T*>(e);
if(_head==_tail) doubleCapacity();
}
template<class T>
void ArrayFIFO<T*>::addLast(const T* e) {
Lock lock(&_mutex);
_elements[_tail] = const_cast<T*>(e);
if((_tail = (_tail+1)&(_size-1))==_head) doubleCapacity();
}
template<class T>
T* ArrayFIFO<T*>::pollFirst() {
Lock lock(&_mutex);
if(isEmpty()) return NULL;
T* result = _elements[_head]; // Element is null if deque empty
_head = (_head+1)&(_size-1);
return result;
}
template<class T>
T* ArrayFIFO<T*>::pollLast() {
Lock lock(&_mutex);
if(isEmpty()) return NULL;
_tail = (_tail-1)&(_size-1);
return _elements[_tail];
}
template<class T>
T* ArrayFIFO<T*>::peekFirst() {
Lock lock(&_mutex);
if(isEmpty()) return NULL;
return _elements[_head];
}
template<class T>
T* ArrayFIFO<T*>::peekLast() {
Lock lock(&_mutex);
if(isEmpty()) return NULL;
return _elements[(_tail-1)&(_size-1)];
}
template<class T>
void ArrayFIFO<T*>::push(const T* e) {
addLast(e);
}
template<class T>
T* ArrayFIFO<T*>::pop() {
return pollLast();
}
template<class T>
T* ArrayFIFO<T*>::peek() {
return peekFirst();
}
template<class T>
size_t ArrayFIFO<T*>::size() {
Lock lock(&_mutex);
return (_tail-_head)&(_size-1);
}
template<class T>
bool ArrayFIFO<T*>::isEmpty() {
Lock lock(&_mutex);
return _head==_tail;
}
template<class T>
void ArrayFIFO<T*>::clear() {
Lock lock(&_mutex);
_head = _tail = 0;
}
template<class T>
bool ArrayFIFO<T*>::del(const size_t i) {
// i is absolute index in the array
size_t mask = _size-1;
size_t h = _head;
size_t t = _tail;
size_t front = (i-h)&mask;
size_t back = (t-i)&mask;
// Invariant: head <= i < tail mod circularity
if(front>=((t-h)&mask)) THROW_BASE_EXCEPTION(
"Illegal State Exception"); // concurrency problem!!!
// Optimize for least element motion
if(front<back) {
if(h<=i) {
arraycopy(_elements, h, _elements, h+1, front);
}
else { // Wrap around
arraycopy(_elements, 0, _elements, 1, i);
if(t>0) _elements[0] = _elements[mask];
arraycopy(_elements, h, _elements, h+1, mask-h);
}
_head = (h+1)&mask;
return false;
}
else {
if(i<t) { // Copy the null tail as well
arraycopy(_elements, i+1, _elements, i, back);
_tail = t-1;
}
else { // Wrap around
arraycopy(_elements, i+1, _elements, i, mask-i);
_elements[mask] = _elements[0];
arraycopy(_elements, 1, _elements, 0, t);
_tail = (t-1)&mask;
}
return true;
}
}
template<class T>
bool ArrayFIFO<T*>::remove(const T* e) {
Lock lock(&_mutex);
if(isEmpty()) return false; // nothing to do
size_t mask = _size-1;
size_t i = _head;
while(i!=_tail) {
if(e==_elements[i]) {
del(i);
return true;
}
i = (i+1)&mask;
}
return false;
}
}
}

View File

@@ -15,7 +15,9 @@ using namespace epics::pvAccess;
using std::cout;
using std::endl;
int main(int argc, char *argv[]) {
void testSimpleType() {
cout<<"\nTests for simple type template."<<endl;
ArrayFIFO<int> fifoInt;
assert(fifoInt.size()==0);
@@ -133,5 +135,137 @@ int main(int argc, char *argv[]) {
assert(fifoInt.isEmpty());
cout<<"\nPASSED!\n";
}
void testPointerType() {
cout<<"\nTests for pointer type template."<<endl;
int buffer[] = {0,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,20};
ArrayFIFO<int*> fifoInt;
assert(fifoInt.size()==0);
assert(fifoInt.isEmpty());
cout<<"Testing clear."<<endl;
fifoInt.push(&buffer[3]);
assert(fifoInt.size()==1);
fifoInt.clear();
assert(fifoInt.isEmpty());
cout<<"Testing push/pop."<<endl;
fifoInt.push(&buffer[5]);
fifoInt.push(&buffer[6]);
fifoInt.push(&buffer[7]);
assert(fifoInt.size()==3);
assert(fifoInt.pop()==&buffer[7]);
assert(fifoInt.size()==2);
assert(fifoInt.pop()==&buffer[6]);
assert(fifoInt.size()==1);
assert(fifoInt.pop()==&buffer[5]);
assert(fifoInt.size()==0);
cout<<"Testing FIFO ops (first/last)."<<endl;
fifoInt.addFirst(&buffer[1]);
fifoInt.addFirst(&buffer[2]);
fifoInt.addFirst(&buffer[3]);
fifoInt.addFirst(&buffer[4]);
fifoInt.addFirst(&buffer[5]);
assert(fifoInt.size()==5);
assert(fifoInt.pollLast()==&buffer[1]);
assert(fifoInt.pollLast()==&buffer[2]);
assert(fifoInt.pollLast()==&buffer[3]);
assert(fifoInt.pollLast()==&buffer[4]);
assert(fifoInt.pollLast()==&buffer[5]);
assert(fifoInt.isEmpty());
cout<<"Testing FIFO ops (last/first)."<<endl;
fifoInt.addLast(&buffer[7]);
fifoInt.addLast(&buffer[8]);
fifoInt.addLast(&buffer[9]);
fifoInt.addLast(&buffer[10]);
fifoInt.addLast(&buffer[11]);
assert(fifoInt.size()==5);
assert(fifoInt.pollFirst()==&buffer[7]);
assert(fifoInt.pollFirst()==&buffer[8]);
assert(fifoInt.pollFirst()==&buffer[9]);
assert(fifoInt.pollFirst()==&buffer[10]);
assert(fifoInt.pollFirst()==&buffer[11]);
assert(fifoInt.isEmpty());
cout<<"Testing remove, peek."<<endl;
fifoInt.addFirst(&buffer[1]);
fifoInt.addFirst(&buffer[2]);
fifoInt.addFirst(&buffer[3]);
fifoInt.addFirst(&buffer[4]);
fifoInt.addFirst(&buffer[5]);
fifoInt.addFirst(&buffer[6]);
fifoInt.addFirst(&buffer[7]);
// - - - - - - - - - - - -
fifoInt.addFirst(&buffer[8]);
fifoInt.addFirst(&buffer[9]);
fifoInt.addFirst(&buffer[10]);
assert(fifoInt.peekFirst()==&buffer[10]);
assert(fifoInt.peekLast()==&buffer[1]);
assert(fifoInt.size()==10);
assert(fifoInt.remove(&buffer[9]));
assert(fifoInt.size()==9);
assert(!fifoInt.remove(&buffer[15]));
assert(fifoInt.size()==9);
assert(fifoInt.pollLast()==&buffer[1]);
assert(fifoInt.pollLast()==&buffer[2]);
assert(fifoInt.pollLast()==&buffer[3]);
assert(fifoInt.pollLast()==&buffer[4]);
assert(fifoInt.pollLast()==&buffer[5]);
assert(fifoInt.pollLast()==&buffer[6]);
assert(fifoInt.pollLast()==&buffer[7]);
assert(fifoInt.pollLast()==&buffer[8]);
// - - - - - - - - - - - -
assert(fifoInt.pollLast()==&buffer[10]);
assert(fifoInt.isEmpty());
cout<<"Testing increase buffer."<<endl;
fifoInt.addLast(&buffer[2]);
fifoInt.addLast(&buffer[3]);
fifoInt.addLast(&buffer[4]);
fifoInt.addLast(&buffer[5]);
fifoInt.addLast(&buffer[6]);
fifoInt.addLast(&buffer[7]);
fifoInt.addLast(&buffer[8]);
fifoInt.addLast(&buffer[9]);
fifoInt.addLast(&buffer[10]);
fifoInt.addLast(&buffer[11]);
fifoInt.addLast(&buffer[12]);
fifoInt.addLast(&buffer[13]);
fifoInt.addLast(&buffer[14]);
fifoInt.addLast(&buffer[15]);
fifoInt.addLast(&buffer[16]);
fifoInt.addLast(&buffer[17]);
fifoInt.addLast(&buffer[18]);
fifoInt.addLast(&buffer[19]);
assert(fifoInt.size()==18);
fifoInt.debugState();
fifoInt.clear();
assert(fifoInt.isEmpty());
cout<<"\nPASSED!\n";
}
int main(int argc, char *argv[]) {
testSimpleType();
cout<<endl;
testPointerType();
return 0;
}