zran.c revision 1.1.1.3 1 1.1 christos /* zran.c -- example of zlib/gzip stream indexing and random access
2 1.1.1.3 christos * Copyright (C) 2005, 2012, 2018 Mark Adler
3 1.1 christos * For conditions of distribution and use, see copyright notice in zlib.h
4 1.1.1.3 christos * Version 1.2 14 Oct 2018 Mark Adler */
5 1.1.1.2 christos
6 1.1.1.2 christos /* Version History:
7 1.1.1.2 christos 1.0 29 May 2005 First version
8 1.1.1.2 christos 1.1 29 Sep 2012 Fix memory reallocation error
9 1.1.1.3 christos 1.2 14 Oct 2018 Handle gzip streams with multiple members
10 1.1.1.3 christos Add a header file to facilitate usage in applications
11 1.1.1.2 christos */
12 1.1 christos
13 1.1 christos /* Illustrate the use of Z_BLOCK, inflatePrime(), and inflateSetDictionary()
14 1.1 christos for random access of a compressed file. A file containing a zlib or gzip
15 1.1 christos stream is provided on the command line. The compressed stream is decoded in
16 1.1 christos its entirety, and an index built with access points about every SPAN bytes
17 1.1 christos in the uncompressed output. The compressed file is left open, and can then
18 1.1 christos be read randomly, having to decompress on the average SPAN/2 uncompressed
19 1.1 christos bytes before getting to the desired block of data.
20 1.1 christos
21 1.1 christos An access point can be created at the start of any deflate block, by saving
22 1.1 christos the starting file offset and bit of that block, and the 32K bytes of
23 1.1 christos uncompressed data that precede that block. Also the uncompressed offset of
24 1.1.1.3 christos that block is saved to provide a reference for locating a desired starting
25 1.1.1.3 christos point in the uncompressed stream. deflate_index_build() works by
26 1.1.1.3 christos decompressing the input zlib or gzip stream a block at a time, and at the
27 1.1.1.3 christos end of each block deciding if enough uncompressed data has gone by to
28 1.1.1.3 christos justify the creation of a new access point. If so, that point is saved in a
29 1.1.1.3 christos data structure that grows as needed to accommodate the points.
30 1.1 christos
31 1.1 christos To use the index, an offset in the uncompressed data is provided, for which
32 1.1.1.2 christos the latest access point at or preceding that offset is located in the index.
33 1.1 christos The input file is positioned to the specified location in the index, and if
34 1.1 christos necessary the first few bits of the compressed data is read from the file.
35 1.1 christos inflate is initialized with those bits and the 32K of uncompressed data, and
36 1.1 christos the decompression then proceeds until the desired offset in the file is
37 1.1 christos reached. Then the decompression continues to read the desired uncompressed
38 1.1 christos data from the file.
39 1.1 christos
40 1.1 christos Another approach would be to generate the index on demand. In that case,
41 1.1 christos requests for random access reads from the compressed data would try to use
42 1.1 christos the index, but if a read far enough past the end of the index is required,
43 1.1 christos then further index entries would be generated and added.
44 1.1 christos
45 1.1 christos There is some fair bit of overhead to starting inflation for the random
46 1.1 christos access, mainly copying the 32K byte dictionary. So if small pieces of the
47 1.1 christos file are being accessed, it would make sense to implement a cache to hold
48 1.1.1.3 christos some lookahead and avoid many calls to deflate_index_extract() for small
49 1.1.1.3 christos lengths.
50 1.1 christos
51 1.1 christos Another way to build an index would be to use inflateCopy(). That would
52 1.1 christos not be constrained to have access points at block boundaries, but requires
53 1.1 christos more memory per access point, and also cannot be saved to file due to the
54 1.1 christos use of pointers in the state. The approach here allows for storage of the
55 1.1 christos index in a file.
56 1.1 christos */
57 1.1 christos
58 1.1 christos #include <stdio.h>
59 1.1 christos #include <stdlib.h>
60 1.1 christos #include <string.h>
61 1.1 christos #include "zlib.h"
62 1.1.1.3 christos #include "zran.h"
63 1.1 christos
64 1.1 christos #define WINSIZE 32768U /* sliding window size */
65 1.1 christos #define CHUNK 16384 /* file input buffer size */
66 1.1 christos
67 1.1.1.3 christos /* Access point entry. */
68 1.1 christos struct point {
69 1.1 christos off_t out; /* corresponding offset in uncompressed data */
70 1.1 christos off_t in; /* offset in input file of first full byte */
71 1.1.1.3 christos int bits; /* number of bits (1-7) from byte at in-1, or 0 */
72 1.1 christos unsigned char window[WINSIZE]; /* preceding 32K of uncompressed data */
73 1.1 christos };
74 1.1 christos
75 1.1.1.3 christos /* See comments in zran.h. */
76 1.1.1.3 christos void deflate_index_free(struct deflate_index *index)
77 1.1 christos {
78 1.1 christos if (index != NULL) {
79 1.1 christos free(index->list);
80 1.1 christos free(index);
81 1.1 christos }
82 1.1 christos }
83 1.1 christos
84 1.1.1.3 christos /* Add an entry to the access point list. If out of memory, deallocate the
85 1.1.1.3 christos existing list and return NULL. index->gzip is the allocated size of the
86 1.1.1.3 christos index in point entries, until it is time for deflate_index_build() to
87 1.1.1.3 christos return, at which point gzip is set to indicate a gzip file or not.
88 1.1.1.3 christos */
89 1.1.1.3 christos static struct deflate_index *addpoint(struct deflate_index *index, int bits,
90 1.1.1.3 christos off_t in, off_t out, unsigned left,
91 1.1.1.3 christos unsigned char *window)
92 1.1 christos {
93 1.1 christos struct point *next;
94 1.1 christos
95 1.1 christos /* if list is empty, create it (start with eight points) */
96 1.1 christos if (index == NULL) {
97 1.1.1.3 christos index = malloc(sizeof(struct deflate_index));
98 1.1 christos if (index == NULL) return NULL;
99 1.1 christos index->list = malloc(sizeof(struct point) << 3);
100 1.1 christos if (index->list == NULL) {
101 1.1 christos free(index);
102 1.1 christos return NULL;
103 1.1 christos }
104 1.1.1.3 christos index->gzip = 8;
105 1.1 christos index->have = 0;
106 1.1 christos }
107 1.1 christos
108 1.1 christos /* if list is full, make it bigger */
109 1.1.1.3 christos else if (index->have == index->gzip) {
110 1.1.1.3 christos index->gzip <<= 1;
111 1.1.1.3 christos next = realloc(index->list, sizeof(struct point) * index->gzip);
112 1.1 christos if (next == NULL) {
113 1.1.1.3 christos deflate_index_free(index);
114 1.1 christos return NULL;
115 1.1 christos }
116 1.1 christos index->list = next;
117 1.1 christos }
118 1.1 christos
119 1.1 christos /* fill in entry and increment how many we have */
120 1.1.1.3 christos next = (struct point *)(index->list) + index->have;
121 1.1 christos next->bits = bits;
122 1.1 christos next->in = in;
123 1.1 christos next->out = out;
124 1.1 christos if (left)
125 1.1 christos memcpy(next->window, window + WINSIZE - left, left);
126 1.1 christos if (left < WINSIZE)
127 1.1 christos memcpy(next->window + left, window, WINSIZE - left);
128 1.1 christos index->have++;
129 1.1 christos
130 1.1 christos /* return list, possibly reallocated */
131 1.1 christos return index;
132 1.1 christos }
133 1.1 christos
134 1.1.1.3 christos /* See comments in zran.h. */
135 1.1.1.3 christos int deflate_index_build(FILE *in, off_t span, struct deflate_index **built)
136 1.1 christos {
137 1.1 christos int ret;
138 1.1.1.3 christos int gzip = 0; /* true if reading a gzip file */
139 1.1 christos off_t totin, totout; /* our own total counters to avoid 4GB limit */
140 1.1 christos off_t last; /* totout value of last access point */
141 1.1.1.3 christos struct deflate_index *index; /* access points being generated */
142 1.1 christos z_stream strm;
143 1.1 christos unsigned char input[CHUNK];
144 1.1 christos unsigned char window[WINSIZE];
145 1.1 christos
146 1.1 christos /* initialize inflate */
147 1.1 christos strm.zalloc = Z_NULL;
148 1.1 christos strm.zfree = Z_NULL;
149 1.1 christos strm.opaque = Z_NULL;
150 1.1 christos strm.avail_in = 0;
151 1.1 christos strm.next_in = Z_NULL;
152 1.1 christos ret = inflateInit2(&strm, 47); /* automatic zlib or gzip decoding */
153 1.1 christos if (ret != Z_OK)
154 1.1 christos return ret;
155 1.1 christos
156 1.1 christos /* inflate the input, maintain a sliding window, and build an index -- this
157 1.1 christos also validates the integrity of the compressed data using the check
158 1.1.1.3 christos information in the gzip or zlib stream */
159 1.1 christos totin = totout = last = 0;
160 1.1 christos index = NULL; /* will be allocated by first addpoint() */
161 1.1 christos strm.avail_out = 0;
162 1.1 christos do {
163 1.1 christos /* get some compressed data from input file */
164 1.1 christos strm.avail_in = fread(input, 1, CHUNK, in);
165 1.1 christos if (ferror(in)) {
166 1.1 christos ret = Z_ERRNO;
167 1.1.1.3 christos goto deflate_index_build_error;
168 1.1 christos }
169 1.1 christos if (strm.avail_in == 0) {
170 1.1 christos ret = Z_DATA_ERROR;
171 1.1.1.3 christos goto deflate_index_build_error;
172 1.1 christos }
173 1.1 christos strm.next_in = input;
174 1.1 christos
175 1.1.1.3 christos /* check for a gzip stream */
176 1.1.1.3 christos if (totin == 0 && strm.avail_in >= 3 &&
177 1.1.1.3 christos input[0] == 31 && input[1] == 139 && input[2] == 8)
178 1.1.1.3 christos gzip = 1;
179 1.1.1.3 christos
180 1.1 christos /* process all of that, or until end of stream */
181 1.1 christos do {
182 1.1 christos /* reset sliding window if necessary */
183 1.1 christos if (strm.avail_out == 0) {
184 1.1 christos strm.avail_out = WINSIZE;
185 1.1 christos strm.next_out = window;
186 1.1 christos }
187 1.1 christos
188 1.1 christos /* inflate until out of input, output, or at end of block --
189 1.1 christos update the total input and output counters */
190 1.1 christos totin += strm.avail_in;
191 1.1 christos totout += strm.avail_out;
192 1.1 christos ret = inflate(&strm, Z_BLOCK); /* return at end of block */
193 1.1 christos totin -= strm.avail_in;
194 1.1 christos totout -= strm.avail_out;
195 1.1 christos if (ret == Z_NEED_DICT)
196 1.1 christos ret = Z_DATA_ERROR;
197 1.1 christos if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
198 1.1.1.3 christos goto deflate_index_build_error;
199 1.1.1.3 christos if (ret == Z_STREAM_END) {
200 1.1.1.3 christos if (gzip &&
201 1.1.1.3 christos (strm.avail_in || ungetc(getc(in), in) != EOF)) {
202 1.1.1.3 christos ret = inflateReset(&strm);
203 1.1.1.3 christos if (ret != Z_OK)
204 1.1.1.3 christos goto deflate_index_build_error;
205 1.1.1.3 christos continue;
206 1.1.1.3 christos }
207 1.1 christos break;
208 1.1.1.3 christos }
209 1.1 christos
210 1.1 christos /* if at end of block, consider adding an index entry (note that if
211 1.1 christos data_type indicates an end-of-block, then all of the
212 1.1 christos uncompressed data from that block has been delivered, and none
213 1.1 christos of the compressed data after that block has been consumed,
214 1.1 christos except for up to seven bits) -- the totout == 0 provides an
215 1.1 christos entry point after the zlib or gzip header, and assures that the
216 1.1 christos index always has at least one access point; we avoid creating an
217 1.1 christos access point after the last block by checking bit 6 of data_type
218 1.1 christos */
219 1.1 christos if ((strm.data_type & 128) && !(strm.data_type & 64) &&
220 1.1 christos (totout == 0 || totout - last > span)) {
221 1.1 christos index = addpoint(index, strm.data_type & 7, totin,
222 1.1 christos totout, strm.avail_out, window);
223 1.1 christos if (index == NULL) {
224 1.1 christos ret = Z_MEM_ERROR;
225 1.1.1.3 christos goto deflate_index_build_error;
226 1.1 christos }
227 1.1 christos last = totout;
228 1.1 christos }
229 1.1 christos } while (strm.avail_in != 0);
230 1.1 christos } while (ret != Z_STREAM_END);
231 1.1 christos
232 1.1 christos /* clean up and return index (release unused entries in list) */
233 1.1 christos (void)inflateEnd(&strm);
234 1.1.1.2 christos index->list = realloc(index->list, sizeof(struct point) * index->have);
235 1.1.1.3 christos index->gzip = gzip;
236 1.1.1.3 christos index->length = totout;
237 1.1 christos *built = index;
238 1.1.1.3 christos return index->have;
239 1.1 christos
240 1.1 christos /* return error */
241 1.1.1.3 christos deflate_index_build_error:
242 1.1 christos (void)inflateEnd(&strm);
243 1.1.1.3 christos deflate_index_free(index);
244 1.1 christos return ret;
245 1.1 christos }
246 1.1 christos
247 1.1.1.3 christos /* See comments in zran.h. */
248 1.1.1.3 christos int deflate_index_extract(FILE *in, struct deflate_index *index, off_t offset,
249 1.1.1.3 christos unsigned char *buf, int len)
250 1.1 christos {
251 1.1 christos int ret, skip;
252 1.1 christos z_stream strm;
253 1.1 christos struct point *here;
254 1.1 christos unsigned char input[CHUNK];
255 1.1 christos unsigned char discard[WINSIZE];
256 1.1 christos
257 1.1 christos /* proceed only if something reasonable to do */
258 1.1 christos if (len < 0)
259 1.1 christos return 0;
260 1.1 christos
261 1.1 christos /* find where in stream to start */
262 1.1 christos here = index->list;
263 1.1 christos ret = index->have;
264 1.1 christos while (--ret && here[1].out <= offset)
265 1.1 christos here++;
266 1.1 christos
267 1.1 christos /* initialize file and inflate state to start there */
268 1.1 christos strm.zalloc = Z_NULL;
269 1.1 christos strm.zfree = Z_NULL;
270 1.1 christos strm.opaque = Z_NULL;
271 1.1 christos strm.avail_in = 0;
272 1.1 christos strm.next_in = Z_NULL;
273 1.1 christos ret = inflateInit2(&strm, -15); /* raw inflate */
274 1.1 christos if (ret != Z_OK)
275 1.1 christos return ret;
276 1.1 christos ret = fseeko(in, here->in - (here->bits ? 1 : 0), SEEK_SET);
277 1.1 christos if (ret == -1)
278 1.1.1.3 christos goto deflate_index_extract_ret;
279 1.1 christos if (here->bits) {
280 1.1 christos ret = getc(in);
281 1.1 christos if (ret == -1) {
282 1.1 christos ret = ferror(in) ? Z_ERRNO : Z_DATA_ERROR;
283 1.1.1.3 christos goto deflate_index_extract_ret;
284 1.1 christos }
285 1.1 christos (void)inflatePrime(&strm, here->bits, ret >> (8 - here->bits));
286 1.1 christos }
287 1.1 christos (void)inflateSetDictionary(&strm, here->window, WINSIZE);
288 1.1 christos
289 1.1 christos /* skip uncompressed bytes until offset reached, then satisfy request */
290 1.1 christos offset -= here->out;
291 1.1 christos strm.avail_in = 0;
292 1.1 christos skip = 1; /* while skipping to offset */
293 1.1 christos do {
294 1.1 christos /* define where to put uncompressed data, and how much */
295 1.1 christos if (offset > WINSIZE) { /* skip WINSIZE bytes */
296 1.1 christos strm.avail_out = WINSIZE;
297 1.1 christos strm.next_out = discard;
298 1.1 christos offset -= WINSIZE;
299 1.1 christos }
300 1.1.1.3 christos else if (offset > 0) { /* last skip */
301 1.1 christos strm.avail_out = (unsigned)offset;
302 1.1 christos strm.next_out = discard;
303 1.1 christos offset = 0;
304 1.1 christos }
305 1.1.1.3 christos else if (skip) { /* at offset now */
306 1.1.1.3 christos strm.avail_out = len;
307 1.1.1.3 christos strm.next_out = buf;
308 1.1.1.3 christos skip = 0; /* only do this once */
309 1.1.1.3 christos }
310 1.1 christos
311 1.1 christos /* uncompress until avail_out filled, or end of stream */
312 1.1 christos do {
313 1.1 christos if (strm.avail_in == 0) {
314 1.1 christos strm.avail_in = fread(input, 1, CHUNK, in);
315 1.1 christos if (ferror(in)) {
316 1.1 christos ret = Z_ERRNO;
317 1.1.1.3 christos goto deflate_index_extract_ret;
318 1.1 christos }
319 1.1 christos if (strm.avail_in == 0) {
320 1.1 christos ret = Z_DATA_ERROR;
321 1.1.1.3 christos goto deflate_index_extract_ret;
322 1.1 christos }
323 1.1 christos strm.next_in = input;
324 1.1 christos }
325 1.1 christos ret = inflate(&strm, Z_NO_FLUSH); /* normal inflate */
326 1.1 christos if (ret == Z_NEED_DICT)
327 1.1 christos ret = Z_DATA_ERROR;
328 1.1 christos if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
329 1.1.1.3 christos goto deflate_index_extract_ret;
330 1.1.1.3 christos if (ret == Z_STREAM_END) {
331 1.1.1.3 christos /* the raw deflate stream has ended */
332 1.1.1.3 christos if (index->gzip == 0)
333 1.1.1.3 christos /* this is a zlib stream that has ended -- done */
334 1.1.1.3 christos break;
335 1.1.1.3 christos
336 1.1.1.3 christos /* near the end of a gzip member, which might be followed by
337 1.1.1.3 christos another gzip member -- skip the gzip trailer and see if
338 1.1.1.3 christos there is more input after it */
339 1.1.1.3 christos if (strm.avail_in < 8) {
340 1.1.1.3 christos fseeko(in, 8 - strm.avail_in, SEEK_CUR);
341 1.1.1.3 christos strm.avail_in = 0;
342 1.1.1.3 christos }
343 1.1.1.3 christos else {
344 1.1.1.3 christos strm.avail_in -= 8;
345 1.1.1.3 christos strm.next_in += 8;
346 1.1.1.3 christos }
347 1.1.1.3 christos if (strm.avail_in == 0 && ungetc(getc(in), in) == EOF)
348 1.1.1.3 christos /* the input ended after the gzip trailer -- done */
349 1.1.1.3 christos break;
350 1.1.1.3 christos
351 1.1.1.3 christos /* there is more input, so another gzip member should follow --
352 1.1.1.3 christos validate and skip the gzip header */
353 1.1.1.3 christos ret = inflateReset2(&strm, 31);
354 1.1.1.3 christos if (ret != Z_OK)
355 1.1.1.3 christos goto deflate_index_extract_ret;
356 1.1.1.3 christos do {
357 1.1.1.3 christos if (strm.avail_in == 0) {
358 1.1.1.3 christos strm.avail_in = fread(input, 1, CHUNK, in);
359 1.1.1.3 christos if (ferror(in)) {
360 1.1.1.3 christos ret = Z_ERRNO;
361 1.1.1.3 christos goto deflate_index_extract_ret;
362 1.1.1.3 christos }
363 1.1.1.3 christos if (strm.avail_in == 0) {
364 1.1.1.3 christos ret = Z_DATA_ERROR;
365 1.1.1.3 christos goto deflate_index_extract_ret;
366 1.1.1.3 christos }
367 1.1.1.3 christos strm.next_in = input;
368 1.1.1.3 christos }
369 1.1.1.3 christos ret = inflate(&strm, Z_BLOCK);
370 1.1.1.3 christos if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
371 1.1.1.3 christos goto deflate_index_extract_ret;
372 1.1.1.3 christos } while ((strm.data_type & 128) == 0);
373 1.1.1.3 christos
374 1.1.1.3 christos /* set up to continue decompression of the raw deflate stream
375 1.1.1.3 christos that follows the gzip header */
376 1.1.1.3 christos ret = inflateReset2(&strm, -15);
377 1.1.1.3 christos if (ret != Z_OK)
378 1.1.1.3 christos goto deflate_index_extract_ret;
379 1.1.1.3 christos }
380 1.1.1.3 christos
381 1.1.1.3 christos /* continue to process the available input before reading more */
382 1.1 christos } while (strm.avail_out != 0);
383 1.1 christos
384 1.1 christos if (ret == Z_STREAM_END)
385 1.1.1.3 christos /* reached the end of the compressed data -- return the data that
386 1.1.1.3 christos was available, possibly less than requested */
387 1.1 christos break;
388 1.1 christos
389 1.1.1.3 christos /* do until offset reached and requested data read */
390 1.1 christos } while (skip);
391 1.1 christos
392 1.1.1.3 christos /* compute the number of uncompressed bytes read after the offset */
393 1.1 christos ret = skip ? 0 : len - strm.avail_out;
394 1.1 christos
395 1.1.1.3 christos /* clean up and return the bytes read, or the negative error */
396 1.1.1.3 christos deflate_index_extract_ret:
397 1.1 christos (void)inflateEnd(&strm);
398 1.1 christos return ret;
399 1.1 christos }
400 1.1 christos
401 1.1.1.3 christos #ifdef TEST
402 1.1.1.3 christos
403 1.1.1.3 christos #define SPAN 1048576L /* desired distance between access points */
404 1.1.1.3 christos #define LEN 16384 /* number of bytes to extract */
405 1.1.1.3 christos
406 1.1.1.3 christos /* Demonstrate the use of deflate_index_build() and deflate_index_extract() by
407 1.1.1.3 christos processing the file provided on the command line, and extracting LEN bytes
408 1.1.1.3 christos from 2/3rds of the way through the uncompressed output, writing that to
409 1.1.1.3 christos stdout. An offset can be provided as the second argument, in which case the
410 1.1.1.3 christos data is extracted from there instead. */
411 1.1 christos int main(int argc, char **argv)
412 1.1 christos {
413 1.1 christos int len;
414 1.1.1.3 christos off_t offset = -1;
415 1.1 christos FILE *in;
416 1.1.1.3 christos struct deflate_index *index = NULL;
417 1.1.1.3 christos unsigned char buf[LEN];
418 1.1 christos
419 1.1 christos /* open input file */
420 1.1.1.3 christos if (argc < 2 || argc > 3) {
421 1.1.1.3 christos fprintf(stderr, "usage: zran file.gz [offset]\n");
422 1.1 christos return 1;
423 1.1 christos }
424 1.1 christos in = fopen(argv[1], "rb");
425 1.1 christos if (in == NULL) {
426 1.1 christos fprintf(stderr, "zran: could not open %s for reading\n", argv[1]);
427 1.1 christos return 1;
428 1.1 christos }
429 1.1 christos
430 1.1.1.3 christos /* get optional offset */
431 1.1.1.3 christos if (argc == 3) {
432 1.1.1.3 christos char *end;
433 1.1.1.3 christos offset = strtoll(argv[2], &end, 10);
434 1.1.1.3 christos if (*end || offset < 0) {
435 1.1.1.3 christos fprintf(stderr, "zran: %s is not a valid offset\n", argv[2]);
436 1.1.1.3 christos return 1;
437 1.1.1.3 christos }
438 1.1.1.3 christos }
439 1.1.1.3 christos
440 1.1 christos /* build index */
441 1.1.1.3 christos len = deflate_index_build(in, SPAN, &index);
442 1.1 christos if (len < 0) {
443 1.1 christos fclose(in);
444 1.1 christos switch (len) {
445 1.1 christos case Z_MEM_ERROR:
446 1.1 christos fprintf(stderr, "zran: out of memory\n");
447 1.1 christos break;
448 1.1 christos case Z_DATA_ERROR:
449 1.1 christos fprintf(stderr, "zran: compressed data error in %s\n", argv[1]);
450 1.1 christos break;
451 1.1 christos case Z_ERRNO:
452 1.1 christos fprintf(stderr, "zran: read error on %s\n", argv[1]);
453 1.1 christos break;
454 1.1 christos default:
455 1.1 christos fprintf(stderr, "zran: error %d while building index\n", len);
456 1.1 christos }
457 1.1 christos return 1;
458 1.1 christos }
459 1.1 christos fprintf(stderr, "zran: built index with %d access points\n", len);
460 1.1 christos
461 1.1 christos /* use index by reading some bytes from an arbitrary offset */
462 1.1.1.3 christos if (offset == -1)
463 1.1.1.3 christos offset = (index->length << 1) / 3;
464 1.1.1.3 christos len = deflate_index_extract(in, index, offset, buf, LEN);
465 1.1 christos if (len < 0)
466 1.1 christos fprintf(stderr, "zran: extraction failed: %s error\n",
467 1.1 christos len == Z_MEM_ERROR ? "out of memory" : "input corrupted");
468 1.1 christos else {
469 1.1 christos fwrite(buf, 1, len, stdout);
470 1.1 christos fprintf(stderr, "zran: extracted %d bytes at %llu\n", len, offset);
471 1.1 christos }
472 1.1 christos
473 1.1 christos /* clean up and exit */
474 1.1.1.3 christos deflate_index_free(index);
475 1.1 christos fclose(in);
476 1.1 christos return 0;
477 1.1 christos }
478 1.1.1.3 christos
479 1.1.1.3 christos #endif
480