| /* |
| * 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 <stdio.h> |
| #include <assert.h> |
| |
| #include <decaf/internal/util/zip/zlib.h> |
| |
| #include <decaf/lang/exceptions/RuntimeException.h> |
| |
| 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<unsigned char>& buffer, int offset, int length) { |
| |
| if (buffer.empty()) { |
| return; |
| } |
| |
| this->setInput(&buffer[0], (int) buffer.size(), offset, length); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Inflater::setInput(const std::vector<unsigned char>& 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<unsigned char>& buffer, int offset, int length) { |
| |
| if (buffer.empty()) { |
| return; |
| } |
| |
| this->setDictionary(&buffer[0], (int) buffer.size(), offset, length); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Inflater::setDictionary(const std::vector<unsigned char>& 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<unsigned char>& 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<unsigned char>& 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); |
| } |
| } |