aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLode Vandevenne <lode@google.com>2013-02-14 14:41:44 +0100
committerLode Vandevenne <lode@google.com>2013-02-14 14:41:44 +0100
commitd5eb5f507386e9933f2d8248d311ceca41fe1df1 (patch)
tree45318cf0d9c787d09bdea01de97eea05fbf0296f
parent04526f14125602622173ccfce017ad503c21bff5 (diff)
downloadzopfli-d5eb5f507386e9933f2d8248d311ceca41fe1df1.tar.gz
Zlib and Gzip support
-rw-r--r--README15
-rw-r--r--blocksplitter.c6
-rw-r--r--gzip.c271
-rw-r--r--gzip_container.c115
-rw-r--r--gzip_container.h42
-rw-r--r--squeeze.c8
-rw-r--r--zlib_container.c78
-rw-r--r--zlib_container.h42
-rw-r--r--zopfli.c216
9 files changed, 509 insertions, 284 deletions
diff --git a/README b/README
index 363ed99..d75856f 100644
--- a/README
+++ b/README
@@ -1,17 +1,20 @@
Zopfli Compression Algorithm is a compression library programmed in C to perform
very good, but slow, deflate or zlib compression.
-gzip.c is separate from the library and contains an example program to create
+zopfli.c is separate from the library and contains an example program to create
very well compressed gzip files.
-The basic function to compress data is Deflate in deflate.h. Use the Options
+The basic functions to compress data are Deflate in deflate.h, ZlibCompress in
+zlib_container.h and GzipCompress in gzip_container.h. Use the Options
object to set parameters that affect the speed and compression. Use the
InitOptions function to place the default values in the Options first.
-This function creates a valid deflate stream. To get a valid zlib stream
-instead, you need to add a small zlib header and a checksum around this stream
-yourself. Please see the Zlib specification for more details:
-http://www.ietf.org/rfc/rfc1950.txt.
+Deflate creates a valid deflate stream in memory, see:
+http://www.ietf.org/rfc/rfc1951.txt
+ZlibCompress creates a valid zlib stream in memory, see:
+http://www.ietf.org/rfc/rfc1950.txt
+GzipCompress creates a valid gzip stream in memory, see:
+http://www.ietf.org/rfc/rfc1952.txt
This library can only compress, not decompress. Existing zlib or deflate
libraries can decompress the data.
diff --git a/blocksplitter.c b/blocksplitter.c
index c54ef77..157273e 100644
--- a/blocksplitter.c
+++ b/blocksplitter.c
@@ -223,13 +223,13 @@ void BlockSplitLZ77(const Options* options,
size_t i;
size_t llpos = 0;
size_t numblocks = 1;
- unsigned char* done = (unsigned char*)malloc(llsize);
+ unsigned char* done;
double splitcost, origcost;
- if (!done) exit(-1); /* Allocation failed. */
-
if (llsize < 10) return; /* This code fails on tiny files. */
+ done = (unsigned char*)malloc(llsize);
+ if (!done) exit(-1); /* Allocation failed. */
for (i = 0; i < llsize; i++) done[i] = 0;
lstart = 0;
diff --git a/gzip.c b/gzip.c
deleted file mode 100644
index ca5bf69..0000000
--- a/gzip.c
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
-Copyright 2011 Google Inc. All Rights Reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-Author: lode.vandevenne@gmail.com (Lode Vandevenne)
-Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
-*/
-
-/*
-Example program using the zopfli compression algorithm library. It is a tool
-which behaves like a subset of gzip which can only compress, not decompress.
-*/
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "deflate.h"
-#include "util.h"
-
-/*
-Loads a file into a memory array.
-*/
-static void LoadFile(const char* filename,
- unsigned char** out, size_t* outsize) {
- FILE* file;
-
- *out = 0;
- *outsize = 0;
- file = fopen(filename, "rb");
- if (!file) return;
-
- fseek(file , 0 , SEEK_END);
- *outsize = ftell(file);
- rewind(file);
-
- *out = (unsigned char*)malloc(*outsize);
-
- if (*outsize && (*out)) {
- size_t testsize = fread(*out, 1, *outsize, file);
- if (testsize != *outsize) {
- /* It could be a directory */
- free(*out);
- *out = 0;
- *outsize = 0;
- }
- }
-
- assert(!(*outsize) || out); /* If size is not zero, out must be allocated. */
- fclose(file);
-}
-
-/*
-Saves a file from a memory array, overwriting the file if it existed.
-*/
-static void SaveFile(const char* filename,
- const unsigned char* in, size_t insize) {
- FILE* file = fopen(filename, "wb" );
- assert(file);
- fwrite((char*)in, 1, insize, file);
- fclose(file);
-}
-
-/* Table of CRCs of all 8-bit messages. */
-static unsigned long crc_table[256];
-
-/* Flag: has the table been computed? Initially false. */
-static int crc_table_computed = 0;
-
-/* Makes the table for a fast CRC. */
-void MakeCRCTable() {
- unsigned long c;
- int n, k;
- for (n = 0; n < 256; n++) {
- c = (unsigned long) n;
- for (k = 0; k < 8; k++) {
- if (c & 1) {
- c = 0xedb88320L ^ (c >> 1);
- } else {
- c = c >> 1;
- }
- }
- crc_table[n] = c;
- }
- crc_table_computed = 1;
-}
-
-/*
-Updates a running crc with the bytes buf[0..len-1] and returns
-the updated crc. The crc should be initialized to zero.
-*/
-unsigned long UpdateCRC(unsigned long crc,
- const unsigned char *buf, size_t len) {
- unsigned long c = crc ^ 0xffffffffL;
- unsigned n;
-
- if (!crc_table_computed)
- MakeCRCTable();
- for (n = 0; n < len; n++) {
- c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
- }
- return c ^ 0xffffffffL;
-}
-
-/* Returns the CRC of the bytes buf[0..len-1]. */
-unsigned long CRC(const unsigned char* buf, int len) {
- return UpdateCRC(0L, buf, len);
-}
-
-/*
-Compresses the data according to the gzip specification.
-*/
-void Gzip(const Options* options,
- const unsigned char* in, size_t insize,
- unsigned char** out, size_t* outsize) {
- unsigned long crcvalue = CRC(in, insize);
- unsigned char bp = 0;
-
- APPEND_DATA(31, out, outsize); /* ID1 */
- APPEND_DATA(139, out, outsize); /* ID2 */
- APPEND_DATA(8, out, outsize); /* CM */
- APPEND_DATA(0, out, outsize); /* FLG */
- /* MTIME */
- APPEND_DATA(0, out, outsize);
- APPEND_DATA(0, out, outsize);
- APPEND_DATA(0, out, outsize);
- APPEND_DATA(0, out, outsize);
-
- APPEND_DATA(2, out, outsize); /* XFL, 2 indicates best compression. */
- APPEND_DATA(3, out, outsize); /* OS follows Unix conventions. */
-
- Deflate(options, 2 /* Dynamic block */, 1, in, insize, &bp, out, outsize);
-
- /* CRC */
- APPEND_DATA(crcvalue % 256, out, outsize);
- APPEND_DATA((crcvalue >> 8) % 256, out, outsize);
- APPEND_DATA((crcvalue >> 16) % 256, out, outsize);
- APPEND_DATA((crcvalue >> 24) % 256, out, outsize);
-
- /* ISIZE */
- APPEND_DATA(insize % 256, out, outsize);
- APPEND_DATA((insize >> 8) % 256, out, outsize);
- APPEND_DATA((insize >> 16) % 256, out, outsize);
- APPEND_DATA((insize >> 24) % 256, out, outsize);
-
- if (options->verbose) {
- fprintf(stderr,
- "Original Size: %d, Compressed: %d, Compression: %f%% Removed\n",
- (int)insize, (int)*outsize,
- 100.0f * (float)(insize - *outsize) / (float)insize);
- }
-}
-
-/*
-outfilename: filename to write output to, or 0 to write to stdout instead
-*/
-void GzipFile(const Options* options,
- const char* infilename,
- const char* outfilename) {
- unsigned char* in;
- size_t insize;
- unsigned char* out = 0;
- size_t outsize = 0;
- LoadFile(infilename, &in, &insize);
- if (insize == 0) {
- fprintf(stderr, "Invalid filename: %s\n", infilename);
- return;
- }
- Gzip(options, in, insize, &out, &outsize);
- if (outfilename) {
- SaveFile(outfilename, out, outsize);
- } else {
- size_t i;
- for (i = 0; i < outsize; i++) {
- /* Works only if terminal does not convert newlines. */
- printf("%c", out[i]);
- }
- }
-
- free(out);
- free(in);
-}
-
-/*
-Add two strings together. Size does not matter. Result must be freed.
-*/
-static char* AddStrings(const char* str1, const char* str2) {
- size_t len = strlen(str1) + strlen(str2);
- char* result = (char*)malloc(len + 1);
- if (!result) exit(-1); /* Allocation failed. */
- strcpy(result, str1);
- strcat(result, str2);
- return result;
-}
-
-static char StringsEqual(const char* str1, const char* str2) {
- return strcmp(str1, str2) == 0;
-}
-
-int main(int argc, char* argv[]) {
- Options options;
- const char* filename = 0;
- int output_to_stdout = 0;
- int i;
-
- InitOptions(&options);
-
- for (i = 1; i < argc; i++) {
- if (StringsEqual(argv[i], "-v")) options.verbose = 1;
- else if (StringsEqual(argv[i], "-c")) output_to_stdout = 1;
- else if (StringsEqual(argv[i], "-i5")) options.numiterations = 5;
- else if (StringsEqual(argv[i], "-i10")) options.numiterations = 10;
- else if (StringsEqual(argv[i], "-i15")) options.numiterations = 15;
- else if (StringsEqual(argv[i], "-i25")) options.numiterations = 25;
- else if (StringsEqual(argv[i], "-i50")) options.numiterations = 50;
- else if (StringsEqual(argv[i], "-i100")) options.numiterations = 100;
- else if (StringsEqual(argv[i], "-i250")) options.numiterations = 250;
- else if (StringsEqual(argv[i], "-i500")) options.numiterations = 500;
- else if (StringsEqual(argv[i], "-i1000")) options.numiterations = 1000;
- else if (StringsEqual(argv[i], "-h")) {
- fprintf(stderr, "Usage: zopfli [OPTION]... FILE\n"
- " -h gives this help\n"
- " -c write the result on standard output, instead of disk"
- " filename + '.gz'\n"
- " -v verbose mode\n"
- " -i5 less compression, but faster\n"
- " -i10 less compression, but faster\n"
- " -i15 default compression, 15 iterations\n"
- " -i25 more compression, but slower\n"
- " -i50 more compression, but slower\n"
- " -i100 more compression, but slower\n"
- " -i250 more compression, but slower\n"
- " -i500 more compression, but slower\n"
- " -i1000 more compression, but slower\n");
- return 0;
- }
- }
-
- for (i = 1; i < argc; i++) {
- if (argv[i][0] != '-') {
- char* outfilename;
- filename = argv[i];
- outfilename = output_to_stdout ?
- 0 : AddStrings(filename, ".gz");
- if (options.verbose && outfilename) {
- fprintf(stderr, "Saving to: %s\n", outfilename);
- }
- GzipFile(&options, filename, outfilename);
- free(outfilename);
- }
- }
-
- if (!filename) {
- fprintf(stderr,
- "Please provide filename\nFor help, type: %s -h\n", argv[0]);
- }
-
- return 0;
-}
diff --git a/gzip_container.c b/gzip_container.c
new file mode 100644
index 0000000..6d38381
--- /dev/null
+++ b/gzip_container.c
@@ -0,0 +1,115 @@
+/*
+Copyright 2013 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Author: lode.vandevenne@gmail.com (Lode Vandevenne)
+Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
+*/
+
+#include "gzip_container.h"
+
+#include <stdio.h>
+
+#include "deflate.h"
+
+/* Table of CRCs of all 8-bit messages. */
+static unsigned long crc_table[256];
+
+/* Flag: has the table been computed? Initially false. */
+static int crc_table_computed = 0;
+
+/* Makes the table for a fast CRC. */
+void MakeCRCTable() {
+ unsigned long c;
+ int n, k;
+ for (n = 0; n < 256; n++) {
+ c = (unsigned long) n;
+ for (k = 0; k < 8; k++) {
+ if (c & 1) {
+ c = 0xedb88320L ^ (c >> 1);
+ } else {
+ c = c >> 1;
+ }
+ }
+ crc_table[n] = c;
+ }
+ crc_table_computed = 1;
+}
+
+
+/*
+Updates a running crc with the bytes buf[0..len-1] and returns
+the updated crc. The crc should be initialized to zero.
+*/
+unsigned long UpdateCRC(unsigned long crc,
+ const unsigned char *buf, size_t len) {
+ unsigned long c = crc ^ 0xffffffffL;
+ unsigned n;
+
+ if (!crc_table_computed)
+ MakeCRCTable();
+ for (n = 0; n < len; n++) {
+ c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
+ }
+ return c ^ 0xffffffffL;
+}
+
+/* Returns the CRC of the bytes buf[0..len-1]. */
+unsigned long CRC(const unsigned char* buf, int len) {
+ return UpdateCRC(0L, buf, len);
+}
+
+/*
+Compresses the data according to the gzip specification.
+*/
+void GzipCompress(const Options* options,
+ const unsigned char* in, size_t insize,
+ unsigned char** out, size_t* outsize) {
+ unsigned long crcvalue = CRC(in, insize);
+ unsigned char bp = 0;
+
+ APPEND_DATA(31, out, outsize); /* ID1 */
+ APPEND_DATA(139, out, outsize); /* ID2 */
+ APPEND_DATA(8, out, outsize); /* CM */
+ APPEND_DATA(0, out, outsize); /* FLG */
+ /* MTIME */
+ APPEND_DATA(0, out, outsize);
+ APPEND_DATA(0, out, outsize);
+ APPEND_DATA(0, out, outsize);
+ APPEND_DATA(0, out, outsize);
+
+ APPEND_DATA(2, out, outsize); /* XFL, 2 indicates best compression. */
+ APPEND_DATA(3, out, outsize); /* OS follows Unix conventions. */
+
+ Deflate(options, 2 /* Dynamic block */, 1, in, insize, &bp, out, outsize);
+
+ /* CRC */
+ APPEND_DATA(crcvalue % 256, out, outsize);
+ APPEND_DATA((crcvalue >> 8) % 256, out, outsize);
+ APPEND_DATA((crcvalue >> 16) % 256, out, outsize);
+ APPEND_DATA((crcvalue >> 24) % 256, out, outsize);
+
+ /* ISIZE */
+ APPEND_DATA(insize % 256, out, outsize);
+ APPEND_DATA((insize >> 8) % 256, out, outsize);
+ APPEND_DATA((insize >> 16) % 256, out, outsize);
+ APPEND_DATA((insize >> 24) % 256, out, outsize);
+
+ if (options->verbose) {
+ fprintf(stderr,
+ "Original Size: %d, Compressed: %d, Compression: %f%% Removed\n",
+ (int)insize, (int)*outsize,
+ 100.0f * (float)(insize - *outsize) / (float)insize);
+ }
+}
diff --git a/gzip_container.h b/gzip_container.h
new file mode 100644
index 0000000..c03e9cd
--- /dev/null
+++ b/gzip_container.h
@@ -0,0 +1,42 @@
+/*
+Copyright 2013 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Author: lode.vandevenne@gmail.com (Lode Vandevenne)
+Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
+*/
+
+#ifndef ZOPFLI_GZIP_H_
+#define ZOPFLI_GZIP_H_
+
+/*
+Functions to compress according to the Gzip specification.
+*/
+
+#include "util.h"
+
+/*
+Compresses according to the gzip specification and append the compressed
+result to the output.
+
+options: global program options
+out: pointer to the dynamic output array to which the result is appended. Must
+ be freed after use.
+outsize: pointer to the dynamic output array size.
+*/
+void GzipCompress(const Options* options,
+ const unsigned char* in, size_t insize,
+ unsigned char** out, size_t* outsize);
+
+#endif /* ZOPFLI_GZIP_H_ */
diff --git a/squeeze.c b/squeeze.c
index 4a72f5a..1620783 100644
--- a/squeeze.c
+++ b/squeeze.c
@@ -205,8 +205,7 @@ static double GetBestLengths(BlockState *s,
unsigned short* length_array) {
/* Best cost to get here so far. */
size_t blocksize = inend - instart;
- float* costs = (float*)malloc(sizeof(float) * (blocksize + 1));
-
+ float* costs;
size_t i = 0, k;
unsigned short leng;
unsigned short dist;
@@ -217,10 +216,11 @@ static double GetBestLengths(BlockState *s,
double result;
double mincost = GetCostModelMinCost(costmodel, costcontext);
- if (!costs) exit(-1); /* Allocation failed. */
-
if (instart == inend) return 0;
+ costs = (float*)malloc(sizeof(float) * (blocksize + 1));
+ if (!costs) exit(-1); /* Allocation failed. */
+
InitHash(WINDOW_SIZE, h);
WarmupHash(in, windowstart, inend, h);
for (i = windowstart; i < instart; i++) {
diff --git a/zlib_container.c b/zlib_container.c
new file mode 100644
index 0000000..b77d023
--- /dev/null
+++ b/zlib_container.c
@@ -0,0 +1,78 @@
+/*
+Copyright 2013 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Author: lode.vandevenne@gmail.com (Lode Vandevenne)
+Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
+*/
+
+#include "zlib_container.h"
+
+#include <stdio.h>
+
+#include "deflate.h"
+
+
+/* Calculates the adler32 checksum of the data */
+static unsigned adler32(const unsigned char* data, size_t size)
+{
+ static const unsigned sums_overflow = 5550;
+ unsigned s1 = 1;
+ unsigned s2 = 1 >> 16;
+
+ while (size > 0) {
+ size_t amount = size > sums_overflow ? sums_overflow : size;
+ size -= amount;
+ while (amount > 0) {
+ s1 += (*data++);
+ s2 += s1;
+ amount--;
+ }
+ s1 %= 65521;
+ s2 %= 65521;
+ }
+
+ return (s2 << 16) | s1;
+}
+
+void ZlibCompress(const Options* options,
+ const unsigned char* in, size_t insize,
+ unsigned char** out, size_t* outsize) {
+ unsigned char bitpointer = 0;
+ unsigned checksum = adler32(in, (unsigned)insize);
+ unsigned cmf = 120; /* CM 8, CINFO 7. See zlib spec.*/
+ unsigned flevel = 0;
+ unsigned fdict = 0;
+ unsigned cmfflg = 256 * cmf + fdict * 32 + flevel * 64;
+ unsigned fcheck = 31 - cmfflg % 31;
+ cmfflg += fcheck;
+
+ APPEND_DATA(cmfflg / 256, out, outsize);
+ APPEND_DATA(cmfflg % 256, out, outsize);
+
+ Deflate(options, 2 /* dynamic block */, 1 /* final */,
+ in, insize, &bitpointer, out, outsize);
+
+ APPEND_DATA((checksum >> 24) % 256, out, outsize);
+ APPEND_DATA((checksum >> 16) % 256, out, outsize);
+ APPEND_DATA((checksum >> 8) % 256, out, outsize);
+ APPEND_DATA(checksum % 256, out, outsize);
+
+ if (options->verbose) {
+ fprintf(stderr,
+ "Original Size: %d, Compressed: %d, Compression: %f%% Removed\n",
+ (int)insize, (int)*outsize,
+ 100.0f * (float)(insize - *outsize) / (float)insize);
+ }
+}
diff --git a/zlib_container.h b/zlib_container.h
new file mode 100644
index 0000000..3b11e7a
--- /dev/null
+++ b/zlib_container.h
@@ -0,0 +1,42 @@
+/*
+Copyright 2013 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Author: lode.vandevenne@gmail.com (Lode Vandevenne)
+Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
+*/
+
+#ifndef ZOPFLI_ZLIB_H_
+#define ZOPFLI_ZLIB_H_
+
+/*
+Functions to compress according to the Zlib specification.
+*/
+
+#include "util.h"
+
+/*
+Compresses according to the zlib specification and append the compressed
+result to the output.
+
+options: global program options
+out: pointer to the dynamic output array to which the result is appended. Must
+ be freed after use.
+outsize: pointer to the dynamic output array size.
+*/
+void ZlibCompress(const Options* options,
+ const unsigned char* in, size_t insize,
+ unsigned char** out, size_t* outsize);
+
+#endif /* ZOPFLI_ZLIB_H_ */
diff --git a/zopfli.c b/zopfli.c
new file mode 100644
index 0000000..377b43f
--- /dev/null
+++ b/zopfli.c
@@ -0,0 +1,216 @@
+/*
+Copyright 2011 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Author: lode.vandevenne@gmail.com (Lode Vandevenne)
+Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
+*/
+
+/*
+Zopfli compressor program. It can output gzip-, zlib- or deflate-compatible
+data. By default it creates a .gz file. This tool can only compress, not
+decompress. Decompression can be done by any standard gzip, zlib or deflate
+decompressor.
+*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "deflate.h"
+#include "gzip_container.h"
+#include "zlib_container.h"
+
+/*
+Loads a file into a memory array.
+*/
+static void LoadFile(const char* filename,
+ unsigned char** out, size_t* outsize) {
+ FILE* file;
+
+ *out = 0;
+ *outsize = 0;
+ file = fopen(filename, "rb");
+ if (!file) return;
+
+ fseek(file , 0 , SEEK_END);
+ *outsize = ftell(file);
+ rewind(file);
+
+ *out = (unsigned char*)malloc(*outsize);
+
+ if (*outsize && (*out)) {
+ size_t testsize = fread(*out, 1, *outsize, file);
+ if (testsize != *outsize) {
+ /* It could be a directory */
+ free(*out);
+ *out = 0;
+ *outsize = 0;
+ }
+ }
+
+ assert(!(*outsize) || out); /* If size is not zero, out must be allocated. */
+ fclose(file);
+}
+
+/*
+Saves a file from a memory array, overwriting the file if it existed.
+*/
+static void SaveFile(const char* filename,
+ const unsigned char* in, size_t insize) {
+ FILE* file = fopen(filename, "wb" );
+ assert(file);
+ fwrite((char*)in, 1, insize, file);
+ fclose(file);
+}
+
+typedef enum {
+ OUTPUT_GZIP,
+ OUTPUT_ZLIB,
+ OUTPUT_DEFLATE
+} OutputType;
+
+/*
+outfilename: filename to write output to, or 0 to write to stdout instead
+*/
+void CompressFile(const Options* options,
+ OutputType output_type,
+ const char* infilename,
+ const char* outfilename) {
+ unsigned char* in;
+ size_t insize;
+ unsigned char* out = 0;
+ size_t outsize = 0;
+ LoadFile(infilename, &in, &insize);
+ if (insize == 0) {
+ fprintf(stderr, "Invalid filename: %s\n", infilename);
+ return;
+ }
+ if (output_type == OUTPUT_GZIP) {
+ GzipCompress(options, in, insize, &out, &outsize);
+ } else if (output_type == OUTPUT_ZLIB) {
+ ZlibCompress(options, in, insize, &out, &outsize);
+ } else if (output_type == OUTPUT_DEFLATE) {
+ unsigned char bp = 0;
+ Deflate(options, 2 /* Dynamic block */, 1, in, insize, &bp, &out, &outsize);
+ } else {
+ assert(0);
+ }
+ if (outfilename) {
+ SaveFile(outfilename, out, outsize);
+ } else {
+ size_t i;
+ for (i = 0; i < outsize; i++) {
+ /* Works only if terminal does not convert newlines. */
+ printf("%c", out[i]);
+ }
+ }
+
+ free(out);
+ free(in);
+}
+
+/*
+Add two strings together. Size does not matter. Result must be freed.
+*/
+static char* AddStrings(const char* str1, const char* str2) {
+ size_t len = strlen(str1) + strlen(str2);
+ char* result = (char*)malloc(len + 1);
+ if (!result) exit(-1); /* Allocation failed. */
+ strcpy(result, str1);
+ strcat(result, str2);
+ return result;
+}
+
+static char StringsEqual(const char* str1, const char* str2) {
+ return strcmp(str1, str2) == 0;
+}
+
+int main(int argc, char* argv[]) {
+ Options options;
+ const char* filename = 0;
+ int output_to_stdout = 0;
+ int i;
+ OutputType output_type = OUTPUT_GZIP;
+
+ InitOptions(&options);
+
+ for (i = 1; i < argc; i++) {
+ if (StringsEqual(argv[i], "-v")) options.verbose = 1;
+ else if (StringsEqual(argv[i], "-c")) output_to_stdout = 1;
+ else if (StringsEqual(argv[i], "--deflate")) output_type = OUTPUT_DEFLATE;
+ else if (StringsEqual(argv[i], "--zlib")) output_type = OUTPUT_ZLIB;
+ else if (StringsEqual(argv[i], "--gzip")) output_type = OUTPUT_GZIP;
+ else if (StringsEqual(argv[i], "--i5")) options.numiterations = 5;
+ else if (StringsEqual(argv[i], "--i10")) options.numiterations = 10;
+ else if (StringsEqual(argv[i], "--i15")) options.numiterations = 15;
+ else if (StringsEqual(argv[i], "--i25")) options.numiterations = 25;
+ else if (StringsEqual(argv[i], "--i50")) options.numiterations = 50;
+ else if (StringsEqual(argv[i], "--i100")) options.numiterations = 100;
+ else if (StringsEqual(argv[i], "--i250")) options.numiterations = 250;
+ else if (StringsEqual(argv[i], "--i500")) options.numiterations = 500;
+ else if (StringsEqual(argv[i], "--i1000")) options.numiterations = 1000;
+ else if (StringsEqual(argv[i], "-h")) {
+ fprintf(stderr, "Usage: zopfli [OPTION]... FILE\n"
+ " -h gives this help\n"
+ " -c write the result on standard output, instead of disk"
+ " filename + '.gz'\n"
+ " -v verbose mode\n"
+ " --gzip output to gzip format (default)\n"
+ " --deflate output to deflate format instead of gzip\n"
+ " --zlib output to zlib format instead of gzip\n");
+ fprintf(stderr, " --i5 less compression, but faster\n"
+ " --i10 less compression, but faster\n"
+ " --i15 default compression, 15 iterations\n"
+ " --i25 more compression, but slower\n"
+ " --i50 more compression, but slower\n"
+ " --i100 more compression, but slower\n"
+ " --i250 more compression, but slower\n"
+ " --i500 more compression, but slower\n"
+ " --i1000 more compression, but slower\n");
+ return 0;
+ }
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] != '-') {
+ char* outfilename;
+ filename = argv[i];
+ if (output_to_stdout) {
+ outfilename = 0;
+ } else if (output_type == OUTPUT_GZIP) {
+ outfilename = AddStrings(filename, ".gz");
+ } else if (output_type == OUTPUT_ZLIB) {
+ outfilename = AddStrings(filename, ".zlib");
+ } else if (output_type == OUTPUT_DEFLATE) {
+ outfilename = AddStrings(filename, ".deflate");
+ } else {
+ assert(0);
+ }
+ if (options.verbose && outfilename) {
+ fprintf(stderr, "Saving to: %s\n", outfilename);
+ }
+ CompressFile(&options, output_type, filename, outfilename);
+ free(outfilename);
+ }
+ }
+
+ if (!filename) {
+ fprintf(stderr,
+ "Please provide filename\nFor help, type: %s -h\n", argv[0]);
+ }
+
+ return 0;
+}