Merge pull request #2678 from esdrubal/filestream
[mono.git] / support / zlib-helper.c
index 587e06bde8b548e8b8c7c5f6e1fc9ec28541eace..f364a66095fcaf23a35524746f18d4accf891736 100644 (file)
 #include "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
 #define ARGUMENT_ERROR -10
 #define IO_ERROR -11
 
-typedef int (*read_write_func) (unsigned char *buffer, int length);
+#define z_malloc(size)          ((gpointer) malloc(size))
+#define z_malloc0(size)         ((gpointer) calloc(1,size))
+#define z_new(type,size)        ((type *) z_malloc (sizeof (type) * (size)))
+#define z_new0(type,size)       ((type *) z_malloc0 (sizeof (type)* (size)))
+
+typedef gint (*read_write_func) (guchar *buffer, gint length, void *gchandle);
 struct _ZStream {
        z_stream *stream;
-       unsigned char *buffer;
+       guchar *buffer;
        read_write_func func;
-       unsigned char compress;
-       unsigned char eof;
+       void *gchandle;
+       guchar compress;
+       guchar eof;
+       guint32 total_in;
 };
 typedef struct _ZStream ZStream;
 
-ZStream *CreateZStream (int compress, unsigned char gzip, read_write_func func);
-int CloseZStream (ZStream *zstream);
-int Flush (ZStream *stream);
-int ReadZStream (ZStream *stream, unsigned char *buffer, int length);
-int WriteZStream (ZStream *stream, unsigned char *buffer, int length);
+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 z_malloc0 (nitems * item_size);
+}
+
+static void
+z_free (void *opaque, void *ptr)
+{
+       free (ptr);
+}
 
 ZStream *
-CreateZStream (int compress, unsigned char gzip, read_write_func func)
+CreateZStream (gint compress, guchar gzip, read_write_func func, void *gchandle)
 {
        z_stream *z;
-       int retval;
+       gint retval;
        ZStream *result;
 
        if (func == NULL)
@@ -57,10 +81,7 @@ CreateZStream (int compress, unsigned char gzip, read_write_func func)
        return NULL;
 #endif
 
-       z = (z_stream *) malloc (sizeof (z_stream));
-       if (z == NULL)
-               return NULL;
-       memset (z, 0, sizeof (z_stream));
+       z = z_new0 (z_stream, 1);
        if (compress) {
                retval = deflateInit2 (z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, gzip ? 31 : -15, 8, Z_DEFAULT_STRATEGY);
        } else {
@@ -71,41 +92,39 @@ CreateZStream (int compress, unsigned char gzip, read_write_func func)
                free (z);
                return NULL;
        }
-       result = malloc (sizeof (ZStream));
-       memset (result, 0, sizeof (ZStream));
+       z->zalloc = z_alloc;
+       z->zfree = z_free;
+       result = z_new0 (ZStream, 1);
        result->stream = z;
        result->func = func;
+       result->gchandle = gchandle;
        result->compress = compress;
-       result->buffer = (unsigned char *) malloc (BUFFER_SIZE);
-       if (result->buffer == NULL) {
-               free (result);
-               if (compress) {
-                       deflateEnd (z);
-               } else {
-                       inflateEnd (z);
-               }
-               free (z);
-               return NULL;
-       }
-       memset (result->buffer, 0, BUFFER_SIZE);
+       result->buffer = z_new (guchar, BUFFER_SIZE);
+       result->stream->next_out = result->buffer;
+       result->stream->avail_out = BUFFER_SIZE;
+       result->stream->total_in = 0;
        return result;
 }
 
-int
+gint
 CloseZStream (ZStream *zstream)
 {
-       int status;
-       int flush_status;
+       gint status;
+       gint flush_status;
 
        if (zstream == NULL)
                return ARGUMENT_ERROR;
 
        status = 0;
        if (zstream->compress) {
-               status = deflate (zstream->stream, Z_FINISH);
-               flush_status = Flush (zstream);
-               if (status == Z_OK || status == Z_STREAM_END)
-                       status = flush_status;
+               if (zstream->stream->total_in > 0) {
+                       do {
+                               status = deflate (zstream->stream, Z_FINISH);
+                               flush_status = flush_internal (zstream, TRUE);
+                       } 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);
@@ -117,37 +136,50 @@ CloseZStream (ZStream *zstream)
        return status;
 }
 
-static int
+static gint
 write_to_managed (ZStream *stream)
 {
-       int n;
+       gint n;
        z_stream *zs;
 
        zs = stream->stream;
        if (zs->avail_out != BUFFER_SIZE) {
-               n = stream->func (stream->buffer, BUFFER_SIZE - zs->avail_out);
+               n = stream->func (stream->buffer, BUFFER_SIZE - zs->avail_out, stream->gchandle);
                zs->next_out = stream->buffer;
-               zs->avail_out =  BUFFER_SIZE;
+               zs->avail_out = BUFFER_SIZE;
                if (n < 0)
                        return IO_ERROR;
        }
        return 0;
 }
 
-int
-Flush (ZStream *stream)
+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);
 }
 
-int
-ReadZStream (ZStream *stream, unsigned char *buffer, int length)
+gint
+Flush (ZStream *stream)
 {
-       int n;
-       int status;
+       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)
@@ -161,27 +193,34 @@ ReadZStream (ZStream *stream, unsigned char *buffer, int length)
        zs->avail_out = length;
        while (zs->avail_out > 0) {
                if (zs->avail_in == 0) {
-                       n = stream->func (stream->buffer, BUFFER_SIZE);
+                       n = stream->func (stream->buffer, BUFFER_SIZE, stream->gchandle);
+                       stream->total_in += n;
                        if (n <= 0) {
                                stream->eof = TRUE;
-                               break;
                        }
                        zs->next_in = stream->buffer;
-                       zs->avail_in = n;
+                       zs->avail_in = n < 0 ? 0 : n;
                }
 
+               if (zs->avail_in == 0 && (zs->total_in == 0 || stream->total_in == zs->total_in))
+                       return Z_STREAM_END;
+
                status = inflate (stream->stream, Z_SYNC_FLUSH);
-               if (status != Z_OK && status != Z_STREAM_END)
+               if (status == Z_STREAM_END) {
+                       stream->eof = TRUE;
+                       break;
+               } else if (status != Z_OK) {
                        return status;
+               }
        }
        return length - zs->avail_out;
 }
 
-int
-WriteZStream (ZStream *stream, unsigned char *buffer, int length)
+gint
+WriteZStream (ZStream *stream, guchar *buffer, gint length)
 {
-       int n;
-       int status;
+       gint n;
+       gint status;
        z_stream *zs;
 
        if (stream == NULL || buffer == NULL || length < 0)
@@ -198,7 +237,7 @@ WriteZStream (ZStream *stream, unsigned char *buffer, int length)
                        zs->next_out = stream->buffer;
                        zs->avail_out = BUFFER_SIZE;
                }
-               status = deflate (stream->stream, Z_SYNC_FLUSH);
+               status = deflate (stream->stream, Z_NO_FLUSH);
                if (status != Z_OK && status != Z_STREAM_END)
                        return status;