1275793eaSopenharmony_ci/* fitblk.c: example of fitting compressed output to a specified size 2275793eaSopenharmony_ci Not copyrighted -- provided to the public domain 3275793eaSopenharmony_ci Version 1.1 25 November 2004 Mark Adler */ 4275793eaSopenharmony_ci 5275793eaSopenharmony_ci/* Version history: 6275793eaSopenharmony_ci 1.0 24 Nov 2004 First version 7275793eaSopenharmony_ci 1.1 25 Nov 2004 Change deflateInit2() to deflateInit() 8275793eaSopenharmony_ci Use fixed-size, stack-allocated raw buffers 9275793eaSopenharmony_ci Simplify code moving compression to subroutines 10275793eaSopenharmony_ci Use assert() for internal errors 11275793eaSopenharmony_ci Add detailed description of approach 12275793eaSopenharmony_ci */ 13275793eaSopenharmony_ci 14275793eaSopenharmony_ci/* Approach to just fitting a requested compressed size: 15275793eaSopenharmony_ci 16275793eaSopenharmony_ci fitblk performs three compression passes on a portion of the input 17275793eaSopenharmony_ci data in order to determine how much of that input will compress to 18275793eaSopenharmony_ci nearly the requested output block size. The first pass generates 19275793eaSopenharmony_ci enough deflate blocks to produce output to fill the requested 20275793eaSopenharmony_ci output size plus a specified excess amount (see the EXCESS define 21275793eaSopenharmony_ci below). The last deflate block may go quite a bit past that, but 22275793eaSopenharmony_ci is discarded. The second pass decompresses and recompresses just 23275793eaSopenharmony_ci the compressed data that fit in the requested plus excess sized 24275793eaSopenharmony_ci buffer. The deflate process is terminated after that amount of 25275793eaSopenharmony_ci input, which is less than the amount consumed on the first pass. 26275793eaSopenharmony_ci The last deflate block of the result will be of a comparable size 27275793eaSopenharmony_ci to the final product, so that the header for that deflate block and 28275793eaSopenharmony_ci the compression ratio for that block will be about the same as in 29275793eaSopenharmony_ci the final product. The third compression pass decompresses the 30275793eaSopenharmony_ci result of the second step, but only the compressed data up to the 31275793eaSopenharmony_ci requested size minus an amount to allow the compressed stream to 32275793eaSopenharmony_ci complete (see the MARGIN define below). That will result in a 33275793eaSopenharmony_ci final compressed stream whose length is less than or equal to the 34275793eaSopenharmony_ci requested size. Assuming sufficient input and a requested size 35275793eaSopenharmony_ci greater than a few hundred bytes, the shortfall will typically be 36275793eaSopenharmony_ci less than ten bytes. 37275793eaSopenharmony_ci 38275793eaSopenharmony_ci If the input is short enough that the first compression completes 39275793eaSopenharmony_ci before filling the requested output size, then that compressed 40275793eaSopenharmony_ci stream is return with no recompression. 41275793eaSopenharmony_ci 42275793eaSopenharmony_ci EXCESS is chosen to be just greater than the shortfall seen in a 43275793eaSopenharmony_ci two pass approach similar to the above. That shortfall is due to 44275793eaSopenharmony_ci the last deflate block compressing more efficiently with a smaller 45275793eaSopenharmony_ci header on the second pass. EXCESS is set to be large enough so 46275793eaSopenharmony_ci that there is enough uncompressed data for the second pass to fill 47275793eaSopenharmony_ci out the requested size, and small enough so that the final deflate 48275793eaSopenharmony_ci block of the second pass will be close in size to the final deflate 49275793eaSopenharmony_ci block of the third and final pass. MARGIN is chosen to be just 50275793eaSopenharmony_ci large enough to assure that the final compression has enough room 51275793eaSopenharmony_ci to complete in all cases. 52275793eaSopenharmony_ci */ 53275793eaSopenharmony_ci 54275793eaSopenharmony_ci#include <stdio.h> 55275793eaSopenharmony_ci#include <stdlib.h> 56275793eaSopenharmony_ci#include <assert.h> 57275793eaSopenharmony_ci#include "zlib.h" 58275793eaSopenharmony_ci 59275793eaSopenharmony_ci#define local static 60275793eaSopenharmony_ci 61275793eaSopenharmony_ci/* print nastygram and leave */ 62275793eaSopenharmony_cilocal void quit(char *why) 63275793eaSopenharmony_ci{ 64275793eaSopenharmony_ci fprintf(stderr, "fitblk abort: %s\n", why); 65275793eaSopenharmony_ci exit(1); 66275793eaSopenharmony_ci} 67275793eaSopenharmony_ci 68275793eaSopenharmony_ci#define RAWLEN 4096 /* intermediate uncompressed buffer size */ 69275793eaSopenharmony_ci 70275793eaSopenharmony_ci/* compress from file to def until provided buffer is full or end of 71275793eaSopenharmony_ci input reached; return last deflate() return value, or Z_ERRNO if 72275793eaSopenharmony_ci there was read error on the file */ 73275793eaSopenharmony_cilocal int partcompress(FILE *in, z_streamp def) 74275793eaSopenharmony_ci{ 75275793eaSopenharmony_ci int ret, flush; 76275793eaSopenharmony_ci unsigned char raw[RAWLEN]; 77275793eaSopenharmony_ci 78275793eaSopenharmony_ci flush = Z_NO_FLUSH; 79275793eaSopenharmony_ci do { 80275793eaSopenharmony_ci def->avail_in = fread(raw, 1, RAWLEN, in); 81275793eaSopenharmony_ci if (ferror(in)) 82275793eaSopenharmony_ci return Z_ERRNO; 83275793eaSopenharmony_ci def->next_in = raw; 84275793eaSopenharmony_ci if (feof(in)) 85275793eaSopenharmony_ci flush = Z_FINISH; 86275793eaSopenharmony_ci ret = deflate(def, flush); 87275793eaSopenharmony_ci assert(ret != Z_STREAM_ERROR); 88275793eaSopenharmony_ci } while (def->avail_out != 0 && flush == Z_NO_FLUSH); 89275793eaSopenharmony_ci return ret; 90275793eaSopenharmony_ci} 91275793eaSopenharmony_ci 92275793eaSopenharmony_ci/* recompress from inf's input to def's output; the input for inf and 93275793eaSopenharmony_ci the output for def are set in those structures before calling; 94275793eaSopenharmony_ci return last deflate() return value, or Z_MEM_ERROR if inflate() 95275793eaSopenharmony_ci was not able to allocate enough memory when it needed to */ 96275793eaSopenharmony_cilocal int recompress(z_streamp inf, z_streamp def) 97275793eaSopenharmony_ci{ 98275793eaSopenharmony_ci int ret, flush; 99275793eaSopenharmony_ci unsigned char raw[RAWLEN]; 100275793eaSopenharmony_ci 101275793eaSopenharmony_ci flush = Z_NO_FLUSH; 102275793eaSopenharmony_ci do { 103275793eaSopenharmony_ci /* decompress */ 104275793eaSopenharmony_ci inf->avail_out = RAWLEN; 105275793eaSopenharmony_ci inf->next_out = raw; 106275793eaSopenharmony_ci ret = inflate(inf, Z_NO_FLUSH); 107275793eaSopenharmony_ci assert(ret != Z_STREAM_ERROR && ret != Z_DATA_ERROR && 108275793eaSopenharmony_ci ret != Z_NEED_DICT); 109275793eaSopenharmony_ci if (ret == Z_MEM_ERROR) 110275793eaSopenharmony_ci return ret; 111275793eaSopenharmony_ci 112275793eaSopenharmony_ci /* compress what was decompressed until done or no room */ 113275793eaSopenharmony_ci def->avail_in = RAWLEN - inf->avail_out; 114275793eaSopenharmony_ci def->next_in = raw; 115275793eaSopenharmony_ci if (inf->avail_out != 0) 116275793eaSopenharmony_ci flush = Z_FINISH; 117275793eaSopenharmony_ci ret = deflate(def, flush); 118275793eaSopenharmony_ci assert(ret != Z_STREAM_ERROR); 119275793eaSopenharmony_ci } while (ret != Z_STREAM_END && def->avail_out != 0); 120275793eaSopenharmony_ci return ret; 121275793eaSopenharmony_ci} 122275793eaSopenharmony_ci 123275793eaSopenharmony_ci#define EXCESS 256 /* empirically determined stream overage */ 124275793eaSopenharmony_ci#define MARGIN 8 /* amount to back off for completion */ 125275793eaSopenharmony_ci 126275793eaSopenharmony_ci/* compress from stdin to fixed-size block on stdout */ 127275793eaSopenharmony_ciint main(int argc, char **argv) 128275793eaSopenharmony_ci{ 129275793eaSopenharmony_ci int ret; /* return code */ 130275793eaSopenharmony_ci unsigned size; /* requested fixed output block size */ 131275793eaSopenharmony_ci unsigned have; /* bytes written by deflate() call */ 132275793eaSopenharmony_ci unsigned char *blk; /* intermediate and final stream */ 133275793eaSopenharmony_ci unsigned char *tmp; /* close to desired size stream */ 134275793eaSopenharmony_ci z_stream def, inf; /* zlib deflate and inflate states */ 135275793eaSopenharmony_ci 136275793eaSopenharmony_ci /* get requested output size */ 137275793eaSopenharmony_ci if (argc != 2) 138275793eaSopenharmony_ci quit("need one argument: size of output block"); 139275793eaSopenharmony_ci ret = strtol(argv[1], argv + 1, 10); 140275793eaSopenharmony_ci if (argv[1][0] != 0) 141275793eaSopenharmony_ci quit("argument must be a number"); 142275793eaSopenharmony_ci if (ret < 8) /* 8 is minimum zlib stream size */ 143275793eaSopenharmony_ci quit("need positive size of 8 or greater"); 144275793eaSopenharmony_ci size = (unsigned)ret; 145275793eaSopenharmony_ci 146275793eaSopenharmony_ci /* allocate memory for buffers and compression engine */ 147275793eaSopenharmony_ci blk = malloc(size + EXCESS); 148275793eaSopenharmony_ci def.zalloc = Z_NULL; 149275793eaSopenharmony_ci def.zfree = Z_NULL; 150275793eaSopenharmony_ci def.opaque = Z_NULL; 151275793eaSopenharmony_ci ret = deflateInit(&def, Z_DEFAULT_COMPRESSION); 152275793eaSopenharmony_ci if (ret != Z_OK || blk == NULL) 153275793eaSopenharmony_ci quit("out of memory"); 154275793eaSopenharmony_ci 155275793eaSopenharmony_ci /* compress from stdin until output full, or no more input */ 156275793eaSopenharmony_ci def.avail_out = size + EXCESS; 157275793eaSopenharmony_ci def.next_out = blk; 158275793eaSopenharmony_ci ret = partcompress(stdin, &def); 159275793eaSopenharmony_ci if (ret == Z_ERRNO) 160275793eaSopenharmony_ci quit("error reading input"); 161275793eaSopenharmony_ci 162275793eaSopenharmony_ci /* if it all fit, then size was undersubscribed -- done! */ 163275793eaSopenharmony_ci if (ret == Z_STREAM_END && def.avail_out >= EXCESS) { 164275793eaSopenharmony_ci /* write block to stdout */ 165275793eaSopenharmony_ci have = size + EXCESS - def.avail_out; 166275793eaSopenharmony_ci if (fwrite(blk, 1, have, stdout) != have || ferror(stdout)) 167275793eaSopenharmony_ci quit("error writing output"); 168275793eaSopenharmony_ci 169275793eaSopenharmony_ci /* clean up and print results to stderr */ 170275793eaSopenharmony_ci ret = deflateEnd(&def); 171275793eaSopenharmony_ci assert(ret != Z_STREAM_ERROR); 172275793eaSopenharmony_ci free(blk); 173275793eaSopenharmony_ci fprintf(stderr, 174275793eaSopenharmony_ci "%u bytes unused out of %u requested (all input)\n", 175275793eaSopenharmony_ci size - have, size); 176275793eaSopenharmony_ci return 0; 177275793eaSopenharmony_ci } 178275793eaSopenharmony_ci 179275793eaSopenharmony_ci /* it didn't all fit -- set up for recompression */ 180275793eaSopenharmony_ci inf.zalloc = Z_NULL; 181275793eaSopenharmony_ci inf.zfree = Z_NULL; 182275793eaSopenharmony_ci inf.opaque = Z_NULL; 183275793eaSopenharmony_ci inf.avail_in = 0; 184275793eaSopenharmony_ci inf.next_in = Z_NULL; 185275793eaSopenharmony_ci ret = inflateInit(&inf); 186275793eaSopenharmony_ci tmp = malloc(size + EXCESS); 187275793eaSopenharmony_ci if (ret != Z_OK || tmp == NULL) 188275793eaSopenharmony_ci quit("out of memory"); 189275793eaSopenharmony_ci ret = deflateReset(&def); 190275793eaSopenharmony_ci assert(ret != Z_STREAM_ERROR); 191275793eaSopenharmony_ci 192275793eaSopenharmony_ci /* do first recompression close to the right amount */ 193275793eaSopenharmony_ci inf.avail_in = size + EXCESS; 194275793eaSopenharmony_ci inf.next_in = blk; 195275793eaSopenharmony_ci def.avail_out = size + EXCESS; 196275793eaSopenharmony_ci def.next_out = tmp; 197275793eaSopenharmony_ci ret = recompress(&inf, &def); 198275793eaSopenharmony_ci if (ret == Z_MEM_ERROR) 199275793eaSopenharmony_ci quit("out of memory"); 200275793eaSopenharmony_ci 201275793eaSopenharmony_ci /* set up for next recompression */ 202275793eaSopenharmony_ci ret = inflateReset(&inf); 203275793eaSopenharmony_ci assert(ret != Z_STREAM_ERROR); 204275793eaSopenharmony_ci ret = deflateReset(&def); 205275793eaSopenharmony_ci assert(ret != Z_STREAM_ERROR); 206275793eaSopenharmony_ci 207275793eaSopenharmony_ci /* do second and final recompression (third compression) */ 208275793eaSopenharmony_ci inf.avail_in = size - MARGIN; /* assure stream will complete */ 209275793eaSopenharmony_ci inf.next_in = tmp; 210275793eaSopenharmony_ci def.avail_out = size; 211275793eaSopenharmony_ci def.next_out = blk; 212275793eaSopenharmony_ci ret = recompress(&inf, &def); 213275793eaSopenharmony_ci if (ret == Z_MEM_ERROR) 214275793eaSopenharmony_ci quit("out of memory"); 215275793eaSopenharmony_ci assert(ret == Z_STREAM_END); /* otherwise MARGIN too small */ 216275793eaSopenharmony_ci 217275793eaSopenharmony_ci /* done -- write block to stdout */ 218275793eaSopenharmony_ci have = size - def.avail_out; 219275793eaSopenharmony_ci if (fwrite(blk, 1, have, stdout) != have || ferror(stdout)) 220275793eaSopenharmony_ci quit("error writing output"); 221275793eaSopenharmony_ci 222275793eaSopenharmony_ci /* clean up and print results to stderr */ 223275793eaSopenharmony_ci free(tmp); 224275793eaSopenharmony_ci ret = inflateEnd(&inf); 225275793eaSopenharmony_ci assert(ret != Z_STREAM_ERROR); 226275793eaSopenharmony_ci ret = deflateEnd(&def); 227275793eaSopenharmony_ci assert(ret != Z_STREAM_ERROR); 228275793eaSopenharmony_ci free(blk); 229275793eaSopenharmony_ci fprintf(stderr, 230275793eaSopenharmony_ci "%u bytes unused out of %u requested (%lu input)\n", 231275793eaSopenharmony_ci size - have, size, def.total_in); 232275793eaSopenharmony_ci return 0; 233275793eaSopenharmony_ci} 234