1package brotli 2 3import ( 4 "errors" 5 "io" 6) 7 8const ( 9 BestSpeed = 0 10 BestCompression = 11 11 DefaultCompression = 6 12) 13 14// WriterOptions configures Writer. 15type WriterOptions struct { 16 // Quality controls the compression-speed vs compression-density trade-offs. 17 // The higher the quality, the slower the compression. Range is 0 to 11. 18 Quality int 19 // LGWin is the base 2 logarithm of the sliding window size. 20 // Range is 10 to 24. 0 indicates automatic configuration based on Quality. 21 LGWin int 22} 23 24var ( 25 errEncode = errors.New("brotli: encode error") 26 errWriterClosed = errors.New("brotli: Writer is closed") 27) 28 29// Writes to the returned writer are compressed and written to dst. 30// It is the caller's responsibility to call Close on the Writer when done. 31// Writes may be buffered and not flushed until Close. 32func NewWriter(dst io.Writer) *Writer { 33 return NewWriterLevel(dst, DefaultCompression) 34} 35 36// NewWriterLevel is like NewWriter but specifies the compression level instead 37// of assuming DefaultCompression. 38// The compression level can be DefaultCompression or any integer value between 39// BestSpeed and BestCompression inclusive. 40func NewWriterLevel(dst io.Writer, level int) *Writer { 41 return NewWriterOptions(dst, WriterOptions{ 42 Quality: level, 43 }) 44} 45 46// NewWriterOptions is like NewWriter but specifies WriterOptions 47func NewWriterOptions(dst io.Writer, options WriterOptions) *Writer { 48 w := new(Writer) 49 w.options = options 50 w.Reset(dst) 51 return w 52} 53 54// Reset discards the Writer's state and makes it equivalent to the result of 55// its original state from NewWriter or NewWriterLevel, but writing to dst 56// instead. This permits reusing a Writer rather than allocating a new one. 57func (w *Writer) Reset(dst io.Writer) { 58 encoderInitState(w) 59 w.params.quality = w.options.Quality 60 if w.options.LGWin > 0 { 61 w.params.lgwin = uint(w.options.LGWin) 62 } 63 w.dst = dst 64} 65 66func (w *Writer) writeChunk(p []byte, op int) (n int, err error) { 67 if w.dst == nil { 68 return 0, errWriterClosed 69 } 70 if w.err != nil { 71 return 0, w.err 72 } 73 74 for { 75 availableIn := uint(len(p)) 76 nextIn := p 77 success := encoderCompressStream(w, op, &availableIn, &nextIn) 78 bytesConsumed := len(p) - int(availableIn) 79 p = p[bytesConsumed:] 80 n += bytesConsumed 81 if !success { 82 return n, errEncode 83 } 84 85 if len(p) == 0 || w.err != nil { 86 return n, w.err 87 } 88 } 89} 90 91// Flush outputs encoded data for all input provided to Write. The resulting 92// output can be decoded to match all input before Flush, but the stream is 93// not yet complete until after Close. 94// Flush has a negative impact on compression. 95func (w *Writer) Flush() error { 96 _, err := w.writeChunk(nil, operationFlush) 97 return err 98} 99 100// Close flushes remaining data to the decorated writer. 101func (w *Writer) Close() error { 102 // If stream is already closed, it is reported by `writeChunk`. 103 _, err := w.writeChunk(nil, operationFinish) 104 w.dst = nil 105 return err 106} 107 108// Write implements io.Writer. Flush or Close must be called to ensure that the 109// encoded bytes are actually flushed to the underlying Writer. 110func (w *Writer) Write(p []byte) (n int, err error) { 111 return w.writeChunk(p, operationProcess) 112} 113 114type nopCloser struct { 115 io.Writer 116} 117 118func (nopCloser) Close() error { return nil } 119