1*f4ee7fbaSAndroid Build Coastguard Worker// Copyright 2016 Google Inc. All Rights Reserved. 2*f4ee7fbaSAndroid Build Coastguard Worker// 3*f4ee7fbaSAndroid Build Coastguard Worker// Distributed under MIT license. 4*f4ee7fbaSAndroid Build Coastguard Worker// See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5*f4ee7fbaSAndroid Build Coastguard Worker 6*f4ee7fbaSAndroid Build Coastguard Workerpackage cbrotli 7*f4ee7fbaSAndroid Build Coastguard Worker 8*f4ee7fbaSAndroid Build Coastguard Worker/* 9*f4ee7fbaSAndroid Build Coastguard Worker#include <stdbool.h> 10*f4ee7fbaSAndroid Build Coastguard Worker#include <stddef.h> 11*f4ee7fbaSAndroid Build Coastguard Worker#include <stdint.h> 12*f4ee7fbaSAndroid Build Coastguard Worker 13*f4ee7fbaSAndroid Build Coastguard Worker#include <brotli/encode.h> 14*f4ee7fbaSAndroid Build Coastguard Worker 15*f4ee7fbaSAndroid Build Coastguard Workerstruct CompressStreamResult { 16*f4ee7fbaSAndroid Build Coastguard Worker size_t bytes_consumed; 17*f4ee7fbaSAndroid Build Coastguard Worker const uint8_t* output_data; 18*f4ee7fbaSAndroid Build Coastguard Worker size_t output_data_size; 19*f4ee7fbaSAndroid Build Coastguard Worker int success; 20*f4ee7fbaSAndroid Build Coastguard Worker int has_more; 21*f4ee7fbaSAndroid Build Coastguard Worker}; 22*f4ee7fbaSAndroid Build Coastguard Worker 23*f4ee7fbaSAndroid Build Coastguard Workerstatic struct CompressStreamResult CompressStream( 24*f4ee7fbaSAndroid Build Coastguard Worker BrotliEncoderState* s, BrotliEncoderOperation op, 25*f4ee7fbaSAndroid Build Coastguard Worker const uint8_t* data, size_t data_size) { 26*f4ee7fbaSAndroid Build Coastguard Worker struct CompressStreamResult result; 27*f4ee7fbaSAndroid Build Coastguard Worker size_t available_in = data_size; 28*f4ee7fbaSAndroid Build Coastguard Worker const uint8_t* next_in = data; 29*f4ee7fbaSAndroid Build Coastguard Worker size_t available_out = 0; 30*f4ee7fbaSAndroid Build Coastguard Worker result.success = BrotliEncoderCompressStream(s, op, 31*f4ee7fbaSAndroid Build Coastguard Worker &available_in, &next_in, &available_out, 0, 0) ? 1 : 0; 32*f4ee7fbaSAndroid Build Coastguard Worker result.bytes_consumed = data_size - available_in; 33*f4ee7fbaSAndroid Build Coastguard Worker result.output_data = 0; 34*f4ee7fbaSAndroid Build Coastguard Worker result.output_data_size = 0; 35*f4ee7fbaSAndroid Build Coastguard Worker if (result.success) { 36*f4ee7fbaSAndroid Build Coastguard Worker result.output_data = BrotliEncoderTakeOutput(s, &result.output_data_size); 37*f4ee7fbaSAndroid Build Coastguard Worker } 38*f4ee7fbaSAndroid Build Coastguard Worker result.has_more = BrotliEncoderHasMoreOutput(s) ? 1 : 0; 39*f4ee7fbaSAndroid Build Coastguard Worker return result; 40*f4ee7fbaSAndroid Build Coastguard Worker} 41*f4ee7fbaSAndroid Build Coastguard Worker*/ 42*f4ee7fbaSAndroid Build Coastguard Workerimport "C" 43*f4ee7fbaSAndroid Build Coastguard Worker 44*f4ee7fbaSAndroid Build Coastguard Workerimport ( 45*f4ee7fbaSAndroid Build Coastguard Worker "bytes" 46*f4ee7fbaSAndroid Build Coastguard Worker "errors" 47*f4ee7fbaSAndroid Build Coastguard Worker "io" 48*f4ee7fbaSAndroid Build Coastguard Worker "unsafe" 49*f4ee7fbaSAndroid Build Coastguard Worker) 50*f4ee7fbaSAndroid Build Coastguard Worker 51*f4ee7fbaSAndroid Build Coastguard Worker// WriterOptions configures Writer. 52*f4ee7fbaSAndroid Build Coastguard Workertype WriterOptions struct { 53*f4ee7fbaSAndroid Build Coastguard Worker // Quality controls the compression-speed vs compression-density trade-offs. 54*f4ee7fbaSAndroid Build Coastguard Worker // The higher the quality, the slower the compression. Range is 0 to 11. 55*f4ee7fbaSAndroid Build Coastguard Worker Quality int 56*f4ee7fbaSAndroid Build Coastguard Worker // LGWin is the base 2 logarithm of the sliding window size. 57*f4ee7fbaSAndroid Build Coastguard Worker // Range is 10 to 24. 0 indicates automatic configuration based on Quality. 58*f4ee7fbaSAndroid Build Coastguard Worker LGWin int 59*f4ee7fbaSAndroid Build Coastguard Worker} 60*f4ee7fbaSAndroid Build Coastguard Worker 61*f4ee7fbaSAndroid Build Coastguard Worker// Writer implements io.WriteCloser by writing Brotli-encoded data to an 62*f4ee7fbaSAndroid Build Coastguard Worker// underlying Writer. 63*f4ee7fbaSAndroid Build Coastguard Workertype Writer struct { 64*f4ee7fbaSAndroid Build Coastguard Worker dst io.Writer 65*f4ee7fbaSAndroid Build Coastguard Worker state *C.BrotliEncoderState 66*f4ee7fbaSAndroid Build Coastguard Worker buf, encoded []byte 67*f4ee7fbaSAndroid Build Coastguard Worker} 68*f4ee7fbaSAndroid Build Coastguard Worker 69*f4ee7fbaSAndroid Build Coastguard Workervar ( 70*f4ee7fbaSAndroid Build Coastguard Worker errEncode = errors.New("cbrotli: encode error") 71*f4ee7fbaSAndroid Build Coastguard Worker errWriterClosed = errors.New("cbrotli: Writer is closed") 72*f4ee7fbaSAndroid Build Coastguard Worker) 73*f4ee7fbaSAndroid Build Coastguard Worker 74*f4ee7fbaSAndroid Build Coastguard Worker// NewWriter initializes new Writer instance. 75*f4ee7fbaSAndroid Build Coastguard Worker// Close MUST be called to free resources. 76*f4ee7fbaSAndroid Build Coastguard Workerfunc NewWriter(dst io.Writer, options WriterOptions) *Writer { 77*f4ee7fbaSAndroid Build Coastguard Worker state := C.BrotliEncoderCreateInstance(nil, nil, nil) 78*f4ee7fbaSAndroid Build Coastguard Worker C.BrotliEncoderSetParameter( 79*f4ee7fbaSAndroid Build Coastguard Worker state, C.BROTLI_PARAM_QUALITY, (C.uint32_t)(options.Quality)) 80*f4ee7fbaSAndroid Build Coastguard Worker if options.LGWin > 0 { 81*f4ee7fbaSAndroid Build Coastguard Worker C.BrotliEncoderSetParameter( 82*f4ee7fbaSAndroid Build Coastguard Worker state, C.BROTLI_PARAM_LGWIN, (C.uint32_t)(options.LGWin)) 83*f4ee7fbaSAndroid Build Coastguard Worker } 84*f4ee7fbaSAndroid Build Coastguard Worker return &Writer{ 85*f4ee7fbaSAndroid Build Coastguard Worker dst: dst, 86*f4ee7fbaSAndroid Build Coastguard Worker state: state, 87*f4ee7fbaSAndroid Build Coastguard Worker } 88*f4ee7fbaSAndroid Build Coastguard Worker} 89*f4ee7fbaSAndroid Build Coastguard Worker 90*f4ee7fbaSAndroid Build Coastguard Workerfunc (w *Writer) writeChunk(p []byte, op C.BrotliEncoderOperation) (n int, err error) { 91*f4ee7fbaSAndroid Build Coastguard Worker if w.state == nil { 92*f4ee7fbaSAndroid Build Coastguard Worker return 0, errWriterClosed 93*f4ee7fbaSAndroid Build Coastguard Worker } 94*f4ee7fbaSAndroid Build Coastguard Worker 95*f4ee7fbaSAndroid Build Coastguard Worker for { 96*f4ee7fbaSAndroid Build Coastguard Worker var data *C.uint8_t 97*f4ee7fbaSAndroid Build Coastguard Worker if len(p) != 0 { 98*f4ee7fbaSAndroid Build Coastguard Worker data = (*C.uint8_t)(&p[0]) 99*f4ee7fbaSAndroid Build Coastguard Worker } 100*f4ee7fbaSAndroid Build Coastguard Worker result := C.CompressStream(w.state, op, data, C.size_t(len(p))) 101*f4ee7fbaSAndroid Build Coastguard Worker if result.success == 0 { 102*f4ee7fbaSAndroid Build Coastguard Worker return n, errEncode 103*f4ee7fbaSAndroid Build Coastguard Worker } 104*f4ee7fbaSAndroid Build Coastguard Worker p = p[int(result.bytes_consumed):] 105*f4ee7fbaSAndroid Build Coastguard Worker n += int(result.bytes_consumed) 106*f4ee7fbaSAndroid Build Coastguard Worker 107*f4ee7fbaSAndroid Build Coastguard Worker length := int(result.output_data_size) 108*f4ee7fbaSAndroid Build Coastguard Worker if length != 0 { 109*f4ee7fbaSAndroid Build Coastguard Worker // It is a workaround for non-copying-wrapping of native memory. 110*f4ee7fbaSAndroid Build Coastguard Worker // C-encoder never pushes output block longer than ((2 << 25) + 502). 111*f4ee7fbaSAndroid Build Coastguard Worker // TODO: use natural wrapper, when it becomes available, see 112*f4ee7fbaSAndroid Build Coastguard Worker // https://golang.org/issue/13656. 113*f4ee7fbaSAndroid Build Coastguard Worker output := (*[1 << 30]byte)(unsafe.Pointer(result.output_data))[:length:length] 114*f4ee7fbaSAndroid Build Coastguard Worker _, err = w.dst.Write(output) 115*f4ee7fbaSAndroid Build Coastguard Worker if err != nil { 116*f4ee7fbaSAndroid Build Coastguard Worker return n, err 117*f4ee7fbaSAndroid Build Coastguard Worker } 118*f4ee7fbaSAndroid Build Coastguard Worker } 119*f4ee7fbaSAndroid Build Coastguard Worker if len(p) == 0 && result.has_more == 0 { 120*f4ee7fbaSAndroid Build Coastguard Worker return n, nil 121*f4ee7fbaSAndroid Build Coastguard Worker } 122*f4ee7fbaSAndroid Build Coastguard Worker } 123*f4ee7fbaSAndroid Build Coastguard Worker} 124*f4ee7fbaSAndroid Build Coastguard Worker 125*f4ee7fbaSAndroid Build Coastguard Worker// Flush outputs encoded data for all input provided to Write. The resulting 126*f4ee7fbaSAndroid Build Coastguard Worker// output can be decoded to match all input before Flush, but the stream is 127*f4ee7fbaSAndroid Build Coastguard Worker// not yet complete until after Close. 128*f4ee7fbaSAndroid Build Coastguard Worker// Flush has a negative impact on compression. 129*f4ee7fbaSAndroid Build Coastguard Workerfunc (w *Writer) Flush() error { 130*f4ee7fbaSAndroid Build Coastguard Worker _, err := w.writeChunk(nil, C.BROTLI_OPERATION_FLUSH) 131*f4ee7fbaSAndroid Build Coastguard Worker return err 132*f4ee7fbaSAndroid Build Coastguard Worker} 133*f4ee7fbaSAndroid Build Coastguard Worker 134*f4ee7fbaSAndroid Build Coastguard Worker// Close flushes remaining data to the decorated writer and frees C resources. 135*f4ee7fbaSAndroid Build Coastguard Workerfunc (w *Writer) Close() error { 136*f4ee7fbaSAndroid Build Coastguard Worker // If stream is already closed, it is reported by `writeChunk`. 137*f4ee7fbaSAndroid Build Coastguard Worker _, err := w.writeChunk(nil, C.BROTLI_OPERATION_FINISH) 138*f4ee7fbaSAndroid Build Coastguard Worker // C-Brotli tolerates `nil` pointer here. 139*f4ee7fbaSAndroid Build Coastguard Worker C.BrotliEncoderDestroyInstance(w.state) 140*f4ee7fbaSAndroid Build Coastguard Worker w.state = nil 141*f4ee7fbaSAndroid Build Coastguard Worker return err 142*f4ee7fbaSAndroid Build Coastguard Worker} 143*f4ee7fbaSAndroid Build Coastguard Worker 144*f4ee7fbaSAndroid Build Coastguard Worker// Write implements io.Writer. Flush or Close must be called to ensure that the 145*f4ee7fbaSAndroid Build Coastguard Worker// encoded bytes are actually flushed to the underlying Writer. 146*f4ee7fbaSAndroid Build Coastguard Workerfunc (w *Writer) Write(p []byte) (n int, err error) { 147*f4ee7fbaSAndroid Build Coastguard Worker return w.writeChunk(p, C.BROTLI_OPERATION_PROCESS) 148*f4ee7fbaSAndroid Build Coastguard Worker} 149*f4ee7fbaSAndroid Build Coastguard Worker 150*f4ee7fbaSAndroid Build Coastguard Worker// Encode returns content encoded with Brotli. 151*f4ee7fbaSAndroid Build Coastguard Workerfunc Encode(content []byte, options WriterOptions) ([]byte, error) { 152*f4ee7fbaSAndroid Build Coastguard Worker var buf bytes.Buffer 153*f4ee7fbaSAndroid Build Coastguard Worker writer := NewWriter(&buf, options) 154*f4ee7fbaSAndroid Build Coastguard Worker _, err := writer.Write(content) 155*f4ee7fbaSAndroid Build Coastguard Worker if closeErr := writer.Close(); err == nil { 156*f4ee7fbaSAndroid Build Coastguard Worker err = closeErr 157*f4ee7fbaSAndroid Build Coastguard Worker } 158*f4ee7fbaSAndroid Build Coastguard Worker return buf.Bytes(), err 159*f4ee7fbaSAndroid Build Coastguard Worker} 160