mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
597 lines
16 KiB
C
597 lines
16 KiB
C
/* $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
|