2014-08-13 10:39:27 +01:00
|
|
|
/*
|
|
|
|
* Used by System.IO.Compression.DeflateStream
|
|
|
|
*
|
|
|
|
* Author:
|
|
|
|
* Gonzalo Paniagua Javier (gonzalo@novell.com)
|
|
|
|
*
|
|
|
|
* (c) Copyright 2009 Novell, Inc.
|
|
|
|
*/
|
|
|
|
#include <config.h>
|
2019-09-24 08:53:40 +00:00
|
|
|
#if defined (HAVE_SYS_ZLIB)
|
2014-08-13 10:39:27 +01:00
|
|
|
#include <zlib.h>
|
|
|
|
#else
|
|
|
|
#include "zlib.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2016-03-16 12:38:19 -04:00
|
|
|
#ifndef MONO_API
|
|
|
|
#define MONO_API
|
|
|
|
#endif
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
#ifndef TRUE
|
|
|
|
#define FALSE 0
|
|
|
|
#define TRUE 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define BUFFER_SIZE 4096
|
|
|
|
#define ARGUMENT_ERROR -10
|
|
|
|
#define IO_ERROR -11
|
2019-04-12 14:10:50 +00:00
|
|
|
#define MONO_EXCEPTION -12
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2019-09-24 08:53:40 +00:00
|
|
|
#define z_new0(type) ((type *) calloc (sizeof (type), 1))
|
2016-03-16 12:38:19 -04:00
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
typedef gint (*read_write_func) (guchar *buffer, gint length, void *gchandle);
|
|
|
|
struct _ZStream {
|
|
|
|
z_stream *stream;
|
|
|
|
guchar *buffer;
|
|
|
|
read_write_func func;
|
|
|
|
void *gchandle;
|
|
|
|
guchar compress;
|
|
|
|
guchar eof;
|
2016-03-16 12:38:19 -04:00
|
|
|
guint32 total_in;
|
2014-08-13 10:39:27 +01:00
|
|
|
};
|
|
|
|
typedef struct _ZStream ZStream;
|
|
|
|
|
2019-09-24 08:53:40 +00:00
|
|
|
// FIXME? Names should start "mono"?
|
2016-03-16 12:38:19 -04:00
|
|
|
MONO_API ZStream *CreateZStream (gint compress, guchar gzip, read_write_func func, void *gchandle);
|
|
|
|
MONO_API gint CloseZStream (ZStream *zstream);
|
|
|
|
MONO_API gint Flush (ZStream *stream);
|
|
|
|
MONO_API gint ReadZStream (ZStream *stream, guchar *buffer, gint length);
|
|
|
|
MONO_API gint WriteZStream (ZStream *stream, guchar *buffer, gint length);
|
2014-08-13 10:39:27 +01:00
|
|
|
static gint flush_internal (ZStream *stream, gboolean is_final);
|
|
|
|
|
|
|
|
static void *
|
2014-09-04 09:07:35 +01:00
|
|
|
z_alloc (void *opaque, unsigned int nitems, unsigned int item_size)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2019-09-24 08:53:40 +00:00
|
|
|
return calloc (nitems, item_size);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
z_free (void *opaque, void *ptr)
|
|
|
|
{
|
2016-03-16 12:38:19 -04:00
|
|
|
free (ptr);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ZStream *
|
|
|
|
CreateZStream (gint compress, guchar gzip, read_write_func func, void *gchandle)
|
|
|
|
{
|
|
|
|
z_stream *z;
|
|
|
|
gint retval;
|
|
|
|
ZStream *result;
|
|
|
|
|
|
|
|
if (func == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2019-09-24 08:53:40 +00:00
|
|
|
z = z_new0 (z_stream);
|
2014-08-13 10:39:27 +01:00
|
|
|
if (compress) {
|
|
|
|
retval = deflateInit2 (z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, gzip ? 31 : -15, 8, Z_DEFAULT_STRATEGY);
|
|
|
|
} else {
|
|
|
|
retval = inflateInit2 (z, gzip ? 31 : -15);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (retval != Z_OK) {
|
2016-03-16 12:38:19 -04:00
|
|
|
free (z);
|
2014-08-13 10:39:27 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
z->zalloc = z_alloc;
|
|
|
|
z->zfree = z_free;
|
2019-09-24 08:53:40 +00:00
|
|
|
result = z_new0 (ZStream);
|
2014-08-13 10:39:27 +01:00
|
|
|
result->stream = z;
|
|
|
|
result->func = func;
|
|
|
|
result->gchandle = gchandle;
|
|
|
|
result->compress = compress;
|
2019-09-24 08:53:40 +00:00
|
|
|
result->buffer = (guchar*)malloc (BUFFER_SIZE);
|
2015-08-25 18:44:33 -04:00
|
|
|
result->stream->next_out = result->buffer;
|
|
|
|
result->stream->avail_out = BUFFER_SIZE;
|
2016-03-16 12:38:19 -04:00
|
|
|
result->stream->total_in = 0;
|
2014-08-13 10:39:27 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
gint
|
|
|
|
CloseZStream (ZStream *zstream)
|
|
|
|
{
|
|
|
|
gint status;
|
|
|
|
gint flush_status;
|
|
|
|
|
|
|
|
if (zstream == NULL)
|
|
|
|
return ARGUMENT_ERROR;
|
|
|
|
|
|
|
|
status = 0;
|
|
|
|
if (zstream->compress) {
|
|
|
|
if (zstream->stream->total_in > 0) {
|
|
|
|
do {
|
|
|
|
status = deflate (zstream->stream, Z_FINISH);
|
|
|
|
flush_status = flush_internal (zstream, TRUE);
|
2019-04-12 14:10:50 +00:00
|
|
|
if (flush_status == MONO_EXCEPTION) {
|
|
|
|
status = flush_status;
|
|
|
|
break;
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
} while (status == Z_OK); /* We want Z_STREAM_END or error here here */
|
|
|
|
if (status == Z_STREAM_END)
|
|
|
|
status = flush_status;
|
|
|
|
}
|
|
|
|
deflateEnd (zstream->stream);
|
|
|
|
} else {
|
|
|
|
inflateEnd (zstream->stream);
|
|
|
|
}
|
2016-03-16 12:38:19 -04:00
|
|
|
free (zstream->buffer);
|
|
|
|
free (zstream->stream);
|
2014-08-13 10:39:27 +01:00
|
|
|
memset (zstream, 0, sizeof (ZStream));
|
2016-03-16 12:38:19 -04:00
|
|
|
free (zstream);
|
2014-08-13 10:39:27 +01:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gint
|
|
|
|
write_to_managed (ZStream *stream)
|
|
|
|
{
|
|
|
|
gint n;
|
|
|
|
z_stream *zs;
|
|
|
|
|
|
|
|
zs = stream->stream;
|
|
|
|
if (zs->avail_out != BUFFER_SIZE) {
|
|
|
|
n = stream->func (stream->buffer, BUFFER_SIZE - zs->avail_out, stream->gchandle);
|
|
|
|
zs->next_out = stream->buffer;
|
|
|
|
zs->avail_out = BUFFER_SIZE;
|
2019-04-12 14:10:50 +00:00
|
|
|
if (n == MONO_EXCEPTION)
|
|
|
|
return n;
|
2014-08-13 10:39:27 +01:00
|
|
|
if (n < 0)
|
|
|
|
return IO_ERROR;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gint
|
|
|
|
flush_internal (ZStream *stream, gboolean is_final)
|
|
|
|
{
|
|
|
|
gint status;
|
|
|
|
|
|
|
|
if (!stream->compress)
|
|
|
|
return 0;
|
|
|
|
|
2015-08-25 18:44:33 -04:00
|
|
|
if (!is_final && stream->stream->avail_in != 0) {
|
2014-08-13 10:39:27 +01:00
|
|
|
status = deflate (stream->stream, Z_PARTIAL_FLUSH);
|
|
|
|
if (status != Z_OK && status != Z_STREAM_END)
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
return write_to_managed (stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
gint
|
|
|
|
Flush (ZStream *stream)
|
|
|
|
{
|
|
|
|
return flush_internal (stream, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
gint
|
|
|
|
ReadZStream (ZStream *stream, guchar *buffer, gint length)
|
|
|
|
{
|
|
|
|
gint n;
|
|
|
|
gint status;
|
|
|
|
z_stream *zs;
|
|
|
|
|
|
|
|
if (stream == NULL || buffer == NULL || length < 0)
|
|
|
|
return ARGUMENT_ERROR;
|
|
|
|
|
|
|
|
if (stream->eof)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
zs = stream->stream;
|
|
|
|
zs->next_out = buffer;
|
|
|
|
zs->avail_out = length;
|
|
|
|
while (zs->avail_out > 0) {
|
|
|
|
if (zs->avail_in == 0) {
|
|
|
|
n = stream->func (stream->buffer, BUFFER_SIZE, stream->gchandle);
|
2016-11-10 13:04:39 +00:00
|
|
|
n = n < 0 ? 0 : n;
|
2016-03-16 12:38:19 -04:00
|
|
|
stream->total_in += n;
|
2014-08-13 10:39:27 +01:00
|
|
|
zs->next_in = stream->buffer;
|
2016-11-10 13:04:39 +00:00
|
|
|
zs->avail_in = n;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
if (zs->avail_in == 0 && zs->total_in == 0)
|
|
|
|
return 0;
|
2015-04-07 09:35:12 +01:00
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
status = inflate (stream->stream, Z_SYNC_FLUSH);
|
|
|
|
if (status == Z_STREAM_END) {
|
|
|
|
stream->eof = TRUE;
|
|
|
|
break;
|
2016-11-10 13:04:39 +00:00
|
|
|
} else if (status == Z_BUF_ERROR && stream->total_in == zs->total_in) {
|
2018-10-09 08:20:59 +00:00
|
|
|
if (zs->avail_in != 0) {
|
|
|
|
stream->eof = TRUE;
|
|
|
|
}
|
2016-11-10 13:04:39 +00:00
|
|
|
break;
|
2014-08-13 10:39:27 +01:00
|
|
|
} else if (status != Z_OK) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return length - zs->avail_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
gint
|
|
|
|
WriteZStream (ZStream *stream, guchar *buffer, gint length)
|
|
|
|
{
|
|
|
|
gint n;
|
|
|
|
gint status;
|
|
|
|
z_stream *zs;
|
|
|
|
|
|
|
|
if (stream == NULL || buffer == NULL || length < 0)
|
|
|
|
return ARGUMENT_ERROR;
|
|
|
|
|
|
|
|
if (stream->eof)
|
|
|
|
return IO_ERROR;
|
|
|
|
|
|
|
|
zs = stream->stream;
|
|
|
|
zs->next_in = buffer;
|
|
|
|
zs->avail_in = length;
|
|
|
|
while (zs->avail_in > 0) {
|
|
|
|
if (zs->avail_out == 0) {
|
|
|
|
zs->next_out = stream->buffer;
|
|
|
|
zs->avail_out = BUFFER_SIZE;
|
|
|
|
}
|
|
|
|
status = deflate (stream->stream, Z_NO_FLUSH);
|
|
|
|
if (status != Z_OK && status != Z_STREAM_END)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
if (zs->avail_out == 0) {
|
|
|
|
n = write_to_managed (stream);
|
|
|
|
if (n < 0)
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|