// Copyright 2016 Google Inc. All Rights Reserved. // // Distributed under MIT license. // See file LICENSE for detail or copy at https://opensource.org/licenses/MIT // Package decoder wraps the brotli decoder C API used by package brotli. package decoder /* #include // Wrap BrotliDecoderDecompressStream so that it doesn't take variable (in-out) // pointers. Instead of updated pointer, deltas are saved in auxiliary struct. struct DecompressStreamResult { size_t bytes_consumed; const uint8_t* output_data; size_t output_data_size; BrotliDecoderResult status; }; struct DecompressStreamResult DecompressStream(BrotliDecoderState* s, const uint8_t* encoded_data, size_t encoded_data_size) { struct DecompressStreamResult result; size_t available_in = encoded_data_size; const uint8_t* next_in = encoded_data; size_t available_out = 0; result.status = BrotliDecoderDecompressStream(s, &available_in, &next_in, &available_out, 0, 0); result.bytes_consumed = encoded_data_size - available_in; result.output_data = 0; result.output_data_size = 0; if (result.status != BROTLI_DECODER_RESULT_ERROR) { result.output_data = BrotliDecoderTakeOutput(s, &result.output_data_size); if (BrotliDecoderIsFinished(s)) { result.status = BROTLI_DECODER_RESULT_SUCCESS; } } return result; } */ import "C" import ( "unsafe" ) // Status represents internal state after DecompressStream invokation type Status int const ( // Error happened Error Status = iota // Done means that no more output will be produced Done // Ok means that more output might be produced with no additional input Ok ) // Decoder is the Brotli c-decoder handle. type Decoder struct { state *C.BrotliDecoderState } // New returns a new Brotli c-decoder handle. // Close MUST be called to free resources. func New() Decoder { return Decoder{state: C.BrotliDecoderCreateInstance(nil, nil, nil)} } // Close frees resources used by decoder. func (z *Decoder) Close() { C.BrotliDecoderDestroyInstance(z.state) z.state = nil } func goStatus(cStatus C.BrotliDecoderResult) (status Status) { switch cStatus { case C.BROTLI_DECODER_RESULT_SUCCESS: return Done case C.BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: return Ok case C.BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT: return Ok } return Error } // cBytes casts a Go []byte into a C uint8_t*. We pass &buf[0] directly to C, // which is legal because C doesn't save the pointer longer than the call and // the byte array itself doesn't contain any pointers. func cBytes(buf []byte) (*C.uint8_t, C.size_t) { if len(buf) == 0 { return (*C.uint8_t)(nil), 0 } return (*C.uint8_t)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)) } // DecompressStream reads Brotli-encoded bytes from in, and returns produced // bytes. Output contents should not be modified. Liveness of output is // hard-limited by Decoder liveness; slice becomes invalid when any Decoder // method is invoked. func (z *Decoder) DecompressStream(in []byte) ( bytesConsumed int, output []byte, status Status) { cin, cinSize := cBytes(in) result := C.DecompressStream(z.state, cin, cinSize) output = C.GoBytes( unsafe.Pointer(result.output_data), C.int(result.output_data_size)) return int(result.bytes_consumed), output, goStatus(result.status) }