Imported Upstream version 3.0
[debian/gnuradio] / gr-audio-osx / src / circular_buffer.h
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2006 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio.
6  *
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifndef _CIRCULAR_BUFFER_H_
24 #define _CIRCULAR_BUFFER_H_
25
26 #include "mld_threads.h"
27 #include <stdexcept>
28
29 #define DO_DEBUG 0
30
31 template <class T> class circular_buffer
32 {
33 private:
34 // the buffer to use
35   T* d_buffer;
36
37 // the following are in Items (type T)
38   UInt32 d_bufLen_I, d_readNdx_I, d_writeNdx_I;
39   UInt32 d_n_avail_write_I, d_n_avail_read_I;
40
41 // stuff to control access to class internals
42   mld_mutex_ptr d_internal;
43   mld_condition_ptr d_readBlock, d_writeBlock;
44
45 // booleans to decide how to control reading, writing, and aborting
46   bool d_doWriteBlock, d_doFullRead, d_doAbort;
47
48   void delete_mutex_cond () {
49     if (d_internal) {
50       delete d_internal;
51       d_internal = NULL;
52     }
53     if (d_readBlock) {
54       delete d_readBlock;
55       d_readBlock = NULL;
56     }
57     if (d_writeBlock) {
58       delete d_writeBlock;
59       d_writeBlock = NULL;
60     }
61   };
62
63 public:
64   circular_buffer (UInt32 bufLen_I,
65                    bool doWriteBlock = true, bool doFullRead = false) {
66     if (bufLen_I == 0)
67       throw std::runtime_error ("circular_buffer(): "
68                                 "Number of items to buffer must be > 0.\n");
69     d_bufLen_I = bufLen_I;
70     d_buffer = (T*) new T[d_bufLen_I];
71     d_doWriteBlock = doWriteBlock;
72     d_doFullRead = doFullRead;
73     d_internal = NULL;
74     d_readBlock = d_writeBlock = NULL;
75     reset ();
76 #if DO_DEBUG
77     fprintf (stderr, "c_b(): buf len (items) = %ld, "
78              "doWriteBlock = %s, doFullRead = %s\n", d_bufLen_I,
79              (d_doWriteBlock ? "true" : "false"),
80              (d_doFullRead ? "true" : "false"));
81 #endif
82   };
83
84   ~circular_buffer () {
85     delete_mutex_cond ();
86     delete [] d_buffer;
87   };
88
89   inline UInt32 n_avail_write_items () {
90     d_internal->lock ();
91     UInt32 retVal = d_n_avail_write_I;
92     d_internal->unlock ();
93     return (retVal);
94   };
95
96   inline UInt32 n_avail_read_items () {
97     d_internal->lock ();
98     UInt32 retVal = d_n_avail_read_I;
99     d_internal->unlock ();
100     return (retVal);
101   };
102
103   inline UInt32 buffer_length_items () {return (d_bufLen_I);};
104   inline bool do_write_block () {return (d_doWriteBlock);};
105   inline bool do_full_read () {return (d_doFullRead);};
106
107   void reset () {
108     d_doAbort = false;
109     bzero (d_buffer, d_bufLen_I * sizeof (T));
110     d_readNdx_I = d_writeNdx_I = d_n_avail_read_I = 0;
111     d_n_avail_write_I = d_bufLen_I;
112     delete_mutex_cond ();
113     d_internal = new mld_mutex ();
114     d_readBlock = new mld_condition ();
115     d_writeBlock = new mld_condition ();
116   };
117
118 /*
119  * enqueue: add the given buffer of item-length to the queue,
120  *     first-in-first-out (FIFO).
121  *
122  * inputs:
123  *     buf: a pointer to the buffer holding the data
124  *
125  *     bufLen_I: the buffer length in items (of the instantiated type)
126  *
127  * returns:
128  *    -1: on overflow (write is not blocking, and data is being
129  *                     written faster than it is being read)
130  *     0: if nothing to do (0 length buffer)
131  *     1: if success
132  *     2: in the process of aborting, do doing nothing
133  *
134  * will throw runtime errors if inputs are improper:
135  *     buffer pointer is NULL
136  *     buffer length is larger than the instantiated buffer length
137  */
138
139   int enqueue (T* buf, UInt32 bufLen_I) {
140 #if DO_DEBUG
141     fprintf (stderr, "enqueue: buf = %X, bufLen = %ld, #av_wr = %ld, "
142              "#av_rd = %ld.\n", (unsigned int)buf, bufLen_I,
143              d_n_avail_write_I, d_n_avail_read_I);
144 #endif
145     if (bufLen_I > d_bufLen_I) {
146       fprintf (stderr, "cannot add buffer longer (%ld"
147                ") than instantiated length (%ld"
148                ").\n", bufLen_I, d_bufLen_I);
149       throw std::runtime_error ("circular_buffer::enqueue()");
150     }
151
152     if (bufLen_I == 0)
153       return (0);
154     if (!buf)
155       throw std::runtime_error ("circular_buffer::enqueue(): "
156                                 "input buffer is NULL.\n");
157     d_internal->lock ();
158     if (d_doAbort) {
159       d_internal->unlock ();
160       return (2);
161     }
162     if (bufLen_I > d_n_avail_write_I) {
163       if (d_doWriteBlock) {
164         while (bufLen_I > d_n_avail_write_I) {
165 #if DO_DEBUG
166           fprintf (stderr, "enqueue: #len > #a, waiting.\n");
167 #endif
168           d_internal->unlock ();
169           d_writeBlock->wait ();
170           d_internal->lock ();
171           if (d_doAbort) {
172             d_internal->unlock ();
173 #if DO_DEBUG
174             fprintf (stderr, "enqueue: #len > #a, aborting.\n");
175 #endif
176             return (2);
177           }
178 #if DO_DEBUG
179           fprintf (stderr, "enqueue: #len > #a, done waiting.\n");
180 #endif
181         }
182       } else {
183         d_n_avail_read_I = d_bufLen_I - bufLen_I;
184         d_n_avail_write_I = bufLen_I;
185 #if DO_DEBUG
186         fprintf (stderr, "circular_buffer::enqueue: overflow\n");
187 #endif
188         return (-1);
189       }
190     }
191     UInt32 n_now_I = d_bufLen_I - d_writeNdx_I, n_start_I = 0;
192     if (n_now_I > bufLen_I)
193       n_now_I = bufLen_I;
194     else if (n_now_I < bufLen_I)
195       n_start_I = bufLen_I - n_now_I;
196     bcopy (buf, &(d_buffer[d_writeNdx_I]), n_now_I * sizeof (T));
197     if (n_start_I) {
198       bcopy (&(buf[n_now_I]), d_buffer, n_start_I * sizeof (T));
199       d_writeNdx_I = n_start_I;
200     } else
201       d_writeNdx_I += n_now_I;
202     d_n_avail_read_I += bufLen_I;
203     d_n_avail_write_I -= bufLen_I;
204     d_readBlock->signal ();
205     d_internal->unlock ();
206     return (1);
207   };
208
209 /*
210  * dequeue: removes from the queue the number of items requested, or
211  *     available, into the given buffer on a FIFO basis.
212  *
213  * inputs:
214  *     buf: a pointer to the buffer into which to copy the data
215  *
216  *     bufLen_I: pointer to the number of items to remove in items
217  *         (of the instantiated type)
218  *
219  * returns:
220  *     0: if nothing to do (0 length buffer)
221  *     1: if success
222  *     2: in the process of aborting, do doing nothing
223  *
224  * will throw runtime errors if inputs are improper:
225  *     buffer pointer is NULL
226  *     buffer length pointer is NULL
227  *     buffer length is larger than the instantiated buffer length
228  */
229
230   int dequeue (T* buf, UInt32* bufLen_I) {
231 #if DO_DEBUG
232     fprintf (stderr, "dequeue: buf = %X, *bufLen = %ld, #av_wr = %ld, "
233              "#av_rd = %ld.\n", (unsigned int)buf, *bufLen_I,
234              d_n_avail_write_I, d_n_avail_read_I);
235 #endif
236     if (!bufLen_I)
237       throw std::runtime_error ("circular_buffer::dequeue(): "
238                                 "input bufLen pointer is NULL.\n");
239     if (!buf)
240       throw std::runtime_error ("circular_buffer::dequeue(): "
241                                 "input buffer pointer is NULL.\n");
242     UInt32 l_bufLen_I = *bufLen_I;
243     if (l_bufLen_I == 0)
244       return (0);
245     if (l_bufLen_I > d_bufLen_I) {
246       fprintf (stderr, "cannot remove buffer longer (%ld"
247                ") than instantiated length (%ld"
248                ").\n", l_bufLen_I, d_bufLen_I);
249       throw std::runtime_error ("circular_buffer::dequeue()");
250     }
251
252     d_internal->lock ();
253     if (d_doAbort) {
254       d_internal->unlock ();
255       return (2);
256     }
257     if (d_doFullRead) {
258       while (d_n_avail_read_I < l_bufLen_I) {
259 #if DO_DEBUG
260         fprintf (stderr, "dequeue: #a < #len, waiting.\n");
261 #endif
262         d_internal->unlock ();
263         d_readBlock->wait ();
264         d_internal->lock ();
265         if (d_doAbort) {
266           d_internal->unlock ();
267 #if DO_DEBUG
268           fprintf (stderr, "dequeue: #a < #len, aborting.\n");
269 #endif
270           return (2);
271         }
272 #if DO_DEBUG
273         fprintf (stderr, "dequeue: #a < #len, done waiting.\n");
274 #endif
275      }
276     } else {
277       while (d_n_avail_read_I == 0) {
278 #if DO_DEBUG
279         fprintf (stderr, "dequeue: #a == 0, waiting.\n");
280 #endif
281         d_internal->unlock ();
282         d_readBlock->wait ();
283         d_internal->lock ();
284         if (d_doAbort) {
285           d_internal->unlock ();
286 #if DO_DEBUG
287           fprintf (stderr, "dequeue: #a == 0, aborting.\n");
288 #endif
289           return (2);
290         }
291 #if DO_DEBUG
292         fprintf (stderr, "dequeue: #a == 0, done waiting.\n");
293 #endif
294       }
295     }
296     if (l_bufLen_I > d_n_avail_read_I)
297       l_bufLen_I = d_n_avail_read_I;
298     UInt32 n_now_I = d_bufLen_I - d_readNdx_I, n_start_I = 0;
299     if (n_now_I > l_bufLen_I)
300       n_now_I = l_bufLen_I;
301     else if (n_now_I < l_bufLen_I)
302       n_start_I = l_bufLen_I - n_now_I;
303     bcopy (&(d_buffer[d_readNdx_I]), buf, n_now_I * sizeof (T));
304     if (n_start_I) {
305       bcopy (d_buffer, &(buf[n_now_I]), n_start_I * sizeof (T));
306       d_readNdx_I = n_start_I;
307     } else
308       d_readNdx_I += n_now_I;
309     *bufLen_I = l_bufLen_I;
310     d_n_avail_read_I -= l_bufLen_I;
311     d_n_avail_write_I += l_bufLen_I;
312     d_writeBlock->signal ();
313     d_internal->unlock ();
314     return (1);
315   };
316
317   void abort () {
318     d_internal->lock ();
319     d_doAbort = true;
320     d_writeBlock->signal ();
321     d_readBlock->signal ();
322     d_internal->unlock ();
323   };
324 };
325
326 #endif /* _CIRCULAR_BUFFER_H_ */