#include "MonoPosixHelper.h" #include #include #include "../external/zlib/zlib.h" #include "vm/Exception.h" #define BUFFER_SIZE 4096 #define ARGUMENT_ERROR -10 #define IO_ERROR -11 typedef int32_t (*read_write_func)(intptr_t buffer, int32_t length, intptr_t gchandle); struct ZStream { z_stream *stream; uint8_t *buffer; read_write_func func; void *gchandle; uint8_t compress; uint8_t eof; uint32_t total_in; }; static int32_t write_to_managed(ZStream *stream) { int32_t n; z_stream *zs; zs = stream->stream; if (zs->avail_out != BUFFER_SIZE) { intptr_t buffer_ptr = reinterpret_cast(stream->buffer); intptr_t gchandle_ptr = reinterpret_cast(stream->gchandle); n = stream->func(buffer_ptr, BUFFER_SIZE - zs->avail_out, gchandle_ptr); zs->next_out = stream->buffer; zs->avail_out = BUFFER_SIZE; if (n < 0) return IO_ERROR; } return 0; } static int32_t flush_internal(ZStream *stream, bool is_final) { int32_t 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); } static void *z_alloc(void *opaque, uint32_t nitems, uint32_t item_size) { return calloc(nitems, item_size); } static void z_free(void *opaque, void *ptr) { free(ptr); } intptr_t CreateZStream(int32_t compress, uint8_t gzip, Il2CppMethodPointer func_ptr, intptr_t gchandle) { z_stream *z; int32_t retval; ZStream *result; intptr_t result_ptr = 0; read_write_func func = (read_write_func)func_ptr; if (func == NULL) return result_ptr; #if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1204) // Older versions of zlib do not support raw deflate or gzip return NULL; #endif z = (z_stream*)calloc(1, sizeof(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 result_ptr; } z->zalloc = z_alloc; z->zfree = z_free; result = (ZStream*)calloc(1, sizeof(ZStream)); result->stream = z; result->func = func; result->gchandle = reinterpret_cast(gchandle); result->compress = compress; result->buffer = (uint8_t*)malloc(BUFFER_SIZE * sizeof(uint8_t)); result->stream->next_out = result->buffer; result->stream->avail_out = BUFFER_SIZE; result->stream->total_in = 0; result_ptr = reinterpret_cast(result); return result_ptr; } int32_t CloseZStream(intptr_t zstream) { int32_t status; int32_t flush_status; ZStream *stream = reinterpret_cast(zstream); if (stream == NULL) return ARGUMENT_ERROR; status = 0; if (stream->compress) { if (stream->stream->total_in > 0) { do { status = deflate(stream->stream, Z_FINISH); flush_status = flush_internal(stream, true); } while (status == Z_OK); /* We want Z_STREAM_END or error here here */ if (status == Z_STREAM_END) status = flush_status; } deflateEnd(stream->stream); } else { inflateEnd(stream->stream); } free(stream->buffer); free(stream->stream); memset(stream, 0, sizeof(ZStream)); free(stream); return status; } int32_t Flush(intptr_t zstream) { ZStream *stream = (ZStream*)zstream; return flush_internal(stream, false); } int32_t ReadZStream(intptr_t zstream, intptr_t zbuffer, int32_t length) { int32_t n; int32_t status; z_stream *zs; ZStream *stream = (ZStream*)zstream; uint8_t *buffer = (uint8_t*)zbuffer; 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) { intptr_t buffer_ptr = reinterpret_cast(stream->buffer); intptr_t gchandle_ptr = reinterpret_cast(stream->gchandle); n = stream->func(buffer_ptr, BUFFER_SIZE, gchandle_ptr); if (n < 0) n = 0; // Even if avail_in reports zero, and we have no more data from the stream, // inflate may have more data to return. So we cannot break here and need to // keep calling inflate until it returns Z_STREAM_END. stream->total_in += n; zs->next_in = stream->buffer; zs->avail_in = n; } status = inflate(stream->stream, Z_SYNC_FLUSH); if (status == Z_STREAM_END) { stream->eof = 1; break; } else if (status == Z_BUF_ERROR && stream->total_in == zs->total_in) { if (zs->avail_in != 0) { stream->eof = 1; } break; } else if (status != Z_OK) { return status; } } return length - zs->avail_out; } int32_t WriteZStream(intptr_t zstream, intptr_t zbuffer, int32_t length) { int32_t n; int32_t status; z_stream *zs; ZStream *stream = (ZStream*)zstream; uint8_t *buffer = (uint8_t*)zbuffer; 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; } // The following methods are used by LinuxNetworkChange // Which the implementation for System.Net.NetworkInformation.NetworkChange on linux // These are here we throw a NotImplemented exception rather than getting an entry point not found // We could probably port this if we hard the time intptr_t CreateNLSocket() { IL2CPP_NOT_IMPLEMENTED(CreateNLSocket); NOT_SUPPORTED_IL2CPP(CreateNLSocket, Not implemented); return 0; } int32_t ReadEvents(intptr_t sock, intptr_t buffer, int32_t count, int32_t size) { IL2CPP_NOT_IMPLEMENTED(ReadEvents); NOT_SUPPORTED_IL2CPP(ReadEvents, Not implemented); return 0; } intptr_t CloseNLSocket(intptr_t sock) { IL2CPP_NOT_IMPLEMENTED(CloseNLSocket); NOT_SUPPORTED_IL2CPP(CloseNLSocket, Not implemented); return 0; }