Path: blob/main/sys/contrib/zstd/zlibWrapper/examples/fitblk_original.c
48375 views
/* fitblk.c: example of fitting compressed output to a specified size1Not copyrighted -- provided to the public domain2Version 1.1 25 November 2004 Mark Adler */34/* Version history:51.0 24 Nov 2004 First version61.1 25 Nov 2004 Change deflateInit2() to deflateInit()7Use fixed-size, stack-allocated raw buffers8Simplify code moving compression to subroutines9Use assert() for internal errors10Add detailed description of approach11*/1213/* Approach to just fitting a requested compressed size:1415fitblk performs three compression passes on a portion of the input16data in order to determine how much of that input will compress to17nearly the requested output block size. The first pass generates18enough deflate blocks to produce output to fill the requested19output size plus a specified excess amount (see the EXCESS define20below). The last deflate block may go quite a bit past that, but21is discarded. The second pass decompresses and recompresses just22the compressed data that fit in the requested plus excess sized23buffer. The deflate process is terminated after that amount of24input, which is less than the amount consumed on the first pass.25The last deflate block of the result will be of a comparable size26to the final product, so that the header for that deflate block and27the compression ratio for that block will be about the same as in28the final product. The third compression pass decompresses the29result of the second step, but only the compressed data up to the30requested size minus an amount to allow the compressed stream to31complete (see the MARGIN define below). That will result in a32final compressed stream whose length is less than or equal to the33requested size. Assuming sufficient input and a requested size34greater than a few hundred bytes, the shortfall will typically be35less than ten bytes.3637If the input is short enough that the first compression completes38before filling the requested output size, then that compressed39stream is return with no recompression.4041EXCESS is chosen to be just greater than the shortfall seen in a42two pass approach similar to the above. That shortfall is due to43the last deflate block compressing more efficiently with a smaller44header on the second pass. EXCESS is set to be large enough so45that there is enough uncompressed data for the second pass to fill46out the requested size, and small enough so that the final deflate47block of the second pass will be close in size to the final deflate48block of the third and final pass. MARGIN is chosen to be just49large enough to assure that the final compression has enough room50to complete in all cases.51*/5253#include <stdio.h>54#include <stdlib.h>55#include <assert.h>56#include "zlib.h"5758#define local static5960/* print nastygram and leave */61local void quit(char *why)62{63fprintf(stderr, "fitblk abort: %s\n", why);64exit(1);65}6667#define RAWLEN 4096 /* intermediate uncompressed buffer size */6869/* compress from file to def until provided buffer is full or end of70input reached; return last deflate() return value, or Z_ERRNO if71there was read error on the file */72local int partcompress(FILE *in, z_streamp def)73{74int ret, flush;75unsigned char raw[RAWLEN];7677flush = Z_NO_FLUSH;78do {79def->avail_in = fread(raw, 1, RAWLEN, in);80if (ferror(in))81return Z_ERRNO;82def->next_in = raw;83if (feof(in))84flush = Z_FINISH;85ret = deflate(def, flush);86assert(ret != Z_STREAM_ERROR);87} while (def->avail_out != 0 && flush == Z_NO_FLUSH);88return ret;89}9091/* recompress from inf's input to def's output; the input for inf and92the output for def are set in those structures before calling;93return last deflate() return value, or Z_MEM_ERROR if inflate()94was not able to allocate enough memory when it needed to */95local int recompress(z_streamp inf, z_streamp def)96{97int ret, flush;98unsigned char raw[RAWLEN];99100flush = Z_NO_FLUSH;101do {102/* decompress */103inf->avail_out = RAWLEN;104inf->next_out = raw;105ret = inflate(inf, Z_NO_FLUSH);106assert(ret != Z_STREAM_ERROR && ret != Z_DATA_ERROR &&107ret != Z_NEED_DICT);108if (ret == Z_MEM_ERROR)109return ret;110111/* compress what was decompressed until done or no room */112def->avail_in = RAWLEN - inf->avail_out;113def->next_in = raw;114if (inf->avail_out != 0)115flush = Z_FINISH;116ret = deflate(def, flush);117assert(ret != Z_STREAM_ERROR);118} while (ret != Z_STREAM_END && def->avail_out != 0);119return ret;120}121122#define EXCESS 256 /* empirically determined stream overage */123#define MARGIN 8 /* amount to back off for completion */124125/* compress from stdin to fixed-size block on stdout */126int main(int argc, char **argv)127{128int ret; /* return code */129unsigned size; /* requested fixed output block size */130unsigned have; /* bytes written by deflate() call */131unsigned char *blk; /* intermediate and final stream */132unsigned char *tmp; /* close to desired size stream */133z_stream def, inf; /* zlib deflate and inflate states */134135/* get requested output size */136if (argc != 2)137quit("need one argument: size of output block");138ret = strtol(argv[1], argv + 1, 10);139if (argv[1][0] != 0)140quit("argument must be a number");141if (ret < 8) /* 8 is minimum zlib stream size */142quit("need positive size of 8 or greater");143size = (unsigned)ret;144145/* allocate memory for buffers and compression engine */146blk = malloc(size + EXCESS);147def.zalloc = Z_NULL;148def.zfree = Z_NULL;149def.opaque = Z_NULL;150ret = deflateInit(&def, Z_DEFAULT_COMPRESSION);151if (ret != Z_OK || blk == NULL)152quit("out of memory");153154/* compress from stdin until output full, or no more input */155def.avail_out = size + EXCESS;156def.next_out = blk;157ret = partcompress(stdin, &def);158if (ret == Z_ERRNO)159quit("error reading input");160161/* if it all fit, then size was undersubscribed -- done! */162if (ret == Z_STREAM_END && def.avail_out >= EXCESS) {163/* write block to stdout */164have = size + EXCESS - def.avail_out;165if (fwrite(blk, 1, have, stdout) != have || ferror(stdout))166quit("error writing output");167168/* clean up and print results to stderr */169ret = deflateEnd(&def);170assert(ret != Z_STREAM_ERROR);171free(blk);172fprintf(stderr,173"%u bytes unused out of %u requested (all input)\n",174size - have, size);175return 0;176}177178/* it didn't all fit -- set up for recompression */179inf.zalloc = Z_NULL;180inf.zfree = Z_NULL;181inf.opaque = Z_NULL;182inf.avail_in = 0;183inf.next_in = Z_NULL;184ret = inflateInit(&inf);185tmp = malloc(size + EXCESS);186if (ret != Z_OK || tmp == NULL)187quit("out of memory");188ret = deflateReset(&def);189assert(ret != Z_STREAM_ERROR);190191/* do first recompression close to the right amount */192inf.avail_in = size + EXCESS;193inf.next_in = blk;194def.avail_out = size + EXCESS;195def.next_out = tmp;196ret = recompress(&inf, &def);197if (ret == Z_MEM_ERROR)198quit("out of memory");199200/* set up for next recompression */201ret = inflateReset(&inf);202assert(ret != Z_STREAM_ERROR);203ret = deflateReset(&def);204assert(ret != Z_STREAM_ERROR);205206/* do second and final recompression (third compression) */207inf.avail_in = size - MARGIN; /* assure stream will complete */208inf.next_in = tmp;209def.avail_out = size;210def.next_out = blk;211ret = recompress(&inf, &def);212if (ret == Z_MEM_ERROR)213quit("out of memory");214assert(ret == Z_STREAM_END); /* otherwise MARGIN too small */215216/* done -- write block to stdout */217have = size - def.avail_out;218if (fwrite(blk, 1, have, stdout) != have || ferror(stdout))219quit("error writing output");220221/* clean up and print results to stderr */222free(tmp);223ret = inflateEnd(&inf);224assert(ret != Z_STREAM_ERROR);225ret = deflateEnd(&def);226assert(ret != Z_STREAM_ERROR);227free(blk);228fprintf(stderr,229"%u bytes unused out of %u requested (%lu input)\n",230size - have, size, def.total_in);231return 0;232}233234235