github.com/andybalholm/brotli@v1.0.6/writer.go (about) 1 package brotli 2 3 import ( 4 "errors" 5 "io" 6 ) 7 8 const ( 9 BestSpeed = 0 10 BestCompression = 11 11 DefaultCompression = 6 12 ) 13 14 // WriterOptions configures Writer. 15 type 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 24 var ( 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. 32 func 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. 40 func 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 47 func 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. 57 func (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 w.err = nil 65 } 66 67 func (w *Writer) writeChunk(p []byte, op int) (n int, err error) { 68 if w.dst == nil { 69 return 0, errWriterClosed 70 } 71 if w.err != nil { 72 return 0, w.err 73 } 74 75 for { 76 availableIn := uint(len(p)) 77 nextIn := p 78 success := encoderCompressStream(w, op, &availableIn, &nextIn) 79 bytesConsumed := len(p) - int(availableIn) 80 p = p[bytesConsumed:] 81 n += bytesConsumed 82 if !success { 83 return n, errEncode 84 } 85 86 if len(p) == 0 || w.err != nil { 87 return n, w.err 88 } 89 } 90 } 91 92 // Flush outputs encoded data for all input provided to Write. The resulting 93 // output can be decoded to match all input before Flush, but the stream is 94 // not yet complete until after Close. 95 // Flush has a negative impact on compression. 96 func (w *Writer) Flush() error { 97 _, err := w.writeChunk(nil, operationFlush) 98 return err 99 } 100 101 // Close flushes remaining data to the decorated writer. 102 func (w *Writer) Close() error { 103 // If stream is already closed, it is reported by `writeChunk`. 104 _, err := w.writeChunk(nil, operationFinish) 105 w.dst = nil 106 return err 107 } 108 109 // Write implements io.Writer. Flush or Close must be called to ensure that the 110 // encoded bytes are actually flushed to the underlying Writer. 111 func (w *Writer) Write(p []byte) (n int, err error) { 112 return w.writeChunk(p, operationProcess) 113 } 114 115 type nopCloser struct { 116 io.Writer 117 } 118 119 func (nopCloser) Close() error { return nil }