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