PageRenderTime 62ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/sys/contrib/zstd/zlibWrapper/examples/fitblk.c

https://bitbucket.org/freebsd/freebsd-base
C | 254 lines | 148 code | 26 blank | 80 comment | 50 complexity | 6c6dd018be625fca9537858bf3cd616f MD5 | raw file
  1. /* fitblk.c contains minimal changes required to be compiled with zlibWrapper:
  2. * - #include "zlib.h" was changed to #include "zstd_zlibwrapper.h"
  3. * - writing block to stdout was disabled */
  4. /* fitblk.c: example of fitting compressed output to a specified size
  5. Not copyrighted -- provided to the public domain
  6. Version 1.1 25 November 2004 Mark Adler */
  7. /* Version history:
  8. 1.0 24 Nov 2004 First version
  9. 1.1 25 Nov 2004 Change deflateInit2() to deflateInit()
  10. Use fixed-size, stack-allocated raw buffers
  11. Simplify code moving compression to subroutines
  12. Use assert() for internal errors
  13. Add detailed description of approach
  14. */
  15. /* Approach to just fitting a requested compressed size:
  16. fitblk performs three compression passes on a portion of the input
  17. data in order to determine how much of that input will compress to
  18. nearly the requested output block size. The first pass generates
  19. enough deflate blocks to produce output to fill the requested
  20. output size plus a specified excess amount (see the EXCESS define
  21. below). The last deflate block may go quite a bit past that, but
  22. is discarded. The second pass decompresses and recompresses just
  23. the compressed data that fit in the requested plus excess sized
  24. buffer. The deflate process is terminated after that amount of
  25. input, which is less than the amount consumed on the first pass.
  26. The last deflate block of the result will be of a comparable size
  27. to the final product, so that the header for that deflate block and
  28. the compression ratio for that block will be about the same as in
  29. the final product. The third compression pass decompresses the
  30. result of the second step, but only the compressed data up to the
  31. requested size minus an amount to allow the compressed stream to
  32. complete (see the MARGIN define below). That will result in a
  33. final compressed stream whose length is less than or equal to the
  34. requested size. Assuming sufficient input and a requested size
  35. greater than a few hundred bytes, the shortfall will typically be
  36. less than ten bytes.
  37. If the input is short enough that the first compression completes
  38. before filling the requested output size, then that compressed
  39. stream is return with no recompression.
  40. EXCESS is chosen to be just greater than the shortfall seen in a
  41. two pass approach similar to the above. That shortfall is due to
  42. the last deflate block compressing more efficiently with a smaller
  43. header on the second pass. EXCESS is set to be large enough so
  44. that there is enough uncompressed data for the second pass to fill
  45. out the requested size, and small enough so that the final deflate
  46. block of the second pass will be close in size to the final deflate
  47. block of the third and final pass. MARGIN is chosen to be just
  48. large enough to assure that the final compression has enough room
  49. to complete in all cases.
  50. */
  51. #include <stdio.h>
  52. #include <stdlib.h>
  53. #include <assert.h>
  54. #include "zstd_zlibwrapper.h"
  55. #define LOG_FITBLK(...) /*printf(__VA_ARGS__)*/
  56. #define local static
  57. /* print nastygram and leave */
  58. local void quit(char *why)
  59. {
  60. fprintf(stderr, "fitblk abort: %s\n", why);
  61. exit(1);
  62. }
  63. #define RAWLEN 4096 /* intermediate uncompressed buffer size */
  64. /* compress from file to def until provided buffer is full or end of
  65. input reached; return last deflate() return value, or Z_ERRNO if
  66. there was read error on the file */
  67. local int partcompress(FILE *in, z_streamp def)
  68. {
  69. int ret, flush;
  70. unsigned char raw[RAWLEN];
  71. flush = Z_SYNC_FLUSH;
  72. do {
  73. def->avail_in = (uInt)fread(raw, 1, RAWLEN, in);
  74. if (ferror(in))
  75. return Z_ERRNO;
  76. def->next_in = raw;
  77. if (feof(in))
  78. flush = Z_FINISH;
  79. LOG_FITBLK("partcompress1 avail_in=%d total_in=%d avail_out=%d total_out=%d\n", (int)def->avail_in, (int)def->total_in, (int)def->avail_out, (int)def->total_out);
  80. ret = deflate(def, flush);
  81. LOG_FITBLK("partcompress2 ret=%d avail_in=%d total_in=%d avail_out=%d total_out=%d\n", ret, (int)def->avail_in, (int)def->total_in, (int)def->avail_out, (int)def->total_out);
  82. assert(ret != Z_STREAM_ERROR);
  83. } while (def->avail_out != 0 && flush == Z_SYNC_FLUSH);
  84. return ret;
  85. }
  86. /* recompress from inf's input to def's output; the input for inf and
  87. the output for def are set in those structures before calling;
  88. return last deflate() return value, or Z_MEM_ERROR if inflate()
  89. was not able to allocate enough memory when it needed to */
  90. local int recompress(z_streamp inf, z_streamp def)
  91. {
  92. int ret, flush;
  93. unsigned char raw[RAWLEN];
  94. flush = Z_NO_FLUSH;
  95. LOG_FITBLK("recompress start\n");
  96. do {
  97. /* decompress */
  98. inf->avail_out = RAWLEN;
  99. inf->next_out = raw;
  100. LOG_FITBLK("recompress1inflate avail_in=%d total_in=%d avail_out=%d total_out=%d\n", (int)inf->avail_in, (int)inf->total_in, (int)inf->avail_out, (int)inf->total_out);
  101. ret = inflate(inf, Z_NO_FLUSH);
  102. LOG_FITBLK("recompress2inflate avail_in=%d total_in=%d avail_out=%d total_out=%d\n", (int)inf->avail_in, (int)inf->total_in, (int)inf->avail_out, (int)inf->total_out);
  103. assert(ret != Z_STREAM_ERROR && ret != Z_DATA_ERROR &&
  104. ret != Z_NEED_DICT);
  105. if (ret == Z_MEM_ERROR)
  106. return ret;
  107. /* compress what was decompresed until done or no room */
  108. def->avail_in = RAWLEN - inf->avail_out;
  109. def->next_in = raw;
  110. if (inf->avail_out != 0)
  111. flush = Z_FINISH;
  112. LOG_FITBLK("recompress1deflate avail_in=%d total_in=%d avail_out=%d total_out=%d\n", (int)def->avail_in, (int)def->total_in, (int)def->avail_out, (int)def->total_out);
  113. ret = deflate(def, flush);
  114. LOG_FITBLK("recompress2deflate ret=%d avail_in=%d total_in=%d avail_out=%d total_out=%d\n", ret, (int)def->avail_in, (int)def->total_in, (int)def->avail_out, (int)def->total_out);
  115. assert(ret != Z_STREAM_ERROR);
  116. } while (ret != Z_STREAM_END && def->avail_out != 0);
  117. return ret;
  118. }
  119. #define EXCESS 256 /* empirically determined stream overage */
  120. #define MARGIN 8 /* amount to back off for completion */
  121. /* compress from stdin to fixed-size block on stdout */
  122. int main(int argc, char **argv)
  123. {
  124. int ret; /* return code */
  125. unsigned size; /* requested fixed output block size */
  126. unsigned have; /* bytes written by deflate() call */
  127. unsigned char *blk; /* intermediate and final stream */
  128. unsigned char *tmp; /* close to desired size stream */
  129. z_stream def, inf; /* zlib deflate and inflate states */
  130. /* get requested output size */
  131. if (argc != 2)
  132. quit("need one argument: size of output block");
  133. ret = (int)strtol(argv[1], argv + 1, 10);
  134. if (argv[1][0] != 0)
  135. quit("argument must be a number");
  136. if (ret < 8) /* 8 is minimum zlib stream size */
  137. quit("need positive size of 8 or greater");
  138. size = (unsigned)ret;
  139. printf("zlib version %s\n", ZLIB_VERSION);
  140. if (ZWRAP_isUsingZSTDcompression()) printf("zstd version %s\n", zstdVersion());
  141. /* allocate memory for buffers and compression engine */
  142. blk = malloc(size + EXCESS);
  143. def.zalloc = Z_NULL;
  144. def.zfree = Z_NULL;
  145. def.opaque = Z_NULL;
  146. ret = deflateInit(&def, Z_DEFAULT_COMPRESSION);
  147. if (ret != Z_OK || blk == NULL)
  148. quit("out of memory");
  149. /* compress from stdin until output full, or no more input */
  150. def.avail_out = size + EXCESS;
  151. def.next_out = blk;
  152. LOG_FITBLK("partcompress1 total_in=%d total_out=%d\n", (int)def.total_in, (int)def.total_out);
  153. ret = partcompress(stdin, &def);
  154. printf("partcompress total_in=%d total_out=%d\n", (int)def.total_in, (int)def.total_out);
  155. if (ret == Z_ERRNO)
  156. quit("error reading input");
  157. /* if it all fit, then size was undersubscribed -- done! */
  158. if (ret == Z_STREAM_END && def.avail_out >= EXCESS) {
  159. /* write block to stdout */
  160. have = size + EXCESS - def.avail_out;
  161. // if (fwrite(blk, 1, have, stdout) != have || ferror(stdout))
  162. // quit("error writing output");
  163. /* clean up and print results to stderr */
  164. ret = deflateEnd(&def);
  165. assert(ret != Z_STREAM_ERROR);
  166. free(blk);
  167. fprintf(stderr,
  168. "%u bytes unused out of %u requested (all input)\n",
  169. size - have, size);
  170. return 0;
  171. }
  172. /* it didn't all fit -- set up for recompression */
  173. inf.zalloc = Z_NULL;
  174. inf.zfree = Z_NULL;
  175. inf.opaque = Z_NULL;
  176. inf.avail_in = 0;
  177. inf.next_in = Z_NULL;
  178. ret = inflateInit(&inf);
  179. tmp = malloc(size + EXCESS);
  180. if (ret != Z_OK || tmp == NULL)
  181. quit("out of memory");
  182. ret = deflateReset(&def);
  183. assert(ret != Z_STREAM_ERROR);
  184. /* do first recompression close to the right amount */
  185. inf.avail_in = size + EXCESS;
  186. inf.next_in = blk;
  187. def.avail_out = size + EXCESS;
  188. def.next_out = tmp;
  189. LOG_FITBLK("recompress1 inf.total_in=%d def.total_out=%d\n", (int)inf.total_in, (int)def.total_out);
  190. ret = recompress(&inf, &def);
  191. LOG_FITBLK("recompress1 inf.total_in=%d def.total_out=%d\n", (int)inf.total_in, (int)def.total_out);
  192. if (ret == Z_MEM_ERROR)
  193. quit("out of memory");
  194. /* set up for next recompression */
  195. ret = inflateReset(&inf);
  196. assert(ret != Z_STREAM_ERROR);
  197. ret = deflateReset(&def);
  198. assert(ret != Z_STREAM_ERROR);
  199. /* do second and final recompression (third compression) */
  200. inf.avail_in = size - MARGIN; /* assure stream will complete */
  201. inf.next_in = tmp;
  202. def.avail_out = size;
  203. def.next_out = blk;
  204. LOG_FITBLK("recompress2 inf.total_in=%d def.total_out=%d\n", (int)inf.total_in, (int)def.total_out);
  205. ret = recompress(&inf, &def);
  206. LOG_FITBLK("recompress2 inf.total_in=%d def.total_out=%d\n", (int)inf.total_in, (int)def.total_out);
  207. if (ret == Z_MEM_ERROR)
  208. quit("out of memory");
  209. assert(ret == Z_STREAM_END); /* otherwise MARGIN too small */
  210. /* done -- write block to stdout */
  211. have = size - def.avail_out;
  212. // if (fwrite(blk, 1, have, stdout) != have || ferror(stdout))
  213. // quit("error writing output");
  214. /* clean up and print results to stderr */
  215. free(tmp);
  216. ret = inflateEnd(&inf);
  217. assert(ret != Z_STREAM_ERROR);
  218. ret = deflateEnd(&def);
  219. assert(ret != Z_STREAM_ERROR);
  220. free(blk);
  221. fprintf(stderr,
  222. "%u bytes unused out of %u requested (%lu input)\n",
  223. size - have, size, def.total_in);
  224. return 0;
  225. }