• 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 decoder wraps the brotli decoder C API used by package brotli.
7package decoder
8
9/*
10#include <brotli/decode.h>
11
12// Wrap BrotliDecoderDecompressStream so that it doesn't take variable (in-out)
13// pointers. Instead of updated pointer, deltas are saved in auxiliary struct.
14
15struct DecompressStreamResult {
16  size_t bytes_consumed;
17  const uint8_t* output_data;
18  size_t output_data_size;
19  BrotliDecoderResult status;
20};
21
22struct DecompressStreamResult DecompressStream(BrotliDecoderState* s,
23    const uint8_t* encoded_data, size_t encoded_data_size) {
24  struct DecompressStreamResult result;
25  size_t available_in = encoded_data_size;
26  const uint8_t* next_in = encoded_data;
27  size_t available_out = 0;
28  result.status = BrotliDecoderDecompressStream(s,
29      &available_in, &next_in, &available_out, 0, 0);
30  result.bytes_consumed = encoded_data_size - available_in;
31  result.output_data = 0;
32  result.output_data_size = 0;
33  if (result.status != BROTLI_DECODER_RESULT_ERROR) {
34    result.output_data = BrotliDecoderTakeOutput(s, &result.output_data_size);
35    if (BrotliDecoderIsFinished(s)) {
36      result.status = BROTLI_DECODER_RESULT_SUCCESS;
37    }
38  }
39  return result;
40}
41
42*/
43import "C"
44import (
45	"unsafe"
46)
47
48// Status represents internal state after DecompressStream invokation
49type Status int
50
51const (
52	// Error happened
53	Error Status = iota
54	// Done means that no more output will be produced
55	Done
56	// Ok means that more output might be produced with no additional input
57	Ok
58)
59
60// Decoder is the Brotli c-decoder handle.
61type Decoder struct {
62	state *C.BrotliDecoderState
63}
64
65// New returns a new Brotli c-decoder handle.
66// Close MUST be called to free resources.
67func New() Decoder {
68	return Decoder{state: C.BrotliDecoderCreateInstance(nil, nil, nil)}
69}
70
71// Close frees resources used by decoder.
72func (z *Decoder) Close() {
73	C.BrotliDecoderDestroyInstance(z.state)
74	z.state = nil
75}
76
77func goStatus(cStatus C.BrotliDecoderResult) (status Status) {
78	switch cStatus {
79	case C.BROTLI_DECODER_RESULT_SUCCESS:
80		return Done
81	case C.BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
82		return Ok
83	case C.BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
84		return Ok
85	}
86	return Error
87}
88
89// cBytes casts a Go []byte into a C uint8_t*. We pass &buf[0] directly to C,
90// which is legal because C doesn't save the pointer longer than the call and
91// the byte array itself doesn't contain any pointers.
92func cBytes(buf []byte) (*C.uint8_t, C.size_t) {
93	if len(buf) == 0 {
94		return (*C.uint8_t)(nil), 0
95	}
96	return (*C.uint8_t)(unsafe.Pointer(&buf[0])), C.size_t(len(buf))
97}
98
99// DecompressStream reads Brotli-encoded bytes from in, and returns produced
100// bytes. Output contents should not be modified. Liveness of output is
101// hard-limited by Decoder liveness; slice becomes invalid when any Decoder
102// method is invoked.
103func (z *Decoder) DecompressStream(in []byte) (
104	bytesConsumed int, output []byte, status Status) {
105	cin, cinSize := cBytes(in)
106	result := C.DecompressStream(z.state, cin, cinSize)
107	output = C.GoBytes(
108		unsafe.Pointer(result.output_data), C.int(result.output_data_size))
109	return int(result.bytes_consumed), output, goStatus(result.status)
110}
111