blob: 48dff4f4bba3e4af601ab02e3551a59c5665b3d1 [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 "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);
}
}