🏡 index : cold-air-networks/mirror-jetstream.git

/*
 * 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");
    }
}