/*** 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 {}; }; }