254 lines
6.8 KiB
Go
Executable file
254 lines
6.8 KiB
Go
Executable file
// 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 cbrotli compresses and decompresses data with C-Brotli library.
|
|
package cbrotli
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
|
|
"github.com/google/brotli/go/cbrotli/internal/decoder"
|
|
"github.com/google/brotli/go/cbrotli/internal/encoder"
|
|
)
|
|
|
|
// An internalError reports an error in the (c-)brotli code itself.
|
|
type internalError string
|
|
|
|
func (e internalError) Error() string {
|
|
return "cbrotli: internal error: " + string(e)
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Encoder
|
|
//------------------------------------------------------------------------------
|
|
|
|
// WriterOptions configures Writer.
|
|
type WriterOptions struct {
|
|
// Quality controls the compression-speed vs compression-density trade-offs.
|
|
// The higher the quality, the slower the compression. Range is 0 to 11.
|
|
Quality int
|
|
// LGWin is the base 2 logarithm of the sliding window size.
|
|
// Range is 10 to 24. 0 indicates automatic configuration based on Quality.
|
|
LGWin int
|
|
}
|
|
|
|
// Writer implements io.WriteCloser, an io.Writer decorator that produces
|
|
// Brotli-encoded data.
|
|
type Writer struct {
|
|
dst io.Writer
|
|
encoder encoder.Encoder
|
|
closed bool
|
|
}
|
|
|
|
// NewWriter initializes new Writer instance.
|
|
// Close MUST be called to free resources.
|
|
func NewWriter(dst io.Writer, options WriterOptions) *Writer {
|
|
return &Writer{
|
|
dst: dst,
|
|
encoder: encoder.New(options.Quality, options.LGWin),
|
|
}
|
|
}
|
|
|
|
// Close implements io.Closer. Close MUST be invoked to free native resources.
|
|
// Also Close implicitly flushes remaining data to the decorated writer.
|
|
func (z *Writer) Close() error {
|
|
if z.closed {
|
|
return nil
|
|
}
|
|
defer z.encoder.Close()
|
|
_, err := z.writeChunk(nil, encoder.Finish)
|
|
z.closed = true
|
|
return err
|
|
}
|
|
|
|
func (z *Writer) writeChunk(p []byte, op encoder.Operation) (int, error) {
|
|
if z.closed {
|
|
return 0, internalError("write after close")
|
|
}
|
|
var totalBytesConsumed int
|
|
var err error
|
|
for {
|
|
bytesConsumed, output, status := z.encoder.CompressStream(p, op)
|
|
if status == encoder.Error {
|
|
err = internalError("encoder failure")
|
|
break
|
|
}
|
|
p = p[bytesConsumed:]
|
|
totalBytesConsumed += bytesConsumed
|
|
_, err = z.dst.Write(output)
|
|
if err != nil {
|
|
break
|
|
}
|
|
if len(p) == 0 && status == encoder.Done {
|
|
break
|
|
}
|
|
}
|
|
return totalBytesConsumed, err
|
|
}
|
|
|
|
// Write implements io.Writer.
|
|
func (z *Writer) Write(p []byte) (int, error) {
|
|
return z.writeChunk(p, encoder.Process)
|
|
}
|
|
|
|
// Flush outputs encoded data for all input provided to Write. The resulting
|
|
// output can be decoded to match all input before Flush, but the stream is
|
|
// not yet complete until after Close.
|
|
// Flush has a negative impact on compression.
|
|
func (z *Writer) Flush() error {
|
|
_, err := z.writeChunk(nil, encoder.Finish)
|
|
return err
|
|
}
|
|
|
|
// Encode returns content encoded with Brotli.
|
|
func Encode(content []byte, options WriterOptions) ([]byte, error) {
|
|
var buf bytes.Buffer
|
|
writer := NewWriter(&buf, options)
|
|
defer writer.Close()
|
|
_, err := writer.Write(content)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := writer.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Decoder
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Reader implements io.ReadCloser, an io.Reader decorator that decodes
|
|
// Brotli-encoded data.
|
|
type Reader struct {
|
|
src io.Reader
|
|
decoder decoder.Decoder
|
|
buf []byte // intermediate read buffer pointed to by next
|
|
eof bool // true if all compressed stream is decoded
|
|
next []byte // buffered data to be passed to decoder
|
|
output []byte // data produced by decoder, but not yet consumed
|
|
srcErr error // last source reader error
|
|
err error // reader state; nil if it is OK to read further
|
|
closed bool // true is stream is already closed
|
|
}
|
|
|
|
// NewReader initializes new Reader instance.
|
|
// Close MUST be called to free resources.
|
|
func NewReader(src io.Reader) *Reader {
|
|
return &Reader{
|
|
src: src,
|
|
decoder: decoder.New(),
|
|
buf: make([]byte, 32*1024),
|
|
eof: false,
|
|
}
|
|
}
|
|
|
|
// Close implements io.Closer. Close MUST be invoked to free native resources.
|
|
func (z *Reader) Close() error {
|
|
if z.closed {
|
|
return nil
|
|
}
|
|
z.decoder.Close()
|
|
z.err = internalError("read after close")
|
|
z.closed = true
|
|
return nil
|
|
}
|
|
|
|
func isEOF(src io.Reader) bool {
|
|
n, err := src.Read(make([]byte, 1))
|
|
return n == 0 && err == io.EOF
|
|
}
|
|
|
|
// Read implements io.Reader.
|
|
func (z *Reader) Read(p []byte) (int, error) {
|
|
// Any error state is unrecoverable.
|
|
if z.err != nil {
|
|
return 0, z.err
|
|
}
|
|
// See io.Reader documentation.
|
|
if len(p) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
var totalOutBytes int
|
|
|
|
// There is no practical limit for amount of bytes being consumed by decoder
|
|
// before producing any output. Continue feeding decoder until some data is
|
|
// produced
|
|
for {
|
|
// Push already produced output first.
|
|
if outBytes := len(z.output); outBytes != 0 {
|
|
outBytes = copy(p, z.output)
|
|
p = p[outBytes:]
|
|
z.output = z.output[outBytes:]
|
|
totalOutBytes += outBytes
|
|
// Output buffer is full.
|
|
if len(p) == 0 {
|
|
break
|
|
}
|
|
continue
|
|
}
|
|
// No more produced output left.
|
|
// If no more output is expected, then we are finished.
|
|
if z.eof {
|
|
z.err = io.EOF
|
|
break
|
|
}
|
|
// Replenish buffer (might cause blocking read), only if necessary.
|
|
if len(z.next) == 0 && totalOutBytes == 0 && z.srcErr != io.EOF {
|
|
var n int
|
|
n, z.srcErr = z.src.Read(z.buf)
|
|
z.next = z.buf[:n]
|
|
if z.srcErr != nil && z.srcErr != io.EOF {
|
|
z.err = z.srcErr
|
|
break
|
|
}
|
|
}
|
|
// Do decoding.
|
|
consumed, output, status := z.decoder.DecompressStream(z.next)
|
|
z.output = output
|
|
z.next = z.next[consumed:]
|
|
if status == decoder.Error {
|
|
// When error happens, the remaining output does not matter.
|
|
z.err = internalError("decoder failure")
|
|
break
|
|
} else if status == decoder.Done {
|
|
// Decoder stream is closed; no further input is expected.
|
|
if len(z.next) != 0 || (z.srcErr != io.EOF && !isEOF(z.src)) {
|
|
z.err = internalError("excessive input")
|
|
break
|
|
}
|
|
// No more output is expected; keep pushing output.
|
|
z.eof = true
|
|
continue
|
|
} else {
|
|
// If can not move any further...
|
|
if consumed == 0 && len(z.output) == 0 {
|
|
// Unexpected end of input.
|
|
if z.srcErr == io.EOF || totalOutBytes == 0 {
|
|
z.err = io.ErrUnexpectedEOF
|
|
}
|
|
// Postpone blocking reads for the next invocation.
|
|
break
|
|
}
|
|
// Continue pushing output.
|
|
}
|
|
}
|
|
return totalOutBytes, z.err
|
|
}
|
|
|
|
// Decode decodes Brotli encoded data.
|
|
func Decode(encodedData []byte) ([]byte, error) {
|
|
var buf bytes.Buffer
|
|
reader := NewReader(bytes.NewReader(encodedData))
|
|
defer reader.Close()
|
|
_, err := io.Copy(&buf, reader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return buf.Bytes(), nil
|
|
}
|