source: lib/ior-peek.c @ bf7e954

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since bf7e954 was bf7e954, checked in by Perry Lorier <perry@…>, 11 years ago

Clean up peeking ahead of files

  • Property mode set to 100644
File size: 7.1 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
43/* Libtrace IO module implementing a peeking reader.
44 *
45 * Assuming my understanding of Perry's code is correct, this module provides
46 * generic support for "peeking" that can be used in concert with any other
47 * implemented IO reader.
48 *
49 * The other IO reader is a "child" to the peeking reader and is used to read
50 * the data into a buffer managed by the peeking reader. Any actual "peeks"
51 * are serviced from the managed buffer, which means that we do not have to
52 * manipulate the read offsets directly in zlib or bzip, for instance.
53 */
54
55/* for O_DIRECT we have to read in multiples of this */
56#define MIN_READ_SIZE 4096
57/* Round reads for peeks into the buffer up to this size */
58#define PEEK_SIZE (1024*1024)
59
60struct peek_t {
61        io_t *child;
62        char *buffer;
63        int length; /* Length of buffer */
64        int offset; /* Offset into buffer */
65};
66
67extern io_source_t peek_source;
68
69#define DATA(io) ((struct peek_t *)((io)->data))
70#define MIN(a,b) ((a) < (b) ? (a) : (b))
71
72io_t *peek_open(io_t *child)
73{
74        io_t *io;
75        if (!child)
76                return NULL;
77        io =  malloc(sizeof(io_t));
78        io->data = malloc(sizeof(struct peek_t));
79        io->source = &peek_source;
80
81        /* Wrap the peeking reader around the "child" */
82        DATA(io)->child = child;
83        DATA(io)->buffer = NULL;
84        DATA(io)->length = 0;
85        DATA(io)->offset = 0;   
86
87        return io;
88}
89
90/* Read at least "len" bytes from the child io into the internal buffer, and return how many
91   bytes was actually read.
92 */
93static off_t refill_buffer(io_t *io, off_t len)
94{
95        off_t bytes_read;
96        assert(DATA(io)->length - DATA(io)->offset == 0);
97        /* Select the largest of "len", PEEK_SIZE and the current peek buffer size
98         * and then round up to the nearest multiple of MIN_READ_SIZE
99         */
100        bytes_read = len < PEEK_SIZE ? PEEK_SIZE : len;
101        bytes_read = bytes_read < DATA(io)->length ? DATA(io)->length : bytes_read;
102        bytes_read += MIN_READ_SIZE - (bytes_read % MIN_READ_SIZE);
103        /* Is the current buffer big enough? */
104        if (DATA(io)->length < bytes_read) {
105                if (DATA(io)->buffer)
106                        free(DATA(io)->buffer);
107                DATA(io)->length = bytes_read;
108                DATA(io)->offset = 0;
109                DATA(io)->buffer = malloc(DATA(io)->length);
110        }
111        else
112                DATA(io)->length = bytes_read;
113
114        /* Now actually attempt to read that many bytes */
115        bytes_read = DATA(io)->child->source->read(     
116                        DATA(io)->child, DATA(io)->buffer, bytes_read);
117
118        DATA(io)->offset = 0;
119        DATA(io)->length = bytes_read;
120
121        /* Error? */
122        if (bytes_read < 1)
123                return bytes_read;
124
125        return bytes_read;
126       
127}
128
129static off_t peek_read(io_t *io, void *buffer, off_t len)
130{
131        off_t ret = 0;
132
133        /* Is some of this data in the buffer? */
134        if (DATA(io)->buffer) {
135                ret = MIN(len,DATA(io)->length - DATA(io)->offset);
136
137                /* Copy anything we've got into their buffer, and shift our
138                 * offset so that we don't peek at the data we've read again */
139                memcpy(buffer, 
140                        DATA(io)->buffer + DATA(io)->offset,
141                        ret);
142                buffer += ret;
143                DATA(io)->offset += ret;
144                len -= ret;
145        }
146
147        /* Use the child reader to get the rest of the required data */
148        if (len>0) {
149                /* To get here, the buffer must be empty */
150                assert(DATA(io)->length-DATA(io)->offset == 0);
151                off_t bytes_read;
152                /* If they're reading exactly a block size, just use that, no point in malloc'ing
153                 * and memcpy()ing needlessly
154                 */
155                if (len % MIN_READ_SIZE  == 0) {
156                        bytes_read = DATA(io)->child->source->read(
157                                        DATA(io)->child, buffer, len);
158                        /* Error? */
159                        if (bytes_read < 1) {
160                                /* Return if we have managed to get some data ok */
161                                if (ret > 0)
162                                        return ret;
163                                /* Return the error upstream */
164                                return bytes_read;
165                        }
166                }
167                else {
168                        bytes_read = refill_buffer(io, len);
169                        if (bytes_read < 1) {
170                                /* Return if we have managed to get some data ok */
171                                if (ret > 0)
172                                        return ret;
173                                /* Return the error upstream */
174                                return bytes_read;
175                        }
176                        /* Now grab the number of bytes asked for. */
177                        len = len < bytes_read ? len : bytes_read;
178                        memcpy(buffer, DATA(io)->buffer, len);
179
180                        DATA(io)->offset = len;
181                        bytes_read = len;
182                }
183                ret += bytes_read;
184        }
185
186        /* Have we read past the end of the buffer? */
187        if (DATA(io)->buffer && DATA(io)->offset >= DATA(io)->length) {
188                /* If so, free the memory it used */
189                free(DATA(io)->buffer);
190                DATA(io)->buffer = NULL;
191                DATA(io)->offset = 0;
192                DATA(io)->length = 0;
193        }
194
195        return ret;
196}
197
198
199static off_t peek_peek(io_t *io, void *buffer, off_t len)
200{
201        off_t ret = 0;
202
203        /* Is there enough data in the buffer to serve this request? */
204        if (DATA(io)->length - DATA(io)->offset < len) {
205                /* No, we need to extend the buffer. */
206                off_t read_amount = len - (DATA(io)->length - DATA(io)->offset);
207                /* Round the read_amount up to the nearest MB */
208                read_amount += PEEK_SIZE - ((DATA(io)->length + read_amount) % PEEK_SIZE);
209                DATA(io)->buffer = realloc(DATA(io)->buffer, DATA(io)->length + read_amount);
210                /* Use the child reader to read more data into our managed
211                 * buffer */
212                read_amount = wandio_read(DATA(io)->child, 
213                        DATA(io)->buffer + DATA(io)->length,
214                        read_amount);
215
216                /* Pass errors up */
217                if (read_amount <1) {
218                        return read_amount;
219                }
220
221                DATA(io)->length += read_amount;
222        }
223
224        /* Right, now return data from the buffer (that now should be large
225         * enough, but might not be if we hit EOF) */
226        ret = MIN(len, DATA(io)->length - DATA(io)->offset);
227        memcpy(buffer, DATA(io)->buffer + DATA(io)->offset, ret);
228        return ret;
229}
230
231static off_t peek_tell(io_t *io)
232{
233        /* We don't actually maintain a read offset as such, so we want to
234         * return the child's read offset */
235        return wandio_tell(DATA(io)->child);
236}
237
238static off_t peek_seek(io_t *io, off_t offset, int whence)
239{
240        /* Again, we don't have a genuine read offset so we need to pass this
241         * one on to the child */
242        return wandio_seek(DATA(io)->child,offset,whence);
243}
244
245static void peek_close(io_t *io)
246{
247        /* Make sure we close the child that is doing the actual reading! */
248        wandio_destroy(DATA(io)->child);
249        if (DATA(io)->buffer)
250                free(DATA(io)->buffer);
251        free(io->data);
252        free(io);
253}
254
255io_source_t peek_source = {
256        "peek",
257        peek_read,
258        peek_peek,
259        peek_tell,
260        peek_seek,
261        peek_close
262};
263
Note: See TracBrowser for help on using the repository browser.