From 7ad6b2e5b1704f5068c5f5d2a32b1a93a1d0bd9b Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Thu, 25 Apr 2019 17:25:59 +0200 Subject: [PATCH] Make brotli behave like official compress/ packages --- reader.go | 13 +++++++++++-- writer.go | 38 ++++++++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/reader.go b/reader.go index a646fa7..ef771a9 100644 --- a/reader.go +++ b/reader.go @@ -20,13 +20,22 @@ var errReaderClosed = errors.New("brotli: Reader is closed") // It is arbitrarily chosen to be equal to the constant used in io.Copy. const readBufSize = 32 * 1024 -// NewReader initializes new Reader instance. +// NewReader creates a new Reader reading the given reader. func NewReader(src io.Reader) *Reader { r := new(Reader) + r.Reset(src) + return r +} + +// Reset discards the Reader's state and makes it equivalent to the result of +// its original state from NewReader, but writing to src instead. +// This permits reusing a Reader rather than allocating a new one. +// Error is always nil +func (r *Reader) Reset(src io.Reader) error { decoderStateInit(r) r.src = src r.buf = make([]byte, readBufSize) - return r + return nil } func (r *Reader) Read(p []byte) (n int, err error) { diff --git a/writer.go b/writer.go index 2be9021..96541b6 100644 --- a/writer.go +++ b/writer.go @@ -5,6 +5,12 @@ import ( "io" ) +const ( + BestSpeed = 0 + BestCompression = 11 + DefaultCompression = 6 +) + // WriterOptions configures Writer. type WriterOptions struct { // Quality controls the compression-speed vs compression-density trade-offs. @@ -20,18 +26,42 @@ var ( errWriterClosed = errors.New("brotli: Writer is closed") ) -// NewWriter initializes new Writer instance. -func NewWriter(dst io.Writer, options WriterOptions) *Writer { +// Writes to the returned writer are compressed and written to dst. +// It is the caller's responsibility to call Close on the Writer when done. +// Writes may be buffered and not flushed until Close. +func NewWriter(dst io.Writer) *Writer { + return NewWriterLevel(dst, DefaultCompression) +} + +// NewWriterLevel is like NewWriter but specifies the compression level instead +// of assuming DefaultCompression. +// The compression level can be DefaultCompression or any integer value between +// BestSpeed and BestCompression inclusive. +func NewWriterLevel(dst io.Writer, level int) *Writer { + return NewWriterOptions(dst, WriterOptions{ + Quality: level, + }) +} + +// NewWriterOptions is like NewWriter but specifies WriterOptions +func NewWriterOptions(dst io.Writer, options WriterOptions) *Writer { w := new(Writer) - encoderInitState(w) + w.Reset(dst) w.params.quality = options.Quality if options.LGWin > 0 { w.params.lgwin = uint(options.LGWin) } - w.dst = dst return w } +// Reset discards the Writer's state and makes it equivalent to the result of +// its original state from NewWriter or NewWriterLevel, but writing to dst +// instead. This permits reusing a Writer rather than allocating a new one. +func (w *Writer) Reset(dst io.Writer) { + encoderInitState(w) + w.dst = dst +} + func (w *Writer) writeChunk(p []byte, op int) (n int, err error) { if w.dst == nil { return 0, errWriterClosed