diff options
Diffstat (limited to 'examples/lzopack.c')
-rw-r--r-- | examples/lzopack.c | 642 |
1 files changed, 642 insertions, 0 deletions
diff --git a/examples/lzopack.c b/examples/lzopack.c new file mode 100644 index 0000000..6a3d848 --- /dev/null +++ b/examples/lzopack.c @@ -0,0 +1,642 @@ +/* lzopack.c -- LZO example program: a simple file packer + + This file is part of the LZO real-time data compression library. + + Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + The LZO library 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Markus F.X.J. Oberhumer + <markus@oberhumer.com> + http://www.oberhumer.com/opensource/lzo/ + */ + + +/************************************************************************* +// NOTE: this is an example program, so do not use to backup your data. +// +// This program lacks things like sophisticated file handling but is +// pretty complete regarding compression - it should provide a good +// starting point for adaption for your applications. +// +// Please study LZO.FAQ and simple.c first. +**************************************************************************/ + +#include "lzo/lzoconf.h" +#include "lzo/lzo1x.h" + +/* portability layer */ +#define WANT_LZO_MALLOC 1 +#define WANT_LZO_FREAD 1 +#define WANT_LZO_WILDARGV 1 +#include "examples/portab.h" + + +static const char *progname = NULL; + +static unsigned long total_in = 0; +static unsigned long total_out = 0; +static lzo_bool opt_debug = 0; + +/* magic file header for lzopack-compressed files */ +static const unsigned char magic[7] = + { 0x00, 0xe9, 0x4c, 0x5a, 0x4f, 0xff, 0x1a }; + + +/************************************************************************* +// file IO +**************************************************************************/ + +lzo_uint xread(FILE *fp, lzo_voidp buf, lzo_uint len, lzo_bool allow_eof) +{ + lzo_uint l; + + l = (lzo_uint) lzo_fread(fp, buf, len); + if (l > len) + { + fprintf(stderr, "\nsomething's wrong with your C library !!!\n"); + exit(1); + } + if (l != len && !allow_eof) + { + fprintf(stderr, "\nread error - premature end of file\n"); + exit(1); + } + total_in += (unsigned long) l; + return l; +} + +lzo_uint xwrite(FILE *fp, const lzo_voidp buf, lzo_uint len) +{ + if (fp != NULL && lzo_fwrite(fp, buf, len) != len) + { + fprintf(stderr, "\nwrite error (disk full ?)\n"); + exit(1); + } + total_out += (unsigned long) len; + return len; +} + + +int xgetc(FILE *fp) +{ + unsigned char c; + xread(fp, (lzo_voidp) &c, 1, 0); + return c; +} + +void xputc(FILE *fp, int c) +{ + unsigned char cc = (unsigned char) (c & 0xff); + xwrite(fp, (const lzo_voidp) &cc, 1); +} + +/* read and write portable 32-bit integers */ + +lzo_uint32 xread32(FILE *fp) +{ + unsigned char b[4]; + lzo_uint32 v; + + xread(fp, b, 4, 0); + v = (lzo_uint32) b[3] << 0; + v |= (lzo_uint32) b[2] << 8; + v |= (lzo_uint32) b[1] << 16; + v |= (lzo_uint32) b[0] << 24; + return v; +} + +void xwrite32(FILE *fp, lzo_xint v) +{ + unsigned char b[4]; + + b[3] = (unsigned char) ((v >> 0) & 0xff); + b[2] = (unsigned char) ((v >> 8) & 0xff); + b[1] = (unsigned char) ((v >> 16) & 0xff); + b[0] = (unsigned char) ((v >> 24) & 0xff); + xwrite(fp, b, 4); +} + + +/************************************************************************* +// compress +// +// possible improvement: we could use overlapping compression to +// save some memory - see overlap.c. This would require some minor +// changes in the decompression code as well, because if a block +// turns out to be incompressible we would still have to store it in its +// "compressed" (i.e. then slightly enlarged) form because the original +// (uncompressed) data would have been lost during the overlapping +// compression. +**************************************************************************/ + +int do_compress(FILE *fi, FILE *fo, int level, lzo_uint block_size) +{ + int r = 0; + lzo_bytep in = NULL; + lzo_bytep out = NULL; + lzo_bytep wrkmem = NULL; + lzo_uint in_len; + lzo_uint out_len; + lzo_uint32 wrk_len = 0; + lzo_uint32 flags = 1; /* do compute a checksum */ + int method = 1; /* compression method: LZO1X */ + lzo_uint32 checksum; + + total_in = total_out = 0; + +/* + * Step 1: write magic header, flags & block size, init checksum + */ + xwrite(fo, magic, sizeof(magic)); + xwrite32(fo, flags); + xputc(fo, method); /* compression method */ + xputc(fo, level); /* compression level */ + xwrite32(fo, block_size); + checksum = lzo_adler32(0, NULL, 0); + +/* + * Step 2: allocate compression buffers and work-memory + */ + in = (lzo_bytep) lzo_malloc(block_size); + out = (lzo_bytep) lzo_malloc(block_size + block_size / 16 + 64 + 3); + if (level == 9) + wrk_len = LZO1X_999_MEM_COMPRESS; + else + wrk_len = LZO1X_1_MEM_COMPRESS; + wrkmem = (lzo_bytep) lzo_malloc(wrk_len); + if (in == NULL || out == NULL || wrkmem == NULL) + { + printf("%s: out of memory\n", progname); + r = 1; + goto err; + } + +/* + * Step 3: process blocks + */ + for (;;) + { + /* read block */ + in_len = xread(fi, in, block_size, 1); + if (in_len <= 0) + break; + + /* update checksum */ + if (flags & 1) + checksum = lzo_adler32(checksum, in, in_len); + + /* clear wrkmem (not needed, only for debug/benchmark purposes) */ + if (opt_debug) + lzo_memset(wrkmem, 0xff, wrk_len); + + /* compress block */ + if (level == 9) + r = lzo1x_999_compress(in, in_len, out, &out_len, wrkmem); + else + r = lzo1x_1_compress(in, in_len, out, &out_len, wrkmem); + if (r != LZO_E_OK || out_len > in_len + in_len / 16 + 64 + 3) + { + /* this should NEVER happen */ + printf("internal error - compression failed: %d\n", r); + r = 2; + goto err; + } + + /* write uncompressed block size */ + xwrite32(fo, in_len); + + if (out_len < in_len) + { + /* write compressed block */ + xwrite32(fo, out_len); + xwrite(fo, out, out_len); + } + else + { + /* not compressible - write uncompressed block */ + xwrite32(fo, in_len); + xwrite(fo, in, in_len); + } + } + + /* write EOF marker */ + xwrite32(fo, 0); + + /* write checksum */ + if (flags & 1) + xwrite32(fo, checksum); + + r = 0; +err: + lzo_free(wrkmem); + lzo_free(out); + lzo_free(in); + return r; +} + + +/************************************************************************* +// decompress / test +// +// We are using overlapping (in-place) decompression to save some +// memory - see overlap.c. +**************************************************************************/ + +int do_decompress(FILE *fi, FILE *fo) +{ + int r = 0; + lzo_bytep buf = NULL; + lzo_uint buf_len; + unsigned char m [ sizeof(magic) ]; + lzo_uint32 flags; + int method; + int level; + lzo_uint block_size; + lzo_uint32 checksum; + + total_in = total_out = 0; + +/* + * Step 1: check magic header, read flags & block size, init checksum + */ + if (xread(fi, m, sizeof(magic),1) != sizeof(magic) || + memcmp(m, magic, sizeof(magic)) != 0) + { + printf("%s: header error - this file is not compressed by lzopack\n", progname); + r = 1; + goto err; + } + flags = xread32(fi); + method = xgetc(fi); + level = xgetc(fi); + if (method != 1) + { + printf("%s: header error - invalid method %d (level %d)\n", + progname, method, level); + r = 2; + goto err; + } + block_size = xread32(fi); + if (block_size < 1024 || block_size > 8*1024*1024L) + { + printf("%s: header error - invalid block size %ld\n", + progname, (long) block_size); + r = 3; + goto err; + } + checksum = lzo_adler32(0,NULL,0); + +/* + * Step 2: allocate buffer for in-place decompression + */ + buf_len = block_size + block_size / 16 + 64 + 3; + buf = (lzo_bytep) lzo_malloc(buf_len); + if (buf == NULL) + { + printf("%s: out of memory\n", progname); + r = 4; + goto err; + } + +/* + * Step 3: process blocks + */ + for (;;) + { + lzo_bytep in; + lzo_bytep out; + lzo_uint in_len; + lzo_uint out_len; + + /* read uncompressed size */ + out_len = xread32(fi); + + /* exit if last block (EOF marker) */ + if (out_len == 0) + break; + + /* read compressed size */ + in_len = xread32(fi); + + /* sanity check of the size values */ + if (in_len > block_size || out_len > block_size || + in_len == 0 || in_len > out_len) + { + printf("%s: block size error - data corrupted\n", progname); + r = 5; + goto err; + } + + /* place compressed block at the top of the buffer */ + in = buf + buf_len - in_len; + out = buf; + + /* read compressed block data */ + xread(fi, in, in_len, 0); + + if (in_len < out_len) + { + /* decompress - use safe decompressor as data might be corrupted + * during a file transfer */ + lzo_uint new_len = out_len; + + r = lzo1x_decompress_safe(in, in_len, out, &new_len, NULL); + if (r != LZO_E_OK || new_len != out_len) + { + printf("%s: compressed data violation\n", progname); + r = 6; + goto err; + } + /* write decompressed block */ + xwrite(fo, out, out_len); + /* update checksum */ + if (flags & 1) + checksum = lzo_adler32(checksum, out, out_len); + } + else + { + /* write original (incompressible) block */ + xwrite(fo, in, in_len); + /* update checksum */ + if (flags & 1) + checksum = lzo_adler32(checksum, in, in_len); + } + } + + /* read and verify checksum */ + if (flags & 1) + { + lzo_uint32 c = xread32(fi); + if (c != checksum) + { + printf("%s: checksum error - data corrupted\n", progname); + r = 7; + goto err; + } + } + + r = 0; +err: + lzo_free(buf); + return r; +} + + +/************************************************************************* +// +**************************************************************************/ + +static void usage(void) +{ + printf("usage: %s [-9] input-file output-file (compress)\n", progname); + printf("usage: %s -d input-file output-file (decompress)\n", progname); + printf("usage: %s -t input-file... (test)\n", progname); + exit(1); +} + + +/* open input file */ +static FILE *xopen_fi(const char *name) +{ + FILE *fp; + + fp = fopen(name, "rb"); + if (fp == NULL) + { + printf("%s: cannot open input file %s\n", progname, name); + exit(1); + } +#if defined(HAVE_STAT) && defined(S_ISREG) + { + struct stat st; + int is_regular = 1; + if (stat(name, &st) != 0 || !S_ISREG(st.st_mode)) + is_regular = 0; + if (!is_regular) + { + printf("%s: %s is not a regular file\n", progname, name); + fclose(fp); fp = NULL; + exit(1); + } + } +#endif + return fp; +} + + +/* open output file */ +static FILE *xopen_fo(const char *name) +{ + FILE *fp; + +#if 0 + /* this is an example program, so make sure we don't overwrite a file */ + fp = fopen(name, "rb"); + if (fp != NULL) + { + printf("%s: file %s already exists -- not overwritten\n", progname, name); + fclose(fp); fp = NULL; + exit(1); + } +#endif + fp = fopen(name, "wb"); + if (fp == NULL) + { + printf("%s: cannot open output file %s\n", progname, name); + exit(1); + } + return fp; +} + + +/* close file */ +static void xclose(FILE *fp) +{ + if (fp) + { + int err; + err = ferror(fp); + if (fclose(fp) != 0) + err = 1; + if (err) + { + printf("%s: error while closing file\n", progname); + exit(1); + } + } +} + + +/************************************************************************* +// +**************************************************************************/ + +int __lzo_cdecl_main main(int argc, char *argv[]) +{ + int i = 1; + int r = 0; + FILE *fi = NULL; + FILE *fo = NULL; + const char *in_name = NULL; + const char *out_name = NULL; + lzo_bool opt_decompress = 0; + lzo_bool opt_test = 0; + int opt_level = 1; + lzo_uint opt_block_size; + const char *s; + + lzo_wildargv(&argc, &argv); + + progname = argv[0]; + for (s = progname; *s; s++) + if ((*s == '/' || *s == '\\') && s[1]) + progname = s + 1; + + printf("\nLZO real-time data compression library (v%s, %s).\n", + lzo_version_string(), lzo_version_date()); + printf("Copyright (C) 1996-2008 Markus Franz Xaver Johannes Oberhumer\nAll Rights Reserved.\n\n"); + +#if 0 + printf( +"*** DISCLAIMER ***\n" +" This is an example program, do not use to backup your data !\n" +" Get LZOP if you're interested into a full-featured packer.\n" +" See http://www.oberhumer.com/opensource/lzop/\n" +"\n"); +#endif + + +/* + * Step 1: initialize the LZO library + */ + if (lzo_init() != LZO_E_OK) + { + printf("internal error - lzo_init() failed !!!\n"); + printf("(this usually indicates a compiler bug - try recompiling\nwithout optimizations, and enable `-DLZO_DEBUG' for diagnostics)\n"); + exit(1); + } + + +/* + * Step 2: setup memory + */ + opt_block_size = 256 * 1024L; + +#if defined(ACC_MM_AHSHIFT) + /* reduce memory requirements for ancient 16-bit DOS 640kB real-mode */ + if (ACC_MM_AHSHIFT != 3) + opt_block_size = 16 * 1024L; +#endif + + +/* + * Step 3: get options + */ + + while (i < argc && argv[i][0] == '-') + { + if (strcmp(argv[i],"-d") == 0) + opt_decompress = 1; + else if (strcmp(argv[i],"-t") == 0) + opt_test = 1; + else if (strcmp(argv[i],"-9") == 0) + opt_level = 9; + else if (argv[i][1] == 'b' && argv[i][2]) + { + long b = atol(&argv[i][2]); + if (b >= 1024L && b <= 8*1024*1024L) + opt_block_size = (lzo_uint) b; + else + { + printf("%s: invalid block_size in option `%s'.\n", progname, argv[i]); + usage(); + } + } + else if (strcmp(argv[i],"--debug") == 0) + opt_debug += 1; + else + usage(); + i++; + } + if (opt_test && i >= argc) + usage(); + if (!opt_test && i + 2 != argc) + usage(); + + +/* + * Step 4: process file(s) + */ + + if (opt_test) + { + while (i < argc && r == 0) + { + in_name = argv[i++]; + fi = xopen_fi(in_name); + r = do_decompress(fi, NULL); + if (r == 0) + printf("%s: %s tested ok (%lu -> %lu bytes)\n", + progname, in_name, total_in, total_out); + xclose(fi); fi = NULL; + } + } + else if (opt_decompress) + { + in_name = argv[i++]; + out_name = argv[i++]; + fi = xopen_fi(in_name); + fo = xopen_fo(out_name); + r = do_decompress(fi, fo); + if (r == 0) + printf("%s: decompressed %lu into %lu bytes\n", + progname, total_in, total_out); + } + else /* compress */ + { + in_name = argv[i++]; + out_name = argv[i++]; + fi = xopen_fi(in_name); + fo = xopen_fo(out_name); + r = do_compress(fi, fo, opt_level, opt_block_size); + if (r == 0) + printf("%s: compressed %lu into %lu bytes\n", + progname, total_in, total_out); + } + + xclose(fi); fi = NULL; + xclose(fo); fo = NULL; + return r; +} + +/* +vi:ts=4:et +*/ + |