summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorAndreas Fankhauser (@tux-six)2022-07-07 02:33:42 +0200
committerAndreas Fankhauser (@tux-six)2022-07-07 02:33:42 +0200
commitc8cae553fb0058e7defef83669c8ac572e7f0d99 (patch)
tree09b9f577d185d65858d22875bf8f0aacb8874e80 /src/main
parent11715e0e2ab344ed9dc2106938277cadeba984a1 (diff)
parentbe5a1fe4a8fe8e79214cb87a870d7e744ea9f200 (diff)
downloadDeflateAndInflate-c8cae553fb0058e7defef83669c8ac572e7f0d99.zip
DeflateAndInflate-c8cae553fb0058e7defef83669c8ac572e7f0d99.tar.gz
Merge extracted sources
Diffstat (limited to 'src/main')
-rw-r--r--src/main/c/deflate.c204
-rw-r--r--src/main/c/inflate.c228
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
+}
+
+