diff --git a/Aurora.json b/Aurora.json index 8adc2b95..1a5b6f0e 100644 --- a/Aurora.json +++ b/Aurora.json @@ -21,7 +21,8 @@ "glm", "bzip2", "liblzma", - "lz4" + "lz4", + "brotli" ], "depends": [ "auROXTL", diff --git a/Include/Aurora/Compression/ECompressionType.hpp b/Include/Aurora/Compression/ECompressionType.hpp index 90a0165e..b28cbb98 100644 --- a/Include/Aurora/Compression/ECompressionType.hpp +++ b/Include/Aurora/Compression/ECompressionType.hpp @@ -17,6 +17,7 @@ namespace Aurora::Compression eZip, eGZip, eLZ4, - eBZIP2 + eBZIP2, + eBrotli )); } \ No newline at end of file diff --git a/Source/Compression/AuBlockCompressor.cpp b/Source/Compression/AuBlockCompressor.cpp index 3a8b2eed..42aa54b5 100644 --- a/Source/Compression/AuBlockCompressor.cpp +++ b/Source/Compression/AuBlockCompressor.cpp @@ -26,6 +26,10 @@ #include "Compressors/BZip2Compressor.hpp" #endif +#if defined(_AUHAS_BROTLI) + #include "Compressors/BrotliCompressor.hpp" +#endif + namespace Aurora::Compression { AUKN_SYM ICompressionStream *CompressorNew(const AuSPtr &pReader, const CompressInfo &ref) @@ -54,7 +58,12 @@ namespace Aurora::Compression #endif #if defined(_AUHAS_LZ4) case ECompressionType::eLZ4: - pRet = new LZ4Deflate(info); + pRet = _new LZ4Deflate(info); + break; + #endif + #if defined(_AUHAS_BROTLI) + case ECompressionType::eBrotli: + pRet = _new BrotliDeflate(info); break; #endif #if defined(_AUHAS_ZLIB) diff --git a/Source/Compression/AuBlockDecompressor.cpp b/Source/Compression/AuBlockDecompressor.cpp index 77c4aa79..37417b8a 100644 --- a/Source/Compression/AuBlockDecompressor.cpp +++ b/Source/Compression/AuBlockDecompressor.cpp @@ -26,6 +26,10 @@ #include "Compressors/BZip2Decompressor.hpp" #endif +#if defined(_AUHAS_BROTLI) + #include "Compressors/BrotliDecompressor.hpp" +#endif + namespace Aurora::Compression { AUKN_SYM ICompressionStream *DecompressorNew(const AuSPtr &pReader, const DecompressInfo &ref) @@ -58,6 +62,11 @@ namespace Aurora::Compression pRet = _new LZ4Inflate(info); break; #endif + #if defined(_AUHAS_BROTLI) + case ECompressionType::eBrotli: + pRet = _new BrotliInflate(info); + break; + #endif #if defined(_AUHAS_ZLIB) case ECompressionType::eDeflate: case ECompressionType::eZip: diff --git a/Source/Compression/Compressors/BrotliCompressor.hpp b/Source/Compression/Compressors/BrotliCompressor.hpp new file mode 100644 index 00000000..7d775326 --- /dev/null +++ b/Source/Compression/Compressors/BrotliCompressor.hpp @@ -0,0 +1,163 @@ +/*** + Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: BrotliCompressor.hpp + Date: 2023-3-12 + Author: Reece +***/ +#pragma once + +#include "brotli/encode.h" + +namespace Aurora::Compression +{ + struct BrotliDeflate : BaseStream + { + CompressInfo meta; + + BrotliDeflate(const CompressInfo &meta) : meta(meta), BaseStream(meta.uInternalStreamSize) + { } + + ~BrotliDeflate() + { + if (this->pState) + { + BrotliEncoderDestroyInstance(this->pState); + } + } + + bool Init(const AuSPtr &reader) override + { + this->pReader_ = reader; + + if (!this->IsValid()) + { + SysPushErrorMem(); + return false; + } + + this->pState = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); + if (!this->pState) + { + SysPushErrorMem("No brotli encoder"); + return false; + } + + this->SetArray(this->din_); + return true; + } + + AuStreamReadWrittenPair_t Ingest_s(AuUInt32 input) override + { + AuUInt32 done {}, read {}; + + if (!this->pReader_) + { + return {}; + } + + if (!this->pState) + { + return {}; + } + + while (read < input) + { + read += IngestForInPointer(this->pReader_, this->pInBuffer, this->uAvailIn, input - read); + + if (!this->uAvailIn) + { + return { read, done }; + } + + size_t outNext {}; + uint8_t *pOut {}; + do + { + outNext = AuArraySize(this->dout_); + pOut = this->dout_; + + auto ret = BrotliEncoderCompressStream(this->pState, BrotliEncoderOperation::BROTLI_OPERATION_PROCESS, &this->uAvailIn, (const uint8_t **) & this->pInBuffer, &outNext, &pOut, nullptr); + if (ret == BROTLI_FALSE) + { + SysPushErrorIO("Brotli Error"); + this->pReader_.reset(); + return AuMakePair(read, 0); + } + + auto have = AuArraySize(this->dout_) - outNext; + done += have; + + if (!Write(reinterpret_cast(this->dout_), have)) + { + this->pReader_.reset(); + SysPushErrorIO("Compression Out of Overhead"); + return AuMakePair(read, 0); + } + } + while (outNext == 0 || BrotliEncoderHasMoreOutput(this->pState)); + } + + return { read, done }; + } + + bool Flush() override + { + return RunFlush(BrotliEncoderOperation::BROTLI_OPERATION_FLUSH); + } + + bool Finish() override + { + return RunFlush(BrotliEncoderOperation::BROTLI_OPERATION_FINISH); + } + + bool RunFlush(BrotliEncoderOperation type) + { + if (!this->pReader_) + { + return false; + } + if (!this->pState) + { + return false; + } + + AuUInt read {}; + { + size_t outNext {}; + uint8_t *pOut {}; + while (outNext == 0 || BrotliEncoderHasMoreOutput(this->pState)); + { + outNext = AuArraySize(this->dout_); + pOut = this->dout_; + + auto ret = BrotliEncoderCompressStream(this->pState, type, &this->uAvailIn, (const uint8_t **)&this->pInBuffer, &outNext, &pOut, nullptr); + if (ret == BROTLI_FALSE) + { + SysPushErrorIO("Brotli Error"); + this->pReader_.reset(); + return false; + } + + auto have = AuArraySize(this->dout_) - outNext; + if (!Write(reinterpret_cast(this->dout_), have)) + { + this->pReader_.reset(); + SysPushErrorIO("Compression Out of Overhead"); + return false; + } + } + } + + return true; + } + + private: + AuSPtr pReader_; + unsigned char din_[kChunkSize]; + unsigned char dout_[kChunkSize]; + uint8_t *pInBuffer; + size_t uAvailIn {}; + BrotliEncoderState *pState {}; + }; +} \ No newline at end of file diff --git a/Source/Compression/Compressors/BrotliDecompressor.hpp b/Source/Compression/Compressors/BrotliDecompressor.hpp new file mode 100644 index 00000000..f85703ae --- /dev/null +++ b/Source/Compression/Compressors/BrotliDecompressor.hpp @@ -0,0 +1,108 @@ +/*** + Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: BrotliDecompressor.hpp + Date: 2023-3-12 + Author: Reece +***/ +#pragma once + +#include "brotli/decode.h" + +namespace Aurora::Compression +{ + struct BrotliInflate : BaseStream + { + DecompressInfo meta; + + BrotliInflate(const DecompressInfo &meta) : meta(meta), BaseStream(meta.uInternalStreamSize) + { } + + ~BrotliInflate() + { + if (this->pState) + { + BrotliDecoderDestroyInstance(this->pState); + } + } + + bool Init(const AuSPtr &reader) override + { + int ret; + this->pReader_ = reader; + + if (!this->IsValid()) + { + SysPushErrorMem(); + return false; + } + + this->pState = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); + if (!this->pState) + { + SysPushErrorMem("No brotli decoder"); + return false; + } + + this->SetArray(this->din_); + return true; + } + + AuStreamReadWrittenPair_t Ingest_s(AuUInt32 input) override + { + AuUInt32 done {}, read {}; + + if (!this->pReader_) + { + return {}; + } + + while (read < input) + { + read += IngestForInPointer(this->pReader_, this->pInBuffer, this->uAvailIn, input - read); + + if (!this->uAvailIn) + { + return { read, done }; + } + + size_t outNext {}; + uint8_t *pOut {}; + do + { + outNext = AuArraySize(this->dout_); + pOut = this->dout_; + + auto ret = BrotliDecoderDecompressStream(this->pState, &this->uAvailIn, (const uint8_t **)&this->pInBuffer, &outNext, &pOut, nullptr); + if (ret == BROTLI_DECODER_RESULT_ERROR) + { + SysPushErrorIO("Brotli Error"); + this->pReader_.reset(); + return AuMakePair(read, 0); + } + + auto have = AuArraySize(this->dout_) - outNext; + done += have; + + if (!Write(reinterpret_cast(this->dout_), have)) + { + this->pReader_.reset(); + SysPushErrorIO("Compression Out of Overhead"); + return AuMakePair(read, 0); + } + } + while (outNext == 0); + } + + return { read, done }; + } + + private: + AuSPtr pReader_; + unsigned char din_[kChunkSize]; + unsigned char dout_[kChunkSize]; + uint8_t *pInBuffer; + size_t uAvailIn {}; + BrotliDecoderState *pState {}; + }; +} \ No newline at end of file