Path: blob/main/sys/contrib/zstd/zlibWrapper/examples/fitblk.c
48375 views
/* fitblk.c contains minimal changes required to be compiled with zlibWrapper:1* - #include "zlib.h" was changed to #include "zstd_zlibwrapper.h"2* - writing block to stdout was disabled */34/* fitblk.c: example of fitting compressed output to a specified size5Not copyrighted -- provided to the public domain6Version 1.1 25 November 2004 Mark Adler */78/* Version history:91.0 24 Nov 2004 First version101.1 25 Nov 2004 Change deflateInit2() to deflateInit()11Use fixed-size, stack-allocated raw buffers12Simplify code moving compression to subroutines13Use assert() for internal errors14Add detailed description of approach15*/1617/* Approach to just fitting a requested compressed size:1819fitblk performs three compression passes on a portion of the input20data in order to determine how much of that input will compress to21nearly the requested output block size. The first pass generates22enough deflate blocks to produce output to fill the requested23output size plus a specified excess amount (see the EXCESS define24below). The last deflate block may go quite a bit past that, but25is discarded. The second pass decompresses and recompresses just26the compressed data that fit in the requested plus excess sized27buffer. The deflate process is terminated after that amount of28input, which is less than the amount consumed on the first pass.29The last deflate block of the result will be of a comparable size30to the final product, so that the header for that deflate block and31the compression ratio for that block will be about the same as in32the final product. The third compression pass decompresses the33result of the second step, but only the compressed data up to the34requested size minus an amount to allow the compressed stream to35complete (see the MARGIN define below). That will result in a36final compressed stream whose length is less than or equal to the37requested size. Assuming sufficient input and a requested size38greater than a few hundred bytes, the shortfall will typically be39less than ten bytes.4041If the input is short enough that the first compression completes42before filling the requested output size, then that compressed43stream is return with no recompression.4445EXCESS is chosen to be just greater than the shortfall seen in a46two pass approach similar to the above. That shortfall is due to47the last deflate block compressing more efficiently with a smaller48header on the second pass. EXCESS is set to be large enough so49that there is enough uncompressed data for the second pass to fill50out the requested size, and small enough so that the final deflate51block of the second pass will be close in size to the final deflate52block of the third and final pass. MARGIN is chosen to be just53large enough to assure that the final compression has enough room54to complete in all cases.55*/5657#include <stdio.h>58#include <stdlib.h>59#include <assert.h>60#include "zstd_zlibwrapper.h"6162#define LOG_FITBLK(...) /*printf(__VA_ARGS__)*/63#define local static6465/* print nastygram and leave */66local void quit(char *why)67{68fprintf(stderr, "fitblk abort: %s\n", why);69exit(1);70}7172#define RAWLEN 4096 /* intermediate uncompressed buffer size */7374/* compress from file to def until provided buffer is full or end of75input reached; return last deflate() return value, or Z_ERRNO if76there was read error on the file */77local int partcompress(FILE *in, z_streamp def)78{79int ret, flush;80unsigned char raw[RAWLEN];8182flush = Z_SYNC_FLUSH;83do {84def->avail_in = (uInt)fread(raw, 1, RAWLEN, in);85if (ferror(in))86return Z_ERRNO;87def->next_in = raw;88if (feof(in))89flush = Z_FINISH;90LOG_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);91ret = deflate(def, flush);92LOG_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);93assert(ret != Z_STREAM_ERROR);94} while (def->avail_out != 0 && flush == Z_SYNC_FLUSH);95return ret;96}9798/* recompress from inf's input to def's output; the input for inf and99the output for def are set in those structures before calling;100return last deflate() return value, or Z_MEM_ERROR if inflate()101was not able to allocate enough memory when it needed to */102local int recompress(z_streamp inf, z_streamp def)103{104int ret, flush;105unsigned char raw[RAWLEN];106107flush = Z_NO_FLUSH;108LOG_FITBLK("recompress start\n");109do {110/* decompress */111inf->avail_out = RAWLEN;112inf->next_out = raw;113LOG_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);114ret = inflate(inf, Z_NO_FLUSH);115LOG_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);116assert(ret != Z_STREAM_ERROR && ret != Z_DATA_ERROR &&117ret != Z_NEED_DICT);118if (ret == Z_MEM_ERROR)119return ret;120121/* compress what was decompressed until done or no room */122def->avail_in = RAWLEN - inf->avail_out;123def->next_in = raw;124if (inf->avail_out != 0)125flush = Z_FINISH;126LOG_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);127ret = deflate(def, flush);128LOG_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);129assert(ret != Z_STREAM_ERROR);130} while (ret != Z_STREAM_END && def->avail_out != 0);131return ret;132}133134#define EXCESS 256 /* empirically determined stream overage */135#define MARGIN 8 /* amount to back off for completion */136137/* compress from stdin to fixed-size block on stdout */138int main(int argc, char **argv)139{140int ret; /* return code */141unsigned size; /* requested fixed output block size */142unsigned have; /* bytes written by deflate() call */143unsigned char *blk; /* intermediate and final stream */144unsigned char *tmp; /* close to desired size stream */145z_stream def, inf; /* zlib deflate and inflate states */146147/* get requested output size */148if (argc != 2)149quit("need one argument: size of output block");150ret = (int)strtol(argv[1], argv + 1, 10);151if (argv[1][0] != 0)152quit("argument must be a number");153if (ret < 8) /* 8 is minimum zlib stream size */154quit("need positive size of 8 or greater");155size = (unsigned)ret;156157printf("zlib version %s\n", ZLIB_VERSION);158if (ZWRAP_isUsingZSTDcompression()) printf("zstd version %s\n", zstdVersion());159160/* allocate memory for buffers and compression engine */161blk = (unsigned char*)malloc(size + EXCESS);162def.zalloc = Z_NULL;163def.zfree = Z_NULL;164def.opaque = Z_NULL;165ret = deflateInit(&def, Z_DEFAULT_COMPRESSION);166if (ret != Z_OK || blk == NULL)167quit("out of memory");168169/* compress from stdin until output full, or no more input */170def.avail_out = size + EXCESS;171def.next_out = blk;172LOG_FITBLK("partcompress1 total_in=%d total_out=%d\n", (int)def.total_in, (int)def.total_out);173ret = partcompress(stdin, &def);174printf("partcompress total_in=%d total_out=%d\n", (int)def.total_in, (int)def.total_out);175if (ret == Z_ERRNO)176quit("error reading input");177178/* if it all fit, then size was undersubscribed -- done! */179if (ret == Z_STREAM_END && def.avail_out >= EXCESS) {180/* write block to stdout */181have = size + EXCESS - def.avail_out;182/* if (fwrite(blk, 1, have, stdout) != have || ferror(stdout))183* quit("error writing output"); */184185/* clean up and print results to stderr */186ret = deflateEnd(&def);187assert(ret != Z_STREAM_ERROR);188free(blk);189fprintf(stderr,190"%u bytes unused out of %u requested (all input)\n",191size - have, size);192return 0;193}194195/* it didn't all fit -- set up for recompression */196inf.zalloc = Z_NULL;197inf.zfree = Z_NULL;198inf.opaque = Z_NULL;199inf.avail_in = 0;200inf.next_in = Z_NULL;201ret = inflateInit(&inf);202tmp = (unsigned char*)malloc(size + EXCESS);203if (ret != Z_OK || tmp == NULL)204quit("out of memory");205ret = deflateReset(&def);206assert(ret != Z_STREAM_ERROR);207208/* do first recompression close to the right amount */209inf.avail_in = size + EXCESS;210inf.next_in = blk;211def.avail_out = size + EXCESS;212def.next_out = tmp;213LOG_FITBLK("recompress1 inf.total_in=%d def.total_out=%d\n", (int)inf.total_in, (int)def.total_out);214ret = recompress(&inf, &def);215LOG_FITBLK("recompress1 inf.total_in=%d def.total_out=%d\n", (int)inf.total_in, (int)def.total_out);216if (ret == Z_MEM_ERROR)217quit("out of memory");218219/* set up for next recompression */220ret = inflateReset(&inf);221assert(ret != Z_STREAM_ERROR);222ret = deflateReset(&def);223assert(ret != Z_STREAM_ERROR);224225/* do second and final recompression (third compression) */226inf.avail_in = size - MARGIN; /* assure stream will complete */227inf.next_in = tmp;228def.avail_out = size;229def.next_out = blk;230LOG_FITBLK("recompress2 inf.total_in=%d def.total_out=%d\n", (int)inf.total_in, (int)def.total_out);231ret = recompress(&inf, &def);232LOG_FITBLK("recompress2 inf.total_in=%d def.total_out=%d\n", (int)inf.total_in, (int)def.total_out);233if (ret == Z_MEM_ERROR)234quit("out of memory");235assert(ret == Z_STREAM_END); /* otherwise MARGIN too small */236237/* done -- write block to stdout */238have = size - def.avail_out;239/* if (fwrite(blk, 1, have, stdout) != have || ferror(stdout))240* quit("error writing output"); */241242/* clean up and print results to stderr */243free(tmp);244ret = inflateEnd(&inf);245assert(ret != Z_STREAM_ERROR);246ret = deflateEnd(&def);247assert(ret != Z_STREAM_ERROR);248free(blk);249fprintf(stderr,250"%u bytes unused out of %u requested (%lu input)\n",251size - have, size, def.total_in);252return 0;253}254255256