/************************************************************************
* IRC - Internet Relay Chat, src/zlink.c
* Copyright (C) 2000 Lucas Madar
*
* This program 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 1, or (at your option)
* any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: zlink.c,v 1.5 2006/01/07 22:13:26 trystanscott Exp $ */
/*
* This streaming ircd zlib implementation was
* inspired mostly by dianora's example in hybrid-6
* - lucas
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "memcount.h"
#include "zlib.h"
#define COMPRESSION_LEVEL 3 /* 0 to 9, 0 = none */
#define ZIP_MIN_BLOCK 1024 /* smallest block to compress */
#define ZIP_MAX_BLOCK 8192 /* largest block to compress */
/*
* This shouldn't be necessary.
* The outbuf should never be larger than
* the maximum block.. should it?
* I'll account for any weirdness in zlib.
*
* WARNING:
* Please be aware that if you are using both encryption
* and ziplinks, rc4buf in send.c MUST be the same size
* as zipOutBuf in zlink.c!
*/
#define zipOutBufSize (ZIP_MAX_BLOCK * 2)
static char zipOutBuf[zipOutBufSize];
/*
* 64k overflowed every now and then.
* This isn't that important, an overflow is
* non-fatal, but causes more calls to deflate()
* 96k seems to overflow a lot now.
*/
#define zipInBufSize (131072) /* 128K */
static char zipInBuf[zipInBufSize];
/* opaque "out" data structure */
struct zipped_link_out
{
z_stream stream; /* zip stream data */
char buf[ZIP_MAX_BLOCK]; /* zipped buffer */
int bufsize; /* size of inbuf content */
};
/* opaque "in" data structure */
struct zipped_link_in
{
z_stream stream; /* zip stream data */
};
/* returns a pointer to a setup opaque input session */
void *zip_create_input_session()
{
struct zipped_link_in *zip;
zip = (struct zipped_link_in *) MyMalloc(sizeof(struct zipped_link_in));
memset(zip, 0, sizeof(struct zipped_link_in));
zip->stream.zalloc = NULL;
zip->stream.zfree = NULL;
zip->stream.data_type = Z_ASCII;
if(inflateInit(&zip->stream) != Z_OK)
return NULL;
return (void *) zip;
}
/* returns a pointer to an opaque output session */
void *zip_create_output_session()
{
struct zipped_link_out *zip;
zip = (struct zipped_link_out *) MyMalloc(sizeof(struct zipped_link_out));
memset(zip, 0, sizeof(struct zipped_link_out));
zip->stream.zalloc = NULL;
zip->stream.zfree = NULL;
zip->stream.data_type = Z_ASCII;
if(deflateInit(&zip->stream, COMPRESSION_LEVEL) != Z_OK)
return NULL;
return (void *) zip;
}
/*
* zip_input()
*
* session - opaque in-session pointer
* buffer - compressed buffer
* len - length of buffer (will change)
* err - numeric error if length is -1 on return
* nbuf - set if this function needs to be called again
* nbuflen - if nbuf is set, length to call with again.
* -- nbuf, if set, should call zip_input when done processing
* first return, with buffer set to nbuf.
* returns:
* len > -1:
* compressed data
* len == -1:
* error message
*/
char *zip_input(void *session, char *buffer, int *len, int *err,
char **nbuf, int *nbuflen)
{
struct zipped_link_in *z = (struct zipped_link_in *) session;
z_stream *zin = &z->stream;
int ret;
*nbuf = NULL;
*err = 0;
zin->next_in = (Bytef *) buffer;
zin->avail_in = *len;
zin->next_out = (Bytef *) zipInBuf;
zin->avail_out = zipInBufSize;
ret = inflate(zin, Z_SYNC_FLUSH);
switch(ret)
{
case Z_OK:
if(zin->avail_in) /* grrr, didn't take all the input */
{
if(zin->avail_out != 0) /* but there was still output left??? */
{
*len = -1;
return zin->msg ? zin->msg : "????";
}
*nbuf = (char *) zin->next_in;
*nbuflen = zin->avail_in;
*len = zipInBufSize - zin->avail_out;
return zipInBuf;
}
else
{
*len = zipInBufSize - zin->avail_out;
return zipInBuf;
}
default:
*len = -1;
*err = ret;
return zin->msg ? zin->msg : "????";
}
}
/* returns the amount of data waiting in the outgoing buffer */
int zip_is_data_out(void *session)
{
struct zipped_link_out *z = (struct zipped_link_out *) session;
return z->bufsize;
}
/*
* zip_output():
* session is opaque session pointer.
* buffer is buffer to compress.
* len is length of buffer, will change.
* forceflush forces inflate to return a buffer, even if it has
* not optimally compressed something.
* Largedata should be nonzero during a split.
* largedata is also an error number, it is set if len is -1.
* if len is -1, returns null terminated error string.
*/
char *zip_output(void *session, char *buffer, int *len,
int forceflush, int *largedata)
{
struct zipped_link_out *z = (struct zipped_link_out *) session;
z_stream *zout = &z->stream;
int ret;
if(buffer)
{
memcpy(z->buf + z->bufsize, buffer, *len);
z->bufsize += *len;
}
if( !forceflush && ((z->bufsize < ZIP_MIN_BLOCK) ||
(largedata && (z->bufsize < (ZIP_MAX_BLOCK - 512)))))
{
*len = 0;
return NULL;
}
zout->next_in = (Bytef *) z->buf;
zout->avail_in = z->bufsize;
zout->next_out = (Bytef *) zipOutBuf;
zout->avail_out = zipOutBufSize;
/*
* We do our own internal buffering,
* so flush all the time.
*/
ret = deflate(zout, Z_SYNC_FLUSH);
if(ret == Z_OK)
{
z->bufsize = 0;
*len = zipOutBufSize - zout->avail_out;
return zipOutBuf;
}
*len = -1;
*largedata = ret;
return zout->msg ? zout->msg : "???";
}
/* if *insiz is zero, there are no stats available for this session. */
void zip_out_get_stats(void *session, unsigned long *insiz,
unsigned long *outsiz, double *ratio)
{
struct zipped_link_out *z = (struct zipped_link_out *) session;
*insiz = z->stream.total_in;
*outsiz = z->stream.total_out;
if(*insiz)
*ratio = ((100.0 * (double)z->stream.total_out) /
(double) z->stream.total_in);
}
void zip_in_get_stats(void *session, unsigned long *insiz,
unsigned long *outsiz, double *ratio)
{
struct zipped_link_in *z = (struct zipped_link_in *) session;
*insiz = z->stream.total_in;
*outsiz = z->stream.total_out;
if(*outsiz)
*ratio = ((100.0 * (double)z->stream.total_in) /
(double) z->stream.total_out);
}
void zip_destroy_output_session(void *session)
{
struct zipped_link_out *z = (struct zipped_link_out *) session;
deflateEnd(&z->stream);
MyFree(session);
}
void zip_destroy_input_session(void *session)
{
struct zipped_link_in *z = (struct zipped_link_in *) session;
inflateEnd(&z->stream);
MyFree(session);
}
u_long
memcount_zlink(MCzlink *mc)
{
mc->file = __FILE__;
mc->m_insession_size = sizeof(struct zipped_link_in);
mc->m_outsession_size = sizeof(struct zipped_link_out);
mc->s_bufs.c++;
mc->s_bufs.m += sizeof(zipOutBuf);
mc->s_bufs.c++;
mc->s_bufs.m += sizeof(zipInBuf);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1