Imported Upstream version 3.0
[debian/gnuradio] / gnuradio-core / src / lib / runtime / gr_buffer.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2004 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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <gr_buffer.h>
28 #include <gr_vmcircbuf.h>
29 #include <gr_math.h>
30 #include <stdexcept>
31 #include <iostream>
32 #include <assert.h>
33 #include <algorithm>
34
35 static long s_buffer_count = 0;         // counts for debugging storage mgmt
36 static long s_buffer_reader_count = 0;
37
38 // ----------------------------------------------------------------------------
39 //                      Notes on storage management
40 //
41 // Pretty much all the fundamental classes are now using the
42 // shared_ptr stuff for automatic reference counting.  To ensure that
43 // no mistakes are made, we make the constructors for classes private,
44 // and then provide a free factory function that returns a smart
45 // pointer to the desired class.
46 //
47 // gr_buffer and gr_buffer_reader are no exceptions.  However, they
48 // both want pointers to each other, and unless we do something, we'll
49 // never delete any of them because of the circular structure.
50 // They'll always have a reference count of at least one.  We could
51 // use boost::weak_ptr's from gr_buffer to gr_buffer_reader but that
52 // introduces it's own problems.  (gr_buffer_reader's destructor needs
53 // to call gr_buffer::drop_reader, but has no easy way to get a
54 // shared_ptr to itself.)
55 //
56 // Instead, we solve this problem by having gr_buffer hold a raw
57 // pointer to gr_buffer_reader in its d_reader vector.
58 // gr_buffer_reader's destructor calls gr_buffer::drop_reader, so
59 // we're never left with an dangling pointer.  gr_buffer_reader still
60 // has a shared_ptr to the buffer ensuring that the buffer doesn't go
61 // away under it.  However, when the reference count of a
62 // gr_buffer_reader goes to zero, we can successfully reclaim it.
63 // ----------------------------------------------------------------------------
64
65
66 /*
67  * Compute the minimum number of buffer items that work (i.e.,
68  * address space wrap-around works).  To work is to satisfy this
69  * contraint for integer buffer_size and k:
70  *
71  *     type_size * nitems == k * page_size
72  */
73 static long
74 minimum_buffer_items (long type_size, long page_size)
75 {
76   return page_size / gr_gcd (type_size, page_size);
77 }
78
79
80 gr_buffer::gr_buffer (int nitems, size_t sizeof_item)
81   : d_base (0), d_bufsize (0), d_vmcircbuf (0),
82     d_sizeof_item (sizeof_item), d_write_index (0),
83     d_done (false)
84 {
85   if (!allocate_buffer (nitems, sizeof_item))
86     throw std::bad_alloc ();
87
88   s_buffer_count++;
89 }
90
91 gr_buffer_sptr 
92 gr_make_buffer (int nitems, size_t sizeof_item)
93 {
94   return gr_buffer_sptr (new gr_buffer (nitems, sizeof_item));
95 }
96
97 gr_buffer::~gr_buffer ()
98 {
99   delete d_vmcircbuf;
100   assert (d_readers.size() == 0);
101   s_buffer_count--;
102 }
103
104 /*!
105  * sets d_vmcircbuf, d_base, d_bufsize.
106  * returns true iff successful.
107  */
108 bool
109 gr_buffer::allocate_buffer (int nitems, size_t sizeof_item)
110 {
111   int   orig_nitems = nitems;
112   
113   // Any buffersize we come up with must be a multiple of min_nitems.
114
115   int granularity = gr_vmcircbuf_sysconfig::granularity ();
116   int min_nitems =  minimum_buffer_items (sizeof_item, granularity);
117
118   // Round-up nitems to a multiple of min_nitems.
119
120   if (nitems % min_nitems != 0)
121     nitems = ((nitems / min_nitems) + 1) * min_nitems;
122
123   // If we rounded-up a whole bunch, give the user a heads up.
124   // This only happens if sizeof_item is not a power of two.
125
126   if (nitems > 2 * orig_nitems && nitems * (int) sizeof_item > granularity){
127     std::cerr << "gr_buffer::allocate_buffer: warning: tried to allocate\n"
128               << "   " << orig_nitems << " items of size "
129               << sizeof_item << ". Due to alignment requirements\n"
130               << "   " << nitems << " were allocated.  If this isn't OK, consider padding\n"
131               << "   your structure to a power-of-two bytes.\n"
132               << "   On this platform, our allocation granularity is " << granularity << " bytes.\n";
133   }
134
135   d_bufsize = nitems;
136   d_vmcircbuf = gr_vmcircbuf_sysconfig::make (d_bufsize * d_sizeof_item);
137   if (d_vmcircbuf == 0){
138     std::cerr << "gr_buffer::allocate_buffer: failed to allocate buffer of size "
139               << d_bufsize * d_sizeof_item / 1024 << " KB\n";
140     return false;
141   }
142
143   d_base = (char *) d_vmcircbuf->pointer_to_first_copy ();
144   return true;
145 }
146
147
148 int
149 gr_buffer::space_available () const
150 {
151   if (d_readers.empty ())
152     return d_bufsize - 1;       // See comment below
153
154   else {
155
156     // Find out the maximum amount of data available to our readers
157
158     int most_data = d_readers[0]->items_available ();
159     for (unsigned int i = 1; i < d_readers.size (); i++)
160       most_data = std::max (most_data, d_readers[i]->items_available ());
161
162     // The -1 ensures that the case d_write_index == d_read_index is
163     // unambiguous.  It indicates that there is no data for the reader
164
165     return d_bufsize - most_data - 1;
166   }
167 }
168
169 void *
170 gr_buffer::write_pointer ()
171 {
172   return &d_base[d_write_index * d_sizeof_item];
173 }
174
175 void
176 gr_buffer::update_write_pointer (int nitems)
177 {
178   d_write_index = index_add (d_write_index, nitems);
179 }
180
181 gr_buffer_reader_sptr
182 gr_buffer_add_reader (gr_buffer_sptr buf, int nzero_preload)
183 {
184   if (nzero_preload < 0)
185     throw std::invalid_argument("gr_buffer_add_reader: nzero_preload must be >= 0");
186
187   gr_buffer_reader_sptr r (new gr_buffer_reader (buf,
188                                                  buf->index_sub(buf->d_write_index,
189                                                                 nzero_preload)));
190   buf->d_readers.push_back (r.get ());
191
192   return r;
193 }
194
195 void
196 gr_buffer::drop_reader (gr_buffer_reader *reader)
197 {
198   // isn't C++ beautiful...    GAG!
199
200   std::vector<gr_buffer_reader *>::iterator result =
201     std::find (d_readers.begin (), d_readers.end (), reader);
202
203   if (result == d_readers.end ())
204     throw std::invalid_argument ("gr_buffer::drop_reader");    // we didn't find it...
205
206   d_readers.erase (result);
207 }
208
209 long
210 gr_buffer_ncurrently_allocated ()
211 {
212   return s_buffer_count;
213 }
214
215 // ----------------------------------------------------------------------------
216
217 gr_buffer_reader::gr_buffer_reader (gr_buffer_sptr buffer, unsigned int read_index)
218   : d_buffer (buffer), d_read_index (read_index)
219 {
220   s_buffer_reader_count++;
221 }
222
223 gr_buffer_reader::~gr_buffer_reader ()
224 {
225   d_buffer->drop_reader(this);
226   s_buffer_reader_count--;
227 }
228    
229 int
230 gr_buffer_reader::items_available () const
231 {
232   return d_buffer->index_sub (d_buffer->d_write_index, d_read_index);
233 }
234
235 const void *
236 gr_buffer_reader::read_pointer ()
237 {
238   return &d_buffer->d_base[d_read_index * d_buffer->d_sizeof_item];
239 }
240
241 void
242 gr_buffer_reader::update_read_pointer (int nitems)
243 {
244   d_read_index = d_buffer->index_add (d_read_index, nitems);
245 }
246
247 long
248 gr_buffer_reader_ncurrently_allocated ()
249 {
250   return s_buffer_reader_count;
251 }