blob: f50ebe193221330a46cfda268f6074ea3850ae9a [file] [log] [blame]
/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version
* 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __RINGBUFFER_H__
#define __RINGBUFFER_H__
#include "capu/Config.h"
#include "capu/Error.h"
namespace capu
{
/*
* RingBuffer to store data.
* The buffer is not thread-safe!
*/
template<typename T>
class RingBuffer
{
/*
* Iterator class.
*/
class RingBufferIterator
{
public:
friend class RingBuffer<T>;
RingBufferIterator(RingBuffer<T>* buffer);
~RingBufferIterator();
bool_t hasNext();
status_t next(T* element);
private:
uint32_t mCurrentIndex;
bool_t mMoved;
RingBuffer<T>* mBuffer;
};
public:
/*
* Used for iterations over the buffer.
*/
typedef typename RingBuffer<T>::RingBufferIterator Iterator;
/*
* Destructor.
*/
virtual ~RingBuffer();
/*
* Gets an iterator to iterate over the buffer.
* @return Iterator for iteration over the buffer.
*/
Iterator begin();
/*
* Inserts new data in the buffer.
* @param element The element that should get added to the buffer.
* @return The status to indicate if the add operation did succeed.
*/
status_t add(const T& element);
/*
* Clears the buffer.
* @return The status to indicate if the clear operation did succeed.
*/
status_t clear();
/*
* Gets the size of the buffer. This is not the current fill status.
* @return The size of the buffer.
*/
uint32_t size();
/*
* Creates a new ring-base buffer with the specified size.
*
* @param bufferSize The size of the buffer.
*/
RingBuffer(uint32_t bufferSize);
private:
uint32_t mStart;
uint32_t mEnd;
uint32_t mBufferSize;
bool_t mEmpty;
T* mData; // we use a backing array for the ring buffer
/*
* Helper function to increase an index so that the new index is always inside the array bounds.
* @param index The index that should get increased.
*/
void increase(uint32_t& index);
/*
* Helper function to determine if the buffer is full.
* @return True if the buffer is full, false otherwise.
*/
bool_t isFull();
};
template<typename T>
inline RingBuffer<T>::RingBuffer(uint32_t bufferSize)
: mStart(0)
, mEnd(0)
, mBufferSize(bufferSize)
, mEmpty(true)
, mData(new T[bufferSize])
{
}
template<typename T>
inline RingBuffer<T>::~RingBuffer()
{
delete[] mData;
}
template<typename T>
inline bool_t RingBuffer<T>::isFull()
{
return mStart == mEnd && !mEmpty;
}
template<typename T>
inline void RingBuffer<T>::increase(uint32_t& index)
{
index = (index + 1) % mBufferSize;
}
template<typename T>
inline uint32_t RingBuffer<T>::size()
{
return mBufferSize;
}
template<typename T>
inline status_t RingBuffer<T>::add(const T& element)
{
if (mBufferSize == 0)
{
// special case of a zero buffer size
return CAPU_ERANGE;
}
mData[mEnd] = element; // write operation into the buffer
if(isFull())
{
// only increase 'start pointer' if the buffer is full
increase(mStart);
}
increase(mEnd);
mEmpty = false;
return CAPU_OK;
}
template<typename T>
inline status_t RingBuffer<T>::clear()
{
mStart = 0;
mEnd = 0;
mEmpty = true;
return CAPU_OK;
}
template<typename T>
inline typename RingBuffer<T>::Iterator RingBuffer<T>::begin()
{
return RingBufferIterator(this);
}
template<typename T>
inline RingBuffer<T>::RingBufferIterator::RingBufferIterator(RingBuffer<T>* buffer)
: mCurrentIndex(buffer->mStart),
mMoved(false),
mBuffer(buffer)
{
// note: we could use a modification-counter on the RingBuffer to detect concurrent modifications
}
template<typename T>
inline RingBuffer<T>::RingBufferIterator::~RingBufferIterator()
{
}
template<typename T>
inline bool_t RingBuffer<T>::RingBufferIterator::hasNext()
{
if(mBuffer->mEmpty)
{
// empty buffer has no next element
return false;
}
if(mCurrentIndex == mBuffer->mEnd)
{
// pointing to end in an non-empty buffer: we are finished if we did move once!
return !mMoved;
}
// buffer is not empty and we're not pointing to the end: iteration can continue
return true;
}
template<typename T>
inline status_t RingBuffer<T>::RingBufferIterator::next(T* element)
{
if (!element)
{
// user did not provide any memory space for the next element
return CAPU_ENO_MEMORY;
}
if (!hasNext())
{
// note: 'hasNext' is probably called twice:
// while(it.hasNext()) <- call in client code
// it.next(&curr); <- call to 'hasNext' in 'next' method (this method)
// can we ignore that?
// but if we do not check here, we cannot know if there is a next element.
return CAPU_ENOT_EXIST;
}
mMoved = true;
*element = mBuffer->mData[mCurrentIndex]; // assignment operation from the buffer
mBuffer->increase(mCurrentIndex); // increase iteration index
return CAPU_OK;
}
}
#endif //__RINGBUFFER_H__