added some debugging comments; fixed a bug in the circular buffer so
[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 3, 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     // set the return value to 1: success; change if needed
163     int retval = 1;
164     if (bufLen_I > d_n_avail_write_I) {
165       if (d_doWriteBlock) {
166         while (bufLen_I > d_n_avail_write_I) {
167 #if DO_DEBUG
168           fprintf (stderr, "enqueue: #len > #a, waiting.\n");
169 #endif
170           d_internal->unlock ();
171           d_writeBlock->wait ();
172           d_internal->lock ();
173           if (d_doAbort) {
174             d_internal->unlock ();
175 #if DO_DEBUG
176             fprintf (stderr, "enqueue: #len > #a, aborting.\n");
177 #endif
178             return (2);
179           }
180 #if DO_DEBUG
181           fprintf (stderr, "enqueue: #len > #a, done waiting.\n");
182 #endif
183         }
184       } else {
185         d_n_avail_read_I = d_bufLen_I - bufLen_I;
186         d_n_avail_write_I = bufLen_I;
187 #if DO_DEBUG
188         fprintf (stderr, "circular_buffer::enqueue: overflow\n");
189 #endif
190         retval = -1;
191       }
192     }
193     UInt32 n_now_I = d_bufLen_I - d_writeNdx_I, n_start_I = 0;
194     if (n_now_I > bufLen_I)
195       n_now_I = bufLen_I;
196     else if (n_now_I < bufLen_I)
197       n_start_I = bufLen_I - n_now_I;
198     bcopy (buf, &(d_buffer[d_writeNdx_I]), n_now_I * sizeof (T));
199     if (n_start_I) {
200       bcopy (&(buf[n_now_I]), d_buffer, n_start_I * sizeof (T));
201       d_writeNdx_I = n_start_I;
202     } else
203       d_writeNdx_I += n_now_I;
204     d_n_avail_read_I += bufLen_I;
205     d_n_avail_write_I -= bufLen_I;
206     d_readBlock->signal ();
207     d_internal->unlock ();
208     return (retval);
209   };
210
211 /*
212  * dequeue: removes from the queue the number of items requested, or
213  *     available, into the given buffer on a FIFO basis.
214  *
215  * inputs:
216  *     buf: a pointer to the buffer into which to copy the data
217  *
218  *     bufLen_I: pointer to the number of items to remove in items
219  *         (of the instantiated type)
220  *
221  * returns:
222  *     0: if nothing to do (0 length buffer)
223  *     1: if success
224  *     2: in the process of aborting, do doing nothing
225  *
226  * will throw runtime errors if inputs are improper:
227  *     buffer pointer is NULL
228  *     buffer length pointer is NULL
229  *     buffer length is larger than the instantiated buffer length
230  */
231
232   int dequeue (T* buf, UInt32* bufLen_I) {
233 #if DO_DEBUG
234     fprintf (stderr, "dequeue: buf = %X, *bufLen = %ld, #av_wr = %ld, "
235              "#av_rd = %ld.\n", (unsigned int)buf, *bufLen_I,
236              d_n_avail_write_I, d_n_avail_read_I);
237 #endif
238     if (!bufLen_I)
239       throw std::runtime_error ("circular_buffer::dequeue(): "
240                                 "input bufLen pointer is NULL.\n");
241     if (!buf)
242       throw std::runtime_error ("circular_buffer::dequeue(): "
243                                 "input buffer pointer is NULL.\n");
244     UInt32 l_bufLen_I = *bufLen_I;
245     if (l_bufLen_I == 0)
246       return (0);
247     if (l_bufLen_I > d_bufLen_I) {
248       fprintf (stderr, "cannot remove buffer longer (%ld"
249                ") than instantiated length (%ld"
250                ").\n", l_bufLen_I, d_bufLen_I);
251       throw std::runtime_error ("circular_buffer::dequeue()");
252     }
253
254     d_internal->lock ();
255     if (d_doAbort) {
256       d_internal->unlock ();
257       return (2);
258     }
259     if (d_doFullRead) {
260       while (d_n_avail_read_I < l_bufLen_I) {
261 #if DO_DEBUG
262         fprintf (stderr, "dequeue: #a < #len, waiting.\n");
263 #endif
264         d_internal->unlock ();
265         d_readBlock->wait ();
266         d_internal->lock ();
267         if (d_doAbort) {
268           d_internal->unlock ();
269 #if DO_DEBUG
270           fprintf (stderr, "dequeue: #a < #len, aborting.\n");
271 #endif
272           return (2);
273         }
274 #if DO_DEBUG
275         fprintf (stderr, "dequeue: #a < #len, done waiting.\n");
276 #endif
277      }
278     } else {
279       while (d_n_avail_read_I == 0) {
280 #if DO_DEBUG
281         fprintf (stderr, "dequeue: #a == 0, waiting.\n");
282 #endif
283         d_internal->unlock ();
284         d_readBlock->wait ();
285         d_internal->lock ();
286         if (d_doAbort) {
287           d_internal->unlock ();
288 #if DO_DEBUG
289           fprintf (stderr, "dequeue: #a == 0, aborting.\n");
290 #endif
291           return (2);
292         }
293 #if DO_DEBUG
294         fprintf (stderr, "dequeue: #a == 0, done waiting.\n");
295 #endif
296       }
297     }
298     if (l_bufLen_I > d_n_avail_read_I)
299       l_bufLen_I = d_n_avail_read_I;
300     UInt32 n_now_I = d_bufLen_I - d_readNdx_I, n_start_I = 0;
301     if (n_now_I > l_bufLen_I)
302       n_now_I = l_bufLen_I;
303     else if (n_now_I < l_bufLen_I)
304       n_start_I = l_bufLen_I - n_now_I;
305     bcopy (&(d_buffer[d_readNdx_I]), buf, n_now_I * sizeof (T));
306     if (n_start_I) {
307       bcopy (d_buffer, &(buf[n_now_I]), n_start_I * sizeof (T));
308       d_readNdx_I = n_start_I;
309     } else
310       d_readNdx_I += n_now_I;
311     *bufLen_I = l_bufLen_I;
312     d_n_avail_read_I -= l_bufLen_I;
313     d_n_avail_write_I += l_bufLen_I;
314     d_writeBlock->signal ();
315     d_internal->unlock ();
316     return (1);
317   };
318
319   void abort () {
320     d_internal->lock ();
321     d_doAbort = true;
322     d_writeBlock->signal ();
323     d_readBlock->signal ();
324     d_internal->unlock ();
325   };
326 };
327
328 #endif /* _CIRCULAR_BUFFER_H_ */