#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include "memgzio.h"
#ifndef Z_BUFSIZE
# ifdef MAXSEG_64K
# define Z_BUFSIZE 4096
# else
# define Z_BUFSIZE 16384
# endif
#endif
#ifndef Z_PRINTF_BUFSIZE
# define Z_PRINTF_BUFSIZE 4096
#endif
#define ALLOC(size) malloc(size)
#define TRYFREE(p) {if (p) free(p);}
static int gz_magic[2] = {0x1f, 0x8b};
#define ASCII_FLAG 0x01
#define HEAD_CRC 0x02
#define EXTRA_FIELD 0x04
#define ORIG_NAME 0x08
#define COMMENT 0x10
#define RESERVED 0xE0
typedef struct _MemFile {
char *memory;
char *next;
int available;
int error;
char mode;
} MEMFILE;
typedef struct mem_stream {
z_stream stream;
int z_err;
int z_eof;
MEMFILE *file;
Byte *inbuf;
Byte *outbuf;
uLong crc;
char *msg;
int transparent;
char mode;
long startpos;
} mem_stream;
local gzFile gz_open OF((char *memory, const int available, const char *mode));
local int do_flush OF((gzFile file, int flush));
local int get_byte OF((mem_stream *s));
local void check_header OF((mem_stream *s));
local int destroy OF((mem_stream *s));
local void putLong OF((MEMFILE *file, uLong x));
local uLong getLong OF((mem_stream *s));
local MEMFILE *memOpen(char *memory, int available, char mode)
{
MEMFILE *f;
if(available <= 8)
return NULL;
if(mode != 'w' && mode != 'r')
return NULL;
f = (MEMFILE *)malloc(sizeof(MEMFILE));
f->memory = memory;
f->mode = mode;
f->error = 0;
if(mode == 'w') {
f->available = available - 8;
f->next = memory + 8;
memory[0] = 'V';
memory[1] = 'B';
memory[2] = 'A';
memory[3] = ' ';
*((int *)(memory+4)) = 0;
} else {
if(memory[0] != 'V' || memory[1] != 'B' || memory[2] != 'A' ||
memory[3] != ' ') {
free(f);
return NULL;
}
f->available = *((int *)(memory+4));
f->next = memory+8;
}
return f;
}
local size_t memWrite(const void *buffer, size_t size, size_t count,
MEMFILE *file)
{
size_t total = size*count;
if(file->mode != 'w') {
file->error = 1;
return 0;
}
if(total > (size_t)file->available) {
total = file->available;
}
memcpy(file->next, buffer, total);
file->available -= total;
file->next += total;
return total;
}
local size_t memRead(void *buffer, size_t size, size_t count,
MEMFILE *file)
{
size_t total = size*count;
if(file->mode != 'r') {
file->error = 1;
return 0;
}
if(file->available == 0)
return -1;
if(total > (size_t)file->available) {
total = file->available;
}
memcpy(buffer, file->next, total);
file->available -= total;
file->next += total;
return total;
}
local int memPutc(int c, MEMFILE *file)
{
if(file->mode != 'w') {
file->error = 1;
return -1;
}
if(file->available >= 1) {
*file->next++ = c;
file->available--;
} else
return -1;
return c;
}
local long memTell(MEMFILE *f)
{
return (f->next - f->memory) - 8;
}
local int memError(MEMFILE *f)
{
return f->error;
}
local int memClose(MEMFILE *f)
{
if(f->mode == 'w') {
*((int *)(f->memory+4)) = memTell(f);
}
free(f);
return 0;
}
local int memPrintf(MEMFILE *f, const char *format, ...)
{
char buffer[80];
va_list list;
int len;
va_start(list, format);
len = vsprintf(buffer, format, list);
va_end(list);
return memWrite(buffer, 1, len, f);
}
local gzFile gz_open (memory, available, mode)
char *memory;
const int available;
const char *mode;
{
int err;
int level = Z_DEFAULT_COMPRESSION;
int strategy = Z_DEFAULT_STRATEGY;
char *p = (char*)mode;
mem_stream *s;
char fmode[80];
char *m = fmode;
s = (mem_stream *)ALLOC(sizeof(mem_stream));
if (!s) return Z_NULL;
s->stream.zalloc = (alloc_func)0;
s->stream.zfree = (free_func)0;
s->stream.opaque = (voidpf)0;
s->stream.next_in = s->inbuf = Z_NULL;
s->stream.next_out = s->outbuf = Z_NULL;
s->stream.avail_in = s->stream.avail_out = 0;
s->z_err = Z_OK;
s->z_eof = 0;
s->crc = crc32(0L, Z_NULL, 0);
s->msg = NULL;
s->transparent = 0;
s->file = NULL;
s->mode = '\0';
do {
if (*p == 'r') s->mode = 'r';
if (*p == 'w' || *p == 'a') s->mode = 'w';
if (*p >= '0' && *p <= '9') {
level = *p - '0';
} else if (*p == 'f') {
strategy = Z_FILTERED;
} else if (*p == 'h') {
strategy = Z_HUFFMAN_ONLY;
} else {
*m++ = *p;
}
} while (*p++ && m != fmode + sizeof(fmode));
if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL;
if (s->mode == 'w') {
#ifdef NO_DEFLATE
err = Z_STREAM_ERROR;
#else
err = deflateInit2(&(s->stream), level,
Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
#endif
if (err != Z_OK || s->outbuf == Z_NULL) {
return destroy(s), (gzFile)Z_NULL;
}
} else {
s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE);
err = inflateInit2(&(s->stream), -MAX_WBITS);
if (err != Z_OK || s->inbuf == Z_NULL) {
return destroy(s), (gzFile)Z_NULL;
}
}
s->stream.avail_out = Z_BUFSIZE;
errno = 0;
s->file = memOpen(memory, available, s->mode);
if (s->file == NULL) {
return destroy(s), (gzFile)Z_NULL;
}
if (s->mode == 'w') {
memPrintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
Z_DEFLATED, 0 , 0,0,0,0 , 0 , OS_CODE);
s->startpos = 10L;
} else {
check_header(s);
s->startpos = (memTell(s->file) - s->stream.avail_in);
}
return (gzFile)s;
}
gzFile ZEXPORT memgzopen (memory, available, mode)
char *memory;
int available;
const char *mode;
{
return gz_open (memory, available, mode);
}
local int get_byte(s)
mem_stream *s;
{
if (s->z_eof) return EOF;
if (s->stream.avail_in == 0) {
errno = 0;
s->stream.avail_in = memRead(s->inbuf, 1, Z_BUFSIZE, s->file);
if (s->stream.avail_in == 0) {
s->z_eof = 1;
if (memError(s->file)) s->z_err = Z_ERRNO;
return EOF;
}
s->stream.next_in = s->inbuf;
}
s->stream.avail_in--;
return *(s->stream.next_in)++;
}
local void check_header(s)
mem_stream *s;
{
int method;
int flags;
uInt len;
int c;
for (len = 0; len < 2; len++) {
c = get_byte(s);
if (c != gz_magic[len]) {
if (len != 0) s->stream.avail_in++, s->stream.next_in--;
if (c != EOF) {
s->stream.avail_in++, s->stream.next_in--;
s->transparent = 1;
}
s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
return;
}
}
method = get_byte(s);
flags = get_byte(s);
if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
s->z_err = Z_DATA_ERROR;
return;
}
for (len = 0; len < 6; len++) (void)get_byte(s);
if ((flags & EXTRA_FIELD) != 0) {
len = (uInt)get_byte(s);
len += ((uInt)get_byte(s))<<8;
while (len-- != 0 && get_byte(s) != EOF) ;
}
if ((flags & ORIG_NAME) != 0) {
while ((c = get_byte(s)) != 0 && c != EOF) ;
}
if ((flags & COMMENT) != 0) {
while ((c = get_byte(s)) != 0 && c != EOF) ;
}
if ((flags & HEAD_CRC) != 0) {
for (len = 0; len < 2; len++) (void)get_byte(s);
}
s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
}
local int destroy (s)
mem_stream *s;
{
int err = Z_OK;
if (!s) return Z_STREAM_ERROR;
TRYFREE(s->msg);
if (s->stream.state != NULL) {
if (s->mode == 'w') {
#ifdef NO_DEFLATE
err = Z_STREAM_ERROR;
#else
err = deflateEnd(&(s->stream));
#endif
} else if (s->mode == 'r') {
err = inflateEnd(&(s->stream));
}
}
if (s->file != NULL && memClose(s->file)) {
#ifdef ESPIPE
if (errno != ESPIPE)
#endif
err = Z_ERRNO;
}
if (s->z_err < 0) err = s->z_err;
TRYFREE(s->inbuf);
TRYFREE(s->outbuf);
TRYFREE(s);
return err;
}
int ZEXPORT memgzread (file, buf, len)
gzFile file;
voidp buf;
unsigned len;
{
mem_stream *s = (mem_stream*)file;
Bytef *start = (Bytef*)buf;
Byte *next_out;
if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR;
if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
if (s->z_err == Z_STREAM_END) return 0;
next_out = (Byte*)buf;
s->stream.next_out = (Bytef*)buf;
s->stream.avail_out = len;
while (s->stream.avail_out != 0) {
if (s->transparent) {
uInt n = s->stream.avail_in;
if (n > s->stream.avail_out) n = s->stream.avail_out;
if (n > 0) {
zmemcpy(s->stream.next_out, s->stream.next_in, n);
next_out += n;
s->stream.next_out = next_out;
s->stream.next_in += n;
s->stream.avail_out -= n;
s->stream.avail_in -= n;
}
if (s->stream.avail_out > 0) {
s->stream.avail_out -= memRead(next_out, 1, s->stream.avail_out,
s->file);
}
len -= s->stream.avail_out;
s->stream.total_in += (uLong)len;
s->stream.total_out += (uLong)len;
if (len == 0) s->z_eof = 1;
return (int)len;
}
if (s->stream.avail_in == 0 && !s->z_eof) {
errno = 0;
s->stream.avail_in = memRead(s->inbuf, 1, Z_BUFSIZE, s->file);
if (s->stream.avail_in == 0) {
s->z_eof = 1;
if (memError(s->file)) {
s->z_err = Z_ERRNO;
break;
}
}
s->stream.next_in = s->inbuf;
}
s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
if (s->z_err == Z_STREAM_END) {
s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
start = s->stream.next_out;
if (getLong(s) != s->crc) {
s->z_err = Z_DATA_ERROR;
} else {
(void)getLong(s);
check_header(s);
if (s->z_err == Z_OK) {
uLong total_in = s->stream.total_in;
uLong total_out = s->stream.total_out;
inflateReset(&(s->stream));
s->stream.total_in = total_in;
s->stream.total_out = total_out;
s->crc = crc32(0L, Z_NULL, 0);
}
}
}
if (s->z_err != Z_OK || s->z_eof) break;
}
s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
return (int)(len - s->stream.avail_out);
}
#ifndef NO_DEFLATE
int ZEXPORT memgzwrite (file, buf, len)
gzFile file;
const voidp buf;
unsigned len;
{
mem_stream *s = (mem_stream*)file;
if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
s->stream.next_in = (Bytef*)buf;
s->stream.avail_in = len;
while (s->stream.avail_in != 0) {
if (s->stream.avail_out == 0) {
s->stream.next_out = s->outbuf;
if (memWrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
s->z_err = Z_ERRNO;
break;
}
s->stream.avail_out = Z_BUFSIZE;
}
s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
if (s->z_err != Z_OK) break;
}
s->crc = crc32(s->crc, (const Bytef *)buf, len);
return (int)(len - s->stream.avail_in);
}
#endif
local int do_flush (file, flush)
gzFile file;
int flush;
{
uInt len;
int done = 0;
mem_stream *s = (mem_stream*)file;
if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
s->stream.avail_in = 0;
for (;;) {
len = Z_BUFSIZE - s->stream.avail_out;
if (len != 0) {
if ((uInt)memWrite(s->outbuf, 1, len, s->file) != len) {
s->z_err = Z_ERRNO;
return Z_ERRNO;
}
s->stream.next_out = s->outbuf;
s->stream.avail_out = Z_BUFSIZE;
}
if (done) break;
s->z_err = deflate(&(s->stream), flush);
if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
}
return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
}
local void putLong (file, x)
MEMFILE *file;
uLong x;
{
int n;
for (n = 0; n < 4; n++) {
memPutc((int)(x & 0xff), file);
x >>= 8;
}
}
local uLong getLong (s)
mem_stream *s;
{
uLong x = (uLong)get_byte(s);
int c;
x += ((uLong)get_byte(s))<<8;
x += ((uLong)get_byte(s))<<16;
c = get_byte(s);
if (c == EOF) s->z_err = Z_DATA_ERROR;
x += ((uLong)c)<<24;
return x;
}
int ZEXPORT memgzclose (file)
gzFile file;
{
int err;
mem_stream *s = (mem_stream*)file;
if (s == NULL) return Z_STREAM_ERROR;
if (s->mode == 'w') {
#ifdef NO_DEFLATE
return Z_STREAM_ERROR;
#else
err = do_flush (file, Z_FINISH);
if (err != Z_OK) return destroy((mem_stream*)file);
putLong (s->file, s->crc);
putLong (s->file, s->stream.total_in);
#endif
}
return destroy((mem_stream*)file);
}
long ZEXPORT memtell(file)
gzFile file;
{
mem_stream *s = (mem_stream*)file;
if (s == NULL) return Z_STREAM_ERROR;
return memTell(s->file);
}