Add B[Zlib]CompressionAlgorithm

* BCompressionAlgorithm is a base class for classes that provide
  compression/decompression functionality. There are methods for
  compressing/decompressing a single buffer and factory methods for
  a compressing/decompressing input/output BDataIO.
* BZlibCompressionAlgorithm is a BCompressionAlgorithm implementation
  using zlib.
This commit is contained in:
Ingo Weinhold 2014-06-29 18:21:44 +02:00
parent 6a89a36aa0
commit dcdc33b0af
8 changed files with 958 additions and 0 deletions

View File

@ -0,0 +1 @@
#include <../private/support/CompressionAlgorithm.h>

View File

@ -0,0 +1 @@
#include <../private/support/ZlibCompressionAlgorithm.h>

View File

@ -0,0 +1,121 @@
/*
* Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef _COMPRESSION_ALGORITHM_H_
#define _COMPRESSION_ALGORITHM_H_
#include <DataIO.h>
class BCompressionParameters {
public:
BCompressionParameters();
virtual ~BCompressionParameters();
};
class BDecompressionParameters {
public:
BDecompressionParameters();
virtual ~BDecompressionParameters();
};
class BCompressionAlgorithm {
public:
BCompressionAlgorithm();
virtual ~BCompressionAlgorithm();
virtual status_t CreateCompressingInputStream(BDataIO* input,
const BCompressionParameters* parameters,
BDataIO*& _stream);
virtual status_t CreateCompressingOutputStream(BDataIO* output,
const BCompressionParameters* parameters,
BDataIO*& _stream);
virtual status_t CreateDecompressingInputStream(BDataIO* input,
const BDecompressionParameters* parameters,
BDataIO*& _stream);
virtual status_t CreateDecompressingOutputStream(BDataIO* output,
const BDecompressionParameters* parameters,
BDataIO*& _stream);
virtual status_t CompressBuffer(const void* input,
size_t inputSize, void* output,
size_t outputSize, size_t& _compressedSize,
const BCompressionParameters* parameters
= NULL);
virtual status_t DecompressBuffer(const void* input,
size_t inputSize, void* output,
size_t outputSize,
size_t& _uncompressedSize,
const BDecompressionParameters* parameters
= NULL);
protected:
class BAbstractStream;
class BAbstractInputStream;
class BAbstractOutputStream;
};
class BCompressionAlgorithm::BAbstractStream : public BDataIO {
public:
BAbstractStream();
virtual ~BAbstractStream();
status_t Init(size_t bufferSize);
protected:
virtual status_t ProcessData(const void* input, size_t inputSize,
void* output, size_t outputSize,
size_t& bytesConsumed,
size_t& bytesProduced) = 0;
// must consume or produce at least 1 byte
// or return an error
virtual status_t FlushPendingData(void* output,
size_t outputSize,
size_t& bytesProduced) = 0;
protected:
uint8* fBuffer;
size_t fBufferCapacity;
size_t fBufferOffset;
size_t fBufferSize;
};
class BCompressionAlgorithm::BAbstractInputStream : public BAbstractStream {
public:
BAbstractInputStream(BDataIO* input);
virtual ~BAbstractInputStream();
virtual ssize_t Read(void* buffer, size_t size);
private:
BDataIO* fInput;
bool fEndOfInput;
bool fNoMorePendingData;
};
class BCompressionAlgorithm::BAbstractOutputStream : public BAbstractStream {
public:
BAbstractOutputStream(BDataIO* output);
virtual ~BAbstractOutputStream();
virtual ssize_t Write(const void* buffer, size_t size);
virtual status_t Flush();
private:
ssize_t _Write(const void* buffer, size_t size,
bool flush);
private:
BDataIO* fOutput;
};
#endif // _COMPRESSION_ALGORITHM_H_

View File

@ -0,0 +1,96 @@
/*
* Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef _ZLIB_COMPRESSION_ALGORITHM_H_
#define _ZLIB_COMPRESSION_ALGORITHM_H_
#include <CompressionAlgorithm.h>
// compression level
enum {
B_ZLIB_COMPRESSION_NONE = 0,
B_ZLIB_COMPRESSION_FASTEST = 1,
B_ZLIB_COMPRESSION_BEST = 9,
B_ZLIB_COMPRESSION_DEFAULT = -1,
};
class BZlibCompressionParameters : public BCompressionParameters {
public:
BZlibCompressionParameters(
int compressionLevel
= B_ZLIB_COMPRESSION_DEFAULT);
virtual ~BZlibCompressionParameters();
int32 CompressionLevel() const;
void SetCompressionLevel(int32 level);
size_t BufferSize() const;
void SetBufferSize(size_t size);
private:
int32 fCompressionLevel;
size_t fBufferSize;
};
class BZlibDecompressionParameters : public BDecompressionParameters {
public:
BZlibDecompressionParameters();
virtual ~BZlibDecompressionParameters();
size_t BufferSize() const;
void SetBufferSize(size_t size);
private:
size_t fBufferSize;
};
class BZlibCompressionAlgorithm : public BCompressionAlgorithm {
public:
BZlibCompressionAlgorithm();
virtual ~BZlibCompressionAlgorithm();
virtual status_t CreateCompressingInputStream(BDataIO* input,
const BCompressionParameters* parameters,
BDataIO*& _stream);
virtual status_t CreateCompressingOutputStream(BDataIO* output,
const BCompressionParameters* parameters,
BDataIO*& _stream);
virtual status_t CreateDecompressingInputStream(BDataIO* input,
const BDecompressionParameters* parameters,
BDataIO*& _stream);
virtual status_t CreateDecompressingOutputStream(BDataIO* output,
const BDecompressionParameters* parameters,
BDataIO*& _stream);
virtual status_t CompressBuffer(const void* input,
size_t inputSize, void* output,
size_t outputSize, size_t& _compressedSize,
const BCompressionParameters* parameters
= NULL);
virtual status_t DecompressBuffer(const void* input,
size_t inputSize, void* output,
size_t outputSize,
size_t& _uncompressedSize,
const BDecompressionParameters* parameters
= NULL);
private:
struct CompressionStrategy;
struct DecompressionStrategy;
template<typename BaseClass, typename Strategy> struct Stream;
template<typename BaseClass, typename Strategy>
friend struct Stream;
private:
static status_t _TranslateZlibError(int error);
};
#endif // _ZLIB_COMPRESSION_ALGORITHM_H_

View File

@ -10,6 +10,7 @@ BuildPlatformMergeObjectPIC <libbe_build>support_kit.o :
Archivable.cpp
BlockCache.cpp
ByteOrder.cpp
CompressionAlgorithm.cpp
DataIO.cpp
Flattenable.cpp
List.cpp
@ -18,6 +19,7 @@ BuildPlatformMergeObjectPIC <libbe_build>support_kit.o :
Referenceable.cpp
String.cpp
StringList.cpp
ZlibCompressionAlgorithm.cpp
ZlibCompressionBase.cpp
ZlibCompressor.cpp
ZlibDecompressor.cpp

View File

@ -0,0 +1,328 @@
/*
* Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <CompressionAlgorithm.h>
#include <stdlib.h>
#include <string.h>
#include <Errors.h>
// #pragma mark - BCompressionParameters
BCompressionParameters::BCompressionParameters()
{
}
BCompressionParameters::~BCompressionParameters()
{
}
// #pragma mark - BDecompressionParameters
BDecompressionParameters::BDecompressionParameters()
{
}
BDecompressionParameters::~BDecompressionParameters()
{
}
// #pragma mark - BCompressionAlgorithm
BCompressionAlgorithm::BCompressionAlgorithm()
{
}
BCompressionAlgorithm::~BCompressionAlgorithm()
{
}
status_t
BCompressionAlgorithm::CreateCompressingInputStream(BDataIO* input,
const BCompressionParameters* parameters, BDataIO*& _stream)
{
return B_NOT_SUPPORTED;
}
status_t
BCompressionAlgorithm::CreateCompressingOutputStream(BDataIO* output,
const BCompressionParameters* parameters, BDataIO*& _stream)
{
return B_NOT_SUPPORTED;
}
status_t
BCompressionAlgorithm::CreateDecompressingInputStream(BDataIO* input,
const BDecompressionParameters* parameters, BDataIO*& _stream)
{
return B_NOT_SUPPORTED;
}
status_t
BCompressionAlgorithm::CreateDecompressingOutputStream(BDataIO* output,
const BDecompressionParameters* parameters, BDataIO*& _stream)
{
return B_NOT_SUPPORTED;
}
status_t
BCompressionAlgorithm::CompressBuffer(const void* input, size_t inputSize,
void* output, size_t outputSize, size_t& _compressedSize,
const BCompressionParameters* parameters)
{
return B_NOT_SUPPORTED;
}
status_t
BCompressionAlgorithm::DecompressBuffer(const void* input,
size_t inputSize, void* output, size_t outputSize,
size_t& _uncompressedSize, const BDecompressionParameters* parameters)
{
return B_NOT_SUPPORTED;
}
// #pragma mark - BAbstractStream
BCompressionAlgorithm::BAbstractStream::BAbstractStream()
:
BDataIO(),
fBuffer(NULL),
fBufferCapacity(0),
fBufferOffset(0),
fBufferSize(0)
{
}
BCompressionAlgorithm::BAbstractStream::~BAbstractStream()
{
free(fBuffer);
}
status_t
BCompressionAlgorithm::BAbstractStream::Init(size_t bufferSize)
{
fBuffer = (uint8*)malloc(bufferSize);
fBufferCapacity = bufferSize;
return fBuffer != NULL ? B_OK : B_NO_MEMORY;
}
// #pragma mark - BAbstractInputStream
BCompressionAlgorithm::BAbstractInputStream::BAbstractInputStream(
BDataIO* input)
:
BAbstractStream(),
fInput(input),
fEndOfInput(false),
fNoMorePendingData(false)
{
}
BCompressionAlgorithm::BAbstractInputStream::~BAbstractInputStream()
{
}
ssize_t
BCompressionAlgorithm::BAbstractInputStream::Read(void* buffer, size_t size)
{
if (size == 0)
return 0;
size_t bytesRemaining = size;
uint8* output = (uint8*)buffer;
while (bytesRemaining > 0) {
// process the data still in the input buffer
if (fBufferSize > 0) {
size_t bytesConsumed;
size_t bytesProduced;
status_t error = ProcessData(fBuffer + fBufferOffset, fBufferSize,
output, bytesRemaining, bytesConsumed, bytesProduced);
if (error != B_OK)
return error;
fBufferOffset += bytesConsumed;
fBufferSize -= bytesConsumed;
output += bytesProduced;
bytesRemaining -= bytesProduced;
continue;
}
// We couldn't process anything, because we don't have any or not enough
// bytes in the input buffer.
if (fEndOfInput)
break;
// Move any remaining data to the start of the buffer.
if (fBufferSize > 0) {
if (fBufferSize == fBufferCapacity)
return B_ERROR;
if (fBufferOffset > 0)
memmove(fBuffer, fBuffer + fBufferOffset, fBufferSize);
}
fBufferOffset = 0;
// read from the source
ssize_t bytesRead = fInput->Read(fBuffer + fBufferSize,
fBufferCapacity - fBufferSize);
if (bytesRead < 0)
return bytesRead;
if (bytesRead == 0) {
fEndOfInput = true;
break;
}
fBufferSize += bytesRead;
}
// If we've reached the end of the input and still have room in the output
// buffer, we have consumed all input data and want to flush all pending
// data, now.
if (fEndOfInput && bytesRemaining > 0 && !fNoMorePendingData) {
size_t bytesProduced;
status_t error = FlushPendingData(output, bytesRemaining,
bytesProduced);
if (error != B_OK)
return error;
if (bytesProduced < bytesRemaining)
fNoMorePendingData = true;
output += bytesProduced;
bytesRemaining -= bytesProduced;
}
return size - bytesRemaining;
}
// #pragma mark - BAbstractOutputStream
BCompressionAlgorithm::BAbstractOutputStream::BAbstractOutputStream(
BDataIO* output)
:
BAbstractStream(),
fOutput(output)
{
}
BCompressionAlgorithm::BAbstractOutputStream::~BAbstractOutputStream()
{
}
ssize_t
BCompressionAlgorithm::BAbstractOutputStream::Write(const void* buffer,
size_t size)
{
if (size == 0)
return 0;
size_t bytesRemaining = size;
uint8* input = (uint8*)buffer;
while (bytesRemaining > 0) {
// try to process more data
if (fBufferSize < fBufferCapacity) {
size_t bytesConsumed;
size_t bytesProduced;
status_t error = ProcessData(input, bytesRemaining,
fBuffer + fBufferSize, fBufferCapacity - fBufferSize,
bytesConsumed, bytesProduced);
if (error != B_OK)
return error;
input += bytesConsumed;
bytesRemaining -= bytesConsumed;
fBufferSize += bytesProduced;
continue;
}
// We couldn't process anything, because we don't have any or not enough
// room in the output buffer.
if (fBufferSize == 0)
return B_ERROR;
// write to the target
ssize_t bytesWritten = fOutput->Write(fBuffer, fBufferSize);
if (bytesWritten < 0)
return bytesWritten;
if (bytesWritten == 0)
break;
// Move any remaining data to the start of the buffer.
fBufferSize -= bytesWritten;
if (fBufferSize > 0)
memmove(fBuffer, fBuffer + bytesWritten, fBufferSize);
}
return size - bytesRemaining;
}
status_t
BCompressionAlgorithm::BAbstractOutputStream::Flush()
{
bool noMorePendingData = false;
for (;;) {
// let the derived class flush all pending data
if (fBufferSize < fBufferCapacity && !noMorePendingData) {
size_t bytesProduced;
status_t error = FlushPendingData(fBuffer + fBufferSize,
fBufferCapacity - fBufferSize, bytesProduced);
if (error != B_OK)
return error;
noMorePendingData = bytesProduced < fBufferCapacity - fBufferSize;
fBufferSize += bytesProduced;
}
// write buffered data to output
if (fBufferSize == 0)
break;
status_t error = fOutput->WriteExactly(fBuffer, fBufferSize);
if (error != B_OK)
return error;
fBufferSize = 0;
}
return fOutput->Flush();
}

View File

@ -24,6 +24,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
BufferedDataIO.cpp
BufferIO.cpp
ByteOrder.cpp
CompressionAlgorithm.cpp
DataIO.cpp
DateTime.cpp
Flattenable.cpp
@ -36,6 +37,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
StringList.cpp
Url.cpp
Uuid.cpp
ZlibCompressionAlgorithm.cpp
ZlibCompressionBase.cpp
ZlibCompressor.cpp
ZlibDecompressor.cpp

View File

@ -0,0 +1,407 @@
/*
* Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <ZlibCompressionAlgorithm.h>
#include <errno.h>
#include <string.h>
#include <algorithm>
#include <new>
#include <zlib.h>
#include <DataIO.h>
// build compression support only for userland
#if !defined(_KERNEL_MODE) && !defined(_BOOT_MODE)
# define B_ZLIB_COMPRESSION_SUPPORT 1
#endif
static const size_t kMinBufferSize = 1024;
static const size_t kMaxBufferSize = 1024 * 1024;
static const size_t kDefaultBufferSize = 4 * 1024;
static size_t
sanitize_buffer_size(size_t size)
{
if (size < kMinBufferSize)
return kMinBufferSize;
return std::min(size, kMaxBufferSize);
}
// #pragma mark - BZlibCompressionParameters
BZlibCompressionParameters::BZlibCompressionParameters(
int compressionLevel)
:
BCompressionParameters(),
fCompressionLevel(compressionLevel),
fBufferSize(kDefaultBufferSize)
{
}
BZlibCompressionParameters::~BZlibCompressionParameters()
{
}
int32
BZlibCompressionParameters::CompressionLevel() const
{
return fCompressionLevel;
}
void
BZlibCompressionParameters::SetCompressionLevel(int32 level)
{
fCompressionLevel = level;
}
size_t
BZlibCompressionParameters::BufferSize() const
{
return fBufferSize;
}
void
BZlibCompressionParameters::SetBufferSize(size_t size)
{
fBufferSize = sanitize_buffer_size(size);
}
// #pragma mark - BZlibDecompressionParameters
BZlibDecompressionParameters::BZlibDecompressionParameters()
:
BDecompressionParameters(),
fBufferSize(kDefaultBufferSize)
{
}
BZlibDecompressionParameters::~BZlibDecompressionParameters()
{
}
size_t
BZlibDecompressionParameters::BufferSize() const
{
return fBufferSize;
}
void
BZlibDecompressionParameters::SetBufferSize(size_t size)
{
fBufferSize = sanitize_buffer_size(size);
}
// #pragma mark - CompressionStrategy
#ifdef B_ZLIB_COMPRESSION_SUPPORT
struct BZlibCompressionAlgorithm::CompressionStrategy {
typedef BZlibCompressionParameters Parameters;
static const bool kNeedsFinalFlush = true;
static int Init(z_stream& stream,
const BZlibCompressionParameters* parameters)
{
return deflateInit(&stream,
parameters != NULL
? parameters->CompressionLevel()
: B_ZLIB_COMPRESSION_DEFAULT);
}
static void Uninit(z_stream& stream)
{
deflateEnd(&stream);
}
static int Process(z_stream& stream, bool flush)
{
return deflate(&stream, flush ? Z_FINISH : 0);
}
};
#endif // B_ZLIB_COMPRESSION_SUPPORT
// #pragma mark - DecompressionStrategy
struct BZlibCompressionAlgorithm::DecompressionStrategy {
typedef BZlibDecompressionParameters Parameters;
static const bool kNeedsFinalFlush = false;
static int Init(z_stream& stream,
const BZlibDecompressionParameters* /*parameters*/)
{
return inflateInit(&stream);
}
static void Uninit(z_stream& stream)
{
inflateEnd(&stream);
}
static int Process(z_stream& stream, bool flush)
{
return inflate(&stream, flush ? Z_FINISH : 0);
}
};
// #pragma mark - Stream
template<typename BaseClass, typename Strategy>
struct BZlibCompressionAlgorithm::Stream : BaseClass {
Stream(BDataIO* io)
:
BaseClass(io),
fStreamInitialized(false)
{
}
~Stream()
{
if (fStreamInitialized) {
if (Strategy::kNeedsFinalFlush)
this->Flush();
Strategy::Uninit(fStream);
}
}
status_t Init(const typename Strategy::Parameters* parameters)
{
status_t error = this->BaseClass::Init(parameters->BufferSize());
if (error != B_OK)
return error;
memset(&fStream, 0, sizeof(fStream));
int zlibError = Strategy::Init(fStream, parameters);
if (zlibError != Z_OK)
return _TranslateZlibError(zlibError);
fStreamInitialized = true;
return B_OK;
}
virtual status_t ProcessData(const void* input, size_t inputSize,
void* output, size_t outputSize, size_t& bytesConsumed,
size_t& bytesProduced)
{
return _ProcessData(input, inputSize, output, outputSize,
bytesConsumed, bytesProduced, false);
}
virtual status_t FlushPendingData(void* output, size_t outputSize,
size_t& bytesProduced)
{
size_t bytesConsumed;
return _ProcessData(NULL, 0, output, outputSize,
bytesConsumed, bytesProduced, true);
}
template<typename BaseParameters>
static status_t Create(BDataIO* io, BaseParameters* _parameters,
BDataIO*& _stream)
{
const typename Strategy::Parameters* parameters
= dynamic_cast<const typename Strategy::Parameters*>(_parameters);
Stream* stream = new(std::nothrow) Stream(io);
if (stream == NULL)
return B_NO_MEMORY;
status_t error = stream->Init(parameters);
if (error != B_OK) {
delete stream;
return error;
}
_stream = stream;
return B_OK;
}
private:
status_t _ProcessData(const void* input, size_t inputSize,
void* output, size_t outputSize, size_t& bytesConsumed,
size_t& bytesProduced, bool flush)
{
fStream.next_in = (Bytef*)input;
fStream.avail_in = inputSize;
fStream.next_out = (Bytef*)output;
fStream.avail_out = outputSize;
int zlibError = Strategy::Process(fStream, flush);
if (zlibError != Z_OK) {
if (zlibError == Z_STREAM_END) {
if (fStream.avail_in != 0)
return B_BAD_DATA;
} else
return _TranslateZlibError(zlibError);
}
bytesConsumed = inputSize - (size_t)fStream.avail_in;
bytesProduced = outputSize - (size_t)fStream.avail_out;
return B_OK;
}
private:
z_stream fStream;
bool fStreamInitialized;
};
// #pragma mark - BZlibCompressionAlgorithm
BZlibCompressionAlgorithm::BZlibCompressionAlgorithm()
:
BCompressionAlgorithm()
{
}
BZlibCompressionAlgorithm::~BZlibCompressionAlgorithm()
{
}
status_t
BZlibCompressionAlgorithm::CreateCompressingInputStream(BDataIO* input,
const BCompressionParameters* parameters, BDataIO*& _stream)
{
#ifdef B_ZLIB_COMPRESSION_SUPPORT
return Stream<BAbstractInputStream, CompressionStrategy>::Create(
input, parameters, _stream);
#else
return B_NOT_SUPPORTED;
#endif
}
status_t
BZlibCompressionAlgorithm::CreateCompressingOutputStream(BDataIO* output,
const BCompressionParameters* parameters, BDataIO*& _stream)
{
#ifdef B_ZLIB_COMPRESSION_SUPPORT
return Stream<BAbstractOutputStream, CompressionStrategy>::Create(
output, parameters, _stream);
#else
return B_NOT_SUPPORTED;
#endif
}
status_t
BZlibCompressionAlgorithm::CreateDecompressingInputStream(BDataIO* input,
const BDecompressionParameters* parameters, BDataIO*& _stream)
{
return Stream<BAbstractInputStream, DecompressionStrategy>::Create(
input, parameters, _stream);
}
status_t
BZlibCompressionAlgorithm::CreateDecompressingOutputStream(BDataIO* output,
const BDecompressionParameters* parameters, BDataIO*& _stream)
{
return Stream<BAbstractOutputStream, DecompressionStrategy>::Create(
output, parameters, _stream);
}
status_t
BZlibCompressionAlgorithm::CompressBuffer(const void* input,
size_t inputSize, void* output, size_t outputSize, size_t& _compressedSize,
const BCompressionParameters* parameters)
{
#ifdef B_ZLIB_COMPRESSION_SUPPORT
const BZlibCompressionParameters* zlibParameters
= dynamic_cast<const BZlibCompressionParameters*>(parameters);
int compressionLevel = zlibParameters != NULL
? zlibParameters->CompressionLevel()
: B_ZLIB_COMPRESSION_DEFAULT;
uLongf bytesUsed = outputSize;
int zlibError = compress2((Bytef*)output, &bytesUsed, (const Bytef*)input,
(uLong)inputSize, compressionLevel);
if (zlibError != Z_OK)
return _TranslateZlibError(zlibError);
_compressedSize = (size_t)bytesUsed;
return B_OK;
#else
return B_NOT_SUPPORTED;
#endif
}
status_t
BZlibCompressionAlgorithm::DecompressBuffer(const void* input,
size_t inputSize, void* output, size_t outputSize,
size_t& _uncompressedSize, const BDecompressionParameters* parameters)
{
uLongf bytesUsed = outputSize;
int zlibError = uncompress((Bytef*)output, &bytesUsed, (const Bytef*)input,
(uLong)inputSize);
if (zlibError != Z_OK)
return _TranslateZlibError(zlibError);
_uncompressedSize = (size_t)bytesUsed;
return B_OK;
}
/*static*/ status_t
BZlibCompressionAlgorithm::_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;
}
}