cd1729996184645089006b14502db3ad4fe87062
[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
63         struct rw_vector {
64             T *buf[2];
65             size_t len[2];
66         };
67
68         void get_read_vector (rw_vector *);
69         void get_write_vector (rw_vector *);
70         
71         void decrement_read_ptr (size_t cnt) {
72                 g_atomic_int_set (&read_ptr, (g_atomic_int_get(&read_ptr) - cnt) % size);
73         }                
74
75         void increment_read_ptr (size_t cnt) {
76                 g_atomic_int_set (&read_ptr, (g_atomic_int_get(&read_ptr) + cnt) % size);
77         }                
78
79         void increment_write_ptr (size_t cnt) {
80                 g_atomic_int_set (&write_ptr,  (g_atomic_int_get(&write_ptr) + cnt) % size);
81         }                
82
83         size_t write_space () {
84                 size_t w, r;
85                 
86                 w = g_atomic_int_get (&write_ptr);
87                 r = g_atomic_int_get (&read_ptr);
88                 
89                 if (w > r) {
90                         return ((r - w + size) % size) - 1;
91                 } else if (w < r) {
92                         return (r - w) - 1;
93                 } else {
94                         return size - 1;
95                 }
96         }
97         
98         size_t read_space () {
99                 size_t w, r;
100                 
101                 w = g_atomic_int_get (&write_ptr);
102                 r = g_atomic_int_get (&read_ptr);
103                 
104                 if (w > r) {
105                         return w - r;
106                 } else {
107                         return (w - r + size) % size;
108                 }
109         }
110
111         T *buffer () { return buf; }
112         size_t get_write_ptr () const { return g_atomic_int_get (&write_ptr); }
113         size_t get_read_ptr () const { return g_atomic_int_get (&read_ptr); }
114         size_t bufsize () const { return size; }
115
116   protected:
117         T *buf;
118         size_t size;
119         mutable gint write_ptr;
120         mutable gint read_ptr;
121 };
122
123 template<class T> /*LIBPBD_API*/ size_t
124 RingBufferNPT<T>::read (T *dest, size_t cnt)
125 {
126         size_t free_cnt;
127         size_t cnt2;
128         size_t to_read;
129         size_t n1, n2;
130         size_t priv_read_ptr;
131
132         priv_read_ptr=g_atomic_int_get(&read_ptr);
133
134         if ((free_cnt = read_space ()) == 0) {
135                 return 0;
136         }
137
138         to_read = cnt > free_cnt ? free_cnt : cnt;
139         
140         cnt2 = priv_read_ptr + to_read;
141
142         if (cnt2 > size) {
143                 n1 = size - priv_read_ptr;
144                 n2 = cnt2 % size;
145         } else {
146                 n1 = to_read;
147                 n2 = 0;
148         }
149         
150         memcpy (dest, &buf[priv_read_ptr], n1 * sizeof (T));
151         priv_read_ptr = (priv_read_ptr + n1) % size;
152
153         if (n2) {
154                 memcpy (dest+n1, buf, n2 * sizeof (T));
155                 priv_read_ptr = n2;
156         }
157
158         g_atomic_int_set(&read_ptr, priv_read_ptr);
159         return to_read;
160 }
161
162 template<class T> /*LIBPBD_API*/ size_t
163 RingBufferNPT<T>::write (const T *src, size_t cnt)
164 {
165         size_t free_cnt;
166         size_t cnt2;
167         size_t to_write;
168         size_t n1, n2;
169         size_t priv_write_ptr;
170
171         priv_write_ptr=g_atomic_int_get(&write_ptr);
172
173         if ((free_cnt = write_space ()) == 0) {
174                 return 0;
175         }
176
177         to_write = cnt > free_cnt ? free_cnt : cnt;
178         
179         cnt2 = priv_write_ptr + to_write;
180
181         if (cnt2 > size) {
182                 n1 = size - priv_write_ptr;
183                 n2 = cnt2 % size;
184         } else {
185                 n1 = to_write;
186                 n2 = 0;
187         }
188
189         memcpy (&buf[priv_write_ptr], src, n1 * sizeof (T));
190         priv_write_ptr = (priv_write_ptr + n1) % size;
191
192         if (n2) {
193                 memcpy (buf, src+n1, n2 * sizeof (T));
194                 priv_write_ptr = n2;
195         }
196
197         g_atomic_int_set(&write_ptr, priv_write_ptr);
198         return to_write;
199 }
200
201 template<class T> /*LIBPBD_API*/ void
202 RingBufferNPT<T>::get_read_vector (typename RingBufferNPT<T>::rw_vector *vec)
203 {
204         size_t free_cnt;
205         size_t cnt2;
206         size_t w, r;
207         
208         w = g_atomic_int_get (&write_ptr);
209         r = g_atomic_int_get (&read_ptr);
210         
211         if (w > r) {
212                 free_cnt = w - r;
213         } else {
214                 free_cnt = (w - r + size) % size;
215         }
216
217         cnt2 = r + free_cnt;
218
219         if (cnt2 > size) {
220                 /* Two part vector: the rest of the buffer after the
221                    current write ptr, plus some from the start of 
222                    the buffer.
223                 */
224
225                 vec->buf[0] = &buf[r];
226                 vec->len[0] = size - r;
227                 vec->buf[1] = buf;
228                 vec->len[1] = cnt2 % size;
229
230         } else {
231                 
232                 /* Single part vector: just the rest of the buffer */
233                 
234                 vec->buf[0] = &buf[r];
235                 vec->len[0] = free_cnt;
236                 vec->buf[1] = 0;
237                 vec->len[1] = 0;
238         }
239 }
240
241 template<class T> /*LIBPBD_API*/ void
242 RingBufferNPT<T>::get_write_vector (typename RingBufferNPT<T>::rw_vector *vec)
243 {
244         size_t free_cnt;
245         size_t cnt2;
246         size_t w, r;
247         
248         w = g_atomic_int_get (&write_ptr);
249         r = g_atomic_int_get (&read_ptr);
250         
251         if (w > r) {
252                 free_cnt = ((r - w + size) % size) - 1;
253         } else if (w < r) {
254                 free_cnt = (r - w) - 1;
255         } else {
256                 free_cnt = size - 1;
257         }
258         
259         cnt2 = w + free_cnt;
260
261         if (cnt2 > size) {
262                 
263                 /* Two part vector: the rest of the buffer after the
264                    current write ptr, plus some from the start of 
265                    the buffer.
266                 */
267
268                 vec->buf[0] = &buf[w];
269                 vec->len[0] = size - w;
270                 vec->buf[1] = buf;
271                 vec->len[1] = cnt2 % size;
272         } else {
273                 vec->buf[0] = &buf[w];
274                 vec->len[0] = free_cnt;
275                 vec->len[1] = 0;
276         }
277 }
278
279 } /* namespace */
280
281 #endif /* __ringbuffer_npt_h__ */