• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2016 Google Inc. All Rights Reserved.
2//
3// Distributed under MIT license.
4// See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
5
6// Package encoder wraps the brotli encoder C API used by package brotli.
7package encoder
8
9/*
10#include <brotli/encode.h>
11
12// Wrap BrotliEncoderCompressStream so that it doesn't take variable (in-out)
13// pointers. Instead of updated pointer, deltas are saved in auxiliary struct.
14struct CompressStreamResult {
15  size_t bytes_consumed;
16  const uint8_t* output_data;
17  size_t output_data_size;
18  int success;
19  int has_more;
20};
21
22struct CompressStreamResult CompressStream(
23    BrotliEncoderState* s, BrotliEncoderOperation op,
24    const uint8_t* data, size_t data_size) {
25  struct CompressStreamResult result;
26  size_t available_in = data_size;
27  const uint8_t* next_in = data;
28  size_t available_out = 0;
29  result.success = BrotliEncoderCompressStream(s, op,
30      &available_in, &next_in, &available_out, 0, 0) ? 1 : 0;
31  result.bytes_consumed = data_size - available_in;
32  result.output_data = 0;
33  result.output_data_size = 0;
34  if (result.success) {
35    result.output_data = BrotliEncoderTakeOutput(s, &result.output_data_size);
36  }
37  result.has_more = BrotliEncoderHasMoreOutput(s) ? 1 : 0;
38  return result;
39}
40*/
41import "C"
42import (
43	"unsafe"
44)
45
46// Operation represents type of request to CompressStream
47type Operation int
48
49const (
50	// Process input
51	Process Operation = iota
52	// Flush input processed so far
53	Flush
54	// Finish stream
55	Finish
56)
57
58// Status represents internal state after CompressStream invocation
59type Status int
60
61const (
62	// Error happened
63	Error Status = iota
64	// Done means that no more output will be produced
65	Done
66	// Ok means that more output might be produced with no additional input
67	Ok
68)
69
70// Encoder is the Brotli c-encoder handle.
71type Encoder struct {
72	state *C.BrotliEncoderState
73}
74
75// New returns a new Brotli c-encoder handle.
76// quality and lgWin are described in third_party/Brotli/enc/encode.h.
77// Close MUST be called to free resources.
78func New(quality, lgWin int) Encoder {
79	state := C.BrotliEncoderCreateInstance(nil, nil, nil)
80	C.BrotliEncoderSetParameter(
81		state, C.BROTLI_PARAM_QUALITY, (C.uint32_t)(quality))
82	C.BrotliEncoderSetParameter(
83		state, C.BROTLI_PARAM_LGWIN, (C.uint32_t)(lgWin))
84	return Encoder{state}
85}
86
87// Close frees resources used by encoder.
88func (z *Encoder) Close() {
89	C.BrotliEncoderDestroyInstance(z.state)
90	z.state = nil
91}
92
93// cBytes casts a Go []byte into a C uint8_t*. We pass &buf[0] directly to C,
94// which is legal because C doesn't save the pointer longer than the call and
95// the byte array itself doesn't contain any pointers.
96func cBytes(buf []byte) (*C.uint8_t, C.size_t) {
97	if len(buf) == 0 {
98		return (*C.uint8_t)(nil), 0
99	}
100	return (*C.uint8_t)(unsafe.Pointer(&buf[0])), C.size_t(len(buf))
101}
102
103func cOperation(op Operation) (cOp C.BrotliEncoderOperation) {
104	switch op {
105	case Flush:
106		return C.BROTLI_OPERATION_FLUSH
107	case Finish:
108		return C.BROTLI_OPERATION_FINISH
109	}
110	return C.BROTLI_OPERATION_PROCESS
111}
112
113// CompressStream processes data and produces Brotli-encoded bytes. Encoder may
114// consume considerable amount of input before the first output bytes come out.
115// Flush and Finish operations force Encoder to produce output that corresponds
116// to input consumed so far. Output contents should not be modified. Liveness of
117// output is hard-limited by Encoder liveness; slice becomes invalid when any
118// Encoder method is invoked.
119func (z *Encoder) CompressStream(in []byte, op Operation) (
120	bytesConsumed int, output []byte, status Status) {
121	cin, cinSize := cBytes(in)
122	result := C.CompressStream(z.state, cOperation(op), cin, cinSize)
123	output = C.GoBytes(
124		unsafe.Pointer(result.output_data), C.int(result.output_data_size))
125	var outcome Status
126	if result.success == 0 {
127		outcome = Error
128	} else if result.has_more != 0 {
129		outcome = Ok
130	} else {
131		outcome = Done
132	}
133	return int(result.bytes_consumed), output, outcome
134}
135