[145] | 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 }
|
---|