X-Git-Url: http://git.ieval.ro/?a=blobdiff_plain;f=Brotli.xs;h=10beb1c6fd891e2903f44539ef189002c4f01726;hb=21c71a390e95f6a04b6a4c244f6e8bddd1554187;hp=60b6428c3728c2a67c4e684b80d71c5470151c38;hpb=f9995f31e58bcdfc9eb528128c6bfcbb0b8fc777;p=io-compress-brotli.git diff --git a/Brotli.xs b/Brotli.xs index 60b6428..10beb1c 100644 --- a/Brotli.xs +++ b/Brotli.xs @@ -6,14 +6,25 @@ #include "ppport.h" #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) SV* buffer PREINIT: size_t decoded_size; @@ -26,54 +37,248 @@ SV* unbro(buffer) } Newx(decoded_buffer, decoded_size+1, uint8_t); decoded_buffer[decoded_size]=0; - if(!BrotliDecompressBuffer(encoded_size, encoded_buffer, &decoded_size, decoded_buffer)){ - croak("Error in BrotliDecompressBuffer"); + 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); OUTPUT: RETVAL -SV* BrotliCreateState() +IO::Uncompress::Brotli +create(class) + SV* class CODE: - RETVAL = newSViv((IV)BrotliCreateState(NULL, NULL, NULL)); + Newx(RETVAL, 1, struct brotli_decoder); + RETVAL->decoder = BrotliDecoderCreateInstance(NULL, NULL, NULL); OUTPUT: RETVAL -void BrotliDestroyState(state) - SV* state +void +DESTROY(self) + IO::Uncompress::Brotli self CODE: - BrotliDestroyState((BrotliState*)SvIV(state)); + BrotliDecoderDestroyInstance(self->decoder); + Safefree(self); -SV* BrotliDecompressStream(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; - BrotliResult result; + 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) { next_out = buffer; available_out=BUFFER_SIZE; - result = BrotliDecompressStream(&available_in, (const uint8_t**) &next_in, &available_out, &next_out, &total_out, (BrotliState*) SvIV(state)); + result = BrotliDecoderDecompressStream( self->decoder, + &available_in, + (const uint8_t**) &next_in, + &available_out, + &next_out, + NULL ); if(!result){ - croak("Error in BrotliDecompressStream"); + Safefree(buffer); + croak("Error in BrotliDecoderDecompressStream"); } sv_catpvn(RETVAL, (const char*)buffer, BUFFER_SIZE-available_out); } + Safefree(buffer); + OUTPUT: + RETVAL + +void +set_dictionary(self, dict) + IO::Uncompress::Brotli self + SV* dict + PREINIT: + size_t size; + uint8_t *data; + CODE: + data = SvPV(dict, size); + BrotliDecoderSetCustomDictionary(self->decoder, size, data); + + +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 < kBrotliMinWindowBits || lgwin > kBrotliMaxWindowBits ) { + 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 + +SV* +window(self, window) + IO::Compress::Brotli self + U32 window + CODE: + if( window < kBrotliMinWindowBits || window > kBrotliMaxWindowBits ) { + croak("Invalid window value"); + } + if( BrotliEncoderSetParameter(self->encoder, BROTLI_PARAM_LGWIN, window) ) + RETVAL = newSVuv(1); + else + RETVAL = newSVuv(0); + OUTPUT: + RETVAL + +SV* +quality(self, quality) + IO::Compress::Brotli self + U32 quality + CODE: + if( quality < BROTLI_MIN_QUALITY || quality > BROTLI_MAX_QUALITY ) { + croak("Invalid quality value"); + } + if( BrotliEncoderSetParameter(self->encoder, BROTLI_PARAM_QUALITY, quality) ) + RETVAL = newSVuv(1); + else + RETVAL = newSVuv(0); + OUTPUT: + RETVAL + +SV* +_mode(self, mode) + IO::Compress::Brotli self + U32 mode + CODE: + if( BrotliEncoderSetParameter(self->encoder, BROTLI_PARAM_MODE, mode) ) + RETVAL = newSVuv(1); + else + RETVAL = newSVuv(0); + OUTPUT: + RETVAL + +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 BrotliSetCustomDictionary(state, dict) - SV* state +void +DESTROY(self) + IO::Compress::Brotli self + CODE: + BrotliEncoderDestroyInstance(self->encoder); + Safefree(self); + +void +set_dictionary(self, dict) + IO::Compress::Brotli self SV* dict PREINIT: size_t size; uint8_t *data; CODE: data = SvPV(dict, size); - BrotliSetCustomDictionary(size, data, (BrotliState*) SvIV(state)); + BrotliEncoderSetCustomDictionary(self->encoder, size, data);