Files
Aerofoil/PortabilityLayer/DeflateCodec.cpp
2020-11-28 11:45:29 -05:00

220 lines
4.7 KiB
C++

#include "DeflateCodec.h"
#include "GpIOStream.h"
#include "MemoryManager.h"
#ifdef GP_ZLIB_BUILTIN
#include <zlib.h>
#else
#include "zlib.h"
#endif
#include <string.h>
namespace
{
static voidpf ZlibAllocShim(voidpf opaque, uInt items, uInt size)
{
return static_cast<PortabilityLayer::MemoryManager*>(opaque)->Alloc(items * size);
}
void ZlibFreeShim(voidpf opaque, voidpf address)
{
static_cast<PortabilityLayer::MemoryManager*>(opaque)->Release(address);
}
}
bool PortabilityLayer::DeflateCodec::DecompressStream(GpIOStream *stream, size_t inSize, void *outBuffer, size_t outSize)
{
z_stream zstream;
zstream.zalloc = ZlibAllocShim;
zstream.zfree = ZlibFreeShim;
zstream.opaque = MemoryManager::GetInstance();
if (inflateInit2(&zstream, -15) != Z_OK)
return false;
const size_t bufferSize = 1024;
uint8_t buffer[1024];
zstream.avail_out = outSize;
zstream.next_out = static_cast<Bytef*>(outBuffer);
zstream.avail_in = 0;
zstream.next_in = buffer;
bool failed = false;
for (;;)
{
if (zstream.avail_in == 0)
{
const size_t sizeToRead = (bufferSize < inSize) ? bufferSize : inSize;
if (sizeToRead == 0)
{
// Ran out of input
failed = true;
break;
}
if (stream->Read(buffer, sizeToRead) != sizeToRead)
{
failed = true;
break;
}
zstream.avail_in = sizeToRead;
zstream.next_in = buffer;
inSize -= sizeToRead;
}
int result = inflate(&zstream, Z_NO_FLUSH);
if (result == Z_STREAM_END)
{
failed = (zstream.avail_out != 0);
break;
}
if (result != Z_OK)
{
failed = true;
break;
}
}
inflateEnd(&zstream);
return !failed;
}
namespace PortabilityLayer
{
class DeflateContextImpl final : public DeflateContext
{
public:
DeflateContextImpl(GpIOStream *stream, int compressionLevel);
~DeflateContextImpl();
bool Init();
void Destroy() override;
bool Append(const void *buffer, size_t size) override;
bool Flush() override;
private:
GpIOStream *m_ioStream;
z_stream m_zStream;
int m_compressionLevel;
bool m_streamInitialized;
uint8_t m_flushBuffer[1024];
};
}
PortabilityLayer::DeflateContextImpl::DeflateContextImpl(GpIOStream *stream, int compressionLevel)
: m_ioStream(stream)
, m_compressionLevel(compressionLevel)
, m_streamInitialized(false)
{
memset(&m_zStream, 0, sizeof(m_zStream));
}
PortabilityLayer::DeflateContextImpl::~DeflateContextImpl()
{
if (m_streamInitialized)
deflateEnd(&m_zStream);
}
bool PortabilityLayer::DeflateContextImpl::Init()
{
m_zStream.zalloc = ZlibAllocShim;
m_zStream.zfree = ZlibFreeShim;
m_zStream.opaque = MemoryManager::GetInstance();
if (deflateInit2(&m_zStream, m_compressionLevel, Z_DEFLATED, -15, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK)
return false;
m_streamInitialized = true;
return true;
}
void PortabilityLayer::DeflateContextImpl::Destroy()
{
this->~DeflateContextImpl();
PortabilityLayer::MemoryManager::GetInstance()->Release(this);
}
bool PortabilityLayer::DeflateContextImpl::Append(const void *buffer, size_t size)
{
m_zStream.avail_out = sizeof(m_flushBuffer);
m_zStream.next_out = m_flushBuffer;
m_zStream.next_in = reinterpret_cast<Bytef*>(const_cast<void*>(buffer));
m_zStream.avail_in = size;
while (m_zStream.avail_in > 0)
{
if (deflate(&m_zStream, Z_NO_FLUSH) != Z_OK)
return false;
if (m_zStream.avail_out != sizeof(m_flushBuffer))
{
size_t amountOut = sizeof(m_flushBuffer) - m_zStream.avail_out;
if (m_ioStream->Write(m_flushBuffer, amountOut) != amountOut)
return false;
m_zStream.avail_out = sizeof(m_flushBuffer);
m_zStream.next_out = m_flushBuffer;
}
}
return true;
}
bool PortabilityLayer::DeflateContextImpl::Flush()
{
for (;;)
{
const int deflateCode = deflate(&m_zStream, Z_FINISH);
if (deflateCode != Z_OK && deflateCode != Z_STREAM_END)
return false;
if (m_zStream.avail_out != sizeof(m_flushBuffer))
{
size_t amountOut = sizeof(m_flushBuffer) - m_zStream.avail_out;
if (m_ioStream->Write(m_flushBuffer, amountOut) != amountOut)
return false;
m_zStream.avail_out = sizeof(m_flushBuffer);
m_zStream.next_out = m_flushBuffer;
}
if (deflateCode == Z_STREAM_END)
break;
}
return true;
}
PortabilityLayer::DeflateContext *PortabilityLayer::DeflateContext::Create(GpIOStream *stream, int compressionLevel)
{
void *storage = PortabilityLayer::MemoryManager::GetInstance()->Alloc(sizeof(PortabilityLayer::DeflateContextImpl));
if (!storage)
return nullptr;
DeflateContextImpl *obj = new (storage) DeflateContextImpl(stream, compressionLevel);
if (!obj->Init())
{
obj->Destroy();
return nullptr;
}
return obj;
}
uint32_t PortabilityLayer::DeflateContext::CRC32(uint32_t inputValue, const void *buffer, size_t bufferLength)
{
return crc32(inputValue, static_cast<const Bytef*>(buffer), bufferLength);
}