Added support for data compression via zlib. Compression support for the TOC

and package attributes sections is still missing, though.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34043 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2009-11-14 21:08:33 +00:00
parent fbe3b5b994
commit aece77090d
13 changed files with 695 additions and 20 deletions

View File

@ -1,11 +1,14 @@
SubDir HAIKU_TOP src bin package ;
UseLibraryHeaders zlib ;
UsePrivateHeaders kernel shared ;
UsePrivateHeaders haiku_package ;
DEFINES += B_ENABLE_INCOMPLETE_POSIX_AT_SUPPORT ;
# TODO: Remove when it is complete!
SEARCH_SOURCE += [ FDirName $(SUBDIR) compression ] ;
BinCommand package :
command_create.cpp
command_dump.cpp
@ -20,6 +23,12 @@ BinCommand package :
PackageReader.cpp
PackageWriter.cpp
Strings.cpp
# compression
ZlibCompressionBase.cpp
ZlibCompressor.cpp
ZlibDecompressor.cpp
:
z
$(TARGET_LIBSUPC++)
;

View File

@ -13,6 +13,7 @@ PackageData::PackageData()
:
fCompressedSize(0),
fUncompressedSize(0),
fChunkSize(0),
fCompression(B_HPKG_COMPRESSION_NONE),
fEncodedInline(true)
{

View File

@ -19,6 +19,7 @@ public:
{ return fUncompressedSize; }
uint64 Offset() const { return fOffset; }
uint32 Compression() const { return fCompression; }
uint32 ChunkSize() const { return fChunkSize; }
bool IsEncodedInline() const
{ return fEncodedInline; }
@ -31,6 +32,8 @@ public:
{ fCompression = compression; }
void SetUncompressedSize(uint64 size)
{ fUncompressedSize = size; }
void SetChunkSize(uint32 size)
{ fChunkSize = size; }
private:
uint64 fCompressedSize;
@ -39,6 +42,7 @@ private:
uint64 fOffset;
uint8 fInlineData[B_HPKG_MAX_INLINE_DATA_SIZE];
};
uint32 fChunkSize;
uint32 fCompression;
bool fEncodedInline;
};

View File

@ -6,11 +6,23 @@
#include "PackageDataReader.h"
#include <string.h>
#include <algorithm>
#include <new>
#include <haiku_package.h>
#include "PackageData.h"
#include "ZlibDecompressor.h"
// minimum/maximum zlib chunk size we consider sane
static const size_t kMinSaneZlibChunkSize = 1024;
static const size_t kMaxSaneZlibChunkSize = 10 * 1024 * 1024;
// maximum number of entries in the zlib offset table buffer
static const uint32 kMaxZlibOffsetTableBufferSize = 512;
// #pragma mark - PackageDataReader
@ -66,7 +78,7 @@ public:
return B_BAD_VALUE;
if ((uint64)offset > fSize || size > fSize - offset)
return B_ERROR;
return B_BAD_VALUE;
return fDataReader->ReadData(fOffset + offset, buffer, size);
}
@ -77,6 +89,252 @@ private:
};
// #pragma mark - ZlibPackageDataReader
class ZlibPackageDataReader : public PackageDataReader {
public:
ZlibPackageDataReader(DataReader* dataReader)
:
PackageDataReader(dataReader),
fOffsetTable(NULL),
fReadBuffer(NULL)
{
}
~ZlibPackageDataReader()
{
delete[] fOffsetTable;
free(fReadBuffer);
}
status_t Init(const PackageData& data)
{
fOffset = data.Offset();
fCompressedSize = data.CompressedSize();
fUncompressedSize = data.UncompressedSize();
fChunkSize = data.ChunkSize();
// validate chunk size
if (fChunkSize == 0)
fChunkSize = B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB;
if (fChunkSize < kMinSaneZlibChunkSize
|| fChunkSize > kMaxSaneZlibChunkSize) {
return B_BAD_DATA;
}
fChunkCount = (fUncompressedSize + (fChunkSize - 1)) / fChunkSize;
fOffsetTableSize = (fChunkCount - 1) * sizeof(uint64);
if (fOffsetTableSize >= fCompressedSize)
return B_BAD_DATA;
// allocate a buffer for the offset table
if (fChunkCount > 1) {
fOffsetTableBufferEntryCount = std::min(fChunkCount - 1,
(uint64)kMaxZlibOffsetTableBufferSize);
fOffsetTable = new(std::nothrow) uint64[
fOffsetTableBufferEntryCount];
if (fOffsetTable == NULL)
return B_NO_MEMORY;
fOffsetTableIndex = -1;
// mark the table content invalid
} else
fChunkSize = fUncompressedSize;
// allocate the read/uncompress buffer
fReadBuffer = (uint8*)malloc(fChunkSize * 2);
if (fReadBuffer == NULL)
return B_NO_MEMORY;
fUncompressBuffer = fReadBuffer + fChunkSize;
fUncompressedChunk = -1;
// mark content invalid
return B_OK;
}
virtual uint64 Size() const
{
return fUncompressedSize;
}
virtual size_t BlockSize() const
{
return fChunkSize;
}
virtual status_t ReadData(off_t offset, void* _buffer, size_t size)
{
uint8* buffer = (uint8*)_buffer;
// check offset and size
if (size == 0)
return B_OK;
if (offset < 0)
return B_BAD_VALUE;
if ((uint64)offset > fUncompressedSize
|| size > fUncompressedSize - offset) {
return B_BAD_VALUE;
}
// uncompress
int64 chunkIndex = offset / fChunkSize;
off_t chunkOffset = chunkIndex * fChunkSize;
size_t inChunkOffset = offset - chunkOffset;
while (size > 0) {
// read and uncompress the chunk
status_t error = _ReadChunk(chunkIndex);
if (error != B_OK)
return error;
// copy data to buffer
size_t toCopy = std::min(size, (size_t)fChunkSize - inChunkOffset);
memcpy(buffer, fUncompressBuffer, toCopy);
buffer += toCopy;
size -= toCopy;
chunkIndex++;
chunkOffset += fChunkSize;
inChunkOffset = 0;
}
return B_OK;
}
private:
status_t _ReadChunk(int64 chunkIndex)
{
if (chunkIndex == fUncompressedChunk)
return B_OK;
// get the chunk offset and size
uint64 offset;
uint32 compressedSize;
status_t error = _GetCompressedChunkOffsetAndSize(chunkIndex, offset,
compressedSize);
if (error != B_OK)
return error;
uint32 uncompressedSize = (uint64)chunkIndex + 1 < fChunkCount
? fChunkSize : fUncompressedSize - chunkIndex * fChunkSize;
// read the chunk
if (compressedSize == uncompressedSize) {
// the chunk is not compressed -- read it directly into the
// uncompressed buffer
error = fDataReader->ReadData(offset, fUncompressBuffer,
compressedSize);
} else {
// read to the read buffer and uncompress
error = fDataReader->ReadData(offset, fReadBuffer, compressedSize);
if (error != B_OK)
return error;
size_t actuallyUncompressedSize;
error = ZlibDecompressor().Decompress(fReadBuffer, compressedSize,
fUncompressBuffer, uncompressedSize, actuallyUncompressedSize);
if (error == B_OK && actuallyUncompressedSize != uncompressedSize)
error = B_BAD_DATA;
}
if (error != B_OK) {
// error reading/decompressing data -- mark the cached data invalid
fUncompressedChunk = -1;
return error;
}
fUncompressedChunk = chunkIndex;
return B_OK;
}
status_t _GetCompressedChunkOffsetAndSize(int64 chunkIndex, uint64& _offset,
uint32& _size)
{
// get the offset
uint64 offset;
if (chunkIndex == 0) {
// first chunk is at 0
offset = 0;
} else {
status_t error = _GetCompressedChunkRelativeOffset(chunkIndex,
offset);
if (error != B_OK)
return error;
}
// get the end offset
uint64 endOffset;
if ((uint64)chunkIndex + 1 == fChunkCount) {
// last chunk end with the end of the data
endOffset = fCompressedSize - fOffsetTableSize;
} else {
status_t error = _GetCompressedChunkRelativeOffset(chunkIndex + 1,
endOffset);
if (error != B_OK)
return error;
}
// sanity check
if (endOffset < offset)
return B_BAD_DATA;
_offset = fOffset + fOffsetTableSize + offset;
_size = endOffset - offset;
return B_OK;
}
status_t _GetCompressedChunkRelativeOffset(int64 chunkIndex,
uint64& _offset)
{
if (fOffsetTableIndex < 0 || fOffsetTableIndex > chunkIndex
|| fOffsetTableIndex + fOffsetTableBufferEntryCount <= chunkIndex) {
// read the table at the given index, or, if we can, the whole table
int64 readAtIndex = fChunkCount - 1 > fOffsetTableBufferEntryCount
? chunkIndex : 1;
uint32 entriesToRead = std::min(
(uint64)fOffsetTableBufferEntryCount,
fChunkCount - readAtIndex);
status_t error = fDataReader->ReadData(
fOffset + (readAtIndex - 1) * sizeof(uint64),
fOffsetTable, entriesToRead * sizeof(uint64));
if (error != B_OK) {
fOffsetTableIndex = -1;
return error;
}
fOffsetTableIndex = readAtIndex;
}
// get and check the offset
_offset = fOffsetTable[chunkIndex - fOffsetTableIndex];
if (_offset > fCompressedSize - fOffsetTableSize)
return B_BAD_DATA;
return B_OK;
}
private:
uint64 fOffset;
uint64 fUncompressedSize;
uint64 fCompressedSize;
uint64 fOffsetTableSize;
uint64 fChunkCount;
uint32 fChunkSize;
uint32 fOffsetTableBufferEntryCount;
uint64* fOffsetTable;
int32 fOffsetTableIndex;
uint8* fReadBuffer;
uint8* fUncompressBuffer;
int64 fUncompressedChunk;
};
// #pragma mark - PackageDataReaderFactory
@ -92,8 +350,8 @@ PackageDataReaderFactory::CreatePackageDataReader(DataReader* dataReader,
dataReader);
break;
case B_HPKG_COMPRESSION_ZLIB:
// TODO:...
return B_UNSUPPORTED;;
reader = new(std::nothrow) ZlibPackageDataReader(dataReader);
break;
default:
return B_BAD_VALUE;
}

View File

@ -46,6 +46,7 @@ enum {
ATTRIBUTE_INDEX_DATA,
ATTRIBUTE_INDEX_DATA_SIZE,
ATTRIBUTE_INDEX_DATA_COMPRESSION,
ATTRIBUTE_INDEX_DATA_CHUNK_SIZE,
ATTRIBUTE_INDEX_SYMLINK_PATH
};
@ -78,6 +79,7 @@ static const standard_attribute_index_entry kStandardAttributeIndices[] = {
MAKE_ATTRIBUTE_INDEX_ENTRY(DATA, RAW),
MAKE_ATTRIBUTE_INDEX_ENTRY(DATA_SIZE, UINT),
MAKE_ATTRIBUTE_INDEX_ENTRY(DATA_COMPRESSION, UINT),
MAKE_ATTRIBUTE_INDEX_ENTRY(DATA_CHUNK_SIZE, UINT),
MAKE_ATTRIBUTE_INDEX_ENTRY(SYMLINK_PATH, STRING),
{}
};
@ -250,6 +252,10 @@ struct PackageReader::DataAttributeHandler : AttributeHandler {
fData->SetCompression(value.unsignedInt);
return B_OK;
}
case ATTRIBUTE_INDEX_DATA_CHUNK_SIZE:
fData->SetChunkSize(value.unsignedInt);
return B_OK;
}
return AttributeHandler::HandleChildAttribute(context, type, typeIndex,

View File

@ -27,6 +27,11 @@
#include "FDCloser.h"
#include "Stacker.h"
#include "ZlibCompressor.h"
// minimum length of data we require before trying to zlib compress them
static const size_t kZlibCompressionSizeThreshold = 64;
// #pragma mark - Data interface
@ -536,7 +541,7 @@ PackageWriter::_Init(const char* fileName)
throw std::bad_alloc();
// allocate data buffer
fDataBufferSize = 128 * 1024;
fDataBufferSize = 2 * B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB;
fDataBuffer = malloc(fDataBufferSize);
if (fDataBuffer == NULL)
throw std::bad_alloc();
@ -1144,13 +1149,51 @@ PackageWriter::_GetAttributeType(const char* attributeName, uint8 type)
status_t
PackageWriter::_AddData(Data& data, off_t size)
{
uint64 dataOffset = fHeapEnd - fHeapOffset;
uint64 dataOffset = fHeapEnd;
uint64 compression = B_HPKG_COMPRESSION_NONE;
uint64 compressedSize;
status_t error = _WriteZlibCompressedData(data, size, dataOffset,
compressedSize);
if (error == B_OK) {
compression = B_HPKG_COMPRESSION_ZLIB;
} else {
error = _WriteUncompressedData(data, size, dataOffset);
compressedSize = size;
}
if (error != B_OK)
return error;
fHeapEnd = dataOffset + compressedSize;
// add data attribute
Attribute* dataAttribute = _AddDataAttribute(B_HPKG_ATTRIBUTE_NAME_DATA,
compressedSize, dataOffset - fHeapOffset);
Stacker<Attribute> attributeAttributeStacker(fTopAttribute, dataAttribute);
// if compressed, add compression attributes
if (compression != B_HPKG_COMPRESSION_NONE) {
_AddAttribute(B_HPKG_ATTRIBUTE_NAME_DATA_COMPRESSION, compression);
_AddAttribute(B_HPKG_ATTRIBUTE_NAME_DATA_SIZE, (uint64)size);
// uncompressed size
}
// TODO: Support inline data!
return B_OK;
}
status_t
PackageWriter::_WriteUncompressedData(Data& data, off_t size,
uint64 writeOffset)
{
// copy the data to the heap
off_t readOffset = 0;
off_t remainingSize = size;
while (remainingSize > 0) {
// read from FD
// read data
size_t toCopy = std::min(remainingSize, (off_t)fDataBufferSize);
ssize_t bytesRead = data.Read(fDataBuffer, toCopy, readOffset);
if (bytesRead < 0) {
@ -1164,8 +1207,7 @@ PackageWriter::_AddData(Data& data, off_t size)
}
// write to heap
ssize_t bytesWritten = pwrite(fFD, fDataBuffer, bytesRead,
fHeapEnd);
ssize_t bytesWritten = pwrite(fFD, fDataBuffer, bytesRead, writeOffset);
if (bytesWritten < 0) {
fprintf(stderr, "Error: Failed to write data: %s\n",
strerror(errno));
@ -1178,19 +1220,118 @@ PackageWriter::_AddData(Data& data, off_t size)
remainingSize -= bytesRead;
readOffset += bytesRead;
fHeapEnd += bytesRead;
writeOffset += bytesRead;
}
// add data attribute
Attribute* dataAttribute = _AddDataAttribute(B_HPKG_ATTRIBUTE_NAME_DATA,
size, dataOffset);
Stacker<Attribute> attributeAttributeStacker(fTopAttribute, dataAttribute);
// _AddAttribute(B_HPKG_ATTRIBUTE_NAME_DATA_SIZE, (uint64)size);
// uncompressed size -- not required for uncompressed data
// TODO: Support inline data!
// TODO: Support compression!
return B_OK;
}
status_t
PackageWriter::_WriteZlibCompressedData(Data& data, off_t size,
uint64 writeOffset, uint64& _compressedSize)
{
// Use zlib compression only for data large enough.
if (size < kZlibCompressionSizeThreshold)
return B_BAD_VALUE;
// fDataBuffer is 2 * B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB, so split it into
// two halves we can use for reading and compressing
const size_t chunkSize = B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB;
uint8* inputBuffer = (uint8*)fDataBuffer;
uint8* outputBuffer = (uint8*)fDataBuffer + chunkSize;
// account for the offset table
uint64 chunkCount = (size + (chunkSize - 1)) / chunkSize;
off_t offsetTableOffset = writeOffset;
uint64* offsetTable = NULL;
if (chunkCount > 1) {
offsetTable = new uint64[chunkCount - 1];
writeOffset = offsetTableOffset + (chunkCount - 1) * sizeof(uint64);
}
ArrayDeleter<uint64> offsetTableDeleter(offsetTable);
const uint64 dataOffset = writeOffset;
const uint64 dataEndLimit = offsetTableOffset + size;
// read the data, compress them and write them to the heap
off_t readOffset = 0;
off_t remainingSize = size;
uint64 chunkIndex = 0;
while (remainingSize > 0) {
// read data
size_t toCopy = std::min(remainingSize, (off_t)chunkSize);
ssize_t bytesRead = data.Read(inputBuffer, toCopy, readOffset);
if (bytesRead < 0) {
fprintf(stderr, "Error: Failed to read data: %s\n",
strerror(errno));
return errno;
}
if ((size_t)bytesRead != toCopy) {
fprintf(stderr, "Error: Failed to read all data\n");
return B_ERROR;
}
// compress
ZlibCompressor compressor;
size_t compressedSize;
status_t error = compressor.Compress(inputBuffer, bytesRead,
outputBuffer, bytesRead, compressedSize);
const void* writeBuffer;
size_t bytesToWrite;
if (error == B_OK) {
writeBuffer = outputBuffer;
bytesToWrite = compressedSize;
} else {
if (error != B_BUFFER_OVERFLOW)
return error;
writeBuffer = inputBuffer;
bytesToWrite = bytesRead;
}
// check the total compressed data size
if (writeOffset + bytesToWrite >= dataEndLimit)
return B_BUFFER_OVERFLOW;
if (chunkIndex > 0)
offsetTable[chunkIndex - 1] = writeOffset - dataOffset;
// write to heap
ssize_t bytesWritten = pwrite(fFD, writeBuffer, bytesToWrite,
writeOffset);
if (bytesWritten < 0) {
fprintf(stderr, "Error: Failed to write data: %s\n",
strerror(errno));
return errno;
}
if ((size_t)bytesWritten != bytesToWrite) {
fprintf(stderr, "Error: Failed to write all data\n");
return B_ERROR;
}
remainingSize -= bytesRead;
readOffset += bytesRead;
writeOffset += bytesToWrite;
chunkIndex++;
}
// write the offset table
if (chunkCount > 1) {
size_t bytesToWrite = (chunkCount - 1) * sizeof(uint64);
ssize_t bytesWritten = pwrite(fFD, offsetTable, bytesToWrite,
offsetTableOffset);
if (bytesWritten < 0) {
fprintf(stderr, "Error: Failed to write data: %s\n",
strerror(errno));
return errno;
}
if ((size_t)bytesWritten != bytesToWrite) {
fprintf(stderr, "Error: Failed to write all data\n");
return B_ERROR;
}
}
_compressedSize = writeOffset - dataOffset;
return B_OK;
}

View File

@ -83,6 +83,11 @@ private:
uint8 type);
status_t _AddData(Data& data, off_t size);
status_t _WriteUncompressedData(Data& data, off_t size,
uint64 writeOffset);
status_t _WriteZlibCompressedData(Data& data, off_t size,
uint64 writeOffset,
uint64& _compressedSize);
private:
const char* fFileName;

View File

@ -0,0 +1,40 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "ZlibCompressionBase.h"
#include <errno.h>
#include <zlib.h>
/*static*/ status_t
ZlibCompressionBase::TranslateZlibError(int error)
{
switch (error) {
case Z_OK:
return B_OK;
case Z_STREAM_END:
case Z_NEED_DICT:
// a special event (no error), but the caller doesn't seem to handle
// it
return B_ERROR;
case Z_ERRNO:
return errno;
case Z_STREAM_ERROR:
return B_BAD_VALUE;
case Z_DATA_ERROR:
return B_BAD_DATA;
case Z_MEM_ERROR:
return B_NO_MEMORY;
case Z_BUF_ERROR:
return B_BUFFER_OVERFLOW;
case Z_VERSION_ERROR:
return B_BAD_VALUE;
default:
return B_ERROR;
}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef ZLIB_COMPRESSION_BASE_H
#define ZLIB_COMPRESSION_BASE_H
#include <SupportDefs.h>
class ZlibCompressionBase {
public:
static status_t TranslateZlibError(int error);
};
#endif // ZLIB_COMPRESSION_BASE_H

View File

@ -0,0 +1,74 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "ZlibDecompressor.h"
#include <errno.h>
#include <zlib.h>
ZlibDecompressor::ZlibDecompressor()
{
}
ZlibDecompressor::~ZlibDecompressor()
{
}
status_t
ZlibDecompressor::Decompress(const void* input, size_t inputSize, void* output,
size_t outputSize, size_t& _uncompressedSize)
{
if (inputSize == 0 || outputSize == 0)
return B_BAD_VALUE;
// prepare stream
z_stream zStream = {
(Bytef*)input, // next_in
inputSize, // avail_in
0, // total_in
(Bytef*)output, // next_out
outputSize, // avail_out
0, // total_out
0, // msg
0, // state;
Z_NULL, // zalloc
Z_NULL, // zfree
Z_NULL, // opaque
0, // data_type
0, // adler
0 // reserved
};
int zlibError = inflateInit(&zStream);
if (zlibError != Z_OK)
return TranslateZlibError(zlibError);
// deflate
status_t error = B_OK;
zlibError = inflate(&zStream, Z_FINISH);
if (zlibError != Z_STREAM_END) {
if (zlibError == Z_OK)
error = B_BUFFER_OVERFLOW;
else
error = TranslateZlibError(zlibError);
}
// clean up
zlibError = inflateEnd(&zStream);
if (zlibError != Z_OK && error == B_OK)
error = TranslateZlibError(zlibError);
if (error != B_OK)
return error;
_uncompressedSize = zStream.total_out;
return B_OK;
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef ZLIB_COMPRESSOR_H
#define ZLIB_COMPRESSOR_H
#include "ZlibCompressionBase.h"
class ZlibCompressor : public ZlibCompressionBase {
public:
ZlibCompressor();
~ZlibCompressor();
status_t Compress(const void* input, size_t inputSize,
void* output, size_t outputSize,
size_t& _compressedSize);
};
#endif // ZLIB_COMPRESSOR_H

View File

@ -0,0 +1,73 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "ZlibCompressor.h"
#include <errno.h>
#include <zlib.h>
ZlibCompressor::ZlibCompressor()
{
}
ZlibCompressor::~ZlibCompressor()
{
}
status_t
ZlibCompressor::Compress(const void* input, size_t inputSize, void* output,
size_t outputSize, size_t& _compressedSize)
{
if (inputSize == 0 || outputSize == 0)
return B_BAD_VALUE;
// prepare stream
z_stream zStream = {
(Bytef*)input, // next_in
inputSize, // avail_in
0, // total_in
(Bytef*)output, // next_out
outputSize, // avail_out
0, // total_out
0, // msg
0, // state;
Z_NULL, // zalloc
Z_NULL, // zfree
Z_NULL, // opaque
0, // data_type
0, // adler
0 // reserved
};
int zlibError = deflateInit(&zStream, Z_BEST_COMPRESSION);
if (zlibError != Z_OK)
return TranslateZlibError(zlibError);
// deflate
status_t error = B_OK;
zlibError = deflate(&zStream, Z_FINISH);
if (zlibError != Z_STREAM_END) {
if (zlibError == Z_OK)
error = B_BUFFER_OVERFLOW;
else
error = TranslateZlibError(zlibError);
}
// clean up
zlibError = deflateEnd(&zStream);
if (zlibError != Z_OK && error == B_OK)
error = TranslateZlibError(zlibError);
if (error != B_OK)
return error;
_compressedSize = zStream.total_out;
return B_OK;
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef ZLIB_DECOMPRESSOR_H
#define ZLIB_DECOMPRESSOR_H
#include "ZlibCompressionBase.h"
class ZlibDecompressor : public ZlibCompressionBase {
public:
ZlibDecompressor();
~ZlibDecompressor();
status_t Decompress(const void* input, size_t inputSize,
void* output, size_t outputSize,
size_t& _uncompressedSize);
};
#endif // ZLIB_DECOMPRESSOR_H