source: libwandio/ior-peek.c @ 954577b9

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since 954577b9 was 60f3c4c, checked in by Shane Alcock <salcock@…>, 9 years ago
  • Shifted wandio into a separate library, seeing as we're going to be exporting the API now
  • Property mode set to 100644
File size: 8.7 KB
Line 
1/*
2 * This file is part of libtrace
3 *
4 * Copyright (c) 2007,2008,2009,2010 The University of Waikato, Hamilton,
5 * New Zealand.
6 *
7 * Authors: Daniel Lawson
8 *          Perry Lorier
9 *          Shane Alcock
10 *         
11 * All rights reserved.
12 *
13 * This code has been developed by the University of Waikato WAND
14 * research group. For further information please see http://www.wand.net.nz/
15 *
16 * libtrace is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * libtrace is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with libtrace; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29 *
30 * $Id$
31 *
32 */
33
34#include "wandio.h"
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <fcntl.h>
38#include <stdlib.h>
39#include <unistd.h>
40#include <string.h>
41#include <assert.h>
42#include <stddef.h>
43
44/* Libtrace IO module implementing a peeking reader.
45 *
46 * Assuming my understanding of Perry's code is correct, this module provides
47 * generic support for "peeking" that can be used in concert with any other
48 * implemented IO reader.
49 *
50 * The other IO reader is a "child" to the peeking reader and is used to read
51 * the data into a buffer managed by the peeking reader. Any actual "peeks"
52 * are serviced from the managed buffer, which means that we do not have to
53 * manipulate the read offsets directly in zlib or bzip, for instance.
54 */
55
56/* for O_DIRECT we have to read in multiples of this */
57#define MIN_READ_SIZE 4096
58/* Round reads for peeks into the buffer up to this size */
59#define PEEK_SIZE (1024*1024)
60
61struct peek_t {
62        io_t *child;
63        char *buffer;
64        int length; /* Length of buffer */
65        int offset; /* Offset into buffer */
66};
67
68extern io_source_t peek_source;
69
70#define DATA(io) ((struct peek_t *)((io)->data))
71#define MIN(a,b) ((a) < (b) ? (a) : (b))
72
73io_t *peek_open(io_t *child)
74{
75        io_t *io;
76        if (!child)
77                return NULL;
78        io =  malloc(sizeof(io_t));
79        io->data = malloc(sizeof(struct peek_t));
80        io->source = &peek_source;
81
82        /* Wrap the peeking reader around the "child" */
83        DATA(io)->child = child;
84        DATA(io)->buffer = NULL;
85        DATA(io)->length = 0;
86        DATA(io)->offset = 0;   
87
88        return io;
89}
90
91/* Read at least "len" bytes from the child io into the internal buffer, and return how many
92   bytes was actually read.
93 */
94static off_t refill_buffer(io_t *io, off_t len)
95{
96        off_t bytes_read;
97        assert(DATA(io)->length - DATA(io)->offset == 0);
98        /* Select the largest of "len", PEEK_SIZE and the current peek buffer size
99         * and then round up to the nearest multiple of MIN_READ_SIZE
100         */
101        bytes_read = len < PEEK_SIZE ? PEEK_SIZE : len;
102        bytes_read = bytes_read < DATA(io)->length ? DATA(io)->length : bytes_read;
103        bytes_read += MIN_READ_SIZE - (bytes_read % MIN_READ_SIZE);
104        /* Is the current buffer big enough? */
105        if (DATA(io)->length < bytes_read) {
106                int res = 0;
107                void *buf_ptr = (void *)(DATA(io)->buffer);
108
109                if (buf_ptr)
110                        free(buf_ptr);
111                DATA(io)->length = bytes_read;
112                DATA(io)->offset = 0;
113#if _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600
114                /* We need to do this as read() of O_DIRECT might happen into
115                 * this buffer.  The docs suggest 512 bytes is all we need to
116                 * align to, but I'm suspicious. I expect disks with 4k blocks
117                 * will arrive soon, and thus 4k is the minimum I'm willing to
118                 * live with.
119                 */
120                res = posix_memalign(&buf_ptr, 4096, DATA(io)->length);
121                if (res != 0) {
122                        fprintf(stderr, "Error aligning IO buffer: %d\n",
123                                        res);
124                        return res;
125                }
126                DATA(io)->buffer = buf_ptr;
127#else
128                res = 0;        /* << Silly warning */ 
129                DATA(io)->buffer = malloc(DATA(io)->length);
130#endif
131        }
132        else
133                DATA(io)->length = bytes_read;
134
135        assert(DATA(io)->buffer);
136
137        /* Now actually attempt to read that many bytes */
138        bytes_read = DATA(io)->child->source->read(     
139                        DATA(io)->child, DATA(io)->buffer, bytes_read);
140
141        DATA(io)->offset = 0;
142        DATA(io)->length = bytes_read;
143       
144        /* Error? */
145        if (bytes_read < 1)
146                return bytes_read;
147
148        return bytes_read;
149       
150}
151
152static off_t peek_read(io_t *io, void *buffer, off_t len)
153{
154        off_t ret = 0;
155
156        /* Is some of this data in the buffer? */
157        if (DATA(io)->buffer) {
158                ret = MIN(len,DATA(io)->length - DATA(io)->offset);
159
160                /* Copy anything we've got into their buffer, and shift our
161                 * offset so that we don't peek at the data we've read again */
162                memcpy(buffer, 
163                        DATA(io)->buffer + DATA(io)->offset,
164                        ret);
165                buffer += ret;
166                DATA(io)->offset += ret;
167                len -= ret;
168        }
169
170        /* Use the child reader to get the rest of the required data */
171        if (len>0) {
172                /* To get here, the buffer must be empty */
173                assert(DATA(io)->length-DATA(io)->offset == 0);
174                off_t bytes_read;
175                /* If they're reading exactly a block size, just use that, no point in malloc'ing
176                 * and memcpy()ing needlessly.  However, if the buffer isn't aligned, we need to
177                 * pass on an aligning buffer, skip this and do it into our own aligned buffer.
178                 */
179                if ((len % MIN_READ_SIZE  == 0) && ((ptrdiff_t)buffer % 4096)==0) {
180                        assert(((ptrdiff_t)buffer % 4096) == 0);
181                        bytes_read = DATA(io)->child->source->read(
182                                        DATA(io)->child, buffer, len);
183                        /* Error? */
184                        if (bytes_read < 1) {
185                                /* Return if we have managed to get some data ok */
186                                if (ret > 0)
187                                        return ret;
188                                /* Return the error upstream */
189                                return bytes_read;
190                        }
191                }
192                else {
193                        bytes_read = refill_buffer(io, len);
194                        if (bytes_read < 1) {
195                                /* Return if we have managed to get some data ok */
196                                if (ret > 0)
197                                        return ret;
198                                /* Return the error upstream */
199                                return bytes_read;
200                        }
201                        /* Now grab the number of bytes asked for. */
202                        len = len < bytes_read ? len : bytes_read;
203                        memcpy(buffer, DATA(io)->buffer, len);
204
205                        DATA(io)->offset = len;
206                        bytes_read = len;
207                }
208                ret += bytes_read;
209        }
210
211        /* Have we read past the end of the buffer? */
212        if (DATA(io)->buffer && DATA(io)->offset >= DATA(io)->length) {
213                /* If so, free the memory it used */
214                free(DATA(io)->buffer);
215                DATA(io)->buffer = NULL;
216                DATA(io)->offset = 0;
217                DATA(io)->length = 0;
218        }
219
220        return ret;
221}
222
223static void *alignedrealloc(void *old, size_t oldsize, size_t size, int *res)
224{
225#if _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600
226        void *new;
227        /* Shortcut resizing */
228        if (size < oldsize)
229                return old;
230        *res = posix_memalign(&new, 4096, size);
231        if (*res != 0) {
232                fprintf(stderr, "Error aligning IO buffer: %d\n", *res);
233               
234                return NULL;
235        }
236        assert(oldsize<size);
237        memcpy(new,old,oldsize);
238        free(old);
239        return new;
240#else
241        /* These no-ops are to stop the compiler whinging about unused
242         * parameters */
243        oldsize = oldsize;
244        res = res;
245        return realloc(old,size);
246#endif
247}
248
249
250static off_t peek_peek(io_t *io, void *buffer, off_t len)
251{
252        off_t ret = 0;
253        int res = 0;
254
255        /* Is there enough data in the buffer to serve this request? */
256        if (DATA(io)->length - DATA(io)->offset < len) {
257                /* No, we need to extend the buffer. */
258                off_t read_amount = len - (DATA(io)->length - DATA(io)->offset);
259                /* Round the read_amount up to the nearest MB */
260                read_amount += PEEK_SIZE - ((DATA(io)->length + read_amount) % PEEK_SIZE);
261                DATA(io)->buffer = alignedrealloc(DATA(io)->buffer, 
262                        DATA(io)->length, 
263                        DATA(io)->length + read_amount, &res);
264
265                if (DATA(io)->buffer == NULL) {
266                        return res;     
267                }
268
269                /* Use the child reader to read more data into our managed
270                 * buffer */
271                read_amount = wandio_read(DATA(io)->child, 
272                        DATA(io)->buffer + DATA(io)->length,
273                        read_amount);
274
275                /* Pass errors up */
276                if (read_amount <0) {
277                        return read_amount;
278                }
279
280                DATA(io)->length += read_amount;
281        }
282
283        /* Right, now return data from the buffer (that now should be large
284         * enough, but might not be if we hit EOF) */
285        ret = MIN(len, DATA(io)->length - DATA(io)->offset);
286        memcpy(buffer, DATA(io)->buffer + DATA(io)->offset, ret);
287        return ret;
288}
289
290static off_t peek_tell(io_t *io)
291{
292        /* We don't actually maintain a read offset as such, so we want to
293         * return the child's read offset */
294        return wandio_tell(DATA(io)->child);
295}
296
297static off_t peek_seek(io_t *io, off_t offset, int whence)
298{
299        /* Again, we don't have a genuine read offset so we need to pass this
300         * one on to the child */
301        return wandio_seek(DATA(io)->child,offset,whence);
302}
303
304static void peek_close(io_t *io)
305{
306        /* Make sure we close the child that is doing the actual reading! */
307        wandio_destroy(DATA(io)->child);
308        if (DATA(io)->buffer)
309                free(DATA(io)->buffer);
310        free(io->data);
311        free(io);
312}
313
314io_source_t peek_source = {
315        "peek",
316        peek_read,
317        peek_peek,
318        peek_tell,
319        peek_seek,
320        peek_close
321};
322
Note: See TracBrowser for help on using the repository browser.