1// Copyright 2016 Google Inc. All Rights Reserved. 2// 3// Distributed under MIT license. 4// See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 6// Package encoder wraps the brotli encoder C API used by package brotli. 7package encoder 8 9/* 10#include <brotli/encode.h> 11 12// Wrap BrotliEncoderCompressStream so that it doesn't take variable (in-out) 13// pointers. Instead of updated pointer, deltas are saved in auxiliary struct. 14struct CompressStreamResult { 15 size_t bytes_consumed; 16 const uint8_t* output_data; 17 size_t output_data_size; 18 int success; 19 int has_more; 20}; 21 22struct CompressStreamResult CompressStream( 23 BrotliEncoderState* s, BrotliEncoderOperation op, 24 const uint8_t* data, size_t data_size) { 25 struct CompressStreamResult result; 26 size_t available_in = data_size; 27 const uint8_t* next_in = data; 28 size_t available_out = 0; 29 result.success = BrotliEncoderCompressStream(s, op, 30 &available_in, &next_in, &available_out, 0, 0) ? 1 : 0; 31 result.bytes_consumed = data_size - available_in; 32 result.output_data = 0; 33 result.output_data_size = 0; 34 if (result.success) { 35 result.output_data = BrotliEncoderTakeOutput(s, &result.output_data_size); 36 } 37 result.has_more = BrotliEncoderHasMoreOutput(s) ? 1 : 0; 38 return result; 39} 40*/ 41import "C" 42import ( 43 "unsafe" 44) 45 46// Operation represents type of request to CompressStream 47type Operation int 48 49const ( 50 // Process input 51 Process Operation = iota 52 // Flush input processed so far 53 Flush 54 // Finish stream 55 Finish 56) 57 58// Status represents internal state after CompressStream invocation 59type Status int 60 61const ( 62 // Error happened 63 Error Status = iota 64 // Done means that no more output will be produced 65 Done 66 // Ok means that more output might be produced with no additional input 67 Ok 68) 69 70// Encoder is the Brotli c-encoder handle. 71type Encoder struct { 72 state *C.BrotliEncoderState 73} 74 75// New returns a new Brotli c-encoder handle. 76// quality and lgWin are described in third_party/Brotli/enc/encode.h. 77// Close MUST be called to free resources. 78func New(quality, lgWin int) Encoder { 79 state := C.BrotliEncoderCreateInstance(nil, nil, nil) 80 C.BrotliEncoderSetParameter( 81 state, C.BROTLI_PARAM_QUALITY, (C.uint32_t)(quality)) 82 C.BrotliEncoderSetParameter( 83 state, C.BROTLI_PARAM_LGWIN, (C.uint32_t)(lgWin)) 84 return Encoder{state} 85} 86 87// Close frees resources used by encoder. 88func (z *Encoder) Close() { 89 C.BrotliEncoderDestroyInstance(z.state) 90 z.state = nil 91} 92 93// cBytes casts a Go []byte into a C uint8_t*. We pass &buf[0] directly to C, 94// which is legal because C doesn't save the pointer longer than the call and 95// the byte array itself doesn't contain any pointers. 96func cBytes(buf []byte) (*C.uint8_t, C.size_t) { 97 if len(buf) == 0 { 98 return (*C.uint8_t)(nil), 0 99 } 100 return (*C.uint8_t)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)) 101} 102 103func cOperation(op Operation) (cOp C.BrotliEncoderOperation) { 104 switch op { 105 case Flush: 106 return C.BROTLI_OPERATION_FLUSH 107 case Finish: 108 return C.BROTLI_OPERATION_FINISH 109 } 110 return C.BROTLI_OPERATION_PROCESS 111} 112 113// CompressStream processes data and produces Brotli-encoded bytes. Encoder may 114// consume considerable amount of input before the first output bytes come out. 115// Flush and Finish operations force Encoder to produce output that corresponds 116// to input consumed so far. Output contents should not be modified. Liveness of 117// output is hard-limited by Encoder liveness; slice becomes invalid when any 118// Encoder method is invoked. 119func (z *Encoder) CompressStream(in []byte, op Operation) ( 120 bytesConsumed int, output []byte, status Status) { 121 cin, cinSize := cBytes(in) 122 result := C.CompressStream(z.state, cOperation(op), cin, cinSize) 123 output = C.GoBytes( 124 unsafe.Pointer(result.output_data), C.int(result.output_data_size)) 125 var outcome Status 126 if result.success == 0 { 127 outcome = Error 128 } else if result.has_more != 0 { 129 outcome = Ok 130 } else { 131 outcome = Done 132 } 133 return int(result.bytes_consumed), output, outcome 134} 135