X-Git-Url: http://git.ieval.ro/?p=io-compress-brotli.git;a=blobdiff_plain;f=Brotli.xs;h=ffbde1e49c6cd6f0e5ac68c422bff6e7c555acdb;hp=b7f33d6012844a79533460534f8c06c40c7506af;hb=c17e7d6329e8a24e6128a9f70e2b597fce1b5b0a;hpb=ea94b872c10d282002d37289d7479347bb370c51 diff --git a/Brotli.xs b/Brotli.xs index b7f33d6..ffbde1e 100644 --- a/Brotli.xs +++ b/Brotli.xs @@ -5,76 +5,248 @@ #include "ppport.h" -#include -#include +#include +#include #define BUFFER_SIZE 1048576 -static uint8_t buffer[BUFFER_SIZE]; /* It's almost 2016, is anyone still using ithreads? */ + +typedef struct brotli_decoder { + BrotliDecoderState *decoder; +}* IO__Uncompress__Brotli; + +typedef struct brotli_encoder { + BrotliEncoderState *encoder; +}* IO__Compress__Brotli; + MODULE = IO::Compress::Brotli PACKAGE = IO::Uncompress::Brotli PROTOTYPES: ENABLE -SV* unbro(buffer) +SV* +unbro(buffer, decoded_size) SV* buffer + size_t decoded_size PREINIT: - size_t decoded_size; STRLEN encoded_size; uint8_t *encoded_buffer, *decoded_buffer; CODE: encoded_buffer = (uint8_t*) SvPV(buffer, encoded_size); - if(!BrotliDecompressedSize(encoded_size, encoded_buffer, &decoded_size)){ - croak("Error in BrotliDecompressedSize"); - } - Newx(decoded_buffer, decoded_size+1, uint8_t); - decoded_buffer[decoded_size]=0; + Newx(decoded_buffer, decoded_size, uint8_t); if(!BrotliDecoderDecompress(encoded_size, encoded_buffer, &decoded_size, decoded_buffer)){ croak("Error in BrotliDecoderDecompress"); } RETVAL = newSV(0); - sv_usepvn_flags(RETVAL, decoded_buffer, decoded_size, SV_HAS_TRAILING_NUL); + sv_usepvn(RETVAL, decoded_buffer, decoded_size); OUTPUT: RETVAL -SV* BrotliDecoderCreateInstance() +IO::Uncompress::Brotli +create(class) + SV* class CODE: - RETVAL = newSViv((IV)BrotliDecoderCreateInstance(NULL, NULL, NULL)); + Newx(RETVAL, 1, struct brotli_decoder); + RETVAL->decoder = BrotliDecoderCreateInstance(NULL, NULL, NULL); OUTPUT: RETVAL -void BrotliDecoderDestroyInstance(state) - SV* state +void +DESTROY(self) + IO::Uncompress::Brotli self CODE: - BrotliDecoderDestroyInstance((BrotliDecoderState*)SvIV(state)); + BrotliDecoderDestroyInstance(self->decoder); + Safefree(self); -SV* BrotliDecoderDecompressStream(state, in) - SV* state +SV* +decompress(self, in) + IO::Uncompress::Brotli self SV* in PREINIT: - uint8_t *next_in, *next_out; - size_t available_in, available_out, total_out; + uint8_t *next_in, *next_out, *buffer; + size_t available_in, available_out; BrotliDecoderResult result; CODE: next_in = (uint8_t*) SvPV(in, available_in); + Newx(buffer, BUFFER_SIZE, uint8_t); RETVAL = newSVpv("", 0); - result = BROTLI_RESULT_NEEDS_MORE_OUTPUT; - while(result == BROTLI_RESULT_NEEDS_MORE_OUTPUT) { + result = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + while(result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { next_out = buffer; available_out=BUFFER_SIZE; - result = BrotliDecoderDecompressStream((BrotliDecoderState*) SvIV(state), &available_in, (const uint8_t**) &next_in, &available_out, &next_out, &total_out); + result = BrotliDecoderDecompressStream( self->decoder, + &available_in, + (const uint8_t**) &next_in, + &available_out, + &next_out, + NULL ); if(!result){ - croak("Error in BrotliDecoderDecompressStream"); + Safefree(buffer); + croak("Error in BrotliDecoderDecompressStream"); } sv_catpvn(RETVAL, (const char*)buffer, BUFFER_SIZE-available_out); } + Safefree(buffer); + OUTPUT: + RETVAL + + +MODULE = IO::Compress::Brotli PACKAGE = IO::Compress::Brotli +PROTOTYPES: ENABLE + +SV* +bro(buffer, quality=BROTLI_DEFAULT_QUALITY, lgwin=BROTLI_DEFAULT_WINDOW) + SV* buffer + U32 quality + U32 lgwin + PREINIT: + size_t encoded_size; + STRLEN decoded_size; + uint8_t *encoded_buffer, *decoded_buffer; + BROTLI_BOOL result; + CODE: + if( quality < BROTLI_MIN_QUALITY || quality > BROTLI_MAX_QUALITY ) { + croak("Invalid quality value"); + } + if( lgwin < BROTLI_MIN_WINDOW_BITS || lgwin > BROTLI_MAX_WINDOW_BITS ) { + croak("Invalid window value"); + } + decoded_buffer = (uint8_t*) SvPV(buffer, decoded_size); + encoded_size = BrotliEncoderMaxCompressedSize(decoded_size); + if(!encoded_size){ + croak("Compressed size overflow"); + } + Newx(encoded_buffer, encoded_size+1, uint8_t); + result = BrotliEncoderCompress( quality, + lgwin, + BROTLI_DEFAULT_MODE, + decoded_size, + decoded_buffer, + &encoded_size, + encoded_buffer ); + if(!result){ + Safefree(buffer); + croak("Error in BrotliEncoderCompress"); + } + encoded_buffer[encoded_size]=0; + RETVAL = newSV(0); + sv_usepvn_flags(RETVAL, encoded_buffer, encoded_size, SV_SMAGIC | SV_HAS_TRAILING_NUL); + OUTPUT: + RETVAL + +IO::Compress::Brotli +create(class) + SV* class + CODE: + Newx(RETVAL, 1, struct brotli_encoder); + RETVAL->encoder = BrotliEncoderCreateInstance(NULL, NULL, NULL); OUTPUT: RETVAL -void BrotliSetCustomDictionary(state, dict) - SV* state - SV* dict +bool BrotliEncoderSetParameter(self, value) + IO::Compress::Brotli self + U32 value + ALIAS: + window = 1 + quality = 2 + _mode = 3 PREINIT: - size_t size; - uint8_t *data; + BrotliEncoderParameter param; + INIT: + switch(ix){ + case 0: + croak("BrotliEncoderSetParameter may not be called directly"); + break; + case 1: + if( value < BROTLI_MIN_WINDOW_BITS || value > BROTLI_MAX_WINDOW_BITS ) { + croak("Invalid window value"); + } + param = BROTLI_PARAM_LGWIN; + break; + case 2: + if( value < BROTLI_MIN_QUALITY || value > BROTLI_MAX_QUALITY ) { + croak("Invalid quality value"); + } + param = BROTLI_PARAM_QUALITY; + break; + case 3: + /* Validation done on Perl side */ + param = BROTLI_PARAM_MODE; + break; + default: + croak("Impossible ix in BrotliEncoderSetParameter"); + break; + } + C_ARGS: + self->encoder, param, value + +SV* +_compress(self, in = &PL_sv_undef) + IO::Compress::Brotli self + SV* in + ALIAS: + compress = 1 + flush = 2 + finish = 3 + PREINIT: + uint8_t *next_in, *next_out, *buffer; + size_t available_in, available_out; + BROTLI_BOOL result; + BrotliEncoderOperation op; + CODE: + switch(ix) { + case 0: + croak("_compress may not be called directly"); + break; + case 1: + op = BROTLI_OPERATION_PROCESS; + break; + case 2: + op = BROTLI_OPERATION_FLUSH; + break; + case 3: + op = BROTLI_OPERATION_FINISH; + break; + default: + croak("Impossible ix in _compress"); + break; + } + + Newx(buffer, BUFFER_SIZE, uint8_t); + if(in == &PL_sv_undef) + next_in = (uint8_t*) buffer, available_in = 0; + else + next_in = (uint8_t*) SvPV(in, available_in); + RETVAL = newSVpv("", 0); + while(1) { + next_out = buffer; + available_out = BUFFER_SIZE; + result = BrotliEncoderCompressStream( self->encoder, + (BrotliEncoderOperation) op, + &available_in, + (const uint8_t**) &next_in, + &available_out, + &next_out, + NULL ); + if(!result) { + Safefree(buffer); + croak("Error in BrotliEncoderCompressStream"); + } + + if( available_out != BUFFER_SIZE ) { + sv_catpvn(RETVAL, (const char*)buffer, BUFFER_SIZE-available_out); + } + + if( + BrotliEncoderIsFinished(self->encoder) || + (!available_in && !BrotliEncoderHasMoreOutput(self->encoder)) + ) break; + } + Safefree(buffer); + OUTPUT: + RETVAL + +void +DESTROY(self) + IO::Compress::Brotli self CODE: - data = SvPV(dict, size); - BrotliDecoderSetCustomDictionary((BrotliDecoderState*) SvIV(state), size, data); + BrotliEncoderDestroyInstance(self->encoder); + Safefree(self);