xref: /aosp_15_r20/external/brotli/go/cbrotli/writer.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 Workerpackage cbrotli
7*f4ee7fbaSAndroid Build Coastguard Worker
8*f4ee7fbaSAndroid Build Coastguard Worker/*
9*f4ee7fbaSAndroid Build Coastguard Worker#include <stdbool.h>
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/encode.h>
14*f4ee7fbaSAndroid Build Coastguard Worker
15*f4ee7fbaSAndroid Build Coastguard Workerstruct CompressStreamResult {
16*f4ee7fbaSAndroid Build Coastguard Worker  size_t bytes_consumed;
17*f4ee7fbaSAndroid Build Coastguard Worker  const uint8_t* output_data;
18*f4ee7fbaSAndroid Build Coastguard Worker  size_t output_data_size;
19*f4ee7fbaSAndroid Build Coastguard Worker  int success;
20*f4ee7fbaSAndroid Build Coastguard Worker  int has_more;
21*f4ee7fbaSAndroid Build Coastguard Worker};
22*f4ee7fbaSAndroid Build Coastguard Worker
23*f4ee7fbaSAndroid Build Coastguard Workerstatic struct CompressStreamResult CompressStream(
24*f4ee7fbaSAndroid Build Coastguard Worker    BrotliEncoderState* s, BrotliEncoderOperation op,
25*f4ee7fbaSAndroid Build Coastguard Worker    const uint8_t* data, size_t data_size) {
26*f4ee7fbaSAndroid Build Coastguard Worker  struct CompressStreamResult result;
27*f4ee7fbaSAndroid Build Coastguard Worker  size_t available_in = data_size;
28*f4ee7fbaSAndroid Build Coastguard Worker  const uint8_t* next_in = data;
29*f4ee7fbaSAndroid Build Coastguard Worker  size_t available_out = 0;
30*f4ee7fbaSAndroid Build Coastguard Worker  result.success = BrotliEncoderCompressStream(s, op,
31*f4ee7fbaSAndroid Build Coastguard Worker      &available_in, &next_in, &available_out, 0, 0) ? 1 : 0;
32*f4ee7fbaSAndroid Build Coastguard Worker  result.bytes_consumed = data_size - available_in;
33*f4ee7fbaSAndroid Build Coastguard Worker  result.output_data = 0;
34*f4ee7fbaSAndroid Build Coastguard Worker  result.output_data_size = 0;
35*f4ee7fbaSAndroid Build Coastguard Worker  if (result.success) {
36*f4ee7fbaSAndroid Build Coastguard Worker    result.output_data = BrotliEncoderTakeOutput(s, &result.output_data_size);
37*f4ee7fbaSAndroid Build Coastguard Worker  }
38*f4ee7fbaSAndroid Build Coastguard Worker  result.has_more = BrotliEncoderHasMoreOutput(s) ? 1 : 0;
39*f4ee7fbaSAndroid Build Coastguard Worker  return result;
40*f4ee7fbaSAndroid Build Coastguard Worker}
41*f4ee7fbaSAndroid Build Coastguard Worker*/
42*f4ee7fbaSAndroid Build Coastguard Workerimport "C"
43*f4ee7fbaSAndroid Build Coastguard Worker
44*f4ee7fbaSAndroid Build Coastguard Workerimport (
45*f4ee7fbaSAndroid Build Coastguard Worker	"bytes"
46*f4ee7fbaSAndroid Build Coastguard Worker	"errors"
47*f4ee7fbaSAndroid Build Coastguard Worker	"io"
48*f4ee7fbaSAndroid Build Coastguard Worker	"unsafe"
49*f4ee7fbaSAndroid Build Coastguard Worker)
50*f4ee7fbaSAndroid Build Coastguard Worker
51*f4ee7fbaSAndroid Build Coastguard Worker// WriterOptions configures Writer.
52*f4ee7fbaSAndroid Build Coastguard Workertype WriterOptions struct {
53*f4ee7fbaSAndroid Build Coastguard Worker	// Quality controls the compression-speed vs compression-density trade-offs.
54*f4ee7fbaSAndroid Build Coastguard Worker	// The higher the quality, the slower the compression. Range is 0 to 11.
55*f4ee7fbaSAndroid Build Coastguard Worker	Quality int
56*f4ee7fbaSAndroid Build Coastguard Worker	// LGWin is the base 2 logarithm of the sliding window size.
57*f4ee7fbaSAndroid Build Coastguard Worker	// Range is 10 to 24. 0 indicates automatic configuration based on Quality.
58*f4ee7fbaSAndroid Build Coastguard Worker	LGWin int
59*f4ee7fbaSAndroid Build Coastguard Worker}
60*f4ee7fbaSAndroid Build Coastguard Worker
61*f4ee7fbaSAndroid Build Coastguard Worker// Writer implements io.WriteCloser by writing Brotli-encoded data to an
62*f4ee7fbaSAndroid Build Coastguard Worker// underlying Writer.
63*f4ee7fbaSAndroid Build Coastguard Workertype Writer struct {
64*f4ee7fbaSAndroid Build Coastguard Worker	dst          io.Writer
65*f4ee7fbaSAndroid Build Coastguard Worker	state        *C.BrotliEncoderState
66*f4ee7fbaSAndroid Build Coastguard Worker	buf, encoded []byte
67*f4ee7fbaSAndroid Build Coastguard Worker}
68*f4ee7fbaSAndroid Build Coastguard Worker
69*f4ee7fbaSAndroid Build Coastguard Workervar (
70*f4ee7fbaSAndroid Build Coastguard Worker	errEncode       = errors.New("cbrotli: encode error")
71*f4ee7fbaSAndroid Build Coastguard Worker	errWriterClosed = errors.New("cbrotli: Writer is closed")
72*f4ee7fbaSAndroid Build Coastguard Worker)
73*f4ee7fbaSAndroid Build Coastguard Worker
74*f4ee7fbaSAndroid Build Coastguard Worker// NewWriter initializes new Writer instance.
75*f4ee7fbaSAndroid Build Coastguard Worker// Close MUST be called to free resources.
76*f4ee7fbaSAndroid Build Coastguard Workerfunc NewWriter(dst io.Writer, options WriterOptions) *Writer {
77*f4ee7fbaSAndroid Build Coastguard Worker	state := C.BrotliEncoderCreateInstance(nil, nil, nil)
78*f4ee7fbaSAndroid Build Coastguard Worker	C.BrotliEncoderSetParameter(
79*f4ee7fbaSAndroid Build Coastguard Worker		state, C.BROTLI_PARAM_QUALITY, (C.uint32_t)(options.Quality))
80*f4ee7fbaSAndroid Build Coastguard Worker	if options.LGWin > 0 {
81*f4ee7fbaSAndroid Build Coastguard Worker		C.BrotliEncoderSetParameter(
82*f4ee7fbaSAndroid Build Coastguard Worker			state, C.BROTLI_PARAM_LGWIN, (C.uint32_t)(options.LGWin))
83*f4ee7fbaSAndroid Build Coastguard Worker	}
84*f4ee7fbaSAndroid Build Coastguard Worker	return &Writer{
85*f4ee7fbaSAndroid Build Coastguard Worker		dst:   dst,
86*f4ee7fbaSAndroid Build Coastguard Worker		state: state,
87*f4ee7fbaSAndroid Build Coastguard Worker	}
88*f4ee7fbaSAndroid Build Coastguard Worker}
89*f4ee7fbaSAndroid Build Coastguard Worker
90*f4ee7fbaSAndroid Build Coastguard Workerfunc (w *Writer) writeChunk(p []byte, op C.BrotliEncoderOperation) (n int, err error) {
91*f4ee7fbaSAndroid Build Coastguard Worker	if w.state == nil {
92*f4ee7fbaSAndroid Build Coastguard Worker		return 0, errWriterClosed
93*f4ee7fbaSAndroid Build Coastguard Worker	}
94*f4ee7fbaSAndroid Build Coastguard Worker
95*f4ee7fbaSAndroid Build Coastguard Worker	for {
96*f4ee7fbaSAndroid Build Coastguard Worker		var data *C.uint8_t
97*f4ee7fbaSAndroid Build Coastguard Worker		if len(p) != 0 {
98*f4ee7fbaSAndroid Build Coastguard Worker			data = (*C.uint8_t)(&p[0])
99*f4ee7fbaSAndroid Build Coastguard Worker		}
100*f4ee7fbaSAndroid Build Coastguard Worker		result := C.CompressStream(w.state, op, data, C.size_t(len(p)))
101*f4ee7fbaSAndroid Build Coastguard Worker		if result.success == 0 {
102*f4ee7fbaSAndroid Build Coastguard Worker			return n, errEncode
103*f4ee7fbaSAndroid Build Coastguard Worker		}
104*f4ee7fbaSAndroid Build Coastguard Worker		p = p[int(result.bytes_consumed):]
105*f4ee7fbaSAndroid Build Coastguard Worker		n += int(result.bytes_consumed)
106*f4ee7fbaSAndroid Build Coastguard Worker
107*f4ee7fbaSAndroid Build Coastguard Worker		length := int(result.output_data_size)
108*f4ee7fbaSAndroid Build Coastguard Worker		if length != 0 {
109*f4ee7fbaSAndroid Build Coastguard Worker			// It is a workaround for non-copying-wrapping of native memory.
110*f4ee7fbaSAndroid Build Coastguard Worker			// C-encoder never pushes output block longer than ((2 << 25) + 502).
111*f4ee7fbaSAndroid Build Coastguard Worker			// TODO: use natural wrapper, when it becomes available, see
112*f4ee7fbaSAndroid Build Coastguard Worker			//               https://golang.org/issue/13656.
113*f4ee7fbaSAndroid Build Coastguard Worker			output := (*[1 << 30]byte)(unsafe.Pointer(result.output_data))[:length:length]
114*f4ee7fbaSAndroid Build Coastguard Worker			_, err = w.dst.Write(output)
115*f4ee7fbaSAndroid Build Coastguard Worker			if err != nil {
116*f4ee7fbaSAndroid Build Coastguard Worker				return n, err
117*f4ee7fbaSAndroid Build Coastguard Worker			}
118*f4ee7fbaSAndroid Build Coastguard Worker		}
119*f4ee7fbaSAndroid Build Coastguard Worker		if len(p) == 0 && result.has_more == 0 {
120*f4ee7fbaSAndroid Build Coastguard Worker			return n, nil
121*f4ee7fbaSAndroid Build Coastguard Worker		}
122*f4ee7fbaSAndroid Build Coastguard Worker	}
123*f4ee7fbaSAndroid Build Coastguard Worker}
124*f4ee7fbaSAndroid Build Coastguard Worker
125*f4ee7fbaSAndroid Build Coastguard Worker// Flush outputs encoded data for all input provided to Write. The resulting
126*f4ee7fbaSAndroid Build Coastguard Worker// output can be decoded to match all input before Flush, but the stream is
127*f4ee7fbaSAndroid Build Coastguard Worker// not yet complete until after Close.
128*f4ee7fbaSAndroid Build Coastguard Worker// Flush has a negative impact on compression.
129*f4ee7fbaSAndroid Build Coastguard Workerfunc (w *Writer) Flush() error {
130*f4ee7fbaSAndroid Build Coastguard Worker	_, err := w.writeChunk(nil, C.BROTLI_OPERATION_FLUSH)
131*f4ee7fbaSAndroid Build Coastguard Worker	return err
132*f4ee7fbaSAndroid Build Coastguard Worker}
133*f4ee7fbaSAndroid Build Coastguard Worker
134*f4ee7fbaSAndroid Build Coastguard Worker// Close flushes remaining data to the decorated writer and frees C resources.
135*f4ee7fbaSAndroid Build Coastguard Workerfunc (w *Writer) Close() error {
136*f4ee7fbaSAndroid Build Coastguard Worker	// If stream is already closed, it is reported by `writeChunk`.
137*f4ee7fbaSAndroid Build Coastguard Worker	_, err := w.writeChunk(nil, C.BROTLI_OPERATION_FINISH)
138*f4ee7fbaSAndroid Build Coastguard Worker	// C-Brotli tolerates `nil` pointer here.
139*f4ee7fbaSAndroid Build Coastguard Worker	C.BrotliEncoderDestroyInstance(w.state)
140*f4ee7fbaSAndroid Build Coastguard Worker	w.state = nil
141*f4ee7fbaSAndroid Build Coastguard Worker	return err
142*f4ee7fbaSAndroid Build Coastguard Worker}
143*f4ee7fbaSAndroid Build Coastguard Worker
144*f4ee7fbaSAndroid Build Coastguard Worker// Write implements io.Writer. Flush or Close must be called to ensure that the
145*f4ee7fbaSAndroid Build Coastguard Worker// encoded bytes are actually flushed to the underlying Writer.
146*f4ee7fbaSAndroid Build Coastguard Workerfunc (w *Writer) Write(p []byte) (n int, err error) {
147*f4ee7fbaSAndroid Build Coastguard Worker	return w.writeChunk(p, C.BROTLI_OPERATION_PROCESS)
148*f4ee7fbaSAndroid Build Coastguard Worker}
149*f4ee7fbaSAndroid Build Coastguard Worker
150*f4ee7fbaSAndroid Build Coastguard Worker// Encode returns content encoded with Brotli.
151*f4ee7fbaSAndroid Build Coastguard Workerfunc Encode(content []byte, options WriterOptions) ([]byte, error) {
152*f4ee7fbaSAndroid Build Coastguard Worker	var buf bytes.Buffer
153*f4ee7fbaSAndroid Build Coastguard Worker	writer := NewWriter(&buf, options)
154*f4ee7fbaSAndroid Build Coastguard Worker	_, err := writer.Write(content)
155*f4ee7fbaSAndroid Build Coastguard Worker	if closeErr := writer.Close(); err == nil {
156*f4ee7fbaSAndroid Build Coastguard Worker		err = closeErr
157*f4ee7fbaSAndroid Build Coastguard Worker	}
158*f4ee7fbaSAndroid Build Coastguard Worker	return buf.Bytes(), err
159*f4ee7fbaSAndroid Build Coastguard Worker}
160