/* * Original version by Jean-loup Gailly. Modified for use by the * Erlang run-time system and efile_driver; names of all external * functions changed to avoid conflicts with the official gzio.c file. * * gzio.c -- IO on .gz files * Copyright (C) 1995-1996 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* %ExternalCopyright% */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include /* ssize_t on Mac OS X */ #include #ifdef HAVE_UNISTD_H #include #endif #include #include "erl_driver.h" #include "sys.h" #include "gzio_zutil.h" #include "erl_zlib.h" #include "gzio.h" static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ /* =========================================================================== Uncompresses the buffer given and returns a pointer to a binary. If the buffer was not compressed with gzip, the buffer contents will be copied unchanged into the binary. If a `gzip' header was found, but there were subsequent errors, a NULL pointer is returned. */ ErlDrvBinary* erts_gzinflate_buffer(char* start, uLong size) { ErlDrvBinary* bin; ErlDrvBinary* bin2; z_stream zstr; unsigned char* bptr; /* * Check for the magic bytes beginning a GZIP header. */ bptr = (unsigned char *) start; if (size < 2 || bptr[0] != gz_magic[0] || bptr[1] != gz_magic[1]) { /* No GZIP header -- just copy the data into a new binary */ if ((bin = driver_alloc_binary(size)) == NULL) { return NULL; } memcpy(bin->orig_bytes, start, size); return bin; } /* * The magic bytes for a GZIP header are there. Now try to decompress. * It is an error if the GZIP header is not correct. */ zstr.next_in = (unsigned char*) start; zstr.avail_in = size; erl_zlib_alloc_init(&zstr); size *= 2; if ((bin = driver_alloc_binary(size)) == NULL) { return NULL; } if (inflateInit2(&zstr, 15+16) != Z_OK) { /* Decode GZIP format */ driver_free(bin); return NULL; } for (;;) { int status; zstr.next_out = (unsigned char *) bin->orig_bytes + zstr.total_out; zstr.avail_out = size - zstr.total_out; status = inflate(&zstr, Z_NO_FLUSH); if (status == Z_OK) { size *= 2; if ((bin2 = driver_realloc_binary(bin, size)) == NULL) { error: driver_free_binary(bin); inflateEnd(&zstr); return NULL; } bin = bin2; } else if (status == Z_STREAM_END) { if ((bin2 = driver_realloc_binary(bin, zstr.total_out)) == NULL) { goto error; } inflateEnd(&zstr); return bin2; } else { goto error; } } } /* =========================================================================== Compresses the buffer given and returns a pointer to a binary. A NULL pointer is returned if any error occurs. Writes a gzip header as well. */ #define GZIP_HD_SIZE 10 #define GZIP_TL_SIZE 8 #define GZIP_X_SIZE (GZIP_HD_SIZE+GZIP_TL_SIZE) ErlDrvBinary* erts_gzdeflate_buffer(char* start, uLong size) { z_stream c_stream; /* compression stream */ ErlDrvBinary* bin; ErlDrvBinary* bin2; uLong crc; /* crc32 of uncompressed data */ uLong szIn; Byte* ptr; int comprLen = size + (size/1000) + 1 + 12; /* see zlib.h */ crc = crc32(0L, Z_NULL, 0); erl_zlib_alloc_init(&c_stream); if (deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, 0) != Z_OK) return NULL; if ((bin = driver_alloc_binary(comprLen+GZIP_X_SIZE)) == NULL) return NULL; sprintf(bin->orig_bytes, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); c_stream.next_out = ((Byte*) bin->orig_bytes)+GZIP_HD_SIZE; c_stream.avail_out = (uInt) bin->orig_size - GZIP_HD_SIZE; c_stream.next_in = (Byte*) start; c_stream.avail_in = (uInt) size; if (deflate(&c_stream, Z_FINISH) != Z_STREAM_END) { driver_free_binary(bin); return NULL; } crc = crc32(crc, (unsigned char*)start, size); ptr = c_stream.next_out; szIn = c_stream.total_in; *ptr++ = (crc & 0xff); crc >>= 8; *ptr++ = (crc & 0xff); crc >>= 8; *ptr++ = (crc & 0xff); crc >>= 8; *ptr++ = (crc & 0xff); crc >>= 8; *ptr++ = (szIn & 0xff); szIn >>= 8; *ptr++ = (szIn & 0xff); szIn >>= 8; *ptr++ = (szIn & 0xff); szIn >>= 8; *ptr++ = (szIn & 0xff); szIn >>= 8; if (deflateEnd(&c_stream) != Z_OK) { driver_free_binary(bin); return NULL; } size = ptr - (Byte*)bin->orig_bytes; if ((bin2 = driver_realloc_binary(bin, size)) == NULL) driver_free_binary(bin); return bin2; }