Towards fixing AU preset invalidation
[ardour.git] / libs / pbd / pbd / ringbufferNPT.h
1 /*
2     Copyright (C) 2000 Paul Davis & Benno Senoner
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifndef ringbuffer_npt_h
21 #define ringbuffer_npt_h
22
23 //#include <sys/mman.h>
24
25 #include <cstring>
26 #include <glib.h>
27
28 #include "pbd/libpbd_visibility.h"
29
30 namespace PBD {
31
32 /* ringbuffer class where the element size is not required to be a power of two */
33
34 template<class T>
35 class /*LIBPBD_API*/ RingBufferNPT
36 {
37   public:
38         RingBufferNPT (size_t sz) {
39                 size = sz;
40                 buf = new T[size];
41                 reset ();
42         }
43
44         virtual ~RingBufferNPT () {
45                 delete [] buf;
46         }
47
48         void reset () {
49                 /* !!! NOT THREAD SAFE !!! */
50                 g_atomic_int_set (&write_ptr, 0);
51                 g_atomic_int_set (&read_ptr, 0);
52         }
53
54         void set (size_t r, size_t w) {
55                 /* !!! NOT THREAD SAFE !!! */
56                 g_atomic_int_set (&write_ptr, w);
57                 g_atomic_int_set (&read_ptr, r);
58         }
59
60         size_t  read  (T *dest, size_t cnt);
61         size_t  write (const T *src, size_t cnt);
62         size_t  write_one (const T src);
63
64         struct rw_vector {
65             T *buf[2];
66             size_t len[2];
67         };
68
69         void get_read_vector (rw_vector *);
70         void get_write_vector (rw_vector *);
71
72         void decrement_read_ptr (size_t cnt) {
73                 g_atomic_int_set (&read_ptr, (g_atomic_int_get(&read_ptr) - cnt) % size);
74         }
75
76         void increment_read_ptr (size_t cnt) {
77                 g_atomic_int_set (&read_ptr, (g_atomic_int_get(&read_ptr) + cnt) % size);
78         }
79
80         void increment_write_ptr (size_t cnt) {
81                 g_atomic_int_set (&write_ptr,  (g_atomic_int_get(&write_ptr) + cnt) % size);
82         }
83
84         size_t write_space () {
85                 size_t w, r;
86
87                 w = g_atomic_int_get (&write_ptr);
88                 r = g_atomic_int_get (&read_ptr);
89
90                 if (w > r) {
91                         return ((r - w + size) % size) - 1;
92                 } else if (w < r) {
93                         return (r - w) - 1;
94                 } else {
95                         return size - 1;
96                 }
97         }
98
99         size_t read_space () {
100                 size_t w, r;
101
102                 w = g_atomic_int_get (&write_ptr);
103                 r = g_atomic_int_get (&read_ptr);
104
105                 if (w > r) {
106                         return w - r;
107                 } else {
108                         return (w - r + size) % size;
109                 }
110         }
111
112         T *buffer () { return buf; }
113         size_t get_write_ptr () const { return g_atomic_int_get (&write_ptr); }
114         size_t get_read_ptr () const { return g_atomic_int_get (&read_ptr); }
115         size_t bufsize () const { return size; }
116
117   protected:
118         T *buf;
119         size_t size;
120         mutable gint write_ptr;
121         mutable gint read_ptr;
122 };
123
124 template<class T> /*LIBPBD_API*/ size_t
125 RingBufferNPT<T>::read (T *dest, size_t cnt)
126 {
127         size_t free_cnt;
128         size_t cnt2;
129         size_t to_read;
130         size_t n1, n2;
131         size_t priv_read_ptr;
132
133         priv_read_ptr=g_atomic_int_get(&read_ptr);
134
135         if ((free_cnt = read_space ()) == 0) {
136                 return 0;
137         }
138
139         to_read = cnt > free_cnt ? free_cnt : cnt;
140
141         cnt2 = priv_read_ptr + to_read;
142
143         if (cnt2 > size) {
144                 n1 = size - priv_read_ptr;
145                 n2 = cnt2 % size;
146         } else {
147                 n1 = to_read;
148                 n2 = 0;
149         }
150
151         memcpy (dest, &buf[priv_read_ptr], n1 * sizeof (T));
152         priv_read_ptr = (priv_read_ptr + n1) % size;
153
154         if (n2) {
155                 memcpy (dest+n1, buf, n2 * sizeof (T));
156                 priv_read_ptr = n2;
157         }
158
159         g_atomic_int_set(&read_ptr, priv_read_ptr);
160         return to_read;
161 }
162
163 template<class T> /*LIBPBD_API*/ size_t
164 RingBufferNPT<T>::write (const T *src, size_t cnt)
165 {
166         size_t free_cnt;
167         size_t cnt2;
168         size_t to_write;
169         size_t n1, n2;
170         size_t priv_write_ptr;
171
172         priv_write_ptr=g_atomic_int_get(&write_ptr);
173
174         if ((free_cnt = write_space ()) == 0) {
175                 return 0;
176         }
177
178         to_write = cnt > free_cnt ? free_cnt : cnt;
179
180         cnt2 = priv_write_ptr + to_write;
181
182         if (cnt2 > size) {
183                 n1 = size - priv_write_ptr;
184                 n2 = cnt2 % size;
185         } else {
186                 n1 = to_write;
187                 n2 = 0;
188         }
189
190         memcpy (&buf[priv_write_ptr], src, n1 * sizeof (T));
191         priv_write_ptr = (priv_write_ptr + n1) % size;
192
193         if (n2) {
194                 memcpy (buf, src+n1, n2 * sizeof (T));
195                 priv_write_ptr = n2;
196         }
197
198         g_atomic_int_set(&write_ptr, priv_write_ptr);
199         return to_write;
200 }
201
202 template<class T> /*LIBPBD_API*/ size_t
203 RingBufferNPT<T>::write_one (const T src)
204 {
205         return write (&src, 1);
206 }
207
208 template<class T> /*LIBPBD_API*/ void
209 RingBufferNPT<T>::get_read_vector (typename RingBufferNPT<T>::rw_vector *vec)
210 {
211         size_t free_cnt;
212         size_t cnt2;
213         size_t w, r;
214
215         w = g_atomic_int_get (&write_ptr);
216         r = g_atomic_int_get (&read_ptr);
217
218         if (w > r) {
219                 free_cnt = w - r;
220         } else {
221                 free_cnt = (w - r + size) % size;
222         }
223
224         cnt2 = r + free_cnt;
225
226         if (cnt2 > size) {
227                 /* Two part vector: the rest of the buffer after the
228                    current write ptr, plus some from the start of
229                    the buffer.
230                 */
231
232                 vec->buf[0] = &buf[r];
233                 vec->len[0] = size - r;
234                 vec->buf[1] = buf;
235                 vec->len[1] = cnt2 % size;
236
237         } else {
238
239                 /* Single part vector: just the rest of the buffer */
240
241                 vec->buf[0] = &buf[r];
242                 vec->len[0] = free_cnt;
243                 vec->buf[1] = 0;
244                 vec->len[1] = 0;
245         }
246 }
247
248 template<class T> /*LIBPBD_API*/ void
249 RingBufferNPT<T>::get_write_vector (typename RingBufferNPT<T>::rw_vector *vec)
250 {
251         size_t free_cnt;
252         size_t cnt2;
253         size_t w, r;
254
255         w = g_atomic_int_get (&write_ptr);
256         r = g_atomic_int_get (&read_ptr);
257
258         if (w > r) {
259                 free_cnt = ((r - w + size) % size) - 1;
260         } else if (w < r) {
261                 free_cnt = (r - w) - 1;
262         } else {
263                 free_cnt = size - 1;
264         }
265
266         cnt2 = w + free_cnt;
267
268         if (cnt2 > size) {
269
270                 /* Two part vector: the rest of the buffer after the
271                    current write ptr, plus some from the start of
272                    the buffer.
273                 */
274
275                 vec->buf[0] = &buf[w];
276                 vec->len[0] = size - w;
277                 vec->buf[1] = buf;
278                 vec->len[1] = cnt2 % size;
279         } else {
280                 vec->buf[0] = &buf[w];
281                 vec->len[0] = free_cnt;
282                 vec->len[1] = 0;
283         }
284 }
285
286 } /* namespace */
287
288 #endif /* __ringbuffer_npt_h__ */