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