Xamarin Public Jenkins (auto-signing) 468663ddbb Imported Upstream version 6.10.0.49
Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
2020-01-16 16:38:04 +00:00

256 lines
5.2 KiB
C

/*
* Used by System.IO.Compression.DeflateStream
*
* Author:
* Gonzalo Paniagua Javier (gonzalo@novell.com)
*
* (c) Copyright 2009 Novell, Inc.
*/
#include <config.h>
#if defined (HAVE_SYS_ZLIB)
#include <zlib.h>
#else
#include "../mono/zlib/zlib.h"
#endif
#include <glib.h>
#include <string.h>
#include <stdlib.h>
#ifndef MONO_API
#define MONO_API
#endif
#ifndef TRUE
#define FALSE 0
#define TRUE 1
#endif
#define BUFFER_SIZE 4096
#define ARGUMENT_ERROR -10
#define IO_ERROR -11
#define MONO_EXCEPTION -12
#define z_new0(type) ((type *) calloc (sizeof (type), 1))
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;
guint32 total_in;
};
typedef struct _ZStream ZStream;
// FIXME? Names should start "mono"?
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);
static gint flush_internal (ZStream *stream, gboolean is_final);
static void *
z_alloc (void *opaque, unsigned int nitems, unsigned int item_size)
{
return calloc (nitems, item_size);
}
static void
z_free (void *opaque, void *ptr)
{
free (ptr);
}
ZStream *
CreateZStream (gint compress, guchar gzip, read_write_func func, void *gchandle)
{
z_stream *z;
gint retval;
ZStream *result;
if (func == NULL)
return NULL;
z = z_new0 (z_stream);
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) {
free (z);
return NULL;
}
z->zalloc = z_alloc;
z->zfree = z_free;
result = z_new0 (ZStream);
result->stream = z;
result->func = func;
result->gchandle = gchandle;
result->compress = compress;
result->buffer = (guchar*)malloc (BUFFER_SIZE);
result->stream->next_out = result->buffer;
result->stream->avail_out = BUFFER_SIZE;
result->stream->total_in = 0;
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);
if (flush_status == MONO_EXCEPTION) {
status = flush_status;
break;
}
} 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);
}
free (zstream->buffer);
free (zstream->stream);
memset (zstream, 0, sizeof (ZStream));
free (zstream);
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;
if (n == MONO_EXCEPTION)
return n;
if (n < 0)
return IO_ERROR;
}
return 0;
}
static gint
flush_internal (ZStream *stream, gboolean is_final)
{
gint status;
if (!stream->compress)
return 0;
if (!is_final && stream->stream->avail_in != 0) {
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);
n = n < 0 ? 0 : n;
stream->total_in += n;
zs->next_in = stream->buffer;
zs->avail_in = n;
}
if (zs->avail_in == 0 && zs->total_in == 0)
return 0;
status = inflate (stream->stream, Z_SYNC_FLUSH);
if (status == Z_STREAM_END) {
stream->eof = TRUE;
break;
} else if (status == Z_BUF_ERROR && stream->total_in == zs->total_in) {
if (zs->avail_in != 0) {
stream->eof = TRUE;
}
break;
} 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;
}