xref: /aosp_15_r20/external/brotli/go/cbrotli/reader.go (revision f4ee7fba7774faf2a30f13154332c0a06550dbc4)
1*f4ee7fbaSAndroid Build Coastguard Worker// Copyright 2016 Google Inc. All Rights Reserved.
2*f4ee7fbaSAndroid Build Coastguard Worker//
3*f4ee7fbaSAndroid Build Coastguard Worker// Distributed under MIT license.
4*f4ee7fbaSAndroid Build Coastguard Worker// See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
5*f4ee7fbaSAndroid Build Coastguard Worker
6*f4ee7fbaSAndroid Build Coastguard Worker// Package cbrotli compresses and decompresses data with C-Brotli library.
7*f4ee7fbaSAndroid Build Coastguard Workerpackage cbrotli
8*f4ee7fbaSAndroid Build Coastguard Worker
9*f4ee7fbaSAndroid Build Coastguard Worker/*
10*f4ee7fbaSAndroid Build Coastguard Worker#include <stddef.h>
11*f4ee7fbaSAndroid Build Coastguard Worker#include <stdint.h>
12*f4ee7fbaSAndroid Build Coastguard Worker
13*f4ee7fbaSAndroid Build Coastguard Worker#include <brotli/decode.h>
14*f4ee7fbaSAndroid Build Coastguard Worker
15*f4ee7fbaSAndroid Build Coastguard Workerstatic BrotliDecoderResult DecompressStream(BrotliDecoderState* s,
16*f4ee7fbaSAndroid Build Coastguard Worker                                            uint8_t* out, size_t out_len,
17*f4ee7fbaSAndroid Build Coastguard Worker                                            const uint8_t* in, size_t in_len,
18*f4ee7fbaSAndroid Build Coastguard Worker                                            size_t* bytes_written,
19*f4ee7fbaSAndroid Build Coastguard Worker                                            size_t* bytes_consumed) {
20*f4ee7fbaSAndroid Build Coastguard Worker  size_t in_remaining = in_len;
21*f4ee7fbaSAndroid Build Coastguard Worker  size_t out_remaining = out_len;
22*f4ee7fbaSAndroid Build Coastguard Worker  BrotliDecoderResult result = BrotliDecoderDecompressStream(
23*f4ee7fbaSAndroid Build Coastguard Worker      s, &in_remaining, &in, &out_remaining, &out, NULL);
24*f4ee7fbaSAndroid Build Coastguard Worker  *bytes_written = out_len - out_remaining;
25*f4ee7fbaSAndroid Build Coastguard Worker  *bytes_consumed = in_len - in_remaining;
26*f4ee7fbaSAndroid Build Coastguard Worker  return result;
27*f4ee7fbaSAndroid Build Coastguard Worker}
28*f4ee7fbaSAndroid Build Coastguard Worker*/
29*f4ee7fbaSAndroid Build Coastguard Workerimport "C"
30*f4ee7fbaSAndroid Build Coastguard Worker
31*f4ee7fbaSAndroid Build Coastguard Workerimport (
32*f4ee7fbaSAndroid Build Coastguard Worker	"bytes"
33*f4ee7fbaSAndroid Build Coastguard Worker	"errors"
34*f4ee7fbaSAndroid Build Coastguard Worker	"io"
35*f4ee7fbaSAndroid Build Coastguard Worker	"io/ioutil"
36*f4ee7fbaSAndroid Build Coastguard Worker)
37*f4ee7fbaSAndroid Build Coastguard Worker
38*f4ee7fbaSAndroid Build Coastguard Workertype decodeError C.BrotliDecoderErrorCode
39*f4ee7fbaSAndroid Build Coastguard Worker
40*f4ee7fbaSAndroid Build Coastguard Workerfunc (err decodeError) Error() string {
41*f4ee7fbaSAndroid Build Coastguard Worker	return "cbrotli: " +
42*f4ee7fbaSAndroid Build Coastguard Worker		C.GoString(C.BrotliDecoderErrorString(C.BrotliDecoderErrorCode(err)))
43*f4ee7fbaSAndroid Build Coastguard Worker}
44*f4ee7fbaSAndroid Build Coastguard Worker
45*f4ee7fbaSAndroid Build Coastguard Workervar errExcessiveInput = errors.New("cbrotli: excessive input")
46*f4ee7fbaSAndroid Build Coastguard Workervar errInvalidState = errors.New("cbrotli: invalid state")
47*f4ee7fbaSAndroid Build Coastguard Workervar errReaderClosed = errors.New("cbrotli: Reader is closed")
48*f4ee7fbaSAndroid Build Coastguard Worker
49*f4ee7fbaSAndroid Build Coastguard Worker// Reader implements io.ReadCloser by reading Brotli-encoded data from an
50*f4ee7fbaSAndroid Build Coastguard Worker// underlying Reader.
51*f4ee7fbaSAndroid Build Coastguard Workertype Reader struct {
52*f4ee7fbaSAndroid Build Coastguard Worker	src   io.Reader
53*f4ee7fbaSAndroid Build Coastguard Worker	state *C.BrotliDecoderState
54*f4ee7fbaSAndroid Build Coastguard Worker	buf   []byte // scratch space for reading from src
55*f4ee7fbaSAndroid Build Coastguard Worker	in    []byte // current chunk to decode; usually aliases buf
56*f4ee7fbaSAndroid Build Coastguard Worker}
57*f4ee7fbaSAndroid Build Coastguard Worker
58*f4ee7fbaSAndroid Build Coastguard Worker// readBufSize is a "good" buffer size that avoids excessive round-trips
59*f4ee7fbaSAndroid Build Coastguard Worker// between C and Go but doesn't waste too much memory on buffering.
60*f4ee7fbaSAndroid Build Coastguard Worker// It is arbitrarily chosen to be equal to the constant used in io.Copy.
61*f4ee7fbaSAndroid Build Coastguard Workerconst readBufSize = 32 * 1024
62*f4ee7fbaSAndroid Build Coastguard Worker
63*f4ee7fbaSAndroid Build Coastguard Worker// NewReader initializes new Reader instance.
64*f4ee7fbaSAndroid Build Coastguard Worker// Close MUST be called to free resources.
65*f4ee7fbaSAndroid Build Coastguard Workerfunc NewReader(src io.Reader) *Reader {
66*f4ee7fbaSAndroid Build Coastguard Worker	return &Reader{
67*f4ee7fbaSAndroid Build Coastguard Worker		src:   src,
68*f4ee7fbaSAndroid Build Coastguard Worker		state: C.BrotliDecoderCreateInstance(nil, nil, nil),
69*f4ee7fbaSAndroid Build Coastguard Worker		buf:   make([]byte, readBufSize),
70*f4ee7fbaSAndroid Build Coastguard Worker	}
71*f4ee7fbaSAndroid Build Coastguard Worker}
72*f4ee7fbaSAndroid Build Coastguard Worker
73*f4ee7fbaSAndroid Build Coastguard Worker// Close implements io.Closer. Close MUST be invoked to free native resources.
74*f4ee7fbaSAndroid Build Coastguard Workerfunc (r *Reader) Close() error {
75*f4ee7fbaSAndroid Build Coastguard Worker	if r.state == nil {
76*f4ee7fbaSAndroid Build Coastguard Worker		return errReaderClosed
77*f4ee7fbaSAndroid Build Coastguard Worker	}
78*f4ee7fbaSAndroid Build Coastguard Worker	// Close despite the state; i.e. there might be some unread decoded data.
79*f4ee7fbaSAndroid Build Coastguard Worker	C.BrotliDecoderDestroyInstance(r.state)
80*f4ee7fbaSAndroid Build Coastguard Worker	r.state = nil
81*f4ee7fbaSAndroid Build Coastguard Worker	return nil
82*f4ee7fbaSAndroid Build Coastguard Worker}
83*f4ee7fbaSAndroid Build Coastguard Worker
84*f4ee7fbaSAndroid Build Coastguard Workerfunc (r *Reader) Read(p []byte) (n int, err error) {
85*f4ee7fbaSAndroid Build Coastguard Worker	if r.state == nil {
86*f4ee7fbaSAndroid Build Coastguard Worker		return 0, errReaderClosed
87*f4ee7fbaSAndroid Build Coastguard Worker	}
88*f4ee7fbaSAndroid Build Coastguard Worker	if int(C.BrotliDecoderHasMoreOutput(r.state)) == 0 && len(r.in) == 0 {
89*f4ee7fbaSAndroid Build Coastguard Worker		m, readErr := r.src.Read(r.buf)
90*f4ee7fbaSAndroid Build Coastguard Worker		if m == 0 {
91*f4ee7fbaSAndroid Build Coastguard Worker			// If readErr is `nil`, we just proxy underlying stream behavior.
92*f4ee7fbaSAndroid Build Coastguard Worker			return 0, readErr
93*f4ee7fbaSAndroid Build Coastguard Worker		}
94*f4ee7fbaSAndroid Build Coastguard Worker		r.in = r.buf[:m]
95*f4ee7fbaSAndroid Build Coastguard Worker	}
96*f4ee7fbaSAndroid Build Coastguard Worker
97*f4ee7fbaSAndroid Build Coastguard Worker	if len(p) == 0 {
98*f4ee7fbaSAndroid Build Coastguard Worker		return 0, nil
99*f4ee7fbaSAndroid Build Coastguard Worker	}
100*f4ee7fbaSAndroid Build Coastguard Worker
101*f4ee7fbaSAndroid Build Coastguard Worker	for {
102*f4ee7fbaSAndroid Build Coastguard Worker		var written, consumed C.size_t
103*f4ee7fbaSAndroid Build Coastguard Worker		var data *C.uint8_t
104*f4ee7fbaSAndroid Build Coastguard Worker		if len(r.in) != 0 {
105*f4ee7fbaSAndroid Build Coastguard Worker			data = (*C.uint8_t)(&r.in[0])
106*f4ee7fbaSAndroid Build Coastguard Worker		}
107*f4ee7fbaSAndroid Build Coastguard Worker		result := C.DecompressStream(r.state,
108*f4ee7fbaSAndroid Build Coastguard Worker			(*C.uint8_t)(&p[0]), C.size_t(len(p)),
109*f4ee7fbaSAndroid Build Coastguard Worker			data, C.size_t(len(r.in)),
110*f4ee7fbaSAndroid Build Coastguard Worker			&written, &consumed)
111*f4ee7fbaSAndroid Build Coastguard Worker		r.in = r.in[int(consumed):]
112*f4ee7fbaSAndroid Build Coastguard Worker		n = int(written)
113*f4ee7fbaSAndroid Build Coastguard Worker
114*f4ee7fbaSAndroid Build Coastguard Worker		switch result {
115*f4ee7fbaSAndroid Build Coastguard Worker		case C.BROTLI_DECODER_RESULT_SUCCESS:
116*f4ee7fbaSAndroid Build Coastguard Worker			if len(r.in) > 0 {
117*f4ee7fbaSAndroid Build Coastguard Worker				return n, errExcessiveInput
118*f4ee7fbaSAndroid Build Coastguard Worker			}
119*f4ee7fbaSAndroid Build Coastguard Worker			return n, nil
120*f4ee7fbaSAndroid Build Coastguard Worker		case C.BROTLI_DECODER_RESULT_ERROR:
121*f4ee7fbaSAndroid Build Coastguard Worker			return n, decodeError(C.BrotliDecoderGetErrorCode(r.state))
122*f4ee7fbaSAndroid Build Coastguard Worker		case C.BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
123*f4ee7fbaSAndroid Build Coastguard Worker			if n == 0 {
124*f4ee7fbaSAndroid Build Coastguard Worker				return 0, io.ErrShortBuffer
125*f4ee7fbaSAndroid Build Coastguard Worker			}
126*f4ee7fbaSAndroid Build Coastguard Worker			return n, nil
127*f4ee7fbaSAndroid Build Coastguard Worker		case C.BROTLI_DECODER_NEEDS_MORE_INPUT:
128*f4ee7fbaSAndroid Build Coastguard Worker		}
129*f4ee7fbaSAndroid Build Coastguard Worker
130*f4ee7fbaSAndroid Build Coastguard Worker		if len(r.in) != 0 {
131*f4ee7fbaSAndroid Build Coastguard Worker			return 0, errInvalidState
132*f4ee7fbaSAndroid Build Coastguard Worker		}
133*f4ee7fbaSAndroid Build Coastguard Worker
134*f4ee7fbaSAndroid Build Coastguard Worker		// Calling r.src.Read may block. Don't block if we have data to return.
135*f4ee7fbaSAndroid Build Coastguard Worker		if n > 0 {
136*f4ee7fbaSAndroid Build Coastguard Worker			return n, nil
137*f4ee7fbaSAndroid Build Coastguard Worker		}
138*f4ee7fbaSAndroid Build Coastguard Worker
139*f4ee7fbaSAndroid Build Coastguard Worker		// Top off the buffer.
140*f4ee7fbaSAndroid Build Coastguard Worker		encN, err := r.src.Read(r.buf)
141*f4ee7fbaSAndroid Build Coastguard Worker		if encN == 0 {
142*f4ee7fbaSAndroid Build Coastguard Worker			// Not enough data to complete decoding.
143*f4ee7fbaSAndroid Build Coastguard Worker			if err == io.EOF {
144*f4ee7fbaSAndroid Build Coastguard Worker				return 0, io.ErrUnexpectedEOF
145*f4ee7fbaSAndroid Build Coastguard Worker			}
146*f4ee7fbaSAndroid Build Coastguard Worker			return 0, err
147*f4ee7fbaSAndroid Build Coastguard Worker		}
148*f4ee7fbaSAndroid Build Coastguard Worker		r.in = r.buf[:encN]
149*f4ee7fbaSAndroid Build Coastguard Worker	}
150*f4ee7fbaSAndroid Build Coastguard Worker
151*f4ee7fbaSAndroid Build Coastguard Worker	return n, nil
152*f4ee7fbaSAndroid Build Coastguard Worker}
153*f4ee7fbaSAndroid Build Coastguard Worker
154*f4ee7fbaSAndroid Build Coastguard Worker// Decode decodes Brotli encoded data.
155*f4ee7fbaSAndroid Build Coastguard Workerfunc Decode(encodedData []byte) ([]byte, error) {
156*f4ee7fbaSAndroid Build Coastguard Worker	r := &Reader{
157*f4ee7fbaSAndroid Build Coastguard Worker		src:   bytes.NewReader(nil),
158*f4ee7fbaSAndroid Build Coastguard Worker		state: C.BrotliDecoderCreateInstance(nil, nil, nil),
159*f4ee7fbaSAndroid Build Coastguard Worker		buf:   make([]byte, 4), // arbitrarily small but nonzero so that r.src.Read returns io.EOF
160*f4ee7fbaSAndroid Build Coastguard Worker		in:    encodedData,
161*f4ee7fbaSAndroid Build Coastguard Worker	}
162*f4ee7fbaSAndroid Build Coastguard Worker	defer r.Close()
163*f4ee7fbaSAndroid Build Coastguard Worker	return ioutil.ReadAll(r)
164*f4ee7fbaSAndroid Build Coastguard Worker}
165