CRingBuffer.h 4.76 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 * CRingBuffer.h
 *
 *  Created on: 20.02.2011
 *      Author: gerstrong
 *
 *  This small class handles a data structure called ring buffer.
 *  This is very useful for audio implementations, because in many
 *  cases you have a buffer which is read and converted to another buffer
 *  which is the waveform for the audio hardware. As those buffers
 *  have different sizes, there are a least two pointers and/or one
 *  size variable we need to handle the data transfer correctly.
 *
 *  Those pointers must be verified all the in order to avoid overflows
 *  The ring buffer implementation ensures that this never happens.
 *
 *  This class manages those pointers and also the memory allocation, deallocation.
 *
 *  Now there are two uses of this class. One is similar to the list structure.
 *  You read one element and if it gets to the end of the buffer
 *  it will return to the start.
 *
 *  The second choice is to read arrays from the buffer.
 *  In that case you have to read slices. (See below)
 */

#ifndef CRINGBUFFER_H_
#define CRINGBUFFER_H_

30

31 32 33
template <typename T>
class RingBuffer {
public:
34

Gerhard Stein's avatar
Gerhard Stein committed
35
    RingBuffer()
36 37 38 39 40 41 42 43
	{}

	~RingBuffer()
	{
		if(!empty())
			clear();
	}

44 45


46
    void operator=(RingBuffer &&second)
47 48 49 50 51 52 53 54 55 56 57 58
    {
        mp_start = second.mp_start;
        mp_cur = second.mp_start;
        mp_end = second.mp_end;
        m_size = second.m_size;

        second.mp_start = nullptr;
        second.mp_cur = nullptr;
        second.mp_end = nullptr;
        second.m_size = 0;
    }

59 60 61
	/**
	 * Allocates memory for the Ring buffer
	 */
Gerhard Stein's avatar
Gerhard Stein committed
62
    bool resize(const unsigned int size)
63 64 65
	{
		if(!empty())
		{
Gerhard Stein's avatar
Gerhard Stein committed
66
            clear();
67 68 69 70
			return false;
		}

		m_size = size;
Gerhard Stein's avatar
Gerhard Stein committed
71

72
		if(m_size == 0)
Gerhard Stein's avatar
Gerhard Stein committed
73
        {
74
			return false;
Gerhard Stein's avatar
Gerhard Stein committed
75 76
        }

77 78 79 80 81 82 83 84 85 86 87 88 89
		mp_cur = new T[m_size];
		mp_start = mp_cur;
		mp_end = mp_start + m_size;

		return true;
	}


	/**
	 * Clears the buffer without checking if ever was reserved. Be careful!
	 */
	void clear()
	{
Gerhard Stein's avatar
Gerhard Stein committed
90 91 92 93 94
        if(mp_start)
        {
            delete [] mp_start;
        }

95
        mp_start = nullptr;
96 97 98 99 100 101 102 103 104
		m_size = 0;
	}


	/**
	 * Just checks and tells if the ring buffer is actually empty or not reserved
	 */
	bool empty()
	{
105
        return (m_size == 0 || mp_start == nullptr);
106 107
	}

108 109 110 111 112 113 114 115 116 117 118 119 120
	/**
	 * Just tell whether the buffer of the as at the initial pointer which must be the same as at the end, because
	 * until this call, the pointer was rewound.
	 */
	bool atStart()
	{	return (mp_cur == mp_start);	}

	/**
	 * Although a ring doesn't have a start, this function will set the pointer to first data chunk that on what it
	 * was initialized
	 */
	void gotoStart()
	{	mp_cur=mp_start;	}
121 122 123 124 125 126

	/**
	 * Just return the absolute start of the pointer.
	 */
	T *getStartPtr()
	{	return mp_start;	}
127

128 129 130
	/**
	 * Just return the absolute end of the pointer.
	 */
Gerstrong's avatar
Gerstrong committed
131 132
	T *getLastElem()
	{	return mp_end-1;	}
133 134


135 136 137 138 139
	/**
	 * This will get the next Element in the ring. Similar to the front function of std::list, but it's a ring.
	 * This will copy the data to the data type variable you are using, so please don't use too big datastructures for this
	 * if you need fast code.
	 */
140 141 142 143 144 145 146
	T getNextElement()
	{
		const T data = *mp_cur;
		this->operator ++();
		return data;
	}

147 148 149 150 151 152 153 154 155 156 157 158 159

	/**
	 * This function will give you a pointer the current data position within the ring
	 * It will also tell you how many Elements of that data type (n_elem) you can use to read.
	 * If it could read all the elements it will return the same number you entered.
	 * If not, it will return the number of read elements, which will be smaller.
	 * That happens, when the ring buffer gets near to the pointers end and might overflow.
	 * This function will avoid such danger. If the function didn't read the desired elements, just use it again
	 * with the number of remaining elements.
	 *
	 * This function will not read data, because it might slow down the code. For reading and writing the
	 * using the read pointer you must care yourself.
	 */
160 161 162 163 164 165 166 167 168 169
	unsigned int getSlicePtr(T *&data, const unsigned int n_elem)
	{
		data = mp_cur;

		if(mp_cur + n_elem <= mp_end) // All elements can be read
			return n_elem;
		else // you cannot read all the elements, return a lower number of readable elements
			return mp_end-mp_cur;
	}

170 171 172 173

	/**
	 * Just makes the current pointer in the ring buffer go further...
	 */
174 175 176 177 178 179 180 181 182 183 184
	void operator+=(const unsigned int n_elem)
				{
		if(mp_cur + n_elem < mp_end)
			mp_cur += n_elem;
		else
		{
			// Hey, we are a ring. End of the data? Go to the start!
			const unsigned int newpos = n_elem-(mp_end-mp_cur);
			mp_cur = mp_start + newpos;
		}
				}
185 186 187 188

	/**
	 * increment pointer by one element or go the start if cur == end ptr
	 */
189
	void operator++()
190 191 192 193
	{
		mp_cur++;

		if( mp_cur == mp_end )
194
			mp_cur = mp_start;
195
	}
196

197 198

private:
Gerhard Stein's avatar
Gerhard Stein committed
199 200 201 202
    T *mp_start = nullptr;
    T *mp_cur   = nullptr;
    T *mp_end   = nullptr;
    unsigned int m_size = 0;
203 204 205
};

#endif /* CRINGBUFFER_H_ */