blob: 4d27eddd8371b0689fdf4fe8055f9778e8e2b851 [file] [log] [blame]
/*
* 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);
}
}