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