#include #include #include #include "al.h" #include "sio.h" #include typedef struct { z_stream zs; int lvl; al_t *ibuf; char *obuf; size_t olen; al_t *al; void *u; } data_t; typedef struct { data_t input; data_t output; int inputlevel; int outputlevel; size_t inputsize; size_t outputsize; al_label_t data_label; } private_t; /* * create stage * * allocate private instance data */ static sio_rc_t zlib_init(sio_t *sio, void **up) { private_t *my; my = (private_t *)malloc(sizeof(private_t)); if (my == NULL) return SIO_ERR_MEM; my->inputsize = 0; my->outputsize = 0; my->inputlevel = 0; my->outputlevel = 0; sio_label(sio, SIO_LN_DATA, &my->data_label); *up = my; return SIO_OK; } /* * configure stage * * pass two void pointers */ static sio_rc_t zlib_configure(sio_t *sio, void *u, void *obj, void *val) { private_t *my = (private_t *)u; const char *name = (const char *)obj; if (!strcmp(name, "inputlevel")) { my->inputlevel = *(int *)val; } else if (!strcmp(name, "outputlevel")) { my->outputlevel = *(int *)val; } else if (!strcmp(name, "inputsize")) { my->inputsize = *(size_t *)val; } else if (!strcmp(name, "outputsize")) { my->outputsize = *(size_t *)val; } else { return SIO_ERR_ARG; } return SIO_OK; } /* * destroy stage */ static sio_rc_t zlib_cleanup(sio_t *sio, void *u) { private_t *my = (private_t *)u; free(my); return SIO_OK; } static int zlib_alloc_data(private_t *my, data_t *dt) { al_rc_t arc; int zrc; dt->u = (void *)my; if (dt->lvl >= 0) zrc = deflateInit(&dt->zs, dt->lvl); else zrc = inflateInit(&dt->zs); if (zrc != Z_OK) return -1; arc = al_create(&dt->ibuf); if (arc != AL_OK) return -1; dt->obuf = NULL; return 0; } static void zlib_free_data(data_t *dt) { al_destroy(dt->ibuf); dt->ibuf = NULL; if (dt->obuf != NULL) { free(dt->obuf); dt->obuf = NULL; } if (dt->lvl >= 0) deflateEnd(&dt->zs); else inflateEnd(&dt->zs); } static sio_rc_t zlib_openr(sio_t *sio, al_t *al, void *u) { private_t *my = (private_t *)u; if (my->inputsize <= 0) return SIO_ERR_ARG; my->input.lvl = my->inputlevel; my->input.olen = my->inputsize; my->input.al = al; if (zlib_alloc_data(my, &my->input)) return SIO_ERR_INT; return SIO_OK; } static sio_rc_t zlib_closer(sio_t *sio, al_t *al, void *u) { private_t *my = (private_t *)u; zlib_free_data(&my->input); return SIO_OK; } static sio_rc_t zlib_openw(sio_t *sio, al_t *al, void *u) { private_t *my = (private_t *)u; if (my->outputsize <= 0) return SIO_ERR_ARG; my->output.lvl = my->outputlevel; my->output.olen = my->outputsize; my->output.al = al; if (zlib_alloc_data(my, &my->output)) return SIO_ERR_INT; return SIO_OK; } static sio_rc_t zlib_closew(sio_t *sio, al_t *al, void *u) { private_t *my = (private_t *)u; if (my->output.lvl >= 0) deflateEnd(&my->output.zs); else inflateEnd(&my->output.zs); free(my->output.obuf); my->output.obuf = NULL; al_destroy(my->output.ibuf); my->output.ibuf = NULL; return SIO_OK; } static void freezlibbuf(char *p, size_t n, void *u) { free(p); } /* * buffer logic * * gather data from producer * if buffer size reached or label changes then push data to consumer * (read -> downstream, write -> upstream) * * buffer size depends on label type * */ static al_rc_t zlib_inout_cb(al_chunk_t *alcp, void *u) { data_t *dt = (data_t *)u; private_t *my = (private_t *)dt->u; z_stream *zs = &dt->zs; int level = dt->lvl; int zrc, flush; al_rc_t arc; char *p; size_t n; arc = AL_OK; p = al_chunk_ptr(alcp, 0); n = al_chunk_len(alcp); if (al_same_label(alcp, my->data_label)) { zs->next_in = (unsigned char *)p; zs->avail_in = n; flush = 0; } else { zs->next_in = (unsigned char *)""; zs->avail_in = 0; flush = Z_FINISH; } do { if (zs->avail_out == 0) { if (dt->obuf == NULL && (dt->obuf = malloc(dt->olen)) == NULL) { arc = AL_ERR_INT; break; } zs->next_out = (unsigned char *)dt->obuf; zs->avail_out = dt->olen; zs->total_out = 0; } if (level >= 0) zrc = deflate(zs, flush); else zrc = inflate(zs, flush); if (zrc != Z_OK && zrc != Z_STREAM_END) { arc = AL_ERR_INT; break; } if (zs->avail_out == 0 || zrc == Z_STREAM_END || flush) { if (zs->total_out > 0) { arc = al_attach_buffer(dt->al, dt->obuf, zs->total_out, my->data_label, freezlibbuf, NULL); dt->obuf = NULL; zs->avail_out = 0; if (arc != AL_OK) break; } } } while (zs->avail_in > 0 || (zrc == Z_OK && zs->avail_out == 0)); if (arc != AL_OK) return arc; if (flush) arc = al_append_bytes(dt->al, p, n, al_chunk_label(alcp)); return arc; } static sio_rc_t zlib_inout(sio_t *sio, al_t *al, private_t *my, data_t *dt) { al_rc_t arc; arc = al_splice(al, 0, al_bytes(al), NULL, dt->ibuf); if (arc != AL_OK) return SIO_ERR_INT; arc = al_traverse_cb(dt->ibuf, 0, al_bytes(dt->ibuf), AL_FORWARD, NULL, zlib_inout_cb, (void *)dt); if (arc != AL_OK) return SIO_ERR_INT; arc = al_splice(dt->ibuf, 0, al_bytes(dt->ibuf), NULL, NULL); if (arc != AL_OK) return SIO_ERR_INT; return SIO_OK; } static sio_rc_t zlib_input(sio_t *sio, al_t *al, void *u, sio_rc_t orc) { private_t *my = (private_t *)u; return zlib_inout(sio, al, my, &my->input); } static sio_rc_t zlib_output(sio_t *sio, al_t *al, void *u, sio_rc_t orc) { private_t *my = (private_t *)u; return zlib_inout(sio, al, my, &my->output); } sio_module_t sio_module_zlib = { "zlib", zlib_init, zlib_configure, zlib_cleanup, zlib_openr, zlib_closer, zlib_openw, zlib_closew, zlib_input, zlib_output, NULL };