summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Adler <madler@alumni.caltech.edu>2018-10-07 21:26:43 -0700
committerMark Adler <madler@alumni.caltech.edu>2018-10-14 09:57:34 -0700
commit921d81b2a853233659d50b3c499a5c711a11e998 (patch)
tree5bba18ecc4b89479c7c24f08ca2ebcec7b3a2462
parent354fa43d123b9759e4308c81e181caa1f97187ed (diff)
downloadzlib-921d81b2a853233659d50b3c499a5c711a11e998.tar.gz
zlib-921d81b2a853233659d50b3c499a5c711a11e998.tar.bz2
zlib-921d81b2a853233659d50b3c499a5c711a11e998.zip
Fix the zran.c example to work on a multiple-member gzip file.
-rw-r--r--examples/README.examples1
-rw-r--r--examples/zran.c256
-rw-r--r--examples/zran.h40
3 files changed, 204 insertions, 93 deletions
diff --git a/examples/README.examples b/examples/README.examples
index 42d9414..e3a4b88 100644
--- a/examples/README.examples
+++ b/examples/README.examples
@@ -48,6 +48,7 @@ zpipe.c
48 - deeply commented in zlib_how.html (see above) 48 - deeply commented in zlib_how.html (see above)
49 49
50zran.c 50zran.c
51zran.h
51 index a zlib or gzip stream and randomly access it 52 index a zlib or gzip stream and randomly access it
52 - illustrates the use of Z_BLOCK, inflatePrime(), and 53 - illustrates the use of Z_BLOCK, inflatePrime(), and
53 inflateSetDictionary() to provide random access 54 inflateSetDictionary() to provide random access
diff --git a/examples/zran.c b/examples/zran.c
index 4fec659..f279db7 100644
--- a/examples/zran.c
+++ b/examples/zran.c
@@ -1,11 +1,13 @@
1/* zran.c -- example of zlib/gzip stream indexing and random access 1/* zran.c -- example of zlib/gzip stream indexing and random access
2 * Copyright (C) 2005, 2012 Mark Adler 2 * Copyright (C) 2005, 2012, 2018 Mark Adler
3 * For conditions of distribution and use, see copyright notice in zlib.h 3 * For conditions of distribution and use, see copyright notice in zlib.h
4 Version 1.1 29 Sep 2012 Mark Adler */ 4 * Version 1.2 14 Oct 2018 Mark Adler */
5 5
6/* Version History: 6/* Version History:
7 1.0 29 May 2005 First version 7 1.0 29 May 2005 First version
8 1.1 29 Sep 2012 Fix memory reallocation error 8 1.1 29 Sep 2012 Fix memory reallocation error
9 1.2 14 Oct 2018 Handle gzip streams with multiple members
10 Add a header file to facilitate usage in applications
9 */ 11 */
10 12
11/* Illustrate the use of Z_BLOCK, inflatePrime(), and inflateSetDictionary() 13/* Illustrate the use of Z_BLOCK, inflatePrime(), and inflateSetDictionary()
@@ -20,11 +22,11 @@
20 the starting file offset and bit of that block, and the 32K bytes of 22 the starting file offset and bit of that block, and the 32K bytes of
21 uncompressed data that precede that block. Also the uncompressed offset of 23 uncompressed data that precede that block. Also the uncompressed offset of
22 that block is saved to provide a referece for locating a desired starting 24 that block is saved to provide a referece for locating a desired starting
23 point in the uncompressed stream. build_index() works by decompressing the 25 point in the uncompressed stream. deflate_index_build() works by
24 input zlib or gzip stream a block at a time, and at the end of each block 26 decompressing the input zlib or gzip stream a block at a time, and at the
25 deciding if enough uncompressed data has gone by to justify the creation of 27 end of each block deciding if enough uncompressed data has gone by to
26 a new access point. If so, that point is saved in a data structure that 28 justify the creation of a new access point. If so, that point is saved in a
27 grows as needed to accommodate the points. 29 data structure that grows as needed to accommodate the points.
28 30
29 To use the index, an offset in the uncompressed data is provided, for which 31 To use the index, an offset in the uncompressed data is provided, for which
30 the latest access point at or preceding that offset is located in the index. 32 the latest access point at or preceding that offset is located in the index.
@@ -43,7 +45,8 @@
43 There is some fair bit of overhead to starting inflation for the random 45 There is some fair bit of overhead to starting inflation for the random
44 access, mainly copying the 32K byte dictionary. So if small pieces of the 46 access, mainly copying the 32K byte dictionary. So if small pieces of the
45 file are being accessed, it would make sense to implement a cache to hold 47 file are being accessed, it would make sense to implement a cache to hold
46 some lookahead and avoid many calls to extract() for small lengths. 48 some lookahead and avoid many calls to deflate_index_extract() for small
49 lengths.
47 50
48 Another way to build an index would be to use inflateCopy(). That would 51 Another way to build an index would be to use inflateCopy(). That would
49 not be constrained to have access points at block boundaries, but requires 52 not be constrained to have access points at block boundaries, but requires
@@ -56,30 +59,21 @@
56#include <stdlib.h> 59#include <stdlib.h>
57#include <string.h> 60#include <string.h>
58#include "zlib.h" 61#include "zlib.h"
62#include "zran.h"
59 63
60#define local static
61
62#define SPAN 1048576L /* desired distance between access points */
63#define WINSIZE 32768U /* sliding window size */ 64#define WINSIZE 32768U /* sliding window size */
64#define CHUNK 16384 /* file input buffer size */ 65#define CHUNK 16384 /* file input buffer size */
65 66
66/* access point entry */ 67/* Access point entry. */
67struct point { 68struct point {
68 off_t out; /* corresponding offset in uncompressed data */ 69 off_t out; /* corresponding offset in uncompressed data */
69 off_t in; /* offset in input file of first full byte */ 70 off_t in; /* offset in input file of first full byte */
70 int bits; /* number of bits (1-7) from byte at in - 1, or 0 */ 71 int bits; /* number of bits (1-7) from byte at in-1, or 0 */
71 unsigned char window[WINSIZE]; /* preceding 32K of uncompressed data */ 72 unsigned char window[WINSIZE]; /* preceding 32K of uncompressed data */
72}; 73};
73 74
74/* access point list */ 75/* See comments in zran.h. */
75struct access { 76void deflate_index_free(struct deflate_index *index)
76 int have; /* number of list entries filled in */
77 int size; /* number of list entries allocated */
78 struct point *list; /* allocated list */
79};
80
81/* Deallocate an index built by build_index() */
82local void free_index(struct access *index)
83{ 77{
84 if (index != NULL) { 78 if (index != NULL) {
85 free(index->list); 79 free(index->list);
@@ -87,39 +81,43 @@ local void free_index(struct access *index)
87 } 81 }
88} 82}
89 83
90/* Add an entry to the access point list. If out of memory, deallocate the 84/* Add an entry to the access point list. If out of memory, deallocate the
91 existing list and return NULL. */ 85 existing list and return NULL. index->gzip is the allocated size of the
92local struct access *addpoint(struct access *index, int bits, 86 index in point entries, until it is time for deflate_index_build() to
93 off_t in, off_t out, unsigned left, unsigned char *window) 87 return, at which point gzip is set to indicate a gzip file or not.
88 */
89static struct deflate_index *addpoint(struct deflate_index *index, int bits,
90 off_t in, off_t out, unsigned left,
91 unsigned char *window)
94{ 92{
95 struct point *next; 93 struct point *next;
96 94
97 /* if list is empty, create it (start with eight points) */ 95 /* if list is empty, create it (start with eight points) */
98 if (index == NULL) { 96 if (index == NULL) {
99 index = malloc(sizeof(struct access)); 97 index = malloc(sizeof(struct deflate_index));
100 if (index == NULL) return NULL; 98 if (index == NULL) return NULL;
101 index->list = malloc(sizeof(struct point) << 3); 99 index->list = malloc(sizeof(struct point) << 3);
102 if (index->list == NULL) { 100 if (index->list == NULL) {
103 free(index); 101 free(index);
104 return NULL; 102 return NULL;
105 } 103 }
106 index->size = 8; 104 index->gzip = 8;
107 index->have = 0; 105 index->have = 0;
108 } 106 }
109 107
110 /* if list is full, make it bigger */ 108 /* if list is full, make it bigger */
111 else if (index->have == index->size) { 109 else if (index->have == index->gzip) {
112 index->size <<= 1; 110 index->gzip <<= 1;
113 next = realloc(index->list, sizeof(struct point) * index->size); 111 next = realloc(index->list, sizeof(struct point) * index->gzip);
114 if (next == NULL) { 112 if (next == NULL) {
115 free_index(index); 113 deflate_index_free(index);
116 return NULL; 114 return NULL;
117 } 115 }
118 index->list = next; 116 index->list = next;
119 } 117 }
120 118
121 /* fill in entry and increment how many we have */ 119 /* fill in entry and increment how many we have */
122 next = index->list + index->have; 120 next = (struct point *)(index->list) + index->have;
123 next->bits = bits; 121 next->bits = bits;
124 next->in = in; 122 next->in = in;
125 next->out = out; 123 next->out = out;
@@ -133,20 +131,14 @@ local struct access *addpoint(struct access *index, int bits,
133 return index; 131 return index;
134} 132}
135 133
136/* Make one entire pass through the compressed stream and build an index, with 134/* See comments in zran.h. */
137 access points about every span bytes of uncompressed output -- span is 135int deflate_index_build(FILE *in, off_t span, struct deflate_index **built)
138 chosen to balance the speed of random access against the memory requirements
139 of the list, about 32K bytes per access point. Note that data after the end
140 of the first zlib or gzip stream in the file is ignored. build_index()
141 returns the number of access points on success (>= 1), Z_MEM_ERROR for out
142 of memory, Z_DATA_ERROR for an error in the input file, or Z_ERRNO for a
143 file read error. On success, *built points to the resulting index. */
144local int build_index(FILE *in, off_t span, struct access **built)
145{ 136{
146 int ret; 137 int ret;
138 int gzip = 0; /* true if reading a gzip file */
147 off_t totin, totout; /* our own total counters to avoid 4GB limit */ 139 off_t totin, totout; /* our own total counters to avoid 4GB limit */
148 off_t last; /* totout value of last access point */ 140 off_t last; /* totout value of last access point */
149 struct access *index; /* access points being generated */ 141 struct deflate_index *index; /* access points being generated */
150 z_stream strm; 142 z_stream strm;
151 unsigned char input[CHUNK]; 143 unsigned char input[CHUNK];
152 unsigned char window[WINSIZE]; 144 unsigned char window[WINSIZE];
@@ -163,7 +155,7 @@ local int build_index(FILE *in, off_t span, struct access **built)
163 155
164 /* inflate the input, maintain a sliding window, and build an index -- this 156 /* inflate the input, maintain a sliding window, and build an index -- this
165 also validates the integrity of the compressed data using the check 157 also validates the integrity of the compressed data using the check
166 information at the end of the gzip or zlib stream */ 158 information in the gzip or zlib stream */
167 totin = totout = last = 0; 159 totin = totout = last = 0;
168 index = NULL; /* will be allocated by first addpoint() */ 160 index = NULL; /* will be allocated by first addpoint() */
169 strm.avail_out = 0; 161 strm.avail_out = 0;
@@ -172,14 +164,19 @@ local int build_index(FILE *in, off_t span, struct access **built)
172 strm.avail_in = fread(input, 1, CHUNK, in); 164 strm.avail_in = fread(input, 1, CHUNK, in);
173 if (ferror(in)) { 165 if (ferror(in)) {
174 ret = Z_ERRNO; 166 ret = Z_ERRNO;
175 goto build_index_error; 167 goto deflate_index_build_error;
176 } 168 }
177 if (strm.avail_in == 0) { 169 if (strm.avail_in == 0) {
178 ret = Z_DATA_ERROR; 170 ret = Z_DATA_ERROR;
179 goto build_index_error; 171 goto deflate_index_build_error;
180 } 172 }
181 strm.next_in = input; 173 strm.next_in = input;
182 174
175 /* check for a gzip stream */
176 if (totin == 0 && strm.avail_in >= 3 &&
177 input[0] == 31 && input[1] == 139 && input[2] == 8)
178 gzip = 1;
179
183 /* process all of that, or until end of stream */ 180 /* process all of that, or until end of stream */
184 do { 181 do {
185 /* reset sliding window if necessary */ 182 /* reset sliding window if necessary */
@@ -198,9 +195,17 @@ local int build_index(FILE *in, off_t span, struct access **built)
198 if (ret == Z_NEED_DICT) 195 if (ret == Z_NEED_DICT)
199 ret = Z_DATA_ERROR; 196 ret = Z_DATA_ERROR;
200 if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR) 197 if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
201 goto build_index_error; 198 goto deflate_index_build_error;
202 if (ret == Z_STREAM_END) 199 if (ret == Z_STREAM_END) {
200 if (gzip &&
201 (strm.avail_in || ungetc(getc(in), in) != EOF)) {
202 ret = inflateReset(&strm);
203 if (ret != Z_OK)
204 goto deflate_index_build_error;
205 continue;
206 }
203 break; 207 break;
208 }
204 209
205 /* if at end of block, consider adding an index entry (note that if 210 /* if at end of block, consider adding an index entry (note that if
206 data_type indicates an end-of-block, then all of the 211 data_type indicates an end-of-block, then all of the
@@ -217,7 +222,7 @@ local int build_index(FILE *in, off_t span, struct access **built)
217 totout, strm.avail_out, window); 222 totout, strm.avail_out, window);
218 if (index == NULL) { 223 if (index == NULL) {
219 ret = Z_MEM_ERROR; 224 ret = Z_MEM_ERROR;
220 goto build_index_error; 225 goto deflate_index_build_error;
221 } 226 }
222 last = totout; 227 last = totout;
223 } 228 }
@@ -227,27 +232,21 @@ local int build_index(FILE *in, off_t span, struct access **built)
227 /* clean up and return index (release unused entries in list) */ 232 /* clean up and return index (release unused entries in list) */
228 (void)inflateEnd(&strm); 233 (void)inflateEnd(&strm);
229 index->list = realloc(index->list, sizeof(struct point) * index->have); 234 index->list = realloc(index->list, sizeof(struct point) * index->have);
230 index->size = index->have; 235 index->gzip = gzip;
236 index->length = totout;
231 *built = index; 237 *built = index;
232 return index->size; 238 return index->have;
233 239
234 /* return error */ 240 /* return error */
235 build_index_error: 241 deflate_index_build_error:
236 (void)inflateEnd(&strm); 242 (void)inflateEnd(&strm);
237 if (index != NULL) 243 deflate_index_free(index);
238 free_index(index);
239 return ret; 244 return ret;
240} 245}
241 246
242/* Use the index to read len bytes from offset into buf, return bytes read or 247/* See comments in zran.h. */
243 negative for error (Z_DATA_ERROR or Z_MEM_ERROR). If data is requested past 248int deflate_index_extract(FILE *in, struct deflate_index *index, off_t offset,
244 the end of the uncompressed data, then extract() will return a value less 249 unsigned char *buf, int len)
245 than len, indicating how much as actually read into buf. This function
246 should not return a data error unless the file was modified since the index
247 was generated. extract() may also return Z_ERRNO if there is an error on
248 reading or seeking the input file. */
249local int extract(FILE *in, struct access *index, off_t offset,
250 unsigned char *buf, int len)
251{ 250{
252 int ret, skip; 251 int ret, skip;
253 z_stream strm; 252 z_stream strm;
@@ -276,12 +275,12 @@ local int extract(FILE *in, struct access *index, off_t offset,
276 return ret; 275 return ret;
277 ret = fseeko(in, here->in - (here->bits ? 1 : 0), SEEK_SET); 276 ret = fseeko(in, here->in - (here->bits ? 1 : 0), SEEK_SET);
278 if (ret == -1) 277 if (ret == -1)
279 goto extract_ret; 278 goto deflate_index_extract_ret;
280 if (here->bits) { 279 if (here->bits) {
281 ret = getc(in); 280 ret = getc(in);
282 if (ret == -1) { 281 if (ret == -1) {
283 ret = ferror(in) ? Z_ERRNO : Z_DATA_ERROR; 282 ret = ferror(in) ? Z_ERRNO : Z_DATA_ERROR;
284 goto extract_ret; 283 goto deflate_index_extract_ret;
285 } 284 }
286 (void)inflatePrime(&strm, here->bits, ret >> (8 - here->bits)); 285 (void)inflatePrime(&strm, here->bits, ret >> (8 - here->bits));
287 } 286 }
@@ -293,21 +292,21 @@ local int extract(FILE *in, struct access *index, off_t offset,
293 skip = 1; /* while skipping to offset */ 292 skip = 1; /* while skipping to offset */
294 do { 293 do {
295 /* define where to put uncompressed data, and how much */ 294 /* define where to put uncompressed data, and how much */
296 if (offset == 0 && skip) { /* at offset now */
297 strm.avail_out = len;
298 strm.next_out = buf;
299 skip = 0; /* only do this once */
300 }
301 if (offset > WINSIZE) { /* skip WINSIZE bytes */ 295 if (offset > WINSIZE) { /* skip WINSIZE bytes */
302 strm.avail_out = WINSIZE; 296 strm.avail_out = WINSIZE;
303 strm.next_out = discard; 297 strm.next_out = discard;
304 offset -= WINSIZE; 298 offset -= WINSIZE;
305 } 299 }
306 else if (offset != 0) { /* last skip */ 300 else if (offset > 0) { /* last skip */
307 strm.avail_out = (unsigned)offset; 301 strm.avail_out = (unsigned)offset;
308 strm.next_out = discard; 302 strm.next_out = discard;
309 offset = 0; 303 offset = 0;
310 } 304 }
305 else if (skip) { /* at offset now */
306 strm.avail_out = len;
307 strm.next_out = buf;
308 skip = 0; /* only do this once */
309 }
311 310
312 /* uncompress until avail_out filled, or end of stream */ 311 /* uncompress until avail_out filled, or end of stream */
313 do { 312 do {
@@ -315,11 +314,11 @@ local int extract(FILE *in, struct access *index, off_t offset,
315 strm.avail_in = fread(input, 1, CHUNK, in); 314 strm.avail_in = fread(input, 1, CHUNK, in);
316 if (ferror(in)) { 315 if (ferror(in)) {
317 ret = Z_ERRNO; 316 ret = Z_ERRNO;
318 goto extract_ret; 317 goto deflate_index_extract_ret;
319 } 318 }
320 if (strm.avail_in == 0) { 319 if (strm.avail_in == 0) {
321 ret = Z_DATA_ERROR; 320 ret = Z_DATA_ERROR;
322 goto extract_ret; 321 goto deflate_index_extract_ret;
323 } 322 }
324 strm.next_in = input; 323 strm.next_in = input;
325 } 324 }
@@ -327,41 +326,99 @@ local int extract(FILE *in, struct access *index, off_t offset,
327 if (ret == Z_NEED_DICT) 326 if (ret == Z_NEED_DICT)
328 ret = Z_DATA_ERROR; 327 ret = Z_DATA_ERROR;
329 if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR) 328 if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
330 goto extract_ret; 329 goto deflate_index_extract_ret;
331 if (ret == Z_STREAM_END) 330 if (ret == Z_STREAM_END) {
332 break; 331 /* the raw deflate stream has ended */
332 if (index->gzip == 0)
333 /* this is a zlib stream that has ended -- done */
334 break;
335
336 /* near the end of a gzip member, which might be followed by
337 another gzip member -- skip the gzip trailer and see if
338 there is more input after it */
339 if (strm.avail_in < 8) {
340 fseeko(in, 8 - strm.avail_in, SEEK_CUR);
341 strm.avail_in = 0;
342 }
343 else {
344 strm.avail_in -= 8;
345 strm.next_in += 8;
346 }
347 if (strm.avail_in == 0 && ungetc(getc(in), in) == EOF)
348 /* the input ended after the gzip trailer -- done */
349 break;
350
351 /* there is more input, so another gzip member should follow --
352 validate and skip the gzip header */
353 ret = inflateReset2(&strm, 31);
354 if (ret != Z_OK)
355 goto deflate_index_extract_ret;
356 do {
357 if (strm.avail_in == 0) {
358 strm.avail_in = fread(input, 1, CHUNK, in);
359 if (ferror(in)) {
360 ret = Z_ERRNO;
361 goto deflate_index_extract_ret;
362 }
363 if (strm.avail_in == 0) {
364 ret = Z_DATA_ERROR;
365 goto deflate_index_extract_ret;
366 }
367 strm.next_in = input;
368 }
369 ret = inflate(&strm, Z_BLOCK);
370 if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
371 goto deflate_index_extract_ret;
372 } while ((strm.data_type & 128) == 0);
373
374 /* set up to continue decompression of the raw deflate stream
375 that follows the gzip header */
376 ret = inflateReset2(&strm, -15);
377 if (ret != Z_OK)
378 goto deflate_index_extract_ret;
379 }
380
381 /* continue to process the available input before reading more */
333 } while (strm.avail_out != 0); 382 } while (strm.avail_out != 0);
334 383
335 /* if reach end of stream, then don't keep trying to get more */
336 if (ret == Z_STREAM_END) 384 if (ret == Z_STREAM_END)
385 /* reached the end of the compressed data -- return the data that
386 was available, possibly less than requested */
337 break; 387 break;
338 388
339 /* do until offset reached and requested data read, or stream ends */ 389 /* do until offset reached and requested data read */
340 } while (skip); 390 } while (skip);
341 391
342 /* compute number of uncompressed bytes read after offset */ 392 /* compute the number of uncompressed bytes read after the offset */
343 ret = skip ? 0 : len - strm.avail_out; 393 ret = skip ? 0 : len - strm.avail_out;
344 394
345 /* clean up and return bytes read or error */ 395 /* clean up and return the bytes read, or the negative error */
346 extract_ret: 396 deflate_index_extract_ret:
347 (void)inflateEnd(&strm); 397 (void)inflateEnd(&strm);
348 return ret; 398 return ret;
349} 399}
350 400
351/* Demonstrate the use of build_index() and extract() by processing the file 401#ifdef TEST
352 provided on the command line, and the extracting 16K from about 2/3rds of 402
353 the way through the uncompressed output, and writing that to stdout. */ 403#define SPAN 1048576L /* desired distance between access points */
404#define LEN 16384 /* number of bytes to extract */
405
406/* Demonstrate the use of deflate_index_build() and deflate_index_extract() by
407 processing the file provided on the command line, and extracting LEN bytes
408 from 2/3rds of the way through the uncompressed output, writing that to
409 stdout. An offset can be provided as the second argument, in which case the
410 data is extracted from there instead. */
354int main(int argc, char **argv) 411int main(int argc, char **argv)
355{ 412{
356 int len; 413 int len;
357 off_t offset; 414 off_t offset = -1;
358 FILE *in; 415 FILE *in;
359 struct access *index = NULL; 416 struct deflate_index *index = NULL;
360 unsigned char buf[CHUNK]; 417 unsigned char buf[LEN];
361 418
362 /* open input file */ 419 /* open input file */
363 if (argc != 2) { 420 if (argc < 2 || argc > 3) {
364 fprintf(stderr, "usage: zran file.gz\n"); 421 fprintf(stderr, "usage: zran file.gz [offset]\n");
365 return 1; 422 return 1;
366 } 423 }
367 in = fopen(argv[1], "rb"); 424 in = fopen(argv[1], "rb");
@@ -370,8 +427,18 @@ int main(int argc, char **argv)
370 return 1; 427 return 1;
371 } 428 }
372 429
430 /* get optional offset */
431 if (argc == 3) {
432 char *end;
433 offset = strtoll(argv[2], &end, 10);
434 if (*end || offset < 0) {
435 fprintf(stderr, "zran: %s is not a valid offset\n", argv[2]);
436 return 1;
437 }
438 }
439
373 /* build index */ 440 /* build index */
374 len = build_index(in, SPAN, &index); 441 len = deflate_index_build(in, SPAN, &index);
375 if (len < 0) { 442 if (len < 0) {
376 fclose(in); 443 fclose(in);
377 switch (len) { 444 switch (len) {
@@ -392,8 +459,9 @@ int main(int argc, char **argv)
392 fprintf(stderr, "zran: built index with %d access points\n", len); 459 fprintf(stderr, "zran: built index with %d access points\n", len);
393 460
394 /* use index by reading some bytes from an arbitrary offset */ 461 /* use index by reading some bytes from an arbitrary offset */
395 offset = (index->list[index->have - 1].out << 1) / 3; 462 if (offset == -1)
396 len = extract(in, index, offset, buf, CHUNK); 463 offset = (index->length << 1) / 3;
464 len = deflate_index_extract(in, index, offset, buf, LEN);
397 if (len < 0) 465 if (len < 0)
398 fprintf(stderr, "zran: extraction failed: %s error\n", 466 fprintf(stderr, "zran: extraction failed: %s error\n",
399 len == Z_MEM_ERROR ? "out of memory" : "input corrupted"); 467 len == Z_MEM_ERROR ? "out of memory" : "input corrupted");
@@ -403,7 +471,9 @@ int main(int argc, char **argv)
403 } 471 }
404 472
405 /* clean up and exit */ 473 /* clean up and exit */
406 free_index(index); 474 deflate_index_free(index);
407 fclose(in); 475 fclose(in);
408 return 0; 476 return 0;
409} 477}
478
479#endif
diff --git a/examples/zran.h b/examples/zran.h
new file mode 100644
index 0000000..2314125
--- /dev/null
+++ b/examples/zran.h
@@ -0,0 +1,40 @@
1/* zran.h -- example of zlib/gzip stream indexing and random access
2 * Copyright (C) 2005, 2012, 2018 Mark Adler
3 * For conditions of distribution and use, see copyright notice in zlib.h
4 * Version 1.2 14 Oct 2018 Mark Adler */
5
6#include <stdio.h>
7#include "zlib.h"
8
9/* Access point list. */
10struct deflate_index {
11 int have; /* number of list entries */
12 int gzip; /* 1 if the index is of a gzip file, 0 if it is of a
13 zlib stream */
14 off_t length; /* total length of uncompressed data */
15 void *list; /* allocated list of entries */
16};
17
18/* Make one entire pass through a zlib or gzip compressed stream and build an
19 index, with access points about every span bytes of uncompressed output.
20 gzip files with multiple members are indexed in their entirety. span should
21 be chosen to balance the speed of random access against the memory
22 requirements of the list, about 32K bytes per access point. The return value
23 is the number of access points on success (>= 1), Z_MEM_ERROR for out of
24 memory, Z_DATA_ERROR for an error in the input file, or Z_ERRNO for a file
25 read error. On success, *built points to the resulting index. */
26int deflate_index_build(FILE *in, off_t span, struct deflate_index **built);
27
28/* Deallocate an index built by deflate_index_build() */
29void deflate_index_free(struct deflate_index *index);
30
31/* Use the index to read len bytes from offset into buf. Return bytes read or
32 negative for error (Z_DATA_ERROR or Z_MEM_ERROR). If data is requested past
33 the end of the uncompressed data, then deflate_index_extract() will return a
34 value less than len, indicating how much was actually read into buf. This
35 function should not return a data error unless the file was modified since
36 the index was generated, since deflate_index_build() validated all of the
37 input. deflate_index_extract() will return Z_ERRNO if there is an error on
38 reading or seeking the input file. */
39int deflate_index_extract(FILE *in, struct deflate_index *index, off_t offset,
40 unsigned char *buf, int len);