annotate src/zlib-1.2.7/examples/fitblk.c @ 169:223a55898ab9 tip default

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