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