summaryrefslogtreecommitdiff
path: root/src/gzip.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gzip.c')
-rw-r--r--src/gzip.c140
1 files changed, 140 insertions, 0 deletions
diff --git a/src/gzip.c b/src/gzip.c
new file mode 100644
index 0000000..dfb1563
--- /dev/null
+++ b/src/gzip.c
@@ -0,0 +1,140 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*******************************************************************************
+**
+** This file contains code used to incrementally generate a GZIP compressed
+** file. The GZIP format is described in RFC-1952.
+**
+** State information is stored in static variables, so this implementation
+** can only be building up a single GZIP file at a time.
+*/
+#include "config.h"
+#include <assert.h>
+#include <zlib.h>
+#include "gzip.h"
+
+/*
+** State information for the GZIP file under construction.
+*/
+struct gzip_state {
+ int eState; /* 0: idle 1: header 2: compressing */
+ unsigned long iCRC; /* The checksum */
+ z_stream stream; /* The working compressor */
+ Blob out; /* Results stored here */
+} gzip;
+
+/*
+** Write a 32-bit integer as little-endian into the given buffer.
+*/
+static void put32(char *z, int v){
+ z[0] = v & 0xff;
+ z[1] = (v>>8) & 0xff;
+ z[2] = (v>>16) & 0xff;
+ z[3] = (v>>24) & 0xff;
+}
+
+/*
+** Begin constructing a gzip file.
+*/
+void gzip_begin(sqlite3_int64 now){
+ char aHdr[10];
+ assert( gzip.eState==0 );
+ blob_zero(&gzip.out);
+ aHdr[0] = 0x1f;
+ aHdr[1] = 0x8b;
+ aHdr[2] = 8;
+ aHdr[3] = 0;
+ if( now==-1 ){
+ now = db_int64(0, "SELECT (julianday('now') - 2440587.5)*86400.0");
+ }
+ put32(&aHdr[4], now&0xffffffff);
+ aHdr[8] = 2;
+ aHdr[9] = -1;
+ blob_append(&gzip.out, aHdr, 10);
+ gzip.iCRC = 0;
+ gzip.eState = 1;
+}
+
+/*
+** Add nIn bytes of content from pIn to the gzip file.
+*/
+#define GZIP_BUFSZ 100000
+void gzip_step(const char *pIn, int nIn){
+ char *zOutBuf;
+ int nOut;
+
+ nOut = nIn + nIn/10 + 100;
+ if( nOut<100000 ) nOut = 100000;
+ zOutBuf = fossil_malloc(nOut);
+ gzip.stream.avail_in = nIn;
+ gzip.stream.next_in = (unsigned char*)pIn;
+ gzip.stream.avail_out = nOut;
+ gzip.stream.next_out = (unsigned char*)zOutBuf;
+ if( gzip.eState==1 ){
+ gzip.stream.zalloc = (alloc_func)0;
+ gzip.stream.zfree = (free_func)0;
+ gzip.stream.opaque = 0;
+ deflateInit2(&gzip.stream, 9, Z_DEFLATED, -MAX_WBITS,8,Z_DEFAULT_STRATEGY);
+ gzip.eState = 2;
+ }
+ gzip.iCRC = crc32(gzip.iCRC, gzip.stream.next_in, gzip.stream.avail_in);
+ do{
+ deflate(&gzip.stream, nIn==0 ? Z_FINISH : 0);
+ blob_append(&gzip.out, zOutBuf, nOut - gzip.stream.avail_out);
+ gzip.stream.avail_out = nOut;
+ gzip.stream.next_out = (unsigned char*)zOutBuf;
+ }while( gzip.stream.avail_in>0 );
+ fossil_free(zOutBuf);
+}
+
+/*
+** Finish the gzip file and put the content in *pOut
+*/
+void gzip_finish(Blob *pOut){
+ char aTrailer[8];
+ assert( gzip.eState>0 );
+ gzip_step("", 0);
+ deflateEnd(&gzip.stream);
+ put32(aTrailer, gzip.iCRC);
+ put32(&aTrailer[4], gzip.stream.total_in);
+ blob_append(&gzip.out, aTrailer, 8);
+ *pOut = gzip.out;
+ blob_zero(&gzip.out);
+ gzip.eState = 0;
+}
+
+/*
+** COMMAND: test-gzip
+**
+** Usage: %fossil test-gzip FILENAME
+**
+** Compress a file using gzip.
+*/
+void test_gzip_cmd(void){
+ Blob b;
+ char *zOut;
+ if( g.argc!=3 ) usage("FILENAME");
+ sqlite3_open(":memory:", &g.db);
+ gzip_begin(-1);
+ blob_read_from_file(&b, g.argv[2], ExtFILE);
+ zOut = mprintf("%s.gz", g.argv[2]);
+ gzip_step(blob_buffer(&b), blob_size(&b));
+ blob_reset(&b);
+ gzip_finish(&b);
+ blob_write_to_file(&b, zOut);
+ blob_reset(&b);
+ fossil_free(zOut);
+}