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