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