Merge remote-tracking branch 'remotes/origin/cairocanvas' into windows
[ardour.git] / libs / pbd / pbd / ringbuffer.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_h
21 #define ringbuffer_h
22
23 #include <cstring>
24 #include <glib.h>
25
26 template<class T>
27 class RingBuffer 
28 {
29   public:
30         RingBuffer (guint sz) {
31 //      size = ffs(sz); /* find first [bit] set is a single inlined assembly instruction. But it looks like the API rounds up so... */
32         guint power_of_two;
33         for (power_of_two = 1; 1U<<power_of_two < sz; power_of_two++) {}
34         size = 1<<power_of_two;
35         size_mask = size;
36         size_mask -= 1;
37         buf = new T[size];
38         reset ();
39         }
40         
41         virtual ~RingBuffer() {
42                 delete [] buf;
43         }
44
45         void reset () {
46                 /* !!! NOT THREAD SAFE !!! */
47                 g_atomic_int_set (&write_idx, 0);
48                 g_atomic_int_set (&read_idx, 0);
49         }
50
51         void set (guint r, guint w) {
52                 /* !!! NOT THREAD SAFE !!! */
53                 g_atomic_int_set (&write_idx, w);
54                 g_atomic_int_set (&read_idx, r);
55         }
56         
57         guint read  (T *dest, guint cnt);
58         guint write (T const * src,  guint cnt);
59
60         struct rw_vector {
61             T *buf[2];
62             guint len[2];
63         };
64
65         void get_read_vector (rw_vector *);
66         void get_write_vector (rw_vector *);
67         
68         void decrement_read_idx (guint cnt) {
69                 g_atomic_int_set (&read_idx, (g_atomic_int_get(&read_idx) - cnt) & size_mask);
70         }                
71
72         void increment_read_idx (guint cnt) {
73                 g_atomic_int_set (&read_idx, (g_atomic_int_get(&read_idx) + cnt) & size_mask);
74         }                
75
76         void increment_write_idx (guint cnt) {
77                 g_atomic_int_set (&write_idx,  (g_atomic_int_get(&write_idx) + cnt) & size_mask);
78         }                
79
80         guint write_space () {
81                 guint w, r;
82                 
83                 w = g_atomic_int_get (&write_idx);
84                 r = g_atomic_int_get (&read_idx);
85                 
86                 if (w > r) {
87                         return ((r - w + size) & size_mask) - 1;
88                 } else if (w < r) {
89                         return (r - w) - 1;
90                 } else {
91                         return size - 1;
92                 }
93         }
94         
95         guint read_space () {
96                 guint w, r;
97                 
98                 w = g_atomic_int_get (&write_idx);
99                 r = g_atomic_int_get (&read_idx);
100                 
101                 if (w > r) {
102                         return w - r;
103                 } else {
104                         return (w - r + size) & size_mask;
105                 }
106         }
107
108         T *buffer () { return buf; }
109         guint get_write_idx () const { return g_atomic_int_get (&write_idx); }
110         guint get_read_idx () const { return g_atomic_int_get (&read_idx); }
111         guint bufsize () const { return size; }
112
113   protected:
114         T *buf;
115         guint size;
116         mutable gint write_idx;
117         mutable gint read_idx;
118         guint size_mask;
119 };
120
121 template<class T> guint 
122 RingBuffer<T>::read (T *dest, guint cnt)
123 {
124         guint free_cnt;
125         guint cnt2;
126         guint to_read;
127         guint n1, n2;
128         guint priv_read_idx;
129
130         priv_read_idx=g_atomic_int_get(&read_idx);
131
132         if ((free_cnt = read_space ()) == 0) {
133                 return 0;
134         }
135
136         to_read = cnt > free_cnt ? free_cnt : cnt;
137         
138         cnt2 = priv_read_idx + to_read;
139
140         if (cnt2 > size) {
141                 n1 = size - priv_read_idx;
142                 n2 = cnt2 & size_mask;
143         } else {
144                 n1 = to_read;
145                 n2 = 0;
146         }
147         
148         memcpy (dest, &buf[priv_read_idx], n1 * sizeof (T));
149         priv_read_idx = (priv_read_idx + n1) & size_mask;
150
151         if (n2) {
152                 memcpy (dest+n1, buf, n2 * sizeof (T));
153                 priv_read_idx = n2;
154         }
155
156         g_atomic_int_set(&read_idx, priv_read_idx);
157         return to_read;
158 }
159
160 template<class T> guint
161 RingBuffer<T>::write (T const *src, guint cnt)
162
163 {
164         guint free_cnt;
165         guint cnt2;
166         guint to_write;
167         guint n1, n2;
168         guint priv_write_idx;
169
170         priv_write_idx=g_atomic_int_get(&write_idx);
171
172         if ((free_cnt = write_space ()) == 0) {
173                 return 0;
174         }
175
176         to_write = cnt > free_cnt ? free_cnt : cnt;
177         
178         cnt2 = priv_write_idx + to_write;
179
180         if (cnt2 > size) {
181                 n1 = size - priv_write_idx;
182                 n2 = cnt2 & size_mask;
183         } else {
184                 n1 = to_write;
185                 n2 = 0;
186         }
187
188         memcpy (&buf[priv_write_idx], src, n1 * sizeof (T));
189         priv_write_idx = (priv_write_idx + n1) & size_mask;
190
191         if (n2) {
192                 memcpy (buf, src+n1, n2 * sizeof (T));
193                 priv_write_idx = n2;
194         }
195
196         g_atomic_int_set(&write_idx, priv_write_idx);
197         return to_write;
198 }
199
200 template<class T> void
201 RingBuffer<T>::get_read_vector (typename RingBuffer<T>::rw_vector *vec)
202
203 {
204         guint free_cnt;
205         guint cnt2;
206         guint w, r;
207         
208         w = g_atomic_int_get (&write_idx);
209         r = g_atomic_int_get (&read_idx);
210         
211         if (w > r) {
212                 free_cnt = w - r;
213         } else {
214                 free_cnt = (w - r + size) & size_mask;
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_mask;
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> void
242 RingBuffer<T>::get_write_vector (typename RingBuffer<T>::rw_vector *vec)
243
244 {
245         guint free_cnt;
246         guint cnt2;
247         guint w, r;
248         
249         w = g_atomic_int_get (&write_idx);
250         r = g_atomic_int_get (&read_idx);
251         
252         if (w > r) {
253                 free_cnt = ((r - w + size) & size_mask) - 1;
254         } else if (w < r) {
255                 free_cnt = (r - w) - 1;
256         } else {
257                 free_cnt = size - 1;
258         }
259         
260         cnt2 = w + free_cnt;
261
262         if (cnt2 > size) {
263                 
264                 /* Two part vector: the rest of the buffer after the
265                    current write ptr, plus some from the start of 
266                    the buffer.
267                 */
268
269                 vec->buf[0] = &buf[w];
270                 vec->len[0] = size - w;
271                 vec->buf[1] = buf;
272                 vec->len[1] = cnt2 & size_mask;
273         } else {
274                 vec->buf[0] = &buf[w];
275                 vec->len[0] = free_cnt;
276                 vec->len[1] = 0;
277         }
278 }
279
280
281 #endif /* __ringbuffer_h__ */