aboutsummaryrefslogtreecommitdiff
path: root/go/cbrotli/cbrotli.go
blob: a7009d095e40fc4d696cb6ef169f1eb848d8246c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Distributed under MIT license.
// See file LICENSE for detail or copy at https://opensource.org/licenses/MIT

// Package cbrotli compresses and decompresses data with C-Brotli library.
package cbrotli

import (
	"bytes"
	"io"

	"github.com/google/brotli/go/cbrotli/internal/decoder"
	"github.com/google/brotli/go/cbrotli/internal/encoder"
)

// An internalError reports an error in the (c-)brotli code itself.
type internalError string

func (e internalError) Error() string {
	return "cbrotli: internal error: " + string(e)
}

//------------------------------------------------------------------------------
// Encoder
//------------------------------------------------------------------------------

// WriterOptions configures Writer.
type WriterOptions struct {
	// Quality controls the compression-speed vs compression-density trade-offs.
	// The higher the quality, the slower the compression. Range is 0 to 11.
	Quality int
	// LGWin is the base 2 logarithm of the sliding window size.
	// Range is 10 to 24. 0 indicates automatic configuration based on Quality.
	LGWin int
}

// Writer implements io.WriteCloser, an io.Writer decorator that produces
// Brotli-encoded data.
type Writer struct {
	dst     io.Writer
	encoder encoder.Encoder
	closed  bool
}

// NewWriter initializes new Writer instance.
// Close MUST be called to free resources.
func NewWriter(dst io.Writer, options WriterOptions) *Writer {
	return &Writer{
		dst:     dst,
		encoder: encoder.New(options.Quality, options.LGWin),
	}
}

// Close implements io.Closer. Close MUST be invoked to free native resources.
// Also Close implicitly flushes remaining data to the decorated writer.
func (z *Writer) Close() error {
	if z.closed {
		return nil
	}
	defer z.encoder.Close()
	_, err := z.writeChunk(nil, encoder.Finish)
	z.closed = true
	return err
}

func (z *Writer) writeChunk(p []byte, op encoder.Operation) (int, error) {
	if z.closed {
		return 0, internalError("write after close")
	}
	var totalBytesConsumed int
	var err error
	for {
		bytesConsumed, output, status := z.encoder.CompressStream(p, op)
		if status == encoder.Error {
			err = internalError("encoder failure")
			break
		}
		p = p[bytesConsumed:]
		totalBytesConsumed += bytesConsumed
		_, err = z.dst.Write(output)
		if err != nil {
			break
		}
		if len(p) == 0 && status == encoder.Done {
			break
		}
	}
	return totalBytesConsumed, err
}

// Write implements io.Writer.
func (z *Writer) Write(p []byte) (int, error) {
	return z.writeChunk(p, encoder.Process)
}

// Flush outputs encoded data for all input provided to Write. The resulting
// output can be decoded to match all input before Flush, but the stream is
// not yet complete until after Close.
// Flush has a negative impact on compression.
func (z *Writer) Flush() error {
	_, err := z.writeChunk(nil, encoder.Finish)
	return err
}

// Encode returns content encoded with Brotli.
func Encode(content []byte, options WriterOptions) ([]byte, error) {
	var buf bytes.Buffer
	writer := NewWriter(&buf, options)
	defer writer.Close()
	_, err := writer.Write(content)
	if err != nil {
		return nil, err
	}
	if err := writer.Close(); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

//------------------------------------------------------------------------------
// Decoder
//------------------------------------------------------------------------------

// Reader implements io.ReadCloser, an io.Reader decorator that decodes
// Brotli-encoded data.
type Reader struct {
	src     io.Reader
	decoder decoder.Decoder
	buf     []byte // intermediate read buffer pointed to by next
	eof     bool   // true if all compressed stream is decoded
	next    []byte // buffered data to be passed to decoder
	output  []byte // data produced by decoder, but not yet consumed
	srcErr  error  // last source reader error
	err     error  // reader state; nil if it is OK to read further
	closed  bool   // true is stream is already closed
}

// NewReader initializes new Reader instance.
// Close MUST be called to free resources.
func NewReader(src io.Reader) *Reader {
	return &Reader{
		src:     src,
		decoder: decoder.New(),
		buf:     make([]byte, 32*1024),
		eof:     false,
	}
}

// Close implements io.Closer. Close MUST be invoked to free native resources.
func (z *Reader) Close() error {
	if z.closed {
		return nil
	}
	z.decoder.Close()
	z.err = internalError("read after close")
	z.closed = true
	return nil
}

func isEOF(src io.Reader) bool {
	n, err := src.Read(make([]byte, 1))
	return n == 0 && err == io.EOF
}

// Read implements io.Reader.
func (z *Reader) Read(p []byte) (int, error) {
	// Any error state is unrecoverable.
	if z.err != nil {
		return 0, z.err
	}
	// See io.Reader documentation.
	if len(p) == 0 {
		return 0, nil
	}

	var totalOutBytes int

	// There is no practical limit for amount of bytes being consumed by decoder
	// before producing any output. Continue feeding decoder until some data is
	// produced
	for {
		// Push already produced output first.
		if outBytes := len(z.output); outBytes != 0 {
			outBytes = copy(p, z.output)
			p = p[outBytes:]
			z.output = z.output[outBytes:]
			totalOutBytes += outBytes
			// Output buffer is full.
			if len(p) == 0 {
				break
			}
			continue
		}
		// No more produced output left.
		// If no more output is expected, then we are finished.
		if z.eof {
			z.err = io.EOF
			break
		}
		// Replenish buffer (might cause blocking read), only if necessary.
		if len(z.next) == 0 && totalOutBytes == 0 && z.srcErr != io.EOF {
			var n int
			n, z.srcErr = z.src.Read(z.buf)
			z.next = z.buf[:n]
			if z.srcErr != nil && z.srcErr != io.EOF {
				z.err = z.srcErr
				break
			}
		}
		// Do decoding.
		consumed, output, status := z.decoder.DecompressStream(z.next)
		z.output = output
		z.next = z.next[consumed:]
		if status == decoder.Error {
			// When error happens, the remaining output does not matter.
			z.err = internalError("decoder failure")
			break
		} else if status == decoder.Done {
			// Decoder stream is closed; no further input is expected.
			if len(z.next) != 0 || (z.srcErr != io.EOF && !isEOF(z.src)) {
				z.err = internalError("excessive input")
				break
			}
			// No more output is expected; keep pushing output.
			z.eof = true
			continue
		} else {
			// If can not move any further...
			if consumed == 0 && len(z.output) == 0 {
				// Unexpected end of input.
				if z.srcErr == io.EOF || totalOutBytes == 0 {
					z.err = io.ErrUnexpectedEOF
				}
				// Postpone blocking reads for the next invocation.
				break
			}
			// Continue pushing output.
		}
	}
	return totalOutBytes, z.err
}

// Decode decodes Brotli encoded data.
func Decode(encodedData []byte) ([]byte, error) {
	var buf bytes.Buffer
	reader := NewReader(bytes.NewReader(encodedData))
	defer reader.Close()
	_, err := io.Copy(&buf, reader)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}