AuroraRuntime/Source/Compression/BlockDecompressor.cpp

511 lines
14 KiB
C++
Raw Normal View History

2021-06-27 21:25:29 +00:00
/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: BlockDecompressor.cpp
Date: 2021-6-17
Author: Reece
***/
2021-09-30 14:57:41 +00:00
#include <Source/RuntimeInternal.hpp>
2021-06-27 21:25:29 +00:00
#include "Compression.hpp"
#include "BlockDecompressor.hpp"
#include "bzlib.h"
#include "zstd.h"
#include "zlib.h"
#include "lz4.h"
#include "lz4frame.h"
2021-06-27 21:25:29 +00:00
namespace Aurora::Compression
{
bool BaseStream::ReadByProcessedN(void *buffer, AuUInt32 minimumInflated, AuStreamReadWrittenPair_t &pair, bool ingestUntilEOS)
2021-06-27 21:25:29 +00:00
{
2021-09-06 10:58:08 +00:00
AuUInt32 read {}, len {};
if (ingestUntilEOS)
2021-06-27 21:25:29 +00:00
{
2021-09-06 10:58:08 +00:00
while (this->_outbuffer.RemainingBytes() < minimumInflated)
2021-06-27 21:25:29 +00:00
{
2022-01-19 17:08:13 +00:00
auto toRead = minimumInflated ? AuMin(AuUInt32(4096), AuUInt32(minimumInflated - this->_outbuffer.RemainingBytes())) : 4096;
if (Ingest(toRead).second == 0)
2021-06-27 21:25:29 +00:00
{
2021-09-06 10:58:08 +00:00
if (!this->_outbuffer.RemainingBytes())
{
2021-09-06 10:58:08 +00:00
return false;
}
2021-09-06 10:58:08 +00:00
break;
2021-06-27 21:25:29 +00:00
}
2021-09-06 10:58:08 +00:00
read += toRead;
2021-06-27 21:25:29 +00:00
}
}
len = this->_outbuffer.Read(buffer, minimumInflated, buffer == nullptr);
2021-09-06 10:58:08 +00:00
pair = {read, len};
return len != 0;
}
bool BaseStream::ReadByProcessedN(void *buffer, AuUInt32 minimumInflated)
{
AuUInt32 read {}, len {};
len = this->_outbuffer.Read(buffer, minimumInflated, buffer == nullptr);
return len != 0;
}
bool BaseStream::GoBackByProcessedN(AuUInt32 offset)
2021-09-06 10:58:08 +00:00
{
return this->_outbuffer.ReaderTryGoBack(offset);
}
bool BaseStream::GoForwardByProcessedN(AuUInt32 offset)
2021-09-06 10:58:08 +00:00
{
return this->_outbuffer.ReaderTryGoForward(offset);
2021-06-27 21:25:29 +00:00
}
bool BaseStream::Write(const void *a, AuUInt32 length)
{
auto written = this->_outbuffer.Write(reinterpret_cast<const AuUInt8 *>(a),
length);
if (written != length)
{
2022-01-19 17:08:13 +00:00
auto increase = AuMax(0, (int)length - (int)this->_outbuffer.RemainingWrite());
increase += this->_outbuffer.length;
if (increase > 64 * 1024 * 1024)
{
return false;
}
if (!this->_outbuffer.Resize(increase))
{
return false;
}
auto remaining = length - written;
written = this->_outbuffer.Write(reinterpret_cast<const AuUInt8 *>(a) + written,
remaining);
if (written != remaining)
{
return false;
}
}
return true;
}
AuUInt32 BaseStream::GetInternalBufferSize()
{
return this->_outbuffer.allocSize;
}
2021-06-27 21:25:29 +00:00
class ZSTDInflate : public BaseStream
{
public:
DecompressInfo meta;
ZSTDInflate(const DecompressInfo &meta) : meta(meta), BaseStream(meta.internalStreamSize)
{}
2021-09-06 10:58:08 +00:00
2021-06-27 21:25:29 +00:00
~ZSTDInflate()
{
2022-01-19 17:08:13 +00:00
if (auto dctx = AuExchange(dctx_, {}))
2021-06-27 21:25:29 +00:00
{
ZSTD_freeDCtx(dctx);
}
}
bool Init(const AuSPtr<IO::IStreamReader> &reader)
2021-06-27 21:25:29 +00:00
{
2021-07-11 17:26:38 +00:00
this->reader_ = reader;
this->dctx_ = ZSTD_createDCtx();
2021-06-27 21:25:29 +00:00
2021-07-11 17:26:38 +00:00
if (!this->dctx_)
2021-06-27 21:25:29 +00:00
{
SysPushErrorGen("Couldn't create decompressor");
return false;
}
return true;
}
2021-09-06 10:58:08 +00:00
AuStreamReadWrittenPair_t Ingest(AuUInt32 input) override
2021-06-27 21:25:29 +00:00
{
AuUInt32 length = ZSTD_DStreamInSize();
AuUInt32 outFrameLength = ZSTD_DStreamOutSize();
AuUInt32 done{}, read{};
2021-06-27 21:25:29 +00:00
2021-09-06 15:47:35 +00:00
while (read != input)
2021-06-27 21:25:29 +00:00
{
2022-01-19 17:08:13 +00:00
AuUInt request = AuMin(input, length);
2021-09-15 01:14:29 +00:00
if (this->reader_->Read(Memory::MemoryViewStreamWrite(din_, request)) != IO::EStreamError::eErrorNone)
2021-06-27 21:25:29 +00:00
{
return AuMakePair(read, done);
2021-06-27 21:25:29 +00:00
}
read += request;
input_ = ZSTD_inBuffer{ din_, request, 0 };
2021-06-27 21:25:29 +00:00
while (input_.pos < input_.size)
2021-06-27 21:25:29 +00:00
{
ZSTD_outBuffer output = { dout_, outFrameLength, 0 };
2021-06-27 21:25:29 +00:00
auto ret = ZSTD_decompressStream(this->dctx_, &output, &input_);
2021-06-27 21:25:29 +00:00
if (ZSTD_isError(ret))
{
SysPushErrorIO("Compression error: {}", ret);
2021-09-06 10:58:08 +00:00
return AuMakePair(read, 0);
2021-06-27 21:25:29 +00:00
}
done += output.pos;
if (!Write(reinterpret_cast<const AuUInt8 *>(output.dst),
output.pos))
{
return {};
}
2021-06-27 21:25:29 +00:00
}
}
2021-09-06 10:58:08 +00:00
return AuMakePair(read, done);
2021-06-27 21:25:29 +00:00
}
private:
2021-07-11 17:26:38 +00:00
AuSPtr<IO::IStreamReader> reader_;
2021-07-11 17:26:38 +00:00
ZSTD_DCtx *dctx_;
char din_[ZSTD_BLOCKSIZE_MAX + 3 /*ZSTD_BLOCKHEADERSIZE*/];
char dout_[ZSTD_BLOCKSIZE_MAX];
ZSTD_inBuffer input_;
2021-06-27 21:25:29 +00:00
};
class ZIPInflate : public BaseStream
{
public:
DecompressInfo meta;
ZIPInflate(const DecompressInfo &meta) : meta(meta), BaseStream(meta.internalStreamSize)
{}
2021-06-27 21:25:29 +00:00
~ZIPInflate()
{
if (this->init_)
2021-06-27 21:25:29 +00:00
{
2021-07-11 17:26:38 +00:00
inflateEnd(&this->ctx_);
2021-06-27 21:25:29 +00:00
}
}
bool Init(const AuSPtr<IO::IStreamReader> &reader)
2021-06-27 21:25:29 +00:00
{
2021-07-11 17:26:38 +00:00
this->reader_ = reader;
2021-06-27 21:25:29 +00:00
2021-07-11 17:26:38 +00:00
auto ret = inflateInit(&this->ctx_);
if (ret < Z_OK)
2021-06-27 21:25:29 +00:00
{
SysPushErrorMem("Error: {}", ret);
return false;
}
2021-07-11 17:26:38 +00:00
this->init_ = true;
2021-06-27 21:25:29 +00:00
return true;
}
2021-09-06 10:58:08 +00:00
AuStreamReadWrittenPair_t Ingest(AuUInt32 input) override
2021-06-27 21:25:29 +00:00
{
int ret;
AuUInt32 done{}, read{};
2021-06-27 21:25:29 +00:00
2021-09-06 15:47:35 +00:00
while (read < input)
2021-06-27 21:25:29 +00:00
{
2022-01-19 17:08:13 +00:00
AuUInt request = AuMin(input, AuUInt32(AuArraySize(din_)));
2021-09-15 01:14:29 +00:00
if (this->reader_->Read(Memory::MemoryViewStreamWrite(din_, request)) != IO::EStreamError::eErrorNone)
2021-06-27 21:25:29 +00:00
{
return AuMakePair(read, done);
}
2021-06-27 21:25:29 +00:00
read += request;
2021-06-27 21:25:29 +00:00
this->ctx_.avail_in = request;
this->ctx_.next_in = reinterpret_cast<unsigned char *>(din_);
2021-06-27 21:25:29 +00:00
do
{
2022-01-19 17:08:13 +00:00
this->ctx_.avail_out = AuArraySize(dout_); // AuMin(AuUInt32(AuArraySize(dout_)), AuUInt32(this->_outbuffer.RemainingWrite()));
2021-07-11 17:26:38 +00:00
this->ctx_.next_out = dout_;
2021-07-11 17:26:38 +00:00
if (!this->ctx_.avail_out)
{
break;
}
2021-06-27 21:25:29 +00:00
2021-07-11 17:26:38 +00:00
ret = inflate(&this->ctx_, Z_NO_FLUSH);
if (ret < Z_OK)
2021-06-27 21:25:29 +00:00
{
SysPushErrorIO("Error: {}", ret);
2021-09-06 10:58:08 +00:00
return AuMakePair(read, 0);
2021-06-27 21:25:29 +00:00
}
2021-09-06 10:58:08 +00:00
auto have = AuArraySize(dout_) - this->ctx_.avail_out;
done += have;
2021-06-27 21:25:29 +00:00
if (!Write(reinterpret_cast<const AuUInt8 *>(dout_),
have))
{
return {};
}
2021-06-27 21:25:29 +00:00
2021-07-11 17:26:38 +00:00
} while (this->ctx_.avail_out == 0);
2021-06-27 21:25:29 +00:00
}
2021-09-06 10:58:08 +00:00
return AuMakePair(read, done);
2021-06-27 21:25:29 +00:00
}
private:
2021-07-11 17:26:38 +00:00
AuSPtr<IO::IStreamReader> reader_;
2021-07-11 17:26:38 +00:00
z_stream ctx_ {};
bool init_ {};
unsigned char din_[4096];
unsigned char dout_[4096];
2021-06-27 21:25:29 +00:00
};
class BZIPInflate : public BaseStream
{
public:
DecompressInfo meta;
BZIPInflate(const DecompressInfo &meta) : meta(meta), BaseStream(meta.internalStreamSize)
{}
2021-06-27 21:25:29 +00:00
~BZIPInflate()
{
if (this->init_)
2021-06-27 21:25:29 +00:00
{
2021-07-11 17:26:38 +00:00
BZ2_bzDecompressEnd(&this->ctx_);
2021-06-27 21:25:29 +00:00
}
}
bool Init(const AuSPtr<IO::IStreamReader> &reader)
2021-06-27 21:25:29 +00:00
{
2021-07-11 17:26:38 +00:00
this->reader_ = reader;
2021-06-27 21:25:29 +00:00
2021-07-11 17:26:38 +00:00
auto ret = BZ2_bzDecompressInit(&this->ctx_, 0, 0);
if (ret < Z_OK)
2021-06-27 21:25:29 +00:00
{
SysPushErrorMem("Error: {}", ret);
return false;
}
2021-07-11 17:26:38 +00:00
this->init_ = true;
2021-06-27 21:25:29 +00:00
return true;
}
2021-09-06 10:58:08 +00:00
AuStreamReadWrittenPair_t Ingest(AuUInt32 input) override
2021-06-27 21:25:29 +00:00
{
int ret;
AuUInt32 done{}, read{};
while (read < input)
2021-06-27 21:25:29 +00:00
{
2022-01-19 17:08:13 +00:00
AuUInt request = AuMin(input, AuUInt32(AuArraySize(din_)));
2021-09-15 01:14:29 +00:00
if (this->reader_->Read(Memory::MemoryViewStreamWrite(din_, request)) != IO::EStreamError::eErrorNone)
2021-06-27 21:25:29 +00:00
{
return AuMakePair(read, done);
}
read += request;
this->ctx_.avail_in = request;
this->ctx_.next_in = reinterpret_cast<char *>(din_);
2021-06-27 21:25:29 +00:00
do
{
this->ctx_.avail_out = AuArraySize(dout_);
2021-07-11 17:26:38 +00:00
this->ctx_.next_out = dout_;
2021-06-27 21:25:29 +00:00
2021-07-11 17:26:38 +00:00
ret = BZ2_bzDecompress(&this->ctx_);
if (ret < Z_OK)
2021-06-27 21:25:29 +00:00
{
SysPushErrorIO("Error: {}", ret);
2021-09-06 10:58:08 +00:00
return AuMakePair(read, 0);
2021-06-27 21:25:29 +00:00
}
2021-09-06 10:58:08 +00:00
auto have = AuArraySize(dout_) - this->ctx_.avail_out;
done += have;
2021-06-27 21:25:29 +00:00
if (!Write(reinterpret_cast<const AuUInt8 *>(dout_),
have))
{
return {};
}
2021-06-27 21:25:29 +00:00
2021-07-11 17:26:38 +00:00
} while (this->ctx_.avail_out == 0);
2021-06-27 21:25:29 +00:00
}
2021-09-06 10:58:08 +00:00
return AuMakePair(read, done);
2021-06-27 21:25:29 +00:00
}
private:
2021-07-11 17:26:38 +00:00
AuSPtr<IO::IStreamReader> reader_;
2021-07-11 17:26:38 +00:00
bz_stream ctx_ {};
bool init_ {};
char dout_[4096];
bool userBound_ {};
char din_[4096];
2021-06-27 21:25:29 +00:00
};
class LZ4Inflate : public BaseStream
{
public:
2021-09-06 10:58:08 +00:00
DecompressInfo meta;
LZ4Inflate(const DecompressInfo &meta) : meta(meta), BaseStream(meta.internalStreamSize)
2021-09-06 10:58:08 +00:00
{}
2021-06-27 21:25:29 +00:00
~LZ4Inflate()
{
if (lz4Stream_)
{
LZ4F_freeDecompressionContext(lz4Stream_);
}
2021-06-27 21:25:29 +00:00
}
bool Init(const AuSPtr<IO::IStreamReader> &reader)
2021-06-27 21:25:29 +00:00
{
2021-07-11 17:26:38 +00:00
this->reader_ = reader;
2021-06-27 21:25:29 +00:00
auto err = LZ4F_createDecompressionContext(&lz4Stream_, LZ4F_getVersion());
if (LZ4F_isError(err))
2021-06-27 21:25:29 +00:00
{
return {};
2021-06-27 21:25:29 +00:00
}
return true;
}
2021-09-06 10:58:08 +00:00
AuStreamReadWrittenPair_t Ingest(AuUInt32 input) override
{
bool ret = true;
AuUInt32 inputStat = 0, outputStat = 0;
size_t bytesRemInFrame {};
LZ4F_decompressOptions_t opts {};
auto bufferSize = meta.internalStreamSize / 2;
auto bufferIn = AuSPtr<char>(new char[bufferSize], std::default_delete<char[]>());
auto bufferOut = AuSPtr<char>(new char[bufferSize], std::default_delete<char[]>());
while (inputStat < input)
{
auto frameSize = bytesRemInFrame ? bytesRemInFrame : LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH;
auto min = frameSize;
if (min > (bufferSize / 2))
{
ret = false;
break;
}
if (frameSize)
{
2021-09-15 01:14:29 +00:00
AuUInt request = frameSize;
if ((input < (inputStat + request)) ||
2021-09-15 01:14:29 +00:00
(this->reader_->Read(Memory::MemoryViewStreamWrite(bufferIn.get(), request)) != IO::EStreamError::eErrorNone) ||
(request != frameSize))
{
ret = request == 0 && inputStat;
break;
}
inputStat += frameSize;
}
if (frameSize)
{
auto mustConsume = frameSize;
size_t frameSPtr = mustConsume;
size_t frameS2Ptr = bufferSize;
bytesRemInFrame = LZ4F_decompress(lz4Stream_, bufferOut.get(), &frameS2Ptr, bufferIn.get(), &frameSPtr, &opts);
if (LZ4F_isError(bytesRemInFrame))
{
ret = false;
break;
}
if (frameS2Ptr)
{
if (!Write(bufferOut.get(), frameS2Ptr))
{
ret = false;
break;
}
}
outputStat += frameS2Ptr;
}
}
if (!ret)
{
return {};
}
return AuMakePair(inputStat, outputStat);
2021-06-27 21:25:29 +00:00
}
private:
AuSPtr<IO::IStreamReader> reader_;
LZ4F_dctx* lz4Stream_ {};
2021-06-27 21:25:29 +00:00
};
AUKN_SYM ICompressionStream *DecompressorNew(const AuSPtr<IO::IStreamReader> &reader, const DecompressInfo &ref)
2021-06-27 21:25:29 +00:00
{
DecompressInfo info = ref;
2021-06-27 21:25:29 +00:00
BaseStream * ret{};
if (!info.internalStreamSize)
{
info.internalStreamSize = 1024 * 64 * 2;
}
switch (info.alg)
2021-06-27 21:25:29 +00:00
{
case ECompresionType::eZSTD:
ret = new ZSTDInflate(info);
2021-06-27 21:25:29 +00:00
break;
case ECompresionType::eBZIP2:
ret = new BZIPInflate(info);
2021-06-27 21:25:29 +00:00
break;
case ECompresionType::eLZ4:
ret = new LZ4Inflate(info);
2021-06-27 21:25:29 +00:00
break;
case ECompresionType::eDeflate:
ret = new ZIPInflate(info);
2021-06-27 21:25:29 +00:00
break;
default:
ret = nullptr;
break;
}
if (ret)
{
if (!ret->Init(reader))
{
delete ret;
ret = nullptr;
2021-06-27 21:25:29 +00:00
}
}
return ret;
}
AUKN_SYM void DecompressorRelease(ICompressionStream * stream)
{
SafeDelete<BaseStream *>(stream);
}
}