diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/emulator/drivers/common/zlib_drv.c | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/emulator/drivers/common/zlib_drv.c')
-rw-r--r-- | erts/emulator/drivers/common/zlib_drv.c | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/erts/emulator/drivers/common/zlib_drv.c b/erts/emulator/drivers/common/zlib_drv.c new file mode 100644 index 0000000000..723efeaa13 --- /dev/null +++ b/erts/emulator/drivers/common/zlib_drv.c @@ -0,0 +1,650 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2003-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* + * ZLib interface for erlang + * + */ +#include <stdio.h> +#include <zlib.h> +#include <errno.h> +#include <string.h> + +#include "erl_driver.h" + + +#define DEFLATE_INIT 1 +#define DEFLATE_INIT2 2 +#define DEFLATE_SETDICT 3 +#define DEFLATE_RESET 4 +#define DEFLATE_END 5 +#define DEFLATE_PARAMS 6 +#define DEFLATE 7 + +#define INFLATE_INIT 8 +#define INFLATE_INIT2 9 +#define INFLATE_SETDICT 10 +#define INFLATE_SYNC 11 +#define INFLATE_RESET 12 +#define INFLATE_END 13 +#define INFLATE 14 + +#define CRC32_0 15 +#define CRC32_1 16 +#define CRC32_2 17 + +#define SET_BUFSZ 18 +#define GET_BUFSZ 19 +#define GET_QSIZE 20 + +#define ADLER32_1 21 +#define ADLER32_2 22 + +#define CRC32_COMBINE 23 +#define ADLER32_COMBINE 24 + +#define DEFAULT_BUFSZ 4000 + +static int zlib_init(void); +static ErlDrvData zlib_start(ErlDrvPort port, char* buf); +static void zlib_stop(ErlDrvData e); +static int zlib_ctl(ErlDrvData drv_data, unsigned int command, char *buf, + int len, char **rbuf, int rlen); +static void zlib_outputv(ErlDrvData drv_data, ErlIOVec *ev); + +ErlDrvEntry zlib_driver_entry = { + zlib_init, + zlib_start, + zlib_stop, + NULL, /* output */ + NULL, /* ready_input */ + NULL, /* ready_output */ + "zlib_drv", + NULL, /* finish */ + NULL, /* handle */ + zlib_ctl, + NULL, /* timeout */ + zlib_outputv, + NULL, /* read_async */ + NULL, /* flush */ + NULL, /* call */ + NULL, /* event */ + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL, /* handle2 */ + NULL, /* process_exit */ +}; + +typedef enum { + ST_NONE = 0, + ST_DEFLATE = 1, + ST_INFLATE = 2 +} ZLibState; + + +typedef struct { + z_stream s; + ZLibState state; + ErlDrvBinary* bin; + int binsz; + int binsz_need; + uLong crc; + int inflate_eos_seen; + int want_crc; /* 1 if crc is calculated on clear text */ + ErlDrvPort port; /* the associcated port */ +} ZLibData; + +static int zlib_inflate(ZLibData* d, int flush); +static int zlib_deflate(ZLibData* d, int flush); + +#if defined(_OSE_) || defined(__WIN32__) +static int i32(char* buf) +#else +static inline int i32(char* buf) +#endif +{ + return (int) ( + (((int)((unsigned char*)buf)[0]) << 24) | + (((int)((unsigned char*)buf)[1]) << 16) | + (((int)((unsigned char*)buf)[2]) << 8) | + (((int)((unsigned char*)buf)[3]) << 0)); +} + +static char* zlib_reason(int code, int* err) +{ + switch(code) { + case Z_OK: + *err = 0; + return "ok"; + case Z_STREAM_END: + *err = 0; + return "stream_end"; + case Z_ERRNO: + *err = 1; + return erl_errno_id(errno); + case Z_STREAM_ERROR: + *err = 1; + return "stream_error"; + case Z_DATA_ERROR: + *err = 1; + return "data_error"; + case Z_MEM_ERROR: + *err = 1; + return "mem_error"; + case Z_BUF_ERROR: + *err = 1; + return "buf_error"; + case Z_VERSION_ERROR: + *err = 1; + return "version_error"; + default: + *err = 1; + return "unknown_error"; + } +} + + +static int zlib_return(int code, char** rbuf, int rlen) +{ + int msg_code = 0; /* 0=ok, 1=error */ + char* dst = *rbuf; + char* src; + int len = 0; + + src = zlib_reason(code, &msg_code); + *dst++ = msg_code; + rlen--; + len = 1; + + while((rlen > 0) && *src) { + *dst++ = *src++; + rlen--; + len++; + } + return len; +} + +static int zlib_value2(int msg_code, int value, char** rbuf, int rlen) +{ + char* dst = *rbuf; + + if (rlen < 5) { + return -1; + } + *dst++ = msg_code; + *dst++ = (value >> 24) & 0xff; + *dst++ = (value >> 16) & 0xff; + *dst++ = (value >> 8) & 0xff; + *dst++ = value & 0xff; + return 5; +} + +static int zlib_value(int value, char** rbuf, int rlen) +{ + return zlib_value2(2, value, rbuf, rlen); +} + +static int zlib_output_init(ZLibData* d) +{ + if (d->bin != NULL) + driver_free_binary(d->bin); + if ((d->bin = driver_alloc_binary(d->binsz_need)) == NULL) + return -1; + d->binsz = d->binsz_need; + d->s.next_out = (unsigned char*)d->bin->orig_bytes; + d->s.avail_out = d->binsz; + return 0; +} + +/* + * Send compressed or uncompressed data + * and restart output procesing + */ +static int zlib_output(ZLibData* d) +{ + if (d->bin != NULL) { + int len = d->binsz - d->s.avail_out; + if (len > 0) { + if (driver_output_binary(d->port, NULL, 0, d->bin, 0, len) < 0) + return -1; + } + driver_free_binary(d->bin); + d->bin = NULL; + d->binsz = 0; + } + return zlib_output_init(d); +} + +static int zlib_inflate(ZLibData* d, int flush) +{ + int res = Z_OK; + + if ((d->bin == NULL) && (zlib_output_init(d) < 0)) { + errno = ENOMEM; + return Z_ERRNO; + } + + while ((driver_sizeq(d->port) > 0) && (res != Z_STREAM_END)) { + int vlen; + SysIOVec* iov = driver_peekq(d->port, &vlen); + int len; + int possibly_more_output = 0; + + d->s.next_in = iov[0].iov_base; + d->s.avail_in = iov[0].iov_len; + while((possibly_more_output || (d->s.avail_in > 0)) && (res != Z_STREAM_END)) { + res = inflate(&d->s, Z_NO_FLUSH); + if (res == Z_NEED_DICT) { + /* Essential to eat the header bytes that zlib has looked at */ + len = iov[0].iov_len - d->s.avail_in; + driver_deq(d->port, len); + return res; + } + if (res == Z_BUF_ERROR) { + /* Was possible more output, but actually not */ + res = Z_OK; + } + else if (res < 0) { + return res; + } + if (d->s.avail_out != 0) { + possibly_more_output = 0; + } else { + if (d->want_crc) + d->crc = crc32(d->crc, (unsigned char*)d->bin->orig_bytes, + d->binsz - d->s.avail_out); + zlib_output(d); + possibly_more_output = 1; + } + } + len = iov[0].iov_len - d->s.avail_in; + driver_deq(d->port, len); + } + + if (d->want_crc) { + d->crc = crc32(d->crc, (unsigned char*) d->bin->orig_bytes, + d->binsz - d->s.avail_out); + } + zlib_output(d); + if (res == Z_STREAM_END) { + d->inflate_eos_seen = 1; + } + return res; +} + +static int zlib_deflate(ZLibData* d, int flush) +{ + int res = Z_OK; + + if ((d->bin == NULL) && (zlib_output_init(d) < 0)) { + errno = ENOMEM; + return Z_ERRNO; + } + + while ((driver_sizeq(d->port) > 0) && (res != Z_STREAM_END)) { + int vlen; + SysIOVec* iov = driver_peekq(d->port, &vlen); + int len; + + d->s.next_in = iov[0].iov_base; + d->s.avail_in = iov[0].iov_len; + + while((d->s.avail_in > 0) && (res != Z_STREAM_END)) { + if ((res = deflate(&d->s, Z_NO_FLUSH)) < 0) { + return res; + } + if (d->s.avail_out == 0) { + zlib_output(d); + } + } + len = iov[0].iov_len - d->s.avail_in; + if (d->want_crc) { + d->crc = crc32(d->crc, iov[0].iov_base, len); + } + driver_deq(d->port, len); + } + + if (flush != Z_NO_FLUSH) { + if ((res = deflate(&d->s, flush)) < 0) { + return res; + } + if (flush == Z_FINISH) { + while (d->s.avail_out < d->binsz) { + zlib_output(d); + if (res == Z_STREAM_END) { + break; + } + if ((res = deflate(&d->s, flush)) < 0) { + return res; + } + } + } else { + while (d->s.avail_out == 0) { + zlib_output(d); + if ((res = deflate(&d->s, flush)) < 0) { + return res; + } + } + if (d->s.avail_out < d->binsz) { + zlib_output(d); + } + } + } + return res; +} + + + +static void* zlib_alloc(void* data, unsigned int items, unsigned int size) +{ + return (void*) driver_alloc(items*size); +} + +static void zlib_free(void* data, void* addr) +{ + driver_free(addr); +} + +static int zlib_init() +{ + return 0; +} + +static ErlDrvData zlib_start(ErlDrvPort port, char* buf) +{ + ZLibData* d; + + if ((d = (ZLibData*) driver_alloc(sizeof(ZLibData))) == NULL) + return ERL_DRV_ERROR_GENERAL; + + memset(&d->s, 0, sizeof(z_stream)); + + d->s.zalloc = zlib_alloc; + d->s.zfree = zlib_free; + d->s.opaque = d; + d->s.data_type = Z_BINARY; + + d->port = port; + d->state = ST_NONE; + d->bin = NULL; + d->binsz = 0; + d->binsz_need = DEFAULT_BUFSZ; + d->crc = crc32(0L, Z_NULL, 0); + d->inflate_eos_seen = 0; + d->want_crc = 0; + return (ErlDrvData)d; +} + + +static void zlib_stop(ErlDrvData e) +{ + ZLibData* d = (ZLibData*)e; + + if (d->state == ST_DEFLATE) + deflateEnd(&d->s); + else if (d->state == ST_INFLATE) + inflateEnd(&d->s); + + if (d->bin != NULL) + driver_free_binary(d->bin); + + driver_free(d); +} + +static int zlib_ctl(ErlDrvData drv_data, unsigned int command, char *buf, + int len, char **rbuf, int rlen) +{ + ZLibData* d = (ZLibData*)drv_data; + int res; + + switch(command) { + case DEFLATE_INIT: + if (len != 4) goto badarg; + if (d->state != ST_NONE) goto badarg; + res = deflateInit(&d->s, i32(buf)); + if (res == Z_OK) { + d->state = ST_DEFLATE; + d->want_crc = 0; + d->crc = crc32(0L, Z_NULL, 0); + } + return zlib_return(res, rbuf, rlen); + + case DEFLATE_INIT2: { + int wbits; + + if (len != 20) goto badarg; + if (d->state != ST_NONE) goto badarg; + wbits = i32(buf+8); + res = deflateInit2(&d->s, i32(buf), i32(buf+4), wbits, + i32(buf+12), i32(buf+16)); + if (res == Z_OK) { + d->state = ST_DEFLATE; + d->want_crc = (wbits < 0); + d->crc = crc32(0L, Z_NULL, 0); + } + return zlib_return(res, rbuf, rlen); + } + + case DEFLATE_SETDICT: + if (d->state != ST_DEFLATE) goto badarg; + res = deflateSetDictionary(&d->s, (unsigned char*)buf, len); + if (res == Z_OK) { + return zlib_value(d->s.adler, rbuf, rlen); + } else { + return zlib_return(res, rbuf, rlen); + } + + case DEFLATE_RESET: + if (len != 0) goto badarg; + if (d->state != ST_DEFLATE) goto badarg; + driver_deq(d->port, driver_sizeq(d->port)); + res = deflateReset(&d->s); + return zlib_return(res, rbuf, rlen); + + case DEFLATE_END: + if (len != 0) goto badarg; + if (d->state != ST_DEFLATE) goto badarg; + driver_deq(d->port, driver_sizeq(d->port)); + res = deflateEnd(&d->s); + d->state = ST_NONE; + return zlib_return(res, rbuf, rlen); + + case DEFLATE_PARAMS: + if (len != 8) goto badarg; + if (d->state != ST_DEFLATE) goto badarg; + res = deflateParams(&d->s, i32(buf), i32(buf+4)); + return zlib_return(res, rbuf, rlen); + + case DEFLATE: + if (d->state != ST_DEFLATE) goto badarg; + if (len != 4) goto badarg; + res = zlib_deflate(d, i32(buf)); + return zlib_return(res, rbuf, rlen); + + case INFLATE_INIT: + if (len != 0) goto badarg; + if (d->state != ST_NONE) goto badarg; + res = inflateInit(&d->s); + if (res == Z_OK) { + d->state = ST_INFLATE; + d->inflate_eos_seen = 0; + d->want_crc = 0; + d->crc = crc32(0L, Z_NULL, 0); + } + return zlib_return(res, rbuf, rlen); + + case INFLATE_INIT2: { + int wbits; + + if (len != 4) goto badarg; + if (d->state != ST_NONE) goto badarg; + wbits = i32(buf); + res = inflateInit2(&d->s, wbits); + if (res == Z_OK) { + d->state = ST_INFLATE; + d->inflate_eos_seen = 0; + d->want_crc = (wbits < 0); + d->crc = crc32(0L, Z_NULL, 0); + } + return zlib_return(res, rbuf, rlen); + } + + case INFLATE_SETDICT: + if (d->state != ST_INFLATE) goto badarg; + res = inflateSetDictionary(&d->s, (unsigned char*)buf, len); + return zlib_return(res, rbuf, rlen); + + case INFLATE_SYNC: + if (d->state != ST_INFLATE) goto badarg; + if (len != 0) goto badarg; + if (driver_sizeq(d->port) == 0) { + res = Z_BUF_ERROR; + } else { + int vlen; + SysIOVec* iov = driver_peekq(d->port, &vlen); + + d->s.next_in = iov[0].iov_base; + d->s.avail_in = iov[0].iov_len; + res = inflateSync(&d->s); + } + return zlib_return(res, rbuf, rlen); + + case INFLATE_RESET: + if (d->state != ST_INFLATE) goto badarg; + if (len != 0) goto badarg; + driver_deq(d->port, driver_sizeq(d->port)); + res = inflateReset(&d->s); + d->inflate_eos_seen = 0; + return zlib_return(res, rbuf, rlen); + + case INFLATE_END: + if (d->state != ST_INFLATE) goto badarg; + if (len != 0) goto badarg; + driver_deq(d->port, driver_sizeq(d->port)); + res = inflateEnd(&d->s); + if (res == Z_OK && d->inflate_eos_seen == 0) { + res = Z_DATA_ERROR; + } + d->state = ST_NONE; + return zlib_return(res, rbuf, rlen); + + case INFLATE: + if (d->state != ST_INFLATE) goto badarg; + if (len != 4) goto badarg; + res = zlib_inflate(d, i32(buf)); + if (res == Z_NEED_DICT) { + return zlib_value2(3, d->s.adler, rbuf, rlen); + } else { + return zlib_return(res, rbuf, rlen); + } + + case GET_QSIZE: + return zlib_value(driver_sizeq(d->port), rbuf, rlen); + + case GET_BUFSZ: + return zlib_value(d->binsz_need, rbuf, rlen); + + case SET_BUFSZ: { + int need; + if (len != 4) goto badarg; + need = i32(buf); + if ((need < 16) || (need > 0x00ffffff)) + goto badarg; + if (d->binsz_need != need) { + d->binsz_need = need; + if (d->bin != NULL) { + if (d->s.avail_out == d->binsz) { + driver_free_binary(d->bin); + d->bin = NULL; + d->binsz = 0; + } + else + zlib_output(d); + } + } + return zlib_return(Z_OK, rbuf, rlen); + } + + case CRC32_0: + return zlib_value(d->crc, rbuf, rlen); + + case CRC32_1: { + uLong crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, (unsigned char*) buf, len); + return zlib_value(crc, rbuf, rlen); + } + + case CRC32_2: { + uLong crc; + if (len < 4) goto badarg; + crc = (unsigned int) i32(buf); + crc = crc32(crc, (unsigned char*) buf+4, len-4); + return zlib_value(crc, rbuf, rlen); + } + + case ADLER32_1: { + uLong adler = adler32(0L, Z_NULL, 0); + adler = adler32(adler, (unsigned char*) buf, len); + return zlib_value(adler, rbuf, rlen); + } + + case ADLER32_2: { + uLong adler; + if (len < 4) goto badarg; + adler = (unsigned int) i32(buf); + adler = adler32(adler, (unsigned char*) buf+4, len-4); + return zlib_value(adler, rbuf, rlen); + } + + case CRC32_COMBINE: { + uLong crc, crc1, crc2, len2; + if (len != 12) goto badarg; + crc1 = (unsigned int) i32(buf); + crc2 = (unsigned int) i32(buf+4); + len2 = (unsigned int) i32(buf+8); + crc = crc32_combine(crc1, crc2, len2); + return zlib_value(crc, rbuf, rlen); + } + + case ADLER32_COMBINE: { + uLong adler, adler1, adler2, len2; + if (len != 12) goto badarg; + adler1 = (unsigned int) i32(buf); + adler2 = (unsigned int) i32(buf+4); + len2 = (unsigned int) i32(buf+8); + adler = adler32_combine(adler1, adler2, len2); + return zlib_value(adler, rbuf, rlen); + } + } + + badarg: + errno = EINVAL; + return zlib_return(Z_ERRNO, rbuf, rlen); +} + + + +static void zlib_outputv(ErlDrvData drv_data, ErlIOVec *ev) +{ + ZLibData* d = (ZLibData*) drv_data; + + driver_enqv(d->port, ev, 0); +} |