| /* |
| * Copyright (C) 2006, 2011 Filip Pizlo. All rights reserved. |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY FILIP PIZLO ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FILIP PIZLO OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "tsf_internal.h" |
| #include "tsf_serial_protocol.h" |
| |
| #ifdef HAVE_ZLIB |
| #include <zlib.h> |
| #endif |
| |
| #ifdef HAVE_BZIP2 |
| #include <bzlib.h> |
| #endif |
| |
| #define Cz(reader) ((z_stream*)((reader)->stream)) |
| #define Cbz(reader) ((bz_stream*)((reader)->stream)) |
| |
| static tsf_bool_t tear_down_mode(tsf_adpt_rdr_t *reader, |
| uint8_t **spillover, |
| uint32_t *spillover_len) { |
| tsf_bool_t stillok=tsf_true; |
| if (reader->mode==TSF_ZIP_ZLIB) { |
| #ifdef HAVE_ZLIB |
| inflateEnd(Cz(reader)); |
| #endif |
| } else if (reader->mode==TSF_ZIP_BZIP2) { |
| #ifdef HAVE_BZIP2 |
| BZ2_bzDecompressEnd(Cbz(reader)); |
| #endif |
| } |
| if (reader->mode==TSF_ZIP_ZLIB || |
| reader->mode==TSF_ZIP_BZIP2) { |
| free(reader->stream); |
| if (spillover!=NULL && spillover_len!=NULL) { |
| *spillover=malloc(reader->abstract.avail_in); |
| if (*spillover==NULL) { |
| tsf_set_errno("Could not allocate spillover buffer"); |
| stillok=tsf_false; |
| } else { |
| *spillover_len=reader->abstract.avail_in; |
| memcpy(*spillover,reader->abstract.next_in,*spillover_len); |
| } |
| } |
| } else if (reader->mode==TSF_ZIP_NONE) { |
| if (spillover!=NULL && spillover_len!=NULL) { |
| *spillover=malloc(reader->puti-reader->geti); |
| if (*spillover==NULL) { |
| tsf_set_errno("Could not allocate spillover buffer"); |
| stillok=tsf_false; |
| } else { |
| *spillover_len=reader->puti-reader->geti; |
| memcpy(*spillover,reader->buf+reader->geti,*spillover_len); |
| } |
| } |
| } |
| if (reader->mode!=TSF_ZIP_UNKNOWN) { |
| free(reader->buf); |
| } |
| reader->stream=NULL; |
| reader->buf=NULL; |
| reader->buf_size=0; |
| reader->mode=TSF_ZIP_UNKNOWN; |
| return stillok; |
| } |
| |
| static tsf_bool_t tear_to_none(tsf_adpt_rdr_t *reader) { |
| tsf_assert(reader->mode==TSF_ZIP_ZLIB || |
| reader->mode==TSF_ZIP_BZIP2); |
| if (reader->mode==TSF_ZIP_ZLIB) { |
| #ifdef HAVE_ZLIB |
| inflateEnd(Cz(reader)); |
| #endif |
| } else { |
| #ifdef HAVE_BZIP2 |
| BZ2_bzDecompressEnd(Cbz(reader)); |
| #endif |
| } |
| free(reader->stream); |
| reader->mode=TSF_ZIP_UNKNOWN; |
| if (reader->nozip_buf_size==reader->buf_size) { |
| reader->geti=reader->abstract.next_in-reader->buf; |
| reader->puti=reader->geti+reader->abstract.avail_in; |
| } else { |
| uint8_t *buf; |
| reader->buf_size=tsf_max(reader->nozip_buf_size, |
| reader->abstract.avail_in); |
| buf=malloc(reader->buf_size); |
| if (buf==NULL) { |
| tsf_set_errno("Could not allocate buffer in tear_to_none() in " |
| "tsf_adaptive_reader.c"); |
| free(reader->buf); |
| /* oh man, this leaves the reader in a flaky state */ |
| return tsf_false; |
| } |
| memcpy(buf, |
| reader->abstract.next_in, |
| reader->abstract.avail_in); |
| free(reader->buf); |
| reader->geti=0; |
| reader->puti=reader->abstract.avail_in; |
| reader->buf=buf; |
| } |
| reader->mode=TSF_ZIP_NONE; |
| return tsf_true; |
| } |
| |
| static tsf_bool_t select_mode(tsf_adpt_rdr_t *reader, |
| uint8_t *spillover, |
| uint32_t spillover_len, |
| tsf_bool_t free_spillover) { |
| uint8_t magic[4]; |
| uint32_t tocopy=tsf_min(4,spillover_len); |
| void *tofree; |
| if (free_spillover && spillover_len>0) { |
| tofree=spillover; |
| } else { |
| tofree=NULL; |
| } |
| memcpy(magic,spillover,tocopy); |
| spillover+=tocopy; |
| spillover_len-=tocopy; |
| if (tocopy<4) { |
| tsf_assert(spillover_len==0); |
| if (!tsf_full_read_of_partial(reader->reader,reader->reader_arg, |
| magic+tocopy,4-tocopy)) { |
| if (tofree!=NULL) free(tofree); |
| return tsf_false; |
| } |
| } |
| if (!memcmp(magic,TSF_SP_MAGIC_CODE,TSF_SP_MAGIC_LEN)) { |
| reader->buf_size=tsf_max(reader->nozip_buf_size, |
| spillover_len+TSF_SP_MAGIC_LEN); |
| reader->buf=malloc(reader->buf_size); |
| if (reader->buf==NULL) { |
| tsf_set_errno("Could not allocate buffer"); |
| if (tofree!=NULL) free(tofree); |
| return tsf_false; |
| } |
| memcpy(reader->buf,spillover,spillover_len); |
| memcpy(reader->buf+spillover_len,TSF_SP_MAGIC_CODE,TSF_SP_MAGIC_LEN); |
| reader->puti=spillover_len+TSF_SP_MAGIC_LEN; |
| reader->geti=0; |
| if (tofree!=NULL) free(tofree); |
| reader->mode=TSF_ZIP_NONE; |
| } else if (!memcmp(magic,TSF_SP_BZIP2_MAGIC_CODE,TSF_SP_MAGIC_LEN) || |
| !memcmp(magic,TSF_SP_ZLIB_MAGIC_CODE,TSF_SP_MAGIC_LEN)) { |
| int res; |
| |
| if (!memcmp(magic,TSF_SP_ZLIB_MAGIC_CODE,TSF_SP_MAGIC_LEN)) { |
| reader->buf_size=tsf_max(reader->attr.z.buf_size,spillover_len); |
| } else { |
| reader->buf_size=tsf_max(reader->attr.b.buf_size,spillover_len); |
| } |
| |
| reader->buf=malloc(reader->buf_size); |
| if (reader->buf==NULL) { |
| tsf_set_errno("Could not allocate buffer"); |
| if (tofree!=NULL) free(tofree); |
| return tsf_false; |
| } |
| |
| memcpy(reader->buf,spillover,spillover_len); |
| if (tofree!=NULL) free(tofree); |
| |
| reader->abstract.next_in=reader->buf; |
| reader->abstract.avail_in=spillover_len; |
| |
| if (!memcmp(magic,TSF_SP_ZLIB_MAGIC_CODE,TSF_SP_MAGIC_LEN)) { |
| if (!reader->attr.z.allow) { |
| tsf_set_error(TSF_E_PARSE_ERROR, |
| "The stream is compressed using ZLib, but " |
| "the given attributes specify that " |
| "ZLib is not allowed"); |
| if (tofree!=NULL) free(tofree); |
| free(reader->buf); |
| return tsf_false; |
| } |
| #ifdef HAVE_ZLIB |
| reader->stream=malloc(sizeof(z_stream)); |
| if (reader->stream==NULL) { |
| tsf_set_errno("Could not malloc z_stream"); |
| free(reader->buf); |
| return tsf_false; |
| } |
| |
| Cz(reader)->zalloc=NULL; |
| Cz(reader)->zfree=NULL; |
| Cz(reader)->opaque=NULL; |
| |
| if (reader->attr.z.advanced_init) { |
| res=inflateInit2(Cz(reader), |
| reader->attr.z.windowBits); |
| } else { |
| res=inflateInit(Cz(reader)); |
| } |
| if (res!=Z_OK) { |
| tsf_set_zlib_error(res,Cz(reader)->msg, |
| "Trying to initialize inflation in ZLib"); |
| free(Cz(reader)); |
| free(reader->buf); |
| return tsf_false; |
| } |
| reader->mode=TSF_ZIP_ZLIB; |
| #else |
| tsf_set_error(TSF_E_NOT_SUPPORTED, |
| "TSF_ZIP_ZLIB is not supported by " |
| "tsf_zip_reader_create() because ZLib support " |
| "was not configured into the TSF library"); |
| if (tofree!=NULL) free(tofree); |
| free(reader->buf); |
| return tsf_false; |
| #endif |
| } else { |
| if (!reader->attr.b.allow) { |
| tsf_set_error(TSF_E_PARSE_ERROR, |
| "The stream is compressed using libbzip2, but " |
| "the given attributes specify that " |
| "libbzip2 is not allowed"); |
| if (tofree!=NULL) free(tofree); |
| free(reader->buf); |
| return tsf_false; |
| } |
| #ifdef HAVE_BZIP2 |
| reader->stream=malloc(sizeof(bz_stream)); |
| if (reader->stream==NULL) { |
| tsf_set_errno("Could not malloc bz_stream"); |
| free(reader->buf); |
| return tsf_false; |
| } |
| |
| Cbz(reader)->bzalloc=NULL; |
| Cbz(reader)->bzfree=NULL; |
| Cbz(reader)->opaque=NULL; |
| |
| res=BZ2_bzDecompressInit(Cbz(reader), |
| reader->attr.b.verbosity, |
| reader->attr.b.small); |
| if (res!=BZ_OK) { |
| tsf_set_libbzip2_error(res, |
| "Trying to initialize decompression in libbzip2"); |
| free(Cbz(reader)); |
| free(reader->buf); |
| return tsf_false; |
| } |
| |
| reader->mode=TSF_ZIP_BZIP2; |
| #else |
| tsf_set_error(TSF_E_NOT_SUPPORTED, |
| "TSF_ZIP_BZIP2 is not supported by " |
| "tsf_zip_reader_create() because libbzip2 " |
| "support was not configured into the TSF " |
| "library"); |
| free(reader->buf); |
| if (tofree!=NULL) free(tofree); |
| return tsf_false; |
| #endif |
| } |
| } else { |
| if (tofree!=NULL) free(tofree); |
| tsf_set_error(TSF_E_PARSE_ERROR, |
| "Not a valid magic code for any TSF stream file format"); |
| return tsf_false; |
| } |
| return tsf_true; |
| } |
| |
| static tsf_bool_t tear_down_and_select_mode(tsf_adpt_rdr_t *reader) { |
| uint8_t *spillover; |
| uint32_t spillover_len; |
| return tear_down_mode(reader,&spillover,&spillover_len) |
| && select_mode(reader,spillover,spillover_len,tsf_true); |
| } |
| |
| tsf_adpt_rdr_t *tsf_adaptive_reader_create(tsf_partial_reader_t reader, |
| void *reader_arg, |
| uint32_t nozip_buf_size, |
| const tsf_zip_rdr_attr_t *attr) { |
| tsf_adpt_rdr_t *result=malloc(sizeof(tsf_adpt_rdr_t)); |
| if (result==NULL) { |
| tsf_set_errno("Could not malloc tsf_adpt_rdr_t"); |
| return NULL; |
| } |
| |
| result->mode=TSF_ZIP_UNKNOWN; |
| |
| result->reader=reader; |
| result->reader_arg=reader_arg; |
| |
| result->stream=NULL; |
| result->expect_switch=tsf_true; |
| |
| result->buf=NULL; |
| result->buf_size=0; |
| |
| if (attr==NULL) { |
| tsf_zip_rdr_attr_get_default(&(result->attr)); |
| } else { |
| result->attr=*attr; |
| } |
| result->nozip_buf_size=nozip_buf_size; |
| |
| return result; |
| } |
| |
| void tsf_adaptive_reader_destroy(tsf_adpt_rdr_t *reader) { |
| tear_down_mode(reader,NULL,NULL); |
| free(reader); |
| } |
| |
| void tsf_adaptive_reader_hint_switch(tsf_adpt_rdr_t *reader) { |
| reader->expect_switch=tsf_true; |
| } |
| |
| tsf_bool_t tsf_adaptive_reader_read(void *_reader, |
| void *_data, |
| uint32_t len) { |
| tsf_adpt_rdr_t *reader=_reader; |
| uint8_t *data=_data; |
| |
| if (len==0) { |
| return tsf_true; |
| } |
| |
| /* this just constitutes lazy initialization upon read */ |
| if (reader->mode==TSF_ZIP_UNKNOWN) { |
| if (!select_mode(reader,NULL,0,tsf_false)) { |
| return tsf_false; |
| } |
| } |
| |
| switch (reader->mode) { |
| case TSF_ZIP_NONE: { |
| /* ok - this code is complicated. it is complicated because it |
| needs to deal with the possibility of a format change - if the |
| magic code for one of the zip formats comes in, this thing's job |
| is to recognize it and switch from no-zip buffering to unzipping. |
| we use the hint_switch() function calls as a hint of when to |
| look for the format changes. */ |
| if (reader->expect_switch && reader->puti>reader->geti) { |
| reader->expect_switch=tsf_false; |
| if (reader->buf[reader->geti]==TSF_SP_C_SWITCH_FORMAT) { |
| if (!tear_down_and_select_mode(reader)) { |
| return tsf_false; |
| } |
| return tsf_adaptive_reader_read(reader,data,len); |
| } |
| } |
| uint32_t toread=tsf_min(reader->puti-reader->geti,len); |
| memcpy(data,reader->buf+reader->geti,toread); |
| reader->geti+=toread; |
| data+=toread; |
| len-=toread; |
| if (len>reader->buf_size) { |
| tsf_assert(reader->geti==reader->puti); |
| if (reader->expect_switch) { |
| int32_t res=reader->reader(reader->reader_arg, |
| data,len); |
| if (res<0) { |
| return tsf_false; |
| } |
| tsf_assert(res>0); |
| if (data[0]==TSF_SP_C_SWITCH_FORMAT) { |
| tear_down_mode(reader,NULL,NULL); |
| if (!select_mode(reader,data,res,tsf_false)) { |
| return tsf_false; |
| } |
| return tsf_adaptive_reader_read(reader,data,len); |
| } else { |
| data+=res; |
| len-=res; |
| } |
| } |
| if (!tsf_full_read_of_partial(reader->reader, |
| reader->reader_arg, |
| data, |
| len)) { |
| if (tsf_get_error_code()==TSF_E_UNEXPECTED_EOF |
| && data!=_data) { |
| tsf_set_error(TSF_E_ERRONEOUS_EOF, |
| "End-of-file after part of a full buffered " |
| "read; original error: %s",tsf_get_error()); |
| } |
| return tsf_false; |
| } |
| } else if (len>0) { |
| reader->puti=0; |
| reader->geti=0; |
| for (;;) { |
| int32_t res; |
| uint32_t tocopy; |
| res=reader->reader(reader->reader_arg, |
| reader->buf, |
| reader->buf_size); |
| if (res<0) { |
| if (tsf_get_error_code()==TSF_E_UNEXPECTED_EOF |
| && data!=_data) { |
| tsf_set_error(TSF_E_ERRONEOUS_EOF, |
| "End-of-file after part of a full buffered " |
| "read; original error: %s",tsf_get_error()); |
| } |
| return tsf_false; |
| } |
| tsf_assert(res>0); |
| if (reader->expect_switch) { |
| reader->expect_switch=tsf_false; |
| if (reader->buf[0]==TSF_SP_C_SWITCH_FORMAT) { |
| reader->puti=res; |
| if (!tear_down_and_select_mode(reader)) { |
| return tsf_false; |
| } |
| return tsf_adaptive_reader_read(reader,data,len); |
| } |
| } |
| tocopy=tsf_min(res,len); |
| memcpy(data,reader->buf,tocopy); |
| data+=tocopy; |
| len-=tocopy; |
| if (len==0) { |
| reader->puti=res; |
| reader->geti=tocopy; |
| break; |
| } |
| } |
| } |
| break; |
| } |
| case TSF_ZIP_ZLIB: |
| case TSF_ZIP_BZIP2: { |
| reader->abstract.next_out=data; |
| reader->abstract.avail_out=len; |
| |
| for (;;) { |
| int res; |
| |
| /* is there ever a case where we would want to call inflate even though |
| the input buffer is empty? */ |
| if (reader->abstract.avail_in!=0) { |
| res=tsf_zip_abstract_inflate(reader->mode, |
| reader->stream, |
| &(reader->abstract), |
| TSF_ZIP_ACT_RUN); |
| |
| if (res==TSF_ZIP_RES_ERROR) { |
| return tsf_false; |
| } else if (res==TSF_ZIP_RES_END) { |
| if (reader->abstract.avail_out!=0) { |
| tsf_set_error(TSF_E_PARSE_ERROR, |
| "A read request straddles a format change"); |
| } |
| tear_to_none(reader); |
| } |
| |
| if (reader->abstract.avail_out==0) { |
| break; |
| } |
| |
| tsf_assert(reader->abstract.avail_in==0); |
| } |
| |
| res=reader->reader(reader->reader_arg, |
| reader->buf, |
| reader->buf_size); |
| |
| if (res==-1) { |
| if (tsf_get_error_code()==TSF_E_UNEXPECTED_EOF) { |
| tsf_set_error(TSF_E_ERRONEOUS_EOF, |
| "Got an EOF from the reader before the " |
| "decompressor had reached end of stream"); |
| } |
| return tsf_false; |
| } |
| |
| reader->abstract.next_in=reader->buf; |
| reader->abstract.avail_in=res; |
| } |
| break; |
| } |
| default: |
| tsf_set_error(TSF_E_INTERNAL, |
| "Invalid reader mode in tsf_adaptive_reader_read()"); |
| return tsf_false; |
| } |
| |
| return tsf_true; |
| } |
| |
| uint32_t tsf_adaptive_reader_get_remaining(tsf_adpt_rdr_t *reader) { |
| switch (reader->mode) { |
| case TSF_ZIP_UNKNOWN: |
| return 0; |
| case TSF_ZIP_NONE: |
| return reader->puti-reader->geti; |
| case TSF_ZIP_ZLIB: |
| case TSF_ZIP_BZIP2: |
| return reader->abstract.avail_in; |
| default: |
| /* should never happen */ |
| tsf_abort("bad reader mode"); |
| } |
| } |
| |
| |
| |