1 // ----------------------------------------------------------------------------
2 //	ringbuffer.h
3 //
4 // Copyright (C) 2007-2009
5 //		Stelios Bounanos, M0GLD
6 //
7 // C++ version of PortAudio's ringbuffer code. The copying read/write methods
8 // use memcpy, so it generally safe to use them only for POD types. Thread safe
9 // for one reader and one writer.
10 //
11 // Licensed according to original copyright notice:
12 //
13 // Author: Phil Burk, http://www.softsynth.com
14 // modified for SMP safety on OS X by Bjorn Roche.
15 // also allowed for const where possible.
16 // Note that this is safe only for a single-thread reader
17 // and a single-thread writer.
18 //
19 // This program is distributed with the PortAudio Portable Audio Library.
20 // For more information see: http://www.portaudio.com
21 // Copyright (c) 1999-2000 Ross Bencina and Phil Burk
22 //
23 // Permission is hereby granted, free of charge, to any person obtaining
24 // a copy of this software and associated documentation files
25 // (the "Software"), to deal in the Software without restriction,
26 // including without limitation the rights to use, copy, modify, merge,
27 // publish, distribute, sublicense, and/or sell copies of the Software,
28 // and to permit persons to whom the Software is furnished to do so,
29 // subject to the following conditions:
30 //
31 // The above copyright notice and this permission notice shall be
32 // included in all copies or substantial portions of the Software.
33 //
34 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
35 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
36 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
37 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
38 // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
39 // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
40 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41 
42 /**
43  The class template ringbuffer is used to transport data elements between
44  different execution contexts (threads, OS callbacks, interrupt handlers)
45  without requiring the use of any locks. This only works when there is
46  a single reader and a single writer (ie. one thread or callback writes
47  to the ring buffer, another thread or callback reads from it).
48 
49  The ringbuffer class manages a first-in-first-out buffer containing N
50  elements, where N must be a power of two. An element may be any size
51  (specified in bytes).
52 
53  Buffer storage is instantiated when the class Type is created and
54  released when the class Type is deleted.
55 */
56 
57 #ifndef RINGBUFFER_H
58 #define RINGBUFFER_H
59 
60 #include <cassert>
61 #include <cstring>
62 #include "util.h"
63 
64 template <typename T>
65 class ringbuffer
66 {
67 protected:
68 	size_t size,			// size of buffer, a power of two
69 	big_mask,				// Used for wrapping indices with extra bit to distinguish full/empty.
70 	small_mask;				// Used for fitting indices to buffer.
71 	T* buf;					// Pointer to the buffer containing the actual data.
72 	volatile size_t ridx;	// Index of next readable element.
73 	volatile size_t widx;	// Index of next writable element.
74 public:
75 	typedef T value_type;
76 	typedef struct { value_type* buf; size_t len; } vector_type;
77 
78 public:
79 
80 // Instantiate ringbuffer to empty state ready to have elements written to it.
81 // buffer size, s, must be a power of two
ringbuffer(size_t s)82 	ringbuffer(size_t s) : ridx(0), widx(0) {
83 		assert(powerof2(s));
84 		size = s;
85 		big_mask = size * 2 - 1;
86 		small_mask = size - 1;
87 		buf = new T[2 * size];
88 	}
89 
90 // Delete ringbuffer and all internal storage
~ringbuffer()91 	~ringbuffer() {
92 		delete [] buf;
93 	}
94 
95 // number of elements available for reading.
read_space(void)96 	size_t read_space(void) {
97 		read_memory_barrier();
98 		return (widx - ridx) & big_mask;
99 	}
100 
101 // number of elements available for writing.
write_space(void)102 	size_t write_space(void) {
103 		return size - read_space();
104 	}
105 
106 // advance read index by n number of elements
read_advance(size_t n)107 	void read_advance(size_t n) {
108 		write_memory_barrier();
109 		ridx = (ridx + n) & big_mask;
110 	}
111 
112 // advance write index by n number of elements
write_advance(size_t n)113 	void write_advance(size_t n) {
114 		write_memory_barrier();
115 		widx = (widx + n) & big_mask;
116 	}
117 
118 	size_t get_rv(vector_type v[2], size_t n = 0) {
119 		size_t rspace = read_space();
120 		size_t index = ridx & small_mask;
121 
122 		if (n == 0 || n > rspace)
123 			n = rspace;
124 
125 		if (index + n > size) { // two part vector
126 			v[0].buf = buf + index;
127 			v[0].len = size - index;
128 			v[1].buf = buf;
129 			v[1].len = n - v[0].len;
130 		} else {
131 			v[0].buf = buf + index;
132 			v[0].len = n;
133 			v[1].len = 0;
134 		}
135 		return n;
136 	}
137 
138 // read n elements from buffer, constrained by read/write indices
read(T * dst,size_t n)139 	size_t read(T* dst, size_t n) {
140 		vector_type v[2] = { {0,0}, {0,0} };
141 		n = get_rv(v, n);
142 
143 		memcpy(dst, v[0].buf, v[0].len * sizeof(T));
144 		if (v[1].len)
145 			memcpy(dst + v[0].len, v[1].buf, v[1].len * sizeof(T));
146 
147 		read_advance(n);
148 		return n;
149 	}
150 
peek(T * dst,size_t n)151 	size_t peek(T* dst, size_t n) {
152 		vector_type v[2] = { {0,0}, {0,0} };
153 		n = get_rv(v, n);
154 
155 		memcpy(dst, v[0].buf, v[0].len * sizeof(T));
156 		if (v[1].len)
157 			memcpy(dst + v[0].len, v[1].buf, v[1].len * sizeof(T));
158 		return n;
159 	}
160 
161 	size_t get_wv(vector_type v[2], size_t n = 0) {
162 		size_t wspace = write_space();
163 		size_t index = widx & small_mask;
164 
165 		if (n == 0 || n > wspace)
166 			n = wspace;
167 
168 		if (index + n > size) { // two part vector
169 			v[0].buf = buf + index;
170 			v[0].len = size - index;
171 			v[1].buf = buf;
172 			v[1].len = n - v[0].len;
173 		} else {
174 			v[0].buf = buf + index;
175 			v[0].len = n;
176 			v[1].len = 0;
177 		}
178 		return n;
179 	}
180 
181 // write n elements to buffer, constrained by read/write indices
write(const T * src,size_t n)182 	size_t write(const T* src, size_t n) {
183 		vector_type v[2] = { {0,0}, {0,0} };
184 		n = get_wv(v, n);
185 
186 		memcpy(v[0].buf, src, v[0].len * sizeof(T));
187 		if (v[1].len)
188 			memcpy(v[1].buf, src + v[0].len, v[1].len * sizeof(T));
189 
190 		write_advance(n);
191 		return n;
192 	}
193 
194 // fill buffer with elements with value v
fill(const value_type & v)195 	void fill(const value_type& v) {
196 		reset();
197 		for (size_t i = 0; i < size; i++)
198 			buf[i] = v;
199 		write_advance(size);
200 	}
201 
202 // fill buffer with elements whose value is zero
zero(void)203 	void zero(void) {
204 		reset();
205 		memset(buf, 0, size * sizeof(T));
206 		write_advance(size);
207 	}
208 
209 // reset the read/write indices
reset(void)210 	void reset(void) {
211 		ridx = widx = 0;
212 	}
213 
214 // return maximum number of T elements that buffer can contain
length(void)215 	size_t length(void) {
216 		return size;
217 	}
218 
219 // return number of memory bytes used by ringbuffer buffer store
bytes(void)220 	size_t bytes(void) {
221 		return size * sizeof(T);
222 	}
223 
rbr_index()224 	size_t rbr_index() { return ridx; }
rbw_index()225 	size_t rbw_index() { return widx; }
226 };
227 
228 #endif // RINGBUFFER_H
229 
230 // Local Variables:
231 // mode: c++
232 // c-file-style: "linux"
233 // End:
234