Merge remote-tracking branch 'remotes/origin/cairocanvas' into windows
[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 namespace PBD {
29
30 /* ringbuffer class where the element size is not required to be a power of two */
31
32 template<class T>
33 class RingBufferNPT
34 {
35   public:
36         RingBufferNPT (size_t sz) {
37                 size = sz;
38                 buf = new T[size];
39                 reset ();
40         }
41         
42         virtual ~RingBufferNPT () {
43                 delete [] buf;
44         }
45
46         void reset () {
47                 /* !!! NOT THREAD SAFE !!! */
48                 g_atomic_int_set (&write_ptr, 0);
49                 g_atomic_int_set (&read_ptr, 0);
50         }
51
52         void set (size_t r, size_t w) {
53                 /* !!! NOT THREAD SAFE !!! */
54                 g_atomic_int_set (&write_ptr, w);
55                 g_atomic_int_set (&read_ptr, r);
56         }
57         
58         size_t  read  (T *dest, size_t cnt);
59         size_t  write (const T *src, size_t cnt);
60
61         struct rw_vector {
62             T *buf[2];
63             size_t len[2];
64         };
65
66         void get_read_vector (rw_vector *);
67         void get_write_vector (rw_vector *);
68         
69         void decrement_read_ptr (size_t cnt) {
70                 g_atomic_int_set (&read_ptr, (g_atomic_int_get(&read_ptr) - cnt) % size);
71         }                
72
73         void increment_read_ptr (size_t cnt) {
74                 g_atomic_int_set (&read_ptr, (g_atomic_int_get(&read_ptr) + cnt) % size);
75         }                
76
77         void increment_write_ptr (size_t cnt) {
78                 g_atomic_int_set (&write_ptr,  (g_atomic_int_get(&write_ptr) + cnt) % size);
79         }                
80
81         size_t write_space () {
82                 size_t w, r;
83                 
84                 w = g_atomic_int_get (&write_ptr);
85                 r = g_atomic_int_get (&read_ptr);
86                 
87                 if (w > r) {
88                         return ((r - w + size) % size) - 1;
89                 } else if (w < r) {
90                         return (r - w) - 1;
91                 } else {
92                         return size - 1;
93                 }
94         }
95         
96         size_t read_space () {
97                 size_t w, r;
98                 
99                 w = g_atomic_int_get (&write_ptr);
100                 r = g_atomic_int_get (&read_ptr);
101                 
102                 if (w > r) {
103                         return w - r;
104                 } else {
105                         return (w - r + size) % size;
106                 }
107         }
108
109         T *buffer () { return buf; }
110         size_t get_write_ptr () const { return g_atomic_int_get (&write_ptr); }
111         size_t get_read_ptr () const { return g_atomic_int_get (&read_ptr); }
112         size_t bufsize () const { return size; }
113
114   protected:
115         T *buf;
116         size_t size;
117         mutable gint write_ptr;
118         mutable gint read_ptr;
119 };
120
121 template<class T> size_t
122 RingBufferNPT<T>::read (T *dest, size_t cnt)
123 {
124         size_t free_cnt;
125         size_t cnt2;
126         size_t to_read;
127         size_t n1, n2;
128         size_t priv_read_ptr;
129
130         priv_read_ptr=g_atomic_int_get(&read_ptr);
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_ptr + to_read;
139
140         if (cnt2 > size) {
141                 n1 = size - priv_read_ptr;
142                 n2 = cnt2 % size;
143         } else {
144                 n1 = to_read;
145                 n2 = 0;
146         }
147         
148         memcpy (dest, &buf[priv_read_ptr], n1 * sizeof (T));
149         priv_read_ptr = (priv_read_ptr + n1) % size;
150
151         if (n2) {
152                 memcpy (dest+n1, buf, n2 * sizeof (T));
153                 priv_read_ptr = n2;
154         }
155
156         g_atomic_int_set(&read_ptr, priv_read_ptr);
157         return to_read;
158 }
159
160 template<class T> size_t
161 RingBufferNPT<T>::write (const T *src, size_t cnt)
162 {
163         size_t free_cnt;
164         size_t cnt2;
165         size_t to_write;
166         size_t n1, n2;
167         size_t priv_write_ptr;
168
169         priv_write_ptr=g_atomic_int_get(&write_ptr);
170
171         if ((free_cnt = write_space ()) == 0) {
172                 return 0;
173         }
174
175         to_write = cnt > free_cnt ? free_cnt : cnt;
176         
177         cnt2 = priv_write_ptr + to_write;
178
179         if (cnt2 > size) {
180                 n1 = size - priv_write_ptr;
181                 n2 = cnt2 % size;
182         } else {
183                 n1 = to_write;
184                 n2 = 0;
185         }
186
187         memcpy (&buf[priv_write_ptr], src, n1 * sizeof (T));
188         priv_write_ptr = (priv_write_ptr + n1) % size;
189
190         if (n2) {
191                 memcpy (buf, src+n1, n2 * sizeof (T));
192                 priv_write_ptr = n2;
193         }
194
195         g_atomic_int_set(&write_ptr, priv_write_ptr);
196         return to_write;
197 }
198
199 template<class T> void
200 RingBufferNPT<T>::get_read_vector (typename RingBufferNPT<T>::rw_vector *vec)
201 {
202         size_t free_cnt;
203         size_t cnt2;
204         size_t w, r;
205         
206         w = g_atomic_int_get (&write_ptr);
207         r = g_atomic_int_get (&read_ptr);
208         
209         if (w > r) {
210                 free_cnt = w - r;
211         } else {
212                 free_cnt = (w - r + size) % size;
213         }
214
215         cnt2 = r + free_cnt;
216
217         if (cnt2 > size) {
218                 /* Two part vector: the rest of the buffer after the
219                    current write ptr, plus some from the start of 
220                    the buffer.
221                 */
222
223                 vec->buf[0] = &buf[r];
224                 vec->len[0] = size - r;
225                 vec->buf[1] = buf;
226                 vec->len[1] = cnt2 % size;
227
228         } else {
229                 
230                 /* Single part vector: just the rest of the buffer */
231                 
232                 vec->buf[0] = &buf[r];
233                 vec->len[0] = free_cnt;
234                 vec->buf[1] = 0;
235                 vec->len[1] = 0;
236         }
237 }
238
239 template<class T> void
240 RingBufferNPT<T>::get_write_vector (typename RingBufferNPT<T>::rw_vector *vec)
241 {
242         size_t free_cnt;
243         size_t cnt2;
244         size_t w, r;
245         
246         w = g_atomic_int_get (&write_ptr);
247         r = g_atomic_int_get (&read_ptr);
248         
249         if (w > r) {
250                 free_cnt = ((r - w + size) % size) - 1;
251         } else if (w < r) {
252                 free_cnt = (r - w) - 1;
253         } else {
254                 free_cnt = size - 1;
255         }
256         
257         cnt2 = w + free_cnt;
258
259         if (cnt2 > size) {
260                 
261                 /* Two part vector: the rest of the buffer after the
262                    current write ptr, plus some from the start of 
263                    the buffer.
264                 */
265
266                 vec->buf[0] = &buf[w];
267                 vec->len[0] = size - w;
268                 vec->buf[1] = buf;
269                 vec->len[1] = cnt2 % size;
270         } else {
271                 vec->buf[0] = &buf[w];
272                 vec->len[0] = free_cnt;
273                 vec->len[1] = 0;
274         }
275 }
276
277 } /* namespace */
278
279 #endif /* __ringbuffer_npt_h__ */