Some useful stuff

Dependents:   FtEncoder FtControlSet

Buffer.h

Committer:
humlet
Date:
2013-03-29
Revision:
2:8882925900db
Parent:
1:bf8fc4455615

File content as of revision 2:8882925900db:

#ifndef BUFFER_H
#define BUFFER_H

#include "stdint.h"
#include "string.h"

/// A template for a simple buffer class holding at max N elements of type T.
/// Can be used as stack, queue or ring buffer
/// This is kind of an abstract base class. There are heap and a stack based based concrete specializations.
template<typename T, uint32_t N>
class Buffer
{
protected:
    /// The buffer for the stored elements, initialized in the concrete specializations.
    T* elements;

    /// Guess what: a constructor! But this class is kind of abstract,
    /// so it has been declared protected to remove it from the public interface.
    /// Called by the ctor of concrete specializations.
    Buffer():elements(0),startCsr(0),endCsr(0),nElements(0) {};

private:
    /// Points to the oldest element (if not empty; equal to the latest if only one element stored)
    uint32_t startCsr;
    /// Points one element behind the latest
    uint32_t endCsr;
    /// Number of elements currently stored in the Buffer
    uint32_t nElements;

    /// Private helper that increments revolving cursors
    inline void incrCsr(uint32_t& csr) {
        if(++csr>=N) csr=0;
    }
    /// Private helper that decrements revolving cursors
    inline void decrCsr(uint32_t& csr) {
        if(--csr>=N) csr=N-1; // will get quite large on underlow since csr is unsigned
    }

    /// Provides default T() return value for out of bounds access.
    const T& outOfBoundsDefaultReturn()const {
        static const T honk=T();
        return honk;
    }

    /// copy ctor: This class is kind of abtract,
    /// so it has been declared protected to remove it from the public interface.
    /// Not even called by the concrete specialization, since they use the assignment op.
    /// That's why it has become private just to get informed whenever it will be needed again
    /// and to prevent a silent implicit creation by the compiler.
    Buffer(const Buffer& buf):elements(0),startCsr(buf.startCsr),endCsr(buf.endCsr),nElements(buf.nElements) {};

public:

    /// If used as queue or stack, use this function to insert new element to the buffer.
    /// Returns true on success and false if Buffer is full
    inline bool push(const T& element) {
        bool ok = !full();
        if(ok) {
            elements[endCsr]=element;
            incrCsr(endCsr);
            ++nElements;
        }
        return ok;
    }

    /// If used as ring buffer, use this function to insert new elements to the Buffer.
    /// If buffer is full, this function overwrites the oldest element.
    inline void pushCircular(const T& element) {
        elements[endCsr]=element;
        incrCsr(endCsr);
        if(full()) {
            incrCsr(startCsr);
        } else {
            ++nElements;
        }
    }

    /// Pop the latest element from buffer. Returns a default instance of type T if empty.
    inline const T& popLatest() {
        if(empty())return outOfBoundsDefaultReturn();
        decrCsr(endCsr);
        --nElements;
        return elements[endCsr];
    }

    /// Pop the oldest element from buffer. Returns a default instance of type T if empty.
    inline const T& popOldest() {
        if(empty())return outOfBoundsDefaultReturn();
        T& oldest = elements[startCsr];
        incrCsr(startCsr);
        --nElements;
        return oldest;
    }

    /// Returns true if buffer is empty.
    inline bool empty() const {
        return nElements==0;
    }

    /// Returns true if buffer is full.
    inline bool full() const {
        return nElements==N;
    }

    /// Retuns number of currently stored elements.
    inline uint32_t size() const {
        return nElements;
    }

    /// returns maximum number of storable elements
    inline uint32_t maxSize() const {
        return N;
    }

    /// Clear the Buffer
    inline void clear() {
        startCsr=0;
        endCsr=0;
        nElements=0;
    }

    /// Read only access operator: Element with index 0 is the oldest and the one with index size()-1 the latest
    inline const T& operator[](uint32_t idx) {
        if(idx>=nElements)return outOfBoundsDefaultReturn();
        idx+=startCsr;
        if(idx>=N)idx-=N;
        return elements[idx];
    }

    /// assignment operator ... does not care about the concrete type of the source Buffer
    /// as long as T and N template parameters are identical.
    Buffer& operator=(const Buffer& buf) {
        if(&buf!=this) {
            startCsr=buf.startCsr;
            endCsr=buf.endCsr;
            nElements=buf.nElements;
            memcpy(elements,buf.elements,N*sizeof(T));
        }
        return *this;
    }
    
    /// virtual destructor
    virtual ~Buffer() {};
};

/// Concrete Buffer class template that implements the element's storage as simple C-array on stack or global memory.
template<typename T, uint32_t N>
class BufferOnStack : public Buffer<T,N>
{
private:
    /// A simple C-array that stores the elements
    T storage[N];
public:
    /// Creates a Buffer class with storage on stack, static or global memory.
    BufferOnStack():Buffer<T,N>() {
        Buffer<T,N>::elements=storage;
    }
    /// generic "copy constructor" that does not care about the concrete type of the source Buffer
    /// as long as T and N template parameters are identical.
    /// Utilizes the assignment operator of the base class.
    BufferOnStack(const Buffer<T,N>& buf):Buffer<T,N>() {
        Buffer<T,N>::elements=storage;
        Buffer<T,N>::operator=(buf);
    }
    /// The real copy constructor. If this is not defined the compiler rather creates a non working
    /// implicit one than using the generic one.
    /// Utilizes the assignment operator of the base class.
    BufferOnStack(const BufferOnStack& buf):Buffer<T,N>() {
        Buffer<T,N>::elements=storage;
        Buffer<T,N>::operator=(buf);
    }
};

/// Concrete Buffer class template that allocates the elements storage on the heap.
template<typename T, uint32_t N>
class BufferOnHeap : public Buffer<T,N>
{
public:
    /// Creates Buffer with a storage allocated on heap memory
    BufferOnHeap():Buffer<T,N>() {
        Buffer<T,N>::elements=new T[N];
    }
    /// Generic "copy constructor" that does not care about the concrete type of the source Buffer
    /// as long as T and N template parameters are identical.
    /// Utilizes the assignment operator of the base class.
    BufferOnHeap(const Buffer<T,N>& buf):Buffer<T,N>() {
        Buffer<T,N>::elements=new T[N];
        Buffer<T,N>::operator=(buf);
    }
    /// The real copy constructor. If this is not defined the compiler rather creates a non working
    /// implicit one than using the generic one.
    /// Utilizes the assignment operator of the base class.
    BufferOnHeap(const BufferOnHeap& buf):Buffer<T,N>() {
        Buffer<T,N>::elements=new T[N];
        Buffer<T,N>::operator=(buf);
    }
    /// destructor
    virtual ~BufferOnHeap() {
        delete[] Buffer<T,N>::elements;
    }
};

#endif