c9222597a873da93ca5f2081bc7f314d73a9a9d1
[debian/gnuradio] / gnuradio-core / src / lib / general / gr_circular_file.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2002,2010 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 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <gr_circular_file.h>
28
29 #include <unistd.h>
30 #ifdef HAVE_SYS_MMAN_H
31 #include <sys/mman.h>
32 #endif
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <assert.h>
39 #include <stdlib.h>
40
41 #include <algorithm>
42 #include <stdio.h>
43 #include <string.h>
44
45 static const int HEADER_SIZE = 4096;
46 static const int HEADER_MAGIC = 0xEB021026;
47
48 static const int HD_MAGIC = 0;
49 static const int HD_HEADER_SIZE = 1;    // integer offsets into header
50 static const int HD_BUFFER_SIZE = 2;
51 static const int HD_BUFFER_BASE = 3;
52 static const int HD_BUFFER_CURRENT = 4;
53
54 gr_circular_file::gr_circular_file (const char *filename,
55                                     bool writable, int size)
56   : d_fd (-1), d_header (0), d_buffer (0), d_mapped_size (0), d_bytes_read (0)
57 {
58   int   mm_prot;
59   if (writable){
60 #ifdef HAVE_MMAP
61     mm_prot = PROT_READ | PROT_WRITE;
62 #endif
63     d_fd = open (filename, O_CREAT | O_RDWR | O_TRUNC, 0664);
64     if (d_fd < 0){
65       perror (filename);
66       exit (1);
67     }
68 #ifdef HAVE_MMAP        /* FIXME */
69     if(ftruncate (d_fd, size + HEADER_SIZE) != 0) {
70       perror (filename);
71       exit (1);
72     }
73 #endif
74   }
75   else {
76 #ifdef HAVE_MMAP
77     mm_prot = PROT_READ;
78 #endif
79     d_fd = open (filename, O_RDONLY);
80     if (d_fd < 0){
81       perror (filename);
82       exit (1);
83     }
84   }
85
86   struct stat statbuf;
87   if (fstat (d_fd, &statbuf) < 0){
88     perror (filename);
89     exit (1);
90   }
91
92   if (statbuf.st_size < HEADER_SIZE){
93     fprintf (stderr, "%s: file too small to be circular buffer\n", filename);
94     exit (1);
95   }
96
97   d_mapped_size = statbuf.st_size;
98 #ifdef HAVE_MMAP
99   void *p = mmap (0, d_mapped_size, mm_prot, MAP_SHARED, d_fd, 0);
100   if (p == MAP_FAILED){
101     perror ("gr_circular_file: mmap failed");
102     exit (1);
103   }
104
105   d_header = (int *) p;
106 #else
107     perror ("gr_circular_file: mmap unsupported by this system");
108     exit (1);
109 #endif
110
111   if (writable){        // init header
112
113     if (size < 0){
114       fprintf (stderr, "gr_circular_buffer: size must be > 0 when writable\n");
115       exit (1);
116     }
117
118     d_header[HD_MAGIC] = HEADER_MAGIC;
119     d_header[HD_HEADER_SIZE] = HEADER_SIZE;
120     d_header[HD_BUFFER_SIZE] = size;
121     d_header[HD_BUFFER_BASE] = HEADER_SIZE;     // right after header
122     d_header[HD_BUFFER_CURRENT] = 0;
123   }
124
125   // sanity check (the asserts are a bit unforgiving...)
126
127   assert (d_header[HD_MAGIC] == HEADER_MAGIC);
128   assert (d_header[HD_HEADER_SIZE] == HEADER_SIZE);
129   assert (d_header[HD_BUFFER_SIZE] > 0);
130   assert (d_header[HD_BUFFER_BASE] >= d_header[HD_HEADER_SIZE]);
131   assert (d_header[HD_BUFFER_BASE] + d_header[HD_BUFFER_SIZE] <= d_mapped_size);
132   assert (d_header[HD_BUFFER_CURRENT] >= 0 &&
133           d_header[HD_BUFFER_CURRENT] < d_header[HD_BUFFER_SIZE]);
134
135   d_bytes_read = 0;
136   d_buffer = (unsigned char *) d_header + d_header[HD_BUFFER_BASE];
137 }
138
139 gr_circular_file::~gr_circular_file ()
140 {
141 #ifdef HAVE_MMAP
142   if (munmap ((char *) d_header, d_mapped_size) < 0){
143     perror ("gr_circular_file: munmap");
144     exit (1);
145   }
146 #endif
147   close (d_fd);
148 }
149
150 bool
151 gr_circular_file::write (void *vdata, int nbytes)
152 {
153   unsigned char *data = (unsigned char *) vdata;
154   int   buffer_size = d_header[HD_BUFFER_SIZE];
155   int   buffer_current = d_header[HD_BUFFER_CURRENT];
156   
157   while (nbytes > 0){
158     int n = std::min (nbytes, buffer_size - buffer_current);
159     memcpy (d_buffer + buffer_current, data, n);
160     
161     buffer_current += n;
162     if (buffer_current >= buffer_size)
163       buffer_current = 0;
164     
165     data += n;
166     nbytes -= n;
167   }
168
169   d_header[HD_BUFFER_CURRENT] = buffer_current;
170   return true;
171 }
172
173 int
174 gr_circular_file::read (void *vdata, int nbytes)
175 {
176   unsigned char *data = (unsigned char *) vdata;
177   int   buffer_current = d_header[HD_BUFFER_CURRENT];
178   int   buffer_size = d_header[HD_BUFFER_SIZE];
179   int   total = 0;
180   
181   nbytes = std::min (nbytes, buffer_size - d_bytes_read);
182
183   while (nbytes > 0){
184     int offset = (buffer_current + d_bytes_read) % buffer_size;
185     int n = std::min (nbytes, buffer_size - offset);
186     memcpy (data, d_buffer + offset, n);
187     data += n;
188     d_bytes_read += n;
189     total += n;
190     nbytes -= n;
191   }
192   return total;
193 }
194
195 void
196 gr_circular_file::reset_read_pointer ()
197 {
198   d_bytes_read = 0;
199 }