diff options
author | Andreas Fankhauser (@tux-six) | 2022-07-07 02:26:29 +0200 |
---|---|---|
committer | Andreas Fankhauser (@tux-six) | 2022-07-07 02:29:04 +0200 |
commit | be5a1fe4a8fe8e79214cb87a870d7e744ea9f200 (patch) | |
tree | 09b9f577d185d65858d22875bf8f0aacb8874e80 /src | |
download | DeflateAndInflate-be5a1fe4a8fe8e79214cb87a870d7e744ea9f200.zip DeflateAndInflate-be5a1fe4a8fe8e79214cb87a870d7e744ea9f200.tar.gz |
Extract from existing sources
I tried this to be a grafted history because MUST NOT include all the other,
unrelated crap from the mess where I did extract this code from. But it seems a
hard thing to do as all sorts of git commands (eg push, clone, ...) did not
work as epxected. So I keep the commit metadata here where this got split-off.
commit 196ff8beda7a23aee6e1e7388cfc24f6247a63c9
Author: Andreas Fankhauser (@tux-six) <https://hiddenalpha.ch/slnk/id/1:c0n74ct>
Date: 2022-06-10 02:52:11 +0200
(scriptlee) Fix bug in http
Diffstat (limited to 'src')
-rw-r--r-- | src/main/c/deflate.c | 204 | ||||
-rw-r--r-- | src/main/c/inflate.c | 228 |
2 files changed, 432 insertions, 0 deletions
diff --git a/src/main/c/deflate.c b/src/main/c/deflate.c new file mode 100644 index 0000000..9043c10 --- /dev/null +++ b/src/main/c/deflate.c @@ -0,0 +1,204 @@ +/* + + See "./inflate.c" for bash helpers. + +*/ + +#include "commonbase.h" + +/* system */ +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +/* libs */ +#include "zlib.h" + +/* project */ +#include "string-util.h" +#include "log.h" + + +typedef struct Deflate Deflate; + + +struct Deflate { + int level; + int useZlibHdr; +}; + + +static void printHelp( void ){ + printf("\n %s%s", strrchr(__FILE__,'/')+1, " @ " STR_QUOT(PROJECT_VERSION) "\n" + "\n" + "deflates stdin to stdout\n" + "\n" + "Options:\n" + "\n" + " --level\n" + " Compression level 0-9 (0 fastest, 9 smallest).\n" + "\n" + " --zlib\n" + " Prepend output with a zlib header.\n" + "\n"); +} + + +static int parseArgs( int argc, char**argv, Deflate*deflateCls ){ + deflateCls->level = Z_DEFAULT_COMPRESSION; + deflateCls->useZlibHdr = 0; + for( uint i=1 ; i<argc ; ++i ){ + char *arg = argv[i]; + if( !strcmp(arg,"--help") ){ + printHelp(); return -1; + }else if( !strcmp(arg,"--level") ){ + arg = argv[++i]; + if( arg == NULL ){ fprintf(stderr, "Arg level needs a value\n"); return -1; } + char *end; + deflateCls->level = strtol(arg, &end, 10); + if( arg == end ){ fprintf(stderr, "Failed to parse '%s'\n", arg); return -1; } + if( deflateCls->level < 0 || deflateCls->level > 9 ){ + fprintf(stderr, "OutOfRange: %s %s\n", argv[i-1], arg); return -1; + } + }else if( !strcmp(arg,"--zlib") ){ + deflateCls->useZlibHdr = !0; + }else{ + fprintf(stderr, "Unknown arg: %s\n", arg); + return -1; + } + } + return 0; +} + + +static int doDeflate( Deflate*deflateCls ){ + int err; + z_stream strm; + long tmpLong; + uchar innBuf[65536]; + const int innBuf_cap = sizeof innBuf; + int innBuf_len = 0, innBuf_off = 0; + uchar outBuf[65536]; + const int outBuf_cap = sizeof outBuf; + int outBuf_len = 0; + int zlibHdrWritten = deflateCls->useZlibHdr ? 2 : 0; + int inputIsEOF = 0; + int outputIsEOF = 0; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.next_in = innBuf; + strm.avail_in = innBuf_len; + + err = deflateInit(&strm, deflateCls->level); + if( err != Z_OK ){ + LOG_ERROR("deflateInit(): (ret=%d): %s", err, strerror(errno)); + return -1; + } + + for(; !outputIsEOF ;){ + + /* input */ { + int space = innBuf_cap - innBuf_len; + /* look if we can shift */ + //LOG_DEBUG("space=%d\n", space); + if( space <= 0 && innBuf_off > 0 ){ + //LOG_DEBUG("do shifting\n"); + int wr = 0, rd = innBuf_off; + for(;;){ + err = rd - wr; /* chunk length to copy */ + if( rd + err > innBuf_cap) err = innBuf_cap - rd; + if( err <= 0 ) break; + memcpy(innBuf + wr, innBuf + rd, err); + rd += err; + wr += err; + } + innBuf_len -= innBuf_off; + innBuf_off = 0; + space = innBuf_cap - innBuf_len; + //LOG_DEBUG("space=%d (after shift)\n", space); + } + if( space > 0 && !inputIsEOF ){ + err = fread(innBuf + innBuf_len, 1, space, stdin); + if(unlikely( err <= 0 )){ + if( feof(stdin) ){ + inputIsEOF = !0; + }else{ + LOG_ERROR("fread(): %s", strerror(errno)); + return -1; + } + } + assert(err <= space); + innBuf_len += err; + } + assert(innBuf_len - innBuf_off >= 0); + } + + /* process */ { + strm.next_in = innBuf + innBuf_off; + strm.avail_in = innBuf_len - innBuf_off; + strm.next_out = outBuf; + strm.avail_out = outBuf_cap; + assert(strm.avail_out > 0); + //LOG_DEBUG("avail_in=%d, inputIsEOF=%d\n", strm.avail_in, inputIsEOF); + assert(strm.avail_in > 0 || inputIsEOF); + err = deflate(&strm, inputIsEOF ? Z_FINISH : Z_NO_FLUSH); + if(unlikely( err != Z_OK )){ + if( err == Z_STREAM_END ){ + outputIsEOF = !0; + }else{ + LOG_ERROR("deflate(): %s\n", strm.msg); + return -1; + } + } + innBuf_off = strm.next_in - innBuf; + outBuf_len = strm.next_out - outBuf; + } + + /* output */ { + int outBuf_off = 0; + if( zlibHdrWritten < 2 ){ + /* try to skip up to 2 bytes */ + err = 2 - zlibHdrWritten; + if( outBuf_len < err ){ err = outBuf_len; } + zlibHdrWritten += err; + outBuf_off += err; + } + err = fwrite(outBuf + outBuf_off, 1, outBuf_len - outBuf_off, stdout); + if(unlikely( err != outBuf_len - outBuf_off )){ + LOG_ERROR("fwrite(): %s\n", strerror(errno)); + return -1; + } + outBuf_len = 0; + } + } + + err = deflateEnd(&strm); + if( err != Z_OK ){ + LOG_WARN("deflateEnd() ret=%d: %s\n", err, strm.msg); + } + + return 0; +} + + +int deflate_main( int argc, char**argv ){ + int err; + Deflate deflateCls; + #define deflateCls (&deflateCls) + + err = parseArgs(argc, argv, deflateCls); + if( err < 0 ){ return err; } + + stdinModeBinary(); + stdoutModeBinary(); + + err = doDeflate(deflateCls); + if( err < 0 ){ return err; } + + return 0; + #undef deflate +} + diff --git a/src/main/c/inflate.c b/src/main/c/inflate.c new file mode 100644 index 0000000..67ddb71 --- /dev/null +++ b/src/main/c/inflate.c @@ -0,0 +1,228 @@ +/* + + bash-deflate(){ gzip -c | tail -c +10; } + bash-inflate(){ (printf "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00"&&cat -)|gzip -dc; } + +*/ + +#include "commonbase.h" + +/* system */ +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +/* libs */ +#include "zlib.h" + +/* project */ +#include "log.h" +#include "string-util.h" + + +typedef struct MyInflate MyInflate; + + +struct MyInflate { + int tryParseHeaders; +}; + + +static void printHelp( void ){ + printf("\n %s%s", strrchr(__FILE__,'/')+1, " @ " STR_QUOT(PROJECT_VERSION) "\n" + "\n" + "inflates stdin to stdout\n" + "\n" + "Options:\n" + "\n" + " --raw\n" + " By default, we'll try to decode leading headers. Using this\n" + " option, no header parsing is done and the input is expected to\n" + " be a pure deflate stream.\n" + "\n" + "\n"); +} + + +static int parseArgs( int argc, char**argv, MyInflate*myInflate ){ + myInflate->tryParseHeaders = !0; /* enabled by default */ + for( uint i=1 ; i<argc ; ++i ){ + char *arg = argv[i]; + if( !strcmp(arg,"--help") ){ + printHelp(); return -1; + }else if( !strcmp(arg,"--raw") ){ + myInflate->tryParseHeaders = 0; + }else{ + fprintf(stderr, "Unknown arg: %s\n", arg); + return -1; + } + } + return 0; +} + + +static int doInflate( MyInflate*myInflate ){ + #define MAX(a, b) ((a) > (b) ? (a) : (b)) + int err; + z_stream strm; + uchar innBuf[65536]; + const int innBuf_cap = sizeof innBuf; + int innBuf_len = 0, innBuf_off = 0; + uchar outBuf[65536]; + const int outBuf_cap = sizeof outBuf; + int outBuf_len = 0; + int inputIsEOF = 0; + int outputIsEOF = 0; + int noProgressSince = 0; + /* only counts while reading through possible file headers. Later, the + * counter will not be updated anymore, to prevent overflows */ + int headerIsPassed = 0; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.next_in = innBuf; + strm.avail_in = innBuf_len; + + // Pass special 2nd arg. See "https://codeandlife.com/2014/01/01/unzip-library-for-c/" + err = inflateInit2(&strm, -MAX_WBITS); + if( err != Z_OK ){ + LOG_ERROR("inflateInit() ret=%d: %s", err, strerror(errno)); + return -1; + } + + for(; !inputIsEOF || !outputIsEOF ;){ + + /* input */ { + int space = innBuf_cap - innBuf_len; + /* look if we can shift */ + if( space <= 0 && innBuf_off > 0 ){ + //LOG_DEBUG("try-shift space=%d, innBuf_off=%d\n", space, innBuf_off); + int wr = 0, rd = innBuf_off; + for(;;){ + err = rd - wr; /* chunk length to copy */ + if( rd + err > innBuf_cap) err = innBuf_cap - rd; + if( err <= 0 ) break; + memcpy(innBuf + wr, innBuf + rd, err); + rd += err; + wr += err; + } + innBuf_len -= innBuf_off; + innBuf_off = 0; + space = innBuf_cap - innBuf_len; + //LOG_DEBUG("after shift space=%d, innBuf_off=%d, innBuf_len=%d\n", space, innBuf_off, innBuf_len); + } + if( space > 0 && !inputIsEOF ){ + //LOG_TRACE("go read space=%d, inputIsEOF=%d\n", space, inputIsEOF); + err = fread(innBuf + innBuf_len, 1, space, stdin); + //LOG_TRACE("fread() -> %d\n", err); + if(unlikely( err <= 0 )){ + if( feof(stdin) ){ + inputIsEOF = !0; + }else{ + LOG_ERROR("fread(): %s", strerror(errno)); + return -1; + } + } + assert(err <= space); + innBuf_len += err; + } + } + + if(unlikely( !headerIsPassed )){ + #define avail (innBuf_len - innBuf_off) + /* Ensure we have at least two bytes for zlib header detection */ + if( avail < 2 && !inputIsEOF ){ LOG_DEBUG("Not enough data for header detection. have" + " %d\n", avail); continue; } + headerIsPassed = !0; + if( myInflate->tryParseHeaders ){ + int b0 = innBuf[0], b1 = innBuf[1]; + if( b0 == 0x78 && (b1 == 0x01 || b1 == 0x5E || b1 == 0x9C || b1 == 0xDA) ){ + //LOG_DEBUG("looks like a zlib header. Skip it\n"); + innBuf_off += 2; + } + } + #undef avail + } + + /* process */ { + strm.next_in = innBuf + innBuf_off; + strm.avail_in = MAX(0, innBuf_len - innBuf_off); + strm.next_out = outBuf; + strm.avail_out = outBuf_cap; + // TODO remove assert(strm.avail_in >= 0 || inputIsEOF); + assert(strm.avail_out > 0); + int flush = inputIsEOF ? Z_FINISH : Z_NO_FLUSH; + //LOG_DEBUG("inflate(in=0x%p, inLen=%d, %s)\n", strm.next_in, strm.avail_in, + // (flush==Z_FINISH) ? "Z_FINISH" : (flush==Z_FULL_FLUSH) ? "Z_FULL_FLUSH" : "Z_NO_FLUSH"); + errno = 0; + err = inflate(&strm, flush); + if(unlikely( err != Z_OK )){ + if( err == Z_STREAM_END ){ + //LOG_DEBUG("inflate() -> Z_STREAM_END\n"); + outputIsEOF = !0; + }else if( err == Z_BUF_ERROR ){ + /* Could not make any progress. Have to call again with updated buffers */ + noProgressSince += 1; + if( noProgressSince > 42 ){ + LOG_WARN("inflate() -> Z_BUF_ERROR: %s (maybe EOF came too early?)\n", + (strm.msg == NULL) ? "Could not make any progress" : strm.msg); + return -1; + } + }else if( strm.msg != NULL || errno ){ + LOG_ERROR("inflate(): %s\n", (strm.msg != NULL) ? strm.msg : strerror(errno)); + return -1; + }else{ + LOG_ERROR("inflate(): -> %d\n", err); + return -1; + } + }else{ + noProgressSince = 0; /* reset as we just made progress */ + } + innBuf_off += strm.next_in - innBuf; + outBuf_len = strm.next_out - outBuf; + if( innBuf_off > innBuf_len ){ innBuf_off = innBuf_len; } + //LOG_DEBUG("strm.remain=%d, outputIsEOF=%d\n", innBuf_len - innBuf_off, outputIsEOF); + } + + /* output */ { + err = fwrite(outBuf, 1, outBuf_len, stdout); + //LOG_DEBUG("fwrite(0x%p, 1, %d, stdout) -> %d\n", outBuf, outBuf_len, err); + if(unlikely( err != outBuf_len )){ + LOG_ERROR("fwrite(): %s\n", strerror(errno)); + return -1; + } + outBuf_len = 0; + } + } + + err = inflateEnd(&strm); + if( err != Z_OK ){ + LOG_WARN("inflateEnd() ret=%d: %s\n", err, strm.msg); + } + + return 0; + #undef MAX +} + + +int inflate_main( int argc, char**argv ){ + int err; + MyInflate myInflate; + #define myInflate (&myInflate) + + err = parseArgs(argc, argv, myInflate); + if( err < 0 ){ return err; } + + stdinModeBinary(); + stdoutModeBinary(); + + err = doInflate(myInflate); + if( err < 0 ){ return err; } + + return 0; + #undef myInflate +} + + |