//vio_gzip.c: /* * Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2011-2012 * * This file is part of libroar a part of RoarAudio, * a cross-platform sound system for both, home and professional use. * See README for details. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 * as published by the Free Software Foundation. * * libroar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * NOTE for everyone want's to change something and send patches: * read README and HACKING! There a addition information on * the license of this document you need to read before you send * any patches. * * NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc * or libpulse*: * The libs libroaresd, libroararts and libroarpulse link this lib * and are therefore GPL. Because of this it may be illigal to use * them with any software that uses libesd, libartsc or libpulse*. */ #include "libroar.h" #ifdef ROAR_HAVE_LIBZ #include struct roar_vio_gzip { struct roar_vio_calls * next; struct roar_buffer * outbuf, * inbuf; int compressor_error, decompressor_error; int compressor_flush; int compressor_used; z_stream compressor, decompressor; gz_header gz_compressorheader, gz_decompressorheader; }; static void * _zalloc(voidpf opaque, uInt items, uInt size) { (void)opaque; return roar_mm_calloc(items, size); } static void _zfree(voidpf opaque, voidpf address) { (void)opaque; roar_mm_free(address); } static void _set_error (int error) { int raerror = ROAR_ERROR_UNKNOWN; switch (error) { case Z_OK: raerror = ROAR_ERROR_NONE; break; case Z_STREAM_END: raerror = ROAR_ERROR_NODATA; break; case Z_NEED_DICT: raerror = ROAR_ERROR_INVAL; break; case Z_ERRNO: roar_err_from_errno(); break; case Z_STREAM_ERROR: raerror = ROAR_ERROR_INVAL; break; case Z_DATA_ERROR: raerror = ROAR_ERROR_BADCKSUM; break; case Z_MEM_ERROR: raerror = ROAR_ERROR_NOMEM; break; case Z_BUF_ERROR: raerror = ROAR_ERROR_FAULT; break; case Z_VERSION_ERROR: raerror = ROAR_ERROR_BADVERSION; break; } ROAR_DBG("_set_error(error=%i): raerror=%s", error, roar_error2str(raerror)); roar_err_set(raerror); } static int _init(struct roar_vio_gzip * self, struct roar_vio_calls * dst, int level, int gzip) { int ret; ROAR_DBG("_init(self=%p, dst=%p, level=%i, gzip=%i) = ?", self, dst, level, gzip); if ( self == NULL ) { roar_err_set(ROAR_ERROR_FAULT); return -1; } ROAR_DBG("_init(self=%p, dst=%p, level=%i, gzip=%i) = ?", self, dst, level, gzip); if ( level == -1 ) { level = Z_DEFAULT_COMPRESSION; } memset(self, 0, sizeof(struct roar_vio_gzip)); self->next = dst; self->outbuf = NULL; self->inbuf = NULL; self->compressor_error = ROAR_ERROR_NONE; self->decompressor_error = ROAR_ERROR_NONE; self->compressor_flush = Z_SYNC_FLUSH; self->compressor_used = 0; self->compressor.zalloc = _zalloc; self->compressor.zfree = _zfree; self->compressor.opaque = NULL; self->compressor.next_in = NULL; self->compressor.next_out = NULL; self->decompressor.zalloc = _zalloc; self->decompressor.zfree = _zfree; self->decompressor.opaque = NULL; self->decompressor.next_in = NULL; self->decompressor.next_out = NULL; ROAR_DBG("_init(self=%p, dst=%p, level=%i, gzip=%i) = ?", self, dst, level, gzip); // for the magic numbers: see the zlib.h header file. There are no const for them. if ( (ret = deflateInit2(&(self->compressor), level, Z_DEFLATED, 15 + (gzip ? 16 : 0), 8, Z_DEFAULT_STRATEGY)) != Z_OK ) { _set_error(ret); return -1; } ROAR_DBG("_init(self=%p, dst=%p, level=%i, gzip=%i) = ?", self, dst, level, gzip); if ( (ret = inflateInit2(&(self->decompressor), 15+32)) != Z_OK ) { deflateEnd(&(self->compressor)); _set_error(ret); return -1; } ROAR_DBG("_init(self=%p, dst=%p, level=%i, gzip=%i) = ?", self, dst, level, gzip); // gzheader is descriped in RFC1952. memset(&(self->gz_compressorheader), 0, sizeof(self->gz_compressorheader)); self->gz_compressorheader.os = 255; // os='unknown' self->gz_compressorheader.hcrc = 0; // for compatibility memset(&(self->gz_decompressorheader), 0, sizeof(self->gz_decompressorheader)); self->gz_decompressorheader.os = 255; // os='unknown' self->gz_decompressorheader.hcrc = 0; // for compatibility if ( gzip ) { if ( (ret = deflateSetHeader(&(self->compressor), &(self->gz_compressorheader))) != Z_OK ) { deflateEnd(&(self->compressor)); inflateEnd(&(self->decompressor)); _set_error(ret); return -1; } } ROAR_DBG("_init(self=%p, dst=%p, level=%i, gzip=%i) = 0", self, dst, level, gzip); return 0; } static ssize_t roar_vio_zlib_read (struct roar_vio_calls * vio, void *buf, size_t count) { struct roar_vio_gzip * self = vio->inst; struct roar_buffer * leftbuf; void * leftbufdata; size_t buflen; ssize_t len; size_t have = 0; int ret; unsigned char inbuf[1024]; ROAR_DBG("roar_vio_zlib_read(vio=%p, buf=%p, count=%llu) = ?", vio, buf, (long long unsigned)count); if ( self->decompressor_error != ROAR_ERROR_NONE ) { roar_err_set(self->decompressor_error); return -1; } ROAR_DBG("roar_vio_zlib_read(vio=%p, buf=%p, count=%llu) = ?", vio, buf, (long long unsigned)count); while ( self->inbuf != NULL ) { ROAR_DBG("roar_vio_zlib_read(vio=%p, buf=%p, count=%llu): self->inbuf=%p", vio, buf, (long long unsigned)count, self->inbuf); if ( roar_buffer_get_data(self->inbuf, &leftbufdata) == -1 ) return -1; if ( roar_buffer_get_len(self->inbuf, &buflen) == -1 ) return -1; ROAR_DBG("roar_vio_zlib_read(vio=%p, buf=%p, count=%llu) = ?", vio, buf, (long long unsigned)count, self->inbuf); self->decompressor.next_out = buf; self->decompressor.avail_out = count; self->decompressor.next_in = leftbufdata; self->decompressor.avail_in = buflen; ROAR_DBG("roar_vio_zlib_read(vio=%p, buf=%p, count=%llu): in: %llu Bytes, out: count Bytes.", vio, buf, (long long unsigned)count, (long long unsigned int)buflen); ret = inflate(&(self->decompressor), Z_NO_FLUSH); ROAR_DBG("roar_vio_zlib_read(vio=%p, buf=%p, count=%llu): inflate() returned %i", vio, buf, (long long unsigned)count, ret); len = count - self->decompressor.avail_out; if ( ret == Z_STREAM_END ) { count = len; ret = Z_OK; } if ( ret != Z_OK ) { if ( len == 0 ) { _set_error(ret); return -1; } else { have += len; return have; } } buf += len; have += len; count -= len; if ( len == (ssize_t)buflen ) { ret = roar_buffer_next(&(self->inbuf)); } else { ret = roar_buffer_set_offset(self->inbuf, len); } if ( ret == -1 ) { roar_err_set((self->decompressor_error = ROAR_ERROR_LOSTSYNC)); return -1; } if ( count == 0 ) return have; } ROAR_DBG("roar_vio_zlib_read(vio=%p, buf=%p, count=%llu) = ?", vio, buf, (long long unsigned)count); self->decompressor.next_out = buf; self->decompressor.avail_out = count; self->decompressor.next_in = NULL; self->decompressor.avail_in = 0; while (self->decompressor.avail_out > 0) { ROAR_DBG("roar_vio_zlib_read(vio=%p, buf=%p, count=%llu): generating new output...", vio, buf, (long long unsigned)count); if ( self->decompressor.avail_in == 0 ) { ROAR_DBG("roar_vio_zlib_read(vio=%p, buf=%p, count=%llu): reading more data", vio, buf, (long long unsigned)count); len = roar_vio_read(self->next, inbuf, sizeof(inbuf)); if ( len == -1 ) { have += count - self->decompressor.avail_out; if ( have == 0 ) { // error is still set (by roar_vio_read()). return -1; } else { return 0; } } else if ( len == 0 ) { break; } ROAR_DBG("roar_vio_zlib_read(vio=%p, buf=%p, count=%llu): len=%llu", vio, buf, (long long unsigned)count, (long long unsigned)len); self->decompressor.next_in = inbuf; self->decompressor.avail_in = len; } ret = inflate(&(self->decompressor), Z_NO_FLUSH); ROAR_DBG("roar_vio_zlib_read(vio=%p, buf=%p, count=%llu): inflate() returned %i", vio, buf, (long long unsigned)count, ret); if ( ret == Z_STREAM_END ) ret = Z_OK; if ( ret != Z_OK ) { if ( (count - self->decompressor.avail_out) == 0 ) { _set_error(ret); return -1; } else { break; } } ROAR_DBG("roar_vio_zlib_read(vio=%p, buf=%p, count=%llu) = ?", vio, buf, (long long unsigned)count); } if ( self->decompressor.avail_in != 0 ) { if ( roar_buffer_new_data(&leftbuf, self->decompressor.avail_in, &leftbufdata) == -1 ) { roar_err_set((self->decompressor_error = ROAR_ERROR_LOSTSYNC)); return -1; } memcpy(leftbufdata, self->decompressor.next_in, self->decompressor.avail_in); if ( self->inbuf == NULL ) { self->inbuf = leftbuf; } else { if ( roar_buffer_moveinto(self->inbuf, &leftbuf) == -1 ) { roar_buffer_free(leftbuf); roar_err_set((self->decompressor_error = ROAR_ERROR_LOSTSYNC)); return -1; } } } have += count - self->decompressor.avail_out; ROAR_DBG("roar_vio_zlib_read(vio=%p, buf=%p, count=%llu) = %llu", vio, buf, (long long unsigned)count, (long long unsigned)have); return have; } static ssize_t roar_vio_zlib_write (struct roar_vio_calls * vio, void *buf, size_t count) { struct roar_vio_gzip * self = vio->inst; struct roar_buffer * leftbuf; void * leftbufdata; size_t outlen; size_t buflen; char outbuf[1024]; ssize_t len; int ret; ROAR_DBG("roar_vio_zlib_write(vio=%p, buf=%p, count=%llu) = ?", vio, buf, (long long unsigned)count); self->compressor_used = 1; if ( self->compressor_error != ROAR_ERROR_NONE ) { roar_err_set(self->compressor_error); return -1; } ROAR_DBG("roar_vio_zlib_write(vio=%p, buf=%p, count=%llu) = ?", vio, buf, (long long unsigned)count); while (self->outbuf != NULL) { if ( roar_buffer_get_data(self->outbuf, &leftbufdata) == -1 ) return -1; if ( roar_buffer_get_len(self->outbuf, &buflen) == -1 ) return -1; len = roar_vio_write(self->next, leftbufdata, buflen); if ( len == -1 ) { return -1; } else if ( len == (ssize_t)buflen ) { if ( roar_buffer_next(&(self->outbuf)) == -1 ) { roar_err_set((self->compressor_error = ROAR_ERROR_LOSTSYNC)); return -1; } } else { if ( roar_buffer_set_offset(self->outbuf, len) == -1 ) { roar_err_set((self->compressor_error = ROAR_ERROR_LOSTSYNC)); return -1; } return 0; } } ROAR_DBG("roar_vio_zlib_write(vio=%p, buf=%p, count=%llu) = ?", vio, buf, (long long unsigned)count); self->compressor.next_in = buf; self->compressor.avail_in = count; while (self->compressor.avail_in || count == 0) { self->compressor.next_out = (unsigned char *)outbuf; self->compressor.avail_out = sizeof(outbuf); outlen = self->compressor.total_out; ret = deflate(&(self->compressor), count == 0 ? self->compressor_flush : Z_NO_FLUSH); outlen = self->compressor.total_out - outlen; ROAR_DBG("roar_vio_zlib_write(vio=%p, buf=%p, count=%llu): deflate() returned %i", vio, buf, (long long unsigned)count, ret); ROAR_DBG("roar_vio_zlib_write(vio=%p, buf=%p, count=%llu): outlen=%llu", vio, buf, (long long unsigned)count, (long long unsigned int)outlen); if ( count != 0 && ret != Z_OK ) { len = count - self->compressor.avail_in; if ( len != 0 ) { return len; } else { _set_error(ret); return -1; } } if ( outlen != 0 ) { len = roar_vio_write(self->next, outbuf, outlen); if ( len < (ssize_t)outlen ) { if ( roar_buffer_new_data(&leftbuf, outlen - len, &leftbufdata) == -1 ) { roar_err_set((self->compressor_error = ROAR_ERROR_LOSTSYNC)); return -1; } memcpy(leftbufdata, outbuf+len, outlen - len); if ( self->outbuf == NULL ) { self->outbuf = leftbuf; } else { if ( roar_buffer_moveinto(self->outbuf, &leftbuf) == -1 ) { roar_buffer_free(leftbuf); roar_err_set((self->compressor_error = ROAR_ERROR_LOSTSYNC)); return -1; } } break; } } ROAR_DBG("roar_vio_zlib_write(vio=%p, buf=%p, count=%llu) = ?", vio, buf, (long long unsigned)count); if ( count == 0 && ( (self->compressor_flush == Z_FINISH && ret == Z_STREAM_END) || (self->compressor_flush != Z_FINISH && ret == Z_BUF_ERROR) ) ) return 0; ROAR_DBG("roar_vio_zlib_write(vio=%p, buf=%p, count=%llu) = ?", vio, buf, (long long unsigned)count); } len = count - self->compressor.avail_in; ROAR_DBG("roar_vio_zlib_write(vio=%p, buf=%p, count=%llu) = %llu", vio, buf, (long long unsigned)count, (long long unsigned int)len); return len; } static roar_off_t roar_vio_zlib_lseek (struct roar_vio_calls * vio, roar_off_t offset, int whence) { (void)vio, (void)offset, (void)whence; roar_err_set(ROAR_ERROR_NOSYS); return (roar_off_t)-1; } static int roar_vio_zlib_sync (struct roar_vio_calls * vio) { struct roar_vio_gzip * self = vio->inst; if ( self->compressor_used ) if ( self->compressor_error == ROAR_ERROR_NONE ) if ( roar_vio_zlib_write(vio, NULL, 0) == -1 ) return -1; roar_err_set(ROAR_ERROR_NOSYS); return -1; } static int roar_vio_zlib_ctl (struct roar_vio_calls * vio, roar_vio_ctl_t cmd, void * data) { struct roar_vio_gzip * self; if ( vio == NULL ) { roar_err_set(ROAR_ERROR_FAULT); return -1; } self = vio->inst; switch (cmd) { case ROAR_VIO_CTL_GET_NAME: if ( data == NULL ) return -1; *(char**)data = "zlib"; return 0; break; case ROAR_VIO_CTL_GET_NEXT: *(struct roar_vio_calls **)data = self->next; return 0; break; case ROAR_VIO_CTL_GET_FH: case ROAR_VIO_CTL_GET_READ_FH: case ROAR_VIO_CTL_GET_WRITE_FH: case ROAR_VIO_CTL_GET_SELECT_FH: case ROAR_VIO_CTL_GET_SELECT_READ_FH: case ROAR_VIO_CTL_GET_SELECT_WRITE_FH: roar_err_set(ROAR_ERROR_NOTSUP); return -1; break; case ROAR_VIO_CTL_NONBLOCK: return roar_vio_ctl(self->next, cmd, data); break; default: roar_err_set(ROAR_ERROR_BADRQC); return -1; } } static int roar_vio_zlib_close (struct roar_vio_calls * vio) { struct roar_vio_gzip * self; if ( vio == NULL ) { roar_err_set(ROAR_ERROR_FAULT); return -1; } self = vio->inst; self->compressor_flush = Z_FINISH; roar_vio_zlib_sync(vio); deflateEnd(&(self->compressor)); inflateEnd(&(self->decompressor)); if ( self->outbuf != NULL ) roar_buffer_free(self->outbuf); if ( self->inbuf != NULL ) roar_buffer_free(self->inbuf); roar_mm_free(self); return 0; } int roar_vio_open_zlib(struct roar_vio_calls * calls, struct roar_vio_calls * dst, int level, int gzip) { struct roar_vio_gzip * self; int err; ROAR_DBG("roar_vio_open_zlib(calls=%p, dst=%p, level=%i, gzip=%i) = ?", calls, dst, level, gzip); if ( calls == NULL || dst == NULL ) { roar_err_set(ROAR_ERROR_FAULT); return -1; } self = roar_mm_malloc(sizeof(struct roar_vio_gzip)); if ( _init(self, dst, level, gzip) == -1 ) { err = roar_error; roar_mm_free(self); roar_err_set(err); ROAR_DBG("roar_vio_open_zlib(calls=%p, dst=%p, level=%i, gzip=%i) = -1 // error=%s", calls, dst, level, gzip, roar_error2str(err)); return -1; } ROAR_DBG("roar_vio_open_zlib(calls=%p, dst=%p, level=%i, gzip=%i) = ?", calls, dst, level, gzip); memset(calls, 0, sizeof(struct roar_vio_calls)); calls->flags = ROAR_VIO_FLAGS_NONE; calls->refc = 1; calls->inst = self; calls->read = roar_vio_zlib_read; calls->write = roar_vio_zlib_write; calls->lseek = roar_vio_zlib_lseek; calls->sync = roar_vio_zlib_sync; calls->ctl = roar_vio_zlib_ctl; calls->close = roar_vio_zlib_close; ROAR_DBG("roar_vio_open_zlib(calls=%p, dst=%p, level=%i, gzip=%i) = 0", calls, dst, level, gzip); return 0; } #endif //ll