zran.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. /* zran.c -- example of zlib/gzip stream indexing and random access
  2. * Copyright (C) 2005, 2012 Mark Adler
  3. * For conditions of distribution and use, see copyright notice in zlib.h
  4. Version 1.1 29 Sep 2012 Mark Adler */
  5. /* Version History:
  6. 1.0 29 May 2005 First version
  7. 1.1 29 Sep 2012 Fix memory reallocation error
  8. */
  9. /* Illustrate the use of Z_BLOCK, inflatePrime(), and inflateSetDictionary()
  10. for random access of a compressed file. A file containing a zlib or gzip
  11. stream is provided on the command line. The compressed stream is decoded in
  12. its entirety, and an index built with access points about every SPAN bytes
  13. in the uncompressed output. The compressed file is left open, and can then
  14. be read randomly, having to decompress on the average SPAN/2 uncompressed
  15. bytes before getting to the desired block of data.
  16. An access point can be created at the start of any deflate block, by saving
  17. the starting file offset and bit of that block, and the 32K bytes of
  18. uncompressed data that precede that block. Also the uncompressed offset of
  19. that block is saved to provide a referece for locating a desired starting
  20. point in the uncompressed stream. build_index() works by decompressing the
  21. input zlib or gzip stream a block at a time, and at the end of each block
  22. deciding if enough uncompressed data has gone by to justify the creation of
  23. a new access point. If so, that point is saved in a data structure that
  24. grows as needed to accommodate the points.
  25. To use the index, an offset in the uncompressed data is provided, for which
  26. the latest access point at or preceding that offset is located in the index.
  27. The input file is positioned to the specified location in the index, and if
  28. necessary the first few bits of the compressed data is read from the file.
  29. inflate is initialized with those bits and the 32K of uncompressed data, and
  30. the decompression then proceeds until the desired offset in the file is
  31. reached. Then the decompression continues to read the desired uncompressed
  32. data from the file.
  33. Another approach would be to generate the index on demand. In that case,
  34. requests for random access reads from the compressed data would try to use
  35. the index, but if a read far enough past the end of the index is required,
  36. then further index entries would be generated and added.
  37. There is some fair bit of overhead to starting inflation for the random
  38. access, mainly copying the 32K byte dictionary. So if small pieces of the
  39. file are being accessed, it would make sense to implement a cache to hold
  40. some lookahead and avoid many calls to extract() for small lengths.
  41. Another way to build an index would be to use inflateCopy(). That would
  42. not be constrained to have access points at block boundaries, but requires
  43. more memory per access point, and also cannot be saved to file due to the
  44. use of pointers in the state. The approach here allows for storage of the
  45. index in a file.
  46. */
  47. #include <stdio.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50. #include "zlib.h"
  51. #define local static
  52. #define SPAN 1048576L /* desired distance between access points */
  53. #define WINSIZE 32768U /* sliding window size */
  54. #define CHUNK 16384 /* file input buffer size */
  55. /* access point entry */
  56. struct point {
  57. off_t out; /* corresponding offset in uncompressed data */
  58. off_t in; /* offset in input file of first full byte */
  59. int bits; /* number of bits (1-7) from byte at in - 1, or 0 */
  60. unsigned char window[WINSIZE]; /* preceding 32K of uncompressed data */
  61. };
  62. /* access point list */
  63. struct access {
  64. int have; /* number of list entries filled in */
  65. int size; /* number of list entries allocated */
  66. struct point *list; /* allocated list */
  67. };
  68. /* Deallocate an index built by build_index() */
  69. local void free_index(struct access *index)
  70. {
  71. if (index != NULL) {
  72. free(index->list);
  73. free(index);
  74. }
  75. }
  76. /* Add an entry to the access point list. If out of memory, deallocate the
  77. existing list and return NULL. */
  78. local struct access *addpoint(struct access *index, int bits,
  79. off_t in, off_t out, unsigned left, unsigned char *window)
  80. {
  81. struct point *next;
  82. /* if list is empty, create it (start with eight points) */
  83. if (index == NULL) {
  84. index = malloc(sizeof(struct access));
  85. if (index == NULL) return NULL;
  86. index->list = malloc(sizeof(struct point) << 3);
  87. if (index->list == NULL) {
  88. free(index);
  89. return NULL;
  90. }
  91. index->size = 8;
  92. index->have = 0;
  93. }
  94. /* if list is full, make it bigger */
  95. else if (index->have == index->size) {
  96. index->size <<= 1;
  97. next = realloc(index->list, sizeof(struct point) * index->size);
  98. if (next == NULL) {
  99. free_index(index);
  100. return NULL;
  101. }
  102. index->list = next;
  103. }
  104. /* fill in entry and increment how many we have */
  105. next = index->list + index->have;
  106. next->bits = bits;
  107. next->in = in;
  108. next->out = out;
  109. if (left)
  110. memcpy(next->window, window + WINSIZE - left, left);
  111. if (left < WINSIZE)
  112. memcpy(next->window + left, window, WINSIZE - left);
  113. index->have++;
  114. /* return list, possibly reallocated */
  115. return index;
  116. }
  117. /* Make one entire pass through the compressed stream and build an index, with
  118. access points about every span bytes of uncompressed output -- span is
  119. chosen to balance the speed of random access against the memory requirements
  120. of the list, about 32K bytes per access point. Note that data after the end
  121. of the first zlib or gzip stream in the file is ignored. build_index()
  122. returns the number of access points on success (>= 1), Z_MEM_ERROR for out
  123. of memory, Z_DATA_ERROR for an error in the input file, or Z_ERRNO for a
  124. file read error. On success, *built points to the resulting index. */
  125. local int build_index(FILE *in, off_t span, struct access **built)
  126. {
  127. int ret;
  128. off_t totin, totout; /* our own total counters to avoid 4GB limit */
  129. off_t last; /* totout value of last access point */
  130. struct access *index; /* access points being generated */
  131. z_stream strm;
  132. unsigned char input[CHUNK];
  133. unsigned char window[WINSIZE];
  134. /* initialize inflate */
  135. strm.zalloc = Z_NULL;
  136. strm.zfree = Z_NULL;
  137. strm.opaque = Z_NULL;
  138. strm.avail_in = 0;
  139. strm.next_in = Z_NULL;
  140. ret = inflateInit2(&strm, 47); /* automatic zlib or gzip decoding */
  141. if (ret != Z_OK)
  142. return ret;
  143. /* inflate the input, maintain a sliding window, and build an index -- this
  144. also validates the integrity of the compressed data using the check
  145. information at the end of the gzip or zlib stream */
  146. totin = totout = last = 0;
  147. index = NULL; /* will be allocated by first addpoint() */
  148. strm.avail_out = 0;
  149. do {
  150. /* get some compressed data from input file */
  151. strm.avail_in = fread(input, 1, CHUNK, in);
  152. if (ferror(in)) {
  153. ret = Z_ERRNO;
  154. goto build_index_error;
  155. }
  156. if (strm.avail_in == 0) {
  157. ret = Z_DATA_ERROR;
  158. goto build_index_error;
  159. }
  160. strm.next_in = input;
  161. /* process all of that, or until end of stream */
  162. do {
  163. /* reset sliding window if necessary */
  164. if (strm.avail_out == 0) {
  165. strm.avail_out = WINSIZE;
  166. strm.next_out = window;
  167. }
  168. /* inflate until out of input, output, or at end of block --
  169. update the total input and output counters */
  170. totin += strm.avail_in;
  171. totout += strm.avail_out;
  172. ret = inflate(&strm, Z_BLOCK); /* return at end of block */
  173. totin -= strm.avail_in;
  174. totout -= strm.avail_out;
  175. if (ret == Z_NEED_DICT)
  176. ret = Z_DATA_ERROR;
  177. if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
  178. goto build_index_error;
  179. if (ret == Z_STREAM_END)
  180. break;
  181. /* if at end of block, consider adding an index entry (note that if
  182. data_type indicates an end-of-block, then all of the
  183. uncompressed data from that block has been delivered, and none
  184. of the compressed data after that block has been consumed,
  185. except for up to seven bits) -- the totout == 0 provides an
  186. entry point after the zlib or gzip header, and assures that the
  187. index always has at least one access point; we avoid creating an
  188. access point after the last block by checking bit 6 of data_type
  189. */
  190. if ((strm.data_type & 128) && !(strm.data_type & 64) &&
  191. (totout == 0 || totout - last > span)) {
  192. index = addpoint(index, strm.data_type & 7, totin,
  193. totout, strm.avail_out, window);
  194. if (index == NULL) {
  195. ret = Z_MEM_ERROR;
  196. goto build_index_error;
  197. }
  198. last = totout;
  199. }
  200. } while (strm.avail_in != 0);
  201. } while (ret != Z_STREAM_END);
  202. /* clean up and return index (release unused entries in list) */
  203. (void)inflateEnd(&strm);
  204. index->list = realloc(index->list, sizeof(struct point) * index->have);
  205. index->size = index->have;
  206. *built = index;
  207. return index->size;
  208. /* return error */
  209. build_index_error:
  210. (void)inflateEnd(&strm);
  211. if (index != NULL)
  212. free_index(index);
  213. return ret;
  214. }
  215. /* Use the index to read len bytes from offset into buf, return bytes read or
  216. negative for error (Z_DATA_ERROR or Z_MEM_ERROR). If data is requested past
  217. the end of the uncompressed data, then extract() will return a value less
  218. than len, indicating how much as actually read into buf. This function
  219. should not return a data error unless the file was modified since the index
  220. was generated. extract() may also return Z_ERRNO if there is an error on
  221. reading or seeking the input file. */
  222. local int extract(FILE *in, struct access *index, off_t offset,
  223. unsigned char *buf, int len)
  224. {
  225. int ret, skip;
  226. z_stream strm;
  227. struct point *here;
  228. unsigned char input[CHUNK];
  229. unsigned char discard[WINSIZE];
  230. /* proceed only if something reasonable to do */
  231. if (len < 0)
  232. return 0;
  233. /* find where in stream to start */
  234. here = index->list;
  235. ret = index->have;
  236. while (--ret && here[1].out <= offset)
  237. here++;
  238. /* initialize file and inflate state to start there */
  239. strm.zalloc = Z_NULL;
  240. strm.zfree = Z_NULL;
  241. strm.opaque = Z_NULL;
  242. strm.avail_in = 0;
  243. strm.next_in = Z_NULL;
  244. ret = inflateInit2(&strm, -15); /* raw inflate */
  245. if (ret != Z_OK)
  246. return ret;
  247. ret = fseeko(in, here->in - (here->bits ? 1 : 0), SEEK_SET);
  248. if (ret == -1)
  249. goto extract_ret;
  250. if (here->bits) {
  251. ret = getc(in);
  252. if (ret == -1) {
  253. ret = ferror(in) ? Z_ERRNO : Z_DATA_ERROR;
  254. goto extract_ret;
  255. }
  256. (void)inflatePrime(&strm, here->bits, ret >> (8 - here->bits));
  257. }
  258. (void)inflateSetDictionary(&strm, here->window, WINSIZE);
  259. /* skip uncompressed bytes until offset reached, then satisfy request */
  260. offset -= here->out;
  261. strm.avail_in = 0;
  262. skip = 1; /* while skipping to offset */
  263. do {
  264. /* define where to put uncompressed data, and how much */
  265. if (offset == 0 && skip) { /* at offset now */
  266. strm.avail_out = len;
  267. strm.next_out = buf;
  268. skip = 0; /* only do this once */
  269. }
  270. if (offset > WINSIZE) { /* skip WINSIZE bytes */
  271. strm.avail_out = WINSIZE;
  272. strm.next_out = discard;
  273. offset -= WINSIZE;
  274. }
  275. else if (offset != 0) { /* last skip */
  276. strm.avail_out = (unsigned)offset;
  277. strm.next_out = discard;
  278. offset = 0;
  279. }
  280. /* uncompress until avail_out filled, or end of stream */
  281. do {
  282. if (strm.avail_in == 0) {
  283. strm.avail_in = fread(input, 1, CHUNK, in);
  284. if (ferror(in)) {
  285. ret = Z_ERRNO;
  286. goto extract_ret;
  287. }
  288. if (strm.avail_in == 0) {
  289. ret = Z_DATA_ERROR;
  290. goto extract_ret;
  291. }
  292. strm.next_in = input;
  293. }
  294. ret = inflate(&strm, Z_NO_FLUSH); /* normal inflate */
  295. if (ret == Z_NEED_DICT)
  296. ret = Z_DATA_ERROR;
  297. if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
  298. goto extract_ret;
  299. if (ret == Z_STREAM_END)
  300. break;
  301. } while (strm.avail_out != 0);
  302. /* if reach end of stream, then don't keep trying to get more */
  303. if (ret == Z_STREAM_END)
  304. break;
  305. /* do until offset reached and requested data read, or stream ends */
  306. } while (skip);
  307. /* compute number of uncompressed bytes read after offset */
  308. ret = skip ? 0 : len - strm.avail_out;
  309. /* clean up and return bytes read or error */
  310. extract_ret:
  311. (void)inflateEnd(&strm);
  312. return ret;
  313. }
  314. /* Demonstrate the use of build_index() and extract() by processing the file
  315. provided on the command line, and the extracting 16K from about 2/3rds of
  316. the way through the uncompressed output, and writing that to stdout. */
  317. int main(int argc, char **argv)
  318. {
  319. int len;
  320. off_t offset;
  321. FILE *in;
  322. struct access *index = NULL;
  323. unsigned char buf[CHUNK];
  324. /* open input file */
  325. if (argc != 2) {
  326. fprintf(stderr, "usage: zran file.gz\n");
  327. return 1;
  328. }
  329. in = fopen(argv[1], "rb");
  330. if (in == NULL) {
  331. fprintf(stderr, "zran: could not open %s for reading\n", argv[1]);
  332. return 1;
  333. }
  334. /* build index */
  335. len = build_index(in, SPAN, &index);
  336. if (len < 0) {
  337. fclose(in);
  338. switch (len) {
  339. case Z_MEM_ERROR:
  340. fprintf(stderr, "zran: out of memory\n");
  341. break;
  342. case Z_DATA_ERROR:
  343. fprintf(stderr, "zran: compressed data error in %s\n", argv[1]);
  344. break;
  345. case Z_ERRNO:
  346. fprintf(stderr, "zran: read error on %s\n", argv[1]);
  347. break;
  348. default:
  349. fprintf(stderr, "zran: error %d while building index\n", len);
  350. }
  351. return 1;
  352. }
  353. fprintf(stderr, "zran: built index with %d access points\n", len);
  354. /* use index by reading some bytes from an arbitrary offset */
  355. offset = (index->list[index->have - 1].out << 1) / 3;
  356. len = extract(in, index, offset, buf, CHUNK);
  357. if (len < 0)
  358. fprintf(stderr, "zran: extraction failed: %s error\n",
  359. len == Z_MEM_ERROR ? "out of memory" : "input corrupted");
  360. else {
  361. fwrite(buf, 1, len, stdout);
  362. fprintf(stderr, "zran: extracted %d bytes at %llu\n", len, offset);
  363. }
  364. /* clean up and exit */
  365. free_index(index);
  366. fclose(in);
  367. return 0;
  368. }