33#include < eepp/system/iostreammemory.hpp>
44#include < eepp/system/scopedbuffer.hpp>
55
6+ #include < brotli/decode.h>
7+ // eepp only brings decoder implementation, encoding won't be available for the moment
8+ // #include <brotli/encode.h>
69#include < zlib.h>
710
811#define DEFLATE_CHUNK_SIZE ( 16384 )
@@ -19,6 +22,62 @@ Compression::Status Compression::compress( Uint8* dst, Uint64 dstMaxSize, const
1922Compression::Status Compression::compress ( IOStream& dst, IOStream& src, Compression::Mode mode,
2023 const Config& config ) {
2124 switch ( mode ) {
25+ case MODE_BROTLI: {
26+ /*
27+ BrotliEncoderState* state = BrotliEncoderCreateInstance( nullptr, nullptr, nullptr );
28+ if ( !state )
29+ return Status::MEM_ERROR;
30+
31+ int quality =
32+ config.brotli.quality == -1 ? BROTLI_DEFAULT_QUALITY : config.brotli.quality;
33+ int windowBits =
34+ config.brotli.windowBits == -1 ? BROTLI_DEFAULT_WINDOW : config.brotli.windowBits;
35+
36+ BrotliEncoderSetParameter( state, BROTLI_PARAM_QUALITY, quality );
37+ BrotliEncoderSetParameter( state, BROTLI_PARAM_LGWIN, windowBits );
38+
39+ src.seek( 0 );
40+
41+ char in[DEFLATE_CHUNK_SIZE];
42+ char out[DEFLATE_CHUNK_SIZE];
43+
44+ bool isEof = false;
45+
46+ while ( !isEof ) {
47+ size_t bytesRead = src.read( in, DEFLATE_CHUNK_SIZE );
48+ isEof = src.tell() == src.getSize();
49+
50+ const uint8_t* next_in = reinterpret_cast<const uint8_t*>( in );
51+ size_t avail_in = bytesRead;
52+
53+ while ( avail_in > 0 || ( isEof && !BrotliEncoderIsFinished( state ) ) ) {
54+ uint8_t* next_out = reinterpret_cast<uint8_t*>( out );
55+ size_t avail_out = DEFLATE_CHUNK_SIZE;
56+
57+ BrotliEncoderOperation op =
58+ isEof ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
59+
60+ if ( !BrotliEncoderCompressStream( state, op, &avail_in, &next_in, &avail_out,
61+ &next_out, nullptr ) ) {
62+ BrotliEncoderDestroyInstance( state );
63+ return Status::STREAM_ERROR;
64+ }
65+
66+ size_t have = DEFLATE_CHUNK_SIZE - avail_out;
67+ if ( have > 0 ) {
68+ if ( dst.write( out, have ) != (ios_size)have ) {
69+ BrotliEncoderDestroyInstance( state );
70+ return Status::ERRNO;
71+ }
72+ }
73+ }
74+ }
75+
76+ BrotliEncoderDestroyInstance( state );
77+ return Status::OK;
78+ */
79+ return Status::VERSION_ERROR;
80+ }
2281 case MODE_DEFLATE:
2382 case MODE_GZIP: {
2483 int ret, flush;
@@ -74,6 +133,10 @@ Compression::Status Compression::compress( IOStream& dst, IOStream& src, Compres
74133
75134int Compression::getMaxCompressedBufferSize ( Uint64 srcSize, Mode mode, const Config& ) {
76135 switch ( mode ) {
136+ case MODE_BROTLI: {
137+ // return BrotliEncoderMaxCompressedSize( srcSize );
138+ break ;
139+ }
77140 case MODE_DEFLATE:
78141 case MODE_GZIP: {
79142 int windowBits = mode == MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16 ;
@@ -101,6 +164,61 @@ Compression::Status Compression::decompress( Uint8* dst, Uint64 dstMaxSize, cons
101164
102165Compression::Status Compression::decompress ( IOStream& dst, IOStream& src, Mode mode ) {
103166 switch ( mode ) {
167+ case MODE_BROTLI: {
168+ BrotliDecoderState* state = BrotliDecoderCreateInstance ( nullptr , nullptr , nullptr );
169+ if ( !state )
170+ return Status::MEM_ERROR;
171+
172+ ScopedBuffer buffer ( DEFLATE_CHUNK_SIZE );
173+ ScopedBuffer bufferDst ( DEFLATE_CHUNK_SIZE );
174+
175+ src.seek ( 0 );
176+
177+ BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
178+ size_t totalSize = src.getSize ();
179+ size_t totalRead = 0 ;
180+
181+ while ( totalRead < totalSize || result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT ) {
182+ size_t bytesRead = 0 ;
183+ if ( result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT ) {
184+ bytesRead = src.read ( (char *)buffer.get (), buffer.length () );
185+ totalRead += bytesRead;
186+ }
187+
188+ const uint8_t * next_in = reinterpret_cast <const uint8_t *>( buffer.get () );
189+ size_t avail_in = bytesRead;
190+
191+ while ( avail_in > 0 || result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT ) {
192+ uint8_t * next_out = reinterpret_cast <uint8_t *>( bufferDst.get () );
193+ size_t avail_out = bufferDst.length ();
194+
195+ result = BrotliDecoderDecompressStream ( state, &avail_in, &next_in, &avail_out,
196+ &next_out, nullptr );
197+
198+ if ( result == BROTLI_DECODER_RESULT_ERROR ) {
199+ BrotliDecoderDestroyInstance ( state );
200+ return Status::DATA_ERROR;
201+ }
202+
203+ size_t have = bufferDst.length () - avail_out;
204+ if ( have > 0 ) {
205+ dst.write ( (const char *)bufferDst.get (), have );
206+ }
207+
208+ if ( result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT ||
209+ result == BROTLI_DECODER_RESULT_SUCCESS ) {
210+ break ;
211+ }
212+ }
213+
214+ if ( result == BROTLI_DECODER_RESULT_SUCCESS ) {
215+ break ;
216+ }
217+ }
218+
219+ BrotliDecoderDestroyInstance ( state );
220+ return Status::OK;
221+ }
104222 case MODE_DEFLATE:
105223 case MODE_GZIP: {
106224 ScopedBuffer buffer ( DEFLATE_CHUNK_SIZE );
0 commit comments