aboutsummaryrefslogtreecommitdiffstats
path: root/tools/libxutil/lzo_stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/libxutil/lzo_stream.c')
-rw-r--r--tools/libxutil/lzo_stream.c596
1 files changed, 596 insertions, 0 deletions
diff --git a/tools/libxutil/lzo_stream.c b/tools/libxutil/lzo_stream.c
new file mode 100644
index 0000000000..bf7c348471
--- /dev/null
+++ b/tools/libxutil/lzo_stream.c
@@ -0,0 +1,596 @@
+/* $Id: lzo_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $ */
+#define __FILE_ID_INFO "$Id: lzo_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $"
+#include <what.h>
+static char __rcsid[] __attribute__((unused)) = WHAT_ID __FILE_ID_INFO;
+/*
+ * Copyright (C) 2003 Hewlett-Packard Company.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ * An IOStream implementation using LZO to provide compression and decompression.
+ * This is designed to provide reasonable compression without output latency.
+ * Flushing an LZO stream flushes all pending data to the underlying stream.
+ * This is essential for stream-based (e.g. networked) applications.
+ *
+ * A compressed data stream is a sequence of blocks.
+ * Each block except the last is the plain data size followed by the compressed data size
+ * and the compressed data. The last block has plain data size zero and omits the rest.
+ * Sizes are 4-byte unsigned in network order. If the compressed size is smaller than
+ * the plain size the block data is compressed, otherwise it is plain (uncompressed).
+ *
+ * This format allows compressed data to be read from a stream without reading
+ * past the logical end of compressed data.
+ *
+ * @author Mike Wray <mike.wray@hpl.hp.com>
+ */
+#ifndef __KERNEL__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "lzo1x.h"
+
+#include "allocate.h"
+#include "lzo_stream.h"
+#include "file_stream.h"
+#include "marshal.h"
+
+#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] LZO>%s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) fprintf(stderr, "[WARN] LZO>%s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) fprintf(stdout, "[INFO] LZO>%s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] LZO>%s" fmt, __FUNCTION__, ##args)
+
+static int lzo_read(IOStream *s, void *buf, size_t size, size_t count);
+static int lzo_write(IOStream *s, const void *buf, size_t size, size_t count);
+static int lzo_print(IOStream *s, const char *msg, va_list args);
+static int lzo_getc(IOStream *s);
+static int lzo_error(IOStream *s);
+static int lzo_close(IOStream *s);
+static void lzo_free(IOStream *s);
+static int lzo_flush(IOStream *s);
+
+enum {
+ LZO_WRITE = 1,
+ LZO_READ = 2,
+};
+
+/** Methods used by a gzFile* IOStream. */
+static const IOMethods lzo_methods = {
+ read: lzo_read,
+ write: lzo_write,
+ print: lzo_print,
+ getc: lzo_getc,
+ error: lzo_error,
+ close: lzo_close,
+ free: lzo_free,
+ flush: lzo_flush,
+};
+
+//#define PLAIN_SIZE (64 * 1024)
+//#define PLAIN_SIZE (128 * 1024)
+#define PLAIN_SIZE (512 * 1024)
+
+//#define NOCOMPRESS
+
+typedef struct LZOState {
+ /** Flags. */
+ int flags;
+ /** Error indicator. */
+ int error;
+ /** Underlying stream for I/O. */
+ IOStream *io;
+ /** Working memory (only needed for compression, not decompression). */
+ lzo_byte *memory;
+ /** Buffer for plain (uncompressed) data. */
+ lzo_byte *plain;
+ /** Size of the plain buffer. */
+ lzo_uint plain_size;
+ /** Pointer into the plain buffer. */
+ lzo_byte *plain_ptr;
+ /** Number of bytes of plain data available. */
+ lzo_uint plain_n;
+ /** Buffer for compressed data. */
+ lzo_byte *comp;
+ /** Size of the compressed buffer. */
+ lzo_uint comp_size;
+
+ int plain_bytes;
+ int comp_bytes;
+} LZOState;
+
+void LZOState_free(LZOState *z){
+ if(!z) return;
+ deallocate(z->memory);
+ deallocate(z->plain);
+ deallocate(z->comp);
+ deallocate(z);
+}
+
+/** Maximum size of compressed data for the given plain data size.
+ *
+ * @param plain_size size of plain data
+ * @return maximum size of compressed data
+ */
+static int comp_size(int plain_size){
+ return plain_size + (plain_size / 64) + 16 + 3;
+}
+
+static int mode_flags(const char *mode, int *flags){
+ int err = 0;
+ int r=0, w=0;
+ if(!mode){
+ err = -EINVAL;
+ goto exit;
+ }
+ for(; *mode; mode++){
+ if(*mode == 'w') w = 1;
+ if(*mode == 'r') r = 1;
+ }
+ if(r + w != 1){
+ err = -EINVAL;
+ goto exit;
+ }
+ if(r) *flags |= LZO_READ;
+ if(w) *flags |= LZO_WRITE;
+ exit:
+ return err;
+}
+
+/** Get the stream state.
+ *
+ * @param s lzo stream
+ * @return stream state.
+ */
+static inline LZOState * lzo_state(IOStream *s){
+ return s->data;
+}
+
+IOStream *lzo_stream_io(IOStream *s){
+ LZOState *state = lzo_state(s);
+ return state->io;
+}
+
+static inline void set_error(LZOState *state, int err){
+ if(err < 0 && !state->error){
+ state->error = err;
+ }
+}
+
+int lzo_stream_plain_bytes(IOStream *s){
+ LZOState *state = lzo_state(s);
+ return state->plain_bytes;
+}
+
+int lzo_stream_comp_bytes(IOStream *s){
+ LZOState *state = lzo_state(s);
+ return state->comp_bytes;
+}
+
+float lzo_stream_ratio(IOStream *s){
+ LZOState *state = lzo_state(s);
+ float ratio = 0.0;
+ if(state->comp_bytes){
+ ratio = ((float) state->comp_bytes)/((float) state->plain_bytes);
+ }
+ return ratio;
+}
+
+static inline int LZOState_writeable(LZOState *state){
+ return (state->flags & LZO_WRITE) != 0;
+}
+
+static inline int LZOState_readable(LZOState *state){
+ return (state->flags & LZO_READ) != 0;
+}
+
+LZOState * LZOState_new(IOStream *io, int flags){
+ int err = -ENOMEM;
+ LZOState *z = ALLOCATE(LZOState);
+ //dprintf(">\n");
+ if(!z) goto exit;
+ z->io = io;
+ z->flags = flags;
+ if(LZOState_writeable(z)){
+ z->memory = allocate(LZO1X_1_MEM_COMPRESS);
+ if(!z->memory) goto exit;
+ }
+ z->plain_size = PLAIN_SIZE;
+ z->plain = allocate(z->plain_size);
+ if(!z->plain) goto exit;
+ z->plain_ptr = z->plain;
+ z->comp_size = comp_size(z->plain_size);
+ z->comp = allocate(z->comp_size);
+ if(!z->comp) goto exit;
+ err = 0;
+ exit:
+ if(err){
+ LZOState_free(z);
+ z = NULL;
+ }
+ //dprintf("< z=%p\n", z);
+ return z;
+}
+
+static int lzo_compress(LZOState *state){
+ int err = 0;
+ int k, comp_n;
+ //dprintf(">\n");
+ //dprintf(">plain=%p plain_n=%d comp=%p memory=%p\n", state->plain, state->plain_n, state->comp, state->memory);
+ // Compress the plain buffer.
+ err = lzo1x_1_compress(state->plain, state->plain_n,
+ state->comp, &comp_n,
+ state->memory);
+ //dprintf("> err=%d plain_n=%d comp_n=%d\n", err, state->plain_n, comp_n);
+ // Write plain size, compressed size.
+ err = marshal_uint32(state->io, state->plain_n);
+ if(err) goto exit;
+ err = marshal_uint32(state->io, comp_n);
+ if(err) goto exit;
+ //dprintf("> write data...\n");
+ // Write the smaller of the compressed and plain data.
+ if(state->plain_n < comp_n){
+ k = state->plain_n;
+ err = marshal_bytes(state->io, state->plain, state->plain_n);
+ } else {
+ k = comp_n;
+ err = marshal_bytes(state->io, state->comp, comp_n);
+ }
+ if(err) goto exit;
+ // Total output bytes.
+ k+= 8;
+ //dprintf("> wrote %d bytes\n", k);
+ state->plain_bytes += state->plain_n;
+ state->comp_bytes += k;
+ //dprintf("> plain=%d, comp=%d, ratio=%3.2f\n",
+ // state->plain_bytes, state->comp_bytes,
+ // ((float)state->comp_bytes)/((float)state->plain_bytes));
+ // Reset the plain buffer.
+ state->plain_ptr = state->plain;
+ state->plain_n = 0;
+ err = k;
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+static int lzo_decompress(LZOState *state){
+ int plain_n, comp_n;
+ int err, k;
+ //dprintf(">\n");
+ err = unmarshal_uint32(state->io, &plain_n);
+ //dprintf("> err=%d plain_n=%d\n", err, plain_n);
+ if(err) goto exit;
+ state->comp_bytes += 4;
+ if(plain_n == 0) goto exit;
+ err = unmarshal_uint32(state->io, &comp_n);
+ //dprintf("> err=%d comp_n=%d\n", err, comp_n);
+ if(err) goto exit;
+ state->comp_bytes += 4;
+ if(plain_n > state->plain_size){
+ err = -EINVAL;
+ goto exit;
+ }
+ if(comp_n > plain_n){
+ //dprintf("> reading plain data %d...\n", plain_n);
+ k = plain_n;
+ err = unmarshal_bytes(state->io, state->plain, plain_n);
+ state->plain_n = plain_n;
+ } else {
+ //dprintf("> reading comp data %d...\n", comp_n);
+ k = comp_n;
+ err = unmarshal_bytes(state->io, state->comp, comp_n);
+ //dprintf("> decompress comp_n=%d\n", comp_n);
+ err = lzo1x_decompress(state->comp, comp_n,
+ state->plain, &state->plain_n,
+ state->memory);
+ //dprintf("> err=%d plain=%d state->plain_n=%d\n", err, plain_n, state->plain_n);
+ if(err != LZO_E_OK || state->plain_n != plain_n){
+ // Bad. Corrupted input.
+ err = -EINVAL;
+ eprintf("> Corrupted!\n");
+ goto exit;
+ }
+ }
+ state->comp_bytes += k;
+ state->plain_bytes += state->plain_n;
+ state->plain_ptr = state->plain;
+ err = k;
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Write to the underlying stream using fwrite();
+ *
+ * @param stream destination
+ * @param buf data
+ * @param size size of data elements
+ * @param count number of data elements to write
+ * @return number of data elements written
+ */
+static int lzo_write(IOStream *s, const void *buf, size_t size, size_t count){
+ int err = 0;
+ int n = size * count; // Total number of bytes to write.
+ int chunk; // Size of chunk to write.
+ int remaining; // Number of bytes remaining to write.
+ int space; // Amount of space left in plain buffer.
+ LZOState *state = lzo_state(s);
+#ifdef NOCOMPRESS
+ //dprintf("> buf=%p size=%d count=%d\n", buf, size, count);
+ err = IOStream_write(state->io, buf, size, count);
+ //dprintf("< err=%d\n", err);
+#else
+ //dprintf("> buf=%p size=%d count=%d n=%d\n", buf, size, count, n);
+ remaining = n;
+ space = state->plain_size - state->plain_n;
+ //dprintf("> plain=%p plain_ptr=%p plain_n=%d space=%d\n",
+ // state->plain, state->plain_ptr, state->plain_n, space);
+ while(remaining){
+ chunk = remaining;
+ if(chunk > space) chunk = space;
+ //dprintf("> memcpy %p %p %d\n", state->plain_ptr, buf, chunk);
+ memcpy(state->plain_ptr, buf, chunk);
+ remaining -= chunk;
+ space -= chunk;
+ state->plain_ptr += chunk;
+ state->plain_n += chunk;
+ if(space == 0){
+ // Input buffer is full. Compress and write it.
+ err = lzo_compress(state);
+ if(err < 0) goto exit;
+ space = state->plain_size - state->plain_n;
+ }
+ }
+ err = (size > 1 ? n / size : n);
+ exit:
+ set_error(state, err);
+#endif
+ return err;
+}
+
+
+/** Read from the underlying stream.
+ *
+ * @param stream input
+ * @param buf where to put input
+ * @param size size of data elements
+ * @param count number of data elements to read
+ * @return number of data elements read
+ */
+static int lzo_read(IOStream *s, void *buf, size_t size, size_t count){
+ int err = 0;
+ int k = 0; // Number of (plain) bytes read.
+ int remaining = size * count; // Number of bytes remaining to read.
+ int chunk; // Size of chunk to read.
+ LZOState *state = lzo_state(s);
+#ifdef NOCOMPRESS
+ //dprintf("> buf=%p size=%d count=%d\n", buf, size, count);
+ err = IOStream_read(state->io, buf, size, count);
+ //dprintf("< err=%d\n", err);
+#else
+ if(!(state->flags & LZO_READ)){
+ err = -EINVAL;
+ goto exit;
+ }
+ while(remaining){
+ if(state->plain_n == 0){
+ // No more plain input, decompress some more.
+ err = lzo_decompress(state);
+ if(err < 0) goto exit;
+ // Stop reading if there is no more input.
+ if(err == 0 || state->plain_n == 0) break;
+ }
+ chunk = remaining;
+ if(chunk > state->plain_n) chunk = state->plain_n;
+ memcpy(buf, state->plain_ptr, chunk);
+ k += chunk;
+ buf += chunk;
+ state->plain_ptr += chunk;
+ state->plain_n -= chunk;
+ remaining -= chunk;
+ }
+ err = k;
+ exit:
+ set_error(state, err);
+#endif
+ return err;
+}
+
+/** Print to the underlying stream.
+ * Returns 0 if the formatted output is too big for the internal buffer.
+ *
+ * @param s lzo stream
+ * @param msg format to use
+ * @param args arguments
+ * @return result of the print
+ */
+static int lzo_print(IOStream *s, const char *msg, va_list args){
+ char buf[1024];
+ int buf_n = sizeof(buf);
+ int n;
+ LZOState *state = lzo_state(s);
+ if(!LZOState_writeable(state)){
+ n = -EINVAL;
+ goto exit;
+ }
+ n = vsnprintf(buf, buf_n, (char*)msg, args);
+ if(n < 0) goto exit;
+ if(n > buf_n){
+ n = 0;
+ } else {
+ n = lzo_write(s, buf, 1, n);
+ }
+ exit:
+ return n;
+}
+
+/** Read a character from the underlying stream
+ *
+ * @param s lzo stream
+ * @return character read, IOSTREAM_EOF on end of file (or error)
+ */
+static int lzo_getc(IOStream *s){
+ int err;
+ char c;
+ err = lzo_read(s, &c, 1, 1);
+ if(err < 1) c = EOF;
+ err = (c==EOF ? IOSTREAM_EOF : c);
+ return err;
+}
+
+/** Flush any pending input to the underlying stream.
+ *
+ * @param s lzo stream
+ * @return 0 on success, error code otherwise
+ */
+static int lzo_flush(IOStream *s){
+ int err = 0;
+ LZOState *state = lzo_state(s);
+ //dprintf(">\n");
+#ifdef NOCOMPRESS
+ err = IOStream_flush(state->io);
+#else
+ if(!LZOState_writeable(state)){
+ err = -EINVAL;
+ goto exit;
+ }
+ if(state->plain_n){
+ err = lzo_compress(state);
+ if(err < 0) goto exit;
+ }
+ err = IOStream_flush(state->io);
+ exit:
+ set_error(state, err);
+#endif
+ //dprintf("< err=%d\n", err);
+ return (err < 0 ? err : 0);
+}
+
+/** Check if a stream has an error.
+ *
+ * @param s lzo stream
+ * @return code if has an error, 0 otherwise
+ */
+static int lzo_error(IOStream *s){
+ int err = 0;
+ LZOState *state = lzo_state(s);
+ err = state->error;
+ if(err) goto exit;
+ err = IOStream_error(state->io);
+ exit:
+ return err;
+}
+
+int lzo_stream_finish(IOStream *s){
+ int err = 0;
+ LZOState *state = lzo_state(s);
+ if(!LZOState_writeable(state)){
+ err = -EINVAL;
+ goto exit;
+ }
+ err = lzo_flush(s);
+ if(err < 0) goto exit;
+ err = marshal_int32(state->io, 0);
+ exit:
+ return err;
+}
+
+/** Close an lzo stream.
+ *
+ * @param s lzo stream to close
+ * @return result of the close
+ */
+static int lzo_close(IOStream *s){
+ int err = 0;
+ LZOState *state = lzo_state(s);
+#ifdef NOCOMPRESS
+ err = IOStream_close(state->io);
+#else
+ if(LZOState_writeable(state)){
+ err = lzo_stream_finish(s);
+ }
+ err = IOStream_close(state->io);
+ set_error(state, err);
+#endif
+ return err;
+}
+
+/** Free an lzo stream.
+ *
+ * @param s lzo stream
+ */
+static void lzo_free(IOStream *s){
+ LZOState *state = lzo_state(s);
+ IOStream_free(state->io);
+ LZOState_free(state);
+ s->data = NULL;
+}
+
+/** Create an lzo stream for an IOStream.
+ *
+ * @param io stream to wrap
+ * @return new IOStream using f for i/o
+ */
+IOStream *lzo_stream_new(IOStream *io, const char *mode){
+ int err = -ENOMEM;
+ int flags = 0;
+ IOStream *zio = NULL;
+ LZOState *state = NULL;
+
+ zio = ALLOCATE(IOStream);
+ if(!zio) goto exit;
+ err = mode_flags(mode, &flags);
+ if(err) goto exit;
+ state = LZOState_new(io, flags);
+ if(!state) goto exit;
+ err = 0;
+ zio->data = state;
+ zio->methods = &lzo_methods;
+ exit:
+ if(err){
+ if(state) LZOState_free(state);
+ if(zio) deallocate(zio);
+ zio = NULL;
+ }
+ return zio;
+}
+
+/** IOStream version of fdopen().
+ *
+ * @param fd file descriptor
+ * @param flags giving the mode to open in (as for fdopen())
+ * @return new stream for the open file, or NULL if failed
+ */
+IOStream *lzo_stream_fdopen(int fd, const char *mode){
+ int err = -ENOMEM;
+ IOStream *io = NULL, *zio = NULL;
+ io = file_stream_fdopen(fd, mode);
+ if(!io) goto exit;
+ zio = lzo_stream_new(io, mode);
+ if(!io) goto exit;
+ err = 0;
+ exit:
+ if(err){
+ IOStream_free(io);
+ IOStream_free(zio);
+ zio = NULL;
+ }
+ return zio;
+}
+#endif