/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Inflater.h" #include #include #include #include using namespace decaf; using namespace decaf::lang; using namespace decaf::lang::exceptions; using namespace decaf::util; using namespace decaf::util::zip; //////////////////////////////////////////////////////////////////////////////// namespace decaf{ namespace util{ namespace zip{ class InflaterData { public: bool nowrap; bool finished; bool needDictionary; int flush; z_stream* stream; private: InflaterData( const InflaterData& ); InflaterData& operator= ( const InflaterData& ); public: InflaterData() : nowrap(true), finished(false), needDictionary(false), flush(0), stream(NULL) {} public: static void initZlibInflate( InflaterData* handle, bool nowrap = false ) { if( handle == NULL ) { throw NullPointerException( __FILE__, __LINE__, "Error While initializing the Decompression Library." ); } handle->stream = new z_stream; handle->finished = false; handle->needDictionary = false; handle->flush = Z_NO_FLUSH; handle->nowrap = nowrap; // Init the ZLib stream to defaults handle->stream->zalloc = Z_NULL; handle->stream->zfree = Z_NULL; handle->stream->opaque = Z_NULL; handle->stream->avail_in = 0; handle->stream->next_in = Z_NULL; int result = Z_OK; if( nowrap == false ) { result = inflateInit( handle->stream ); } else { // Disable the ZLib header. result = inflateInit2( handle->stream, -15 ); } if( result != Z_OK ) { throw RuntimeException( __FILE__, __LINE__, "Error While initializing the Decompression Library." ); } } static void finishZlibDeflate( InflaterData* handle ) { if( handle == NULL ) { throw NullPointerException( __FILE__, __LINE__, "Error While initializing the Decompression Library." ); } if( handle->stream != NULL ) { // Shutdown the ZLib stream inflateEnd( handle->stream ); delete handle->stream; handle->stream = NULL; } } static void resetZlibStream( InflaterData* handle ) { if( handle == NULL ) { throw NullPointerException( __FILE__, __LINE__, "Error While initializing the Decompression Library." ); } if( handle->stream != NULL ) { handle->finished = false; handle->needDictionary = false; handle->flush = Z_NO_FLUSH; // Ask ZLib to do the reset. inflateReset( handle->stream ); // clear any old data handle->stream->opaque = Z_NULL; handle->stream->avail_in = 0; handle->stream->next_in = Z_NULL; } } }; }}} //////////////////////////////////////////////////////////////////////////////// Inflater::Inflater( bool nowrap ) : data( new InflaterData() ) { InflaterData::initZlibInflate( this->data, nowrap ); } //////////////////////////////////////////////////////////////////////////////// Inflater::Inflater() : data( new InflaterData() ) { InflaterData::initZlibInflate( this->data ); } //////////////////////////////////////////////////////////////////////////////// Inflater::~Inflater() { try{ this->end(); delete data; } DECAF_CATCH_NOTHROW( Exception ) DECAF_CATCHALL_NOTHROW() } //////////////////////////////////////////////////////////////////////////////// void Inflater::setInput( const unsigned char* buffer, int size, int offset, int length ) { try{ if( buffer == NULL ) { throw NullPointerException( __FILE__, __LINE__, "Passed Buffer was NULL." ); } if( this->data->stream == NULL ) { throw IllegalStateException( __FILE__, __LINE__, "The Inflater end method has already been called." ); } if( offset + length > size ) { throw IndexOutOfBoundsException( __FILE__, __LINE__, "The offset + length given is greater than the specified buffer size." ); } this->data->stream->avail_in = (uInt)length; this->data->stream->next_in = (Bytef*)( buffer + offset ); } DECAF_CATCH_RETHROW( NullPointerException ) DECAF_CATCH_RETHROW( IndexOutOfBoundsException ) DECAF_CATCH_RETHROW( IllegalStateException ) DECAF_CATCHALL_THROW( IllegalStateException ) } //////////////////////////////////////////////////////////////////////////////// void Inflater::setInput( const std::vector& buffer, int offset, int length ) { if( buffer.empty() ) { return; } this->setInput( &buffer[0], (int)buffer.size(), offset, length ); } //////////////////////////////////////////////////////////////////////////////// void Inflater::setInput( const std::vector& buffer ) { if( buffer.empty() ) { return; } this->setInput( &buffer[0], (int)buffer.size(), 0, (int)buffer.size() ); } //////////////////////////////////////////////////////////////////////////////// int Inflater::getRemaining() const { if( this->data->stream != NULL ) { return this->data->stream->avail_in; } return 0; } //////////////////////////////////////////////////////////////////////////////// void Inflater::setDictionary( const unsigned char* buffer, int size, int offset, int length ) { try{ if( buffer == NULL ) { throw NullPointerException( __FILE__, __LINE__, "Passed Buffer was NULL." ); } if( this->data->stream == NULL ) { throw IllegalStateException( __FILE__, __LINE__, "The Inflater end method has already been called." ); } if( offset + length > size ) { throw IndexOutOfBoundsException( __FILE__, __LINE__, "The offset + length given is greater than the specified buffer size." ); } // From the ZLib documentation // inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a parameter is invalid (such // as NULL dictionary) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary // doesn't match the expected one (incorrect adler32 value). inflateSetDictionary does not // perform any decompression: this will be done by subsequent calls of inflate(). int result = inflateSetDictionary( this->data->stream, buffer + offset, (uInt)length ); if( result != Z_OK ) { throw IllegalArgumentException( __FILE__, __LINE__, "Dictionary given does not match required checksum value." ); } } DECAF_CATCH_RETHROW( NullPointerException ) DECAF_CATCH_RETHROW( IndexOutOfBoundsException ) DECAF_CATCH_RETHROW( IllegalStateException ) DECAF_CATCH_RETHROW( IllegalArgumentException ) DECAF_CATCHALL_THROW( IllegalStateException ) } //////////////////////////////////////////////////////////////////////////////// void Inflater::setDictionary( const std::vector& buffer, int offset, int length ) { if( buffer.empty() ) { return; } this->setDictionary( &buffer[0], (int)buffer.size(), offset, length ); } //////////////////////////////////////////////////////////////////////////////// void Inflater::setDictionary( const std::vector& buffer ) { if( buffer.empty() ) { return; } this->setDictionary( &buffer[0], (int)buffer.size(), 0, (int)buffer.size() ); } //////////////////////////////////////////////////////////////////////////////// bool Inflater::needsInput() const { if( this->data->stream == NULL ) { return false; } return this->data->stream->avail_in == 0; } //////////////////////////////////////////////////////////////////////////////// bool Inflater::needsDictionary() const { return this->data->needDictionary; } //////////////////////////////////////////////////////////////////////////////// void Inflater::finish() { this->data->flush = Z_FINISH; } //////////////////////////////////////////////////////////////////////////////// bool Inflater::finished() const { return this->data->finished; } //////////////////////////////////////////////////////////////////////////////// int Inflater::inflate( unsigned char* buffer, int size, int offset, int length ) { try{ if( buffer == NULL ) { throw NullPointerException( __FILE__, __LINE__, "Passed Buffer was NULL." ); } if( this->data->stream == NULL ) { throw IllegalStateException( __FILE__, __LINE__, "The Inflater end method has already been called." ); } if( size < 0 ) { throw IndexOutOfBoundsException( __FILE__, __LINE__, "size parameter out of Bounds: %d.", size ); } if( offset > size || offset < 0 ) { throw IndexOutOfBoundsException( __FILE__, __LINE__, "offset parameter out of Bounds: %d.", offset ); } if( length < 0 || length > size - offset ) { throw IndexOutOfBoundsException( __FILE__, __LINE__, "length parameter out of Bounds: %d.", length ); } unsigned long outStart = this->data->stream->total_out; this->data->stream->next_out = buffer + offset; this->data->stream->avail_out = (uInt)length; // Call ZLib and then process the resulting data to figure out what happened. int result = ::inflate( this->data->stream, this->data->flush ); if( result == Z_STREAM_END ) { this->data->finished = true; } else if( result == Z_NEED_DICT ) { if( this->needsDictionary() ) { throw DataFormatException( __FILE__, __LINE__, "Inflate cannot proceed until a Dictionary is set." ); } this->data->needDictionary = true; } else if( result == Z_DATA_ERROR ) { throw DataFormatException( __FILE__, __LINE__, "Inflate failed because a block of invalid data was found." ); } return (int)( this->data->stream->total_out - outStart ); } DECAF_CATCH_RETHROW( NullPointerException ) DECAF_CATCH_RETHROW( IndexOutOfBoundsException ) DECAF_CATCH_RETHROW( DataFormatException ) DECAF_CATCH_RETHROW( IllegalStateException ) DECAF_CATCHALL_THROW( IllegalStateException ) } //////////////////////////////////////////////////////////////////////////////// int Inflater::inflate( std::vector& buffer, int offset, int length ) { if( buffer.empty() ) { return 0; } return this->inflate( &buffer[0], (int)buffer.size(), offset, length ); } //////////////////////////////////////////////////////////////////////////////// int Inflater::inflate( std::vector& buffer ) { if( buffer.empty() ) { return 0; } return this->inflate( &buffer[0], (int)buffer.size(), 0, (int)buffer.size() ); } //////////////////////////////////////////////////////////////////////////////// long long Inflater::getAdler() const { if( this->data->stream == NULL ) { throw IllegalStateException( __FILE__, __LINE__, "The Inflater has already been ended." ); } return this->data->stream->adler; } //////////////////////////////////////////////////////////////////////////////// long long Inflater::getBytesRead() const { if( this->data->stream == NULL ) { throw IllegalStateException( __FILE__, __LINE__, "The Inflater has already been ended." ); } return this->data->stream->total_in; } //////////////////////////////////////////////////////////////////////////////// long long Inflater::getBytesWritten() const { if( this->data->stream == NULL ) { throw IllegalStateException( __FILE__, __LINE__, "The Inflater has already been ended." ); } return this->data->stream->total_out; } //////////////////////////////////////////////////////////////////////////////// void Inflater::reset() { if( this->data->stream == NULL ) { throw IllegalStateException( __FILE__, __LINE__, "The Inflater has already been ended." ); } InflaterData::resetZlibStream( this->data ); } //////////////////////////////////////////////////////////////////////////////// void Inflater::end() { if( this->data ) { InflaterData::finishZlibDeflate( this->data ); } }