| /* |
| * 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 "Deflater.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 DeflaterData { |
| public: |
| |
| int level; |
| bool nowrap; |
| int strategy; |
| int flush; |
| bool finished; |
| bool ended; |
| |
| z_stream* stream; |
| |
| private: |
| |
| DeflaterData(const DeflaterData&); |
| DeflaterData& operator=(const DeflaterData&); |
| |
| public: |
| |
| DeflaterData() : level(0), |
| nowrap(true), |
| strategy(0), |
| flush(0), |
| finished(false), |
| ended(false), |
| stream(NULL) { |
| } |
| |
| public: |
| |
| static void initZLibDeflate(DeflaterData* handle, int level, bool nowrap = false) { |
| |
| if (handle == NULL) { |
| throw NullPointerException( |
| __FILE__, __LINE__, "Error While initializing the Compression Library."); |
| } |
| |
| handle->stream = new z_stream; |
| handle->ended = false; |
| handle->level = level; |
| handle->strategy = 0; |
| handle->nowrap = nowrap; |
| handle->finished = false; |
| handle->flush = Z_NO_FLUSH; |
| |
| // 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 = deflateInit(handle->stream, handle->level); |
| } else { |
| |
| // Turn off ZLib header wrapping and encode raw. Attempts |
| // to set all other values to their normal defaults. |
| result = deflateInit2(handle->stream, |
| handle->level, |
| Z_DEFLATED, |
| -15, 8, |
| Z_DEFAULT_STRATEGY); |
| } |
| |
| if (result != Z_OK) { |
| throw RuntimeException( |
| __FILE__, __LINE__, "Error While initializing the Compression Library."); |
| } |
| } |
| |
| static void finishZlibDeflate(DeflaterData* handle) { |
| |
| if (handle == NULL) { |
| throw NullPointerException( |
| __FILE__, __LINE__, "Error While initializing the Compression Library."); |
| } |
| |
| handle->ended = true; |
| |
| if (handle->stream != NULL) { |
| |
| // Shutdown the ZLib stream |
| deflateEnd(handle->stream); |
| delete handle->stream; |
| handle->stream = NULL; |
| } |
| } |
| |
| static void resetZlibStream(DeflaterData* handle) { |
| |
| if (handle == NULL) { |
| throw NullPointerException( |
| __FILE__, __LINE__, "Error While initializing the Compression Library."); |
| } |
| |
| if (handle->stream != NULL) { |
| |
| handle->finished = false; |
| handle->flush = Z_NO_FLUSH; |
| handle->ended = false; |
| |
| // Ask ZLib to do the reset. |
| deflateReset(handle->stream); |
| |
| // Clear any old data that might still be around. |
| handle->stream->opaque = Z_NULL; |
| handle->stream->avail_in = 0; |
| handle->stream->next_in = Z_NULL; |
| } |
| } |
| |
| }; |
| |
| }}} |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| const int Deflater::BEST_COMPRESSION = 9; |
| const int Deflater::BEST_SPEED = 1; |
| const int Deflater::NO_COMPRESSION = 0; |
| const int Deflater::DEFAULT_COMPRESSION = -1; |
| |
| const int Deflater::DEFAULT_STRATEGY = 0; |
| const int Deflater::DEFLATED = 0; |
| const int Deflater::FILTERED = 1; |
| const int Deflater::HUFFMAN_ONLY = 2; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| Deflater::Deflater(int level, bool nowrap) : data(new DeflaterData()) { |
| |
| if (level < DEFAULT_COMPRESSION || level > BEST_COMPRESSION) { |
| throw IllegalArgumentException( |
| __FILE__, __LINE__, "Compression level passed was Invalid: %d", level); |
| } |
| |
| // Initialize all the ZLib structures. |
| DeflaterData::initZLibDeflate(this->data, level, nowrap); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| Deflater::Deflater() : data(new DeflaterData()) { |
| // Initialize all the ZLib structures. |
| DeflaterData::initZLibDeflate(this->data, DEFAULT_COMPRESSION); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| Deflater::~Deflater() { |
| try { |
| this->end(); |
| delete this->data; |
| } |
| DECAF_CATCH_NOTHROW(Exception) |
| DECAF_CATCHALL_NOTHROW() |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Deflater::setInput(const unsigned char* buffer, int size, int offset, int length) { |
| |
| try { |
| |
| if (buffer == NULL) { |
| throw NullPointerException( |
| __FILE__, __LINE__, "Buffer passed cannot be NULL."); |
| } |
| |
| if (this->data->stream == NULL) { |
| throw IllegalStateException( |
| __FILE__, __LINE__, "The Deflator has already been ended."); |
| } |
| |
| if (offset + length > size) { |
| throw IndexOutOfBoundsException( |
| __FILE__, __LINE__, "Given offset + length greater than the size of the buffer."); |
| } |
| |
| // We can only change the level and strategy once an entire block of data is compressed. |
| if (this->data->stream->next_in == NULL) { |
| deflateParams(this->data->stream, this->data->level, this->data->strategy); |
| } |
| |
| this->data->stream->avail_in = (uInt) length; |
| this->data->stream->next_in = (Bytef*) (buffer + offset); |
| } |
| DECAF_CATCH_RETHROW(NullPointerException) |
| DECAF_CATCH_RETHROW(IllegalStateException) |
| DECAF_CATCH_RETHROW(IndexOutOfBoundsException) |
| DECAF_CATCHALL_THROW(IllegalStateException) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Deflater::setInput(const std::vector<unsigned char>& buffer, int offset, int length) { |
| this->setInput(&buffer[0], (int) buffer.size(), offset, length); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Deflater::setInput(const std::vector<unsigned char>& buffer) { |
| |
| if (buffer.empty()) { |
| return; |
| } |
| |
| this->setInput(&buffer[0], (int) buffer.size(), 0, (int) buffer.size()); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Deflater::setDictionary(const unsigned char* buffer, int size, int offset, int length) { |
| |
| try { |
| |
| if (buffer == NULL) { |
| throw NullPointerException( |
| __FILE__, __LINE__, "Buffer passed cannot be NULL."); |
| } |
| |
| if (this->data->stream == NULL) { |
| throw IllegalStateException( |
| __FILE__, __LINE__, "The Deflator has already been ended."); |
| } |
| |
| if (offset + length > size) { |
| throw IndexOutOfBoundsException( |
| __FILE__, __LINE__, "Given offset + length greater than the size of the buffer."); |
| } |
| |
| // From the ZLib documentation. |
| // deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a parameter is invalid |
| // (such as NULL dictionary) or the stream state is inconsistent (for example if deflate has |
| // already been called for this stream or if the compression method is bsort). |
| // deflateSetDictionary does not perform any compression: this will be done by deflate(). |
| if (deflateSetDictionary(this->data->stream, buffer + offset, (uInt) length) != Z_OK) { |
| throw IllegalStateException( |
| __FILE__, __LINE__, "Deflator could not accept the dictionary."); |
| } |
| } |
| DECAF_CATCH_RETHROW(NullPointerException) |
| DECAF_CATCH_RETHROW(IllegalStateException) |
| DECAF_CATCH_RETHROW(IndexOutOfBoundsException) |
| DECAF_CATCHALL_THROW(IllegalStateException) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Deflater::setDictionary(const std::vector<unsigned char>& buffer, int offset, int length) { |
| this->setDictionary(&buffer[0], (int) buffer.size(), offset, length); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Deflater::setDictionary(const std::vector<unsigned char>& buffer) { |
| |
| if (buffer.empty()) { |
| return; |
| } |
| |
| this->setDictionary(&buffer[0], (int) buffer.size(), 0, (int) buffer.size()); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Deflater::setStrategy(int strategy) { |
| |
| if (strategy < DEFAULT_STRATEGY || strategy > HUFFMAN_ONLY) { |
| throw IllegalArgumentException( |
| __FILE__, __LINE__, "Strategy value {%d} is not valid.", strategy); |
| } |
| |
| if (this->data->stream == NULL && !this->data->ended) { |
| throw IllegalStateException( |
| __FILE__, __LINE__, "The Deflator is in an invalid state."); |
| } |
| |
| this->data->strategy = strategy; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Deflater::setLevel(int level) { |
| |
| if (level < DEFAULT_COMPRESSION || level > BEST_COMPRESSION) { |
| throw IllegalArgumentException( |
| __FILE__, __LINE__, "Strategy value {%d} is not valid.", level); |
| } |
| |
| if (this->data->stream == NULL && !this->data->ended) { |
| throw IllegalStateException( |
| __FILE__, __LINE__, "The Deflator is in an invalid state."); |
| } |
| |
| this->data->level = level; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| bool Deflater::needsInput() const { |
| if (this->data->stream == NULL) { |
| return false; |
| } |
| |
| return this->data->stream->avail_in == 0; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Deflater::finish() { |
| this->data->flush = Z_FINISH; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| bool Deflater::finished() const { |
| return this->data->finished; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| int Deflater::deflate(unsigned char* buffer, int size, int offset, int length) { |
| |
| try { |
| |
| if (buffer == NULL) { |
| throw NullPointerException( |
| __FILE__, __LINE__, "Buffer passed cannot be NULL."); |
| } |
| |
| if (this->data->stream == NULL) { |
| throw IllegalStateException( |
| __FILE__, __LINE__, "The Deflator has already been ended."); |
| } |
| |
| 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 = ::deflate(this->data->stream, this->data->flush); |
| |
| if (result == Z_STREAM_END) { |
| this->data->finished = true; |
| } |
| |
| return (int) (this->data->stream->total_out - outStart); |
| } |
| DECAF_CATCH_RETHROW(NullPointerException) |
| DECAF_CATCH_RETHROW(IllegalStateException) |
| DECAF_CATCH_RETHROW(IndexOutOfBoundsException) |
| DECAF_CATCHALL_THROW(IllegalStateException) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| int Deflater::deflate(std::vector<unsigned char>& buffer, int offset, int length) { |
| return this->deflate(&buffer[0], (int) buffer.size(), offset, length); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| int Deflater::deflate(std::vector<unsigned char>& buffer) { |
| return this->deflate(&buffer[0], (int) buffer.size(), 0, (int) buffer.size()); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| long long Deflater::getAdler() const { |
| if (this->data->stream == NULL) { |
| throw IllegalStateException( |
| __FILE__, __LINE__, "The Deflator has already been ended."); |
| } |
| |
| return this->data->stream->adler; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| long long Deflater::getBytesRead() const { |
| if (this->data->stream == NULL) { |
| throw IllegalStateException( |
| __FILE__, __LINE__, "The Deflator has already been ended."); |
| } |
| |
| return this->data->stream->total_in; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| long long Deflater::getBytesWritten() const { |
| if (this->data->stream == NULL) { |
| throw IllegalStateException( |
| __FILE__, __LINE__, "The Deflator has already been ended."); |
| } |
| |
| return this->data->stream->total_out; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Deflater::reset() { |
| |
| if (this->data->stream == NULL) { |
| throw IllegalStateException( |
| __FILE__, __LINE__, "The Deflator has already been ended."); |
| } |
| |
| DeflaterData::resetZlibStream(this->data); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Deflater::end() { |
| |
| if (this->data) { |
| DeflaterData::finishZlibDeflate(this->data); |
| } |
| } |