#define PERL_NO_GET_CONTEXT #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" #include #include #include #define BUFFER_SIZE 1048576 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* buffer 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; 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 IO::Uncompress::Brotli create(class) SV* class CODE: Newx(RETVAL, 1, struct brotli_decoder); RETVAL->decoder = BrotliDecoderCreateInstance(NULL, NULL, NULL); OUTPUT: RETVAL void DESTROY(self) IO::Uncompress::Brotli self CODE: BrotliDecoderDestroyInstance(self->decoder); Safefree(self); SV* decompress(self, in) IO::Uncompress::Brotli self SV* in PREINIT: 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 = BrotliDecoderDecompressStream( self->decoder, &available_in, (const uint8_t**) &next_in, &available_out, &next_out, NULL ); if(!result){ 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 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); BrotliEncoderSetCustomDictionary(self->encoder, size, data);