#include "DeflateCodec.h" #include "GpIOStream.h" #include "MemoryManager.h" #ifdef GP_ZLIB_BUILTIN #include #else #include "zlib.h" #endif #include namespace { static voidpf ZlibAllocShim(voidpf opaque, uInt items, uInt size) { return static_cast(opaque)->Alloc(items * size); } void ZlibFreeShim(voidpf opaque, voidpf address) { static_cast(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(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]; }; class InflateContextImpl final : public InflateContext { public: static InflateContext *Create(); void Destroy() override; bool Append(const void *buffer, size_t size, size_t &sizeConsumed) override; bool Read(void *buffer, size_t size, size_t &sizeRead) override; bool Reset() override; bool Init(); private: InflateContextImpl(); ~InflateContextImpl(); bool m_streamInitialized; bool m_isEndOfStream; z_stream m_zStream; uint8_t m_flushBuffer[1024]; const uint8_t *m_readPos; }; } 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(const_cast(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::InflateContext *PortabilityLayer::InflateContextImpl::Create() { void *storage = PortabilityLayer::MemoryManager::GetInstance()->Alloc(sizeof(PortabilityLayer::InflateContextImpl)); if (!storage) return nullptr; InflateContextImpl *obj = new (storage) InflateContextImpl(); if (!obj->Init()) { obj->Destroy(); return nullptr; } return obj; } void PortabilityLayer::InflateContextImpl::Destroy() { this->~InflateContextImpl(); PortabilityLayer::MemoryManager::GetInstance()->Release(this); } bool PortabilityLayer::InflateContextImpl::Append(const void *buffer, size_t size, size_t &sizeConsumed) { size_t consumed = 0; m_zStream.avail_in = size; m_zStream.next_in = static_cast(const_cast(buffer)); for (;;) { if (m_isEndOfStream) { m_zStream.avail_in = 0; m_zStream.next_in = nullptr; } if (m_zStream.avail_in == 0 || m_zStream.avail_out == 0) { sizeConsumed = consumed; return true; } size_t lastAvailIn = m_zStream.avail_in; int result = inflate(&m_zStream, Z_NO_FLUSH); if (result == Z_STREAM_END) m_isEndOfStream = true; else if (result != Z_OK) return false; consumed += lastAvailIn - m_zStream.avail_in; } } bool PortabilityLayer::InflateContextImpl::Read(void *buffer, size_t size, size_t &sizeRead) { size_t amountInOutputBuffer = static_cast(m_zStream.next_out) - m_readPos; if (size > amountInOutputBuffer) size = amountInOutputBuffer; if (size > 0) { if (buffer) memcpy(buffer, m_readPos, size); m_readPos += size; if (m_readPos == m_zStream.next_out) { m_zStream.avail_out = sizeof(m_flushBuffer); m_zStream.next_out = m_flushBuffer; m_readPos = m_flushBuffer; } } sizeRead = size; return true; } bool PortabilityLayer::InflateContextImpl::Reset() { if (inflateReset2(&m_zStream, -15) != Z_OK) return false; m_isEndOfStream = false; m_zStream.avail_out = sizeof(m_flushBuffer); m_zStream.next_out = m_flushBuffer; m_readPos = m_flushBuffer; return true; } bool PortabilityLayer::InflateContextImpl::Init() { m_zStream.zalloc = ZlibAllocShim; m_zStream.zfree = ZlibFreeShim; m_zStream.opaque = MemoryManager::GetInstance(); if (inflateInit2(&m_zStream, -15) != Z_OK) return false; m_zStream.next_out = m_flushBuffer; m_zStream.avail_out = sizeof(m_flushBuffer); m_streamInitialized = true; return true; } PortabilityLayer::InflateContextImpl::InflateContextImpl() : m_streamInitialized(false) , m_isEndOfStream(false) , m_readPos(m_flushBuffer) { memset(&m_zStream, 0, sizeof(m_zStream)); } PortabilityLayer::InflateContextImpl::~InflateContextImpl() { if (m_streamInitialized) inflateEnd(&m_zStream); } 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(buffer), bufferLength); } PortabilityLayer::InflateContext *PortabilityLayer::InflateContext::Create() { return InflateContextImpl::Create(); }