Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 15 additions & 45 deletions go/cgzip/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,7 @@

package cgzip

/*
#cgo CFLAGS: -Werror=implicit
#cgo pkg-config: zlib

#include "zlib.h"

// inflateInit2 is a macro, so using a wrapper function
int cgzipInflateInit(z_stream *strm) {
strm->zalloc = Z_NULL;
strm->zfree = Z_NULL;
strm->opaque = Z_NULL;
strm->avail_in = 0;
strm->next_in = Z_NULL;
return inflateInit2(strm,
16+15); // 16 makes it understand only gzip files
}
*/
import "C"

import (
"fmt"
"io"
"unsafe"
)
import "io"

// err starts out as nil
// we will call inflateEnd when we set err to a value:
Expand All @@ -36,7 +13,7 @@ import (
type reader struct {
r io.Reader
in []byte
strm C.z_stream
strm zstream
err error
skipIn bool
}
Expand All @@ -47,9 +24,8 @@ func NewReader(r io.Reader) (io.ReadCloser, error) {

func NewReaderBuffer(r io.Reader, bufferSize int) (io.ReadCloser, error) {
z := &reader{r: r, in: make([]byte, bufferSize)}
result := C.cgzipInflateInit(&z.strm)
if result != Z_OK {
return nil, fmt.Errorf("cgzip: failed to initialize (%v): %v", result, C.GoString(z.strm.msg))
if err := z.strm.inflateInit(); err != nil {
return nil, err
}
return z, nil
}
Expand All @@ -64,12 +40,11 @@ func (z *reader) Read(p []byte) (int, error) {
}

// read and deflate until the output buffer is full
z.strm.next_out = (*C.Bytef)(unsafe.Pointer(&p[0]))
z.strm.avail_out = (C.uInt)(len(p))
z.strm.setOutBuf(p, len(p))

for {
// if we have no data to inflate, read more
if !z.skipIn && z.strm.avail_in == 0 {
if !z.skipIn && z.strm.availIn() == 0 {
var n int
n, z.err = z.r.Read(z.in)
// If we got data and EOF, pretend we didn't get the
Expand All @@ -85,32 +60,27 @@ func (z *reader) Read(p []byte) (int, error) {
// data we got from the reader, and then return the
// error, whatever it is.
if (z.err != nil && z.err != io.EOF) || (n == 0 && z.err == io.EOF) {
C.inflateEnd(&z.strm)
z.strm.inflateEnd()
return 0, z.err
}

z.strm.next_in = (*C.Bytef)(unsafe.Pointer(&z.in[0]))
z.strm.avail_in = (C.uInt)(n)
z.strm.setInBuf(z.in, n)
} else {
z.skipIn = false
}

// inflate some
ret := C.inflate(&z.strm, C.Z_NO_FLUSH)
switch ret {
case Z_NEED_DICT:
ret = Z_DATA_ERROR
fallthrough
case Z_DATA_ERROR, Z_MEM_ERROR:
z.err = fmt.Errorf("cgzip: failed to inflate (%v): %v", ret, C.GoString(z.strm.msg))
C.inflateEnd(&z.strm)
ret, err := z.strm.inflate(zNoFlush)
if err != nil {
z.err = err
z.strm.inflateEnd()
return 0, z.err
}

// if we read something, we're good
have := len(p) - int(z.strm.avail_out)
have := len(p) - z.strm.availOut()
if have > 0 {
z.skipIn = ret == Z_OK && z.strm.avail_out == 0
z.skipIn = ret == Z_OK && z.strm.availOut() == 0
return have, z.err
}
}
Expand All @@ -124,7 +94,7 @@ func (z *reader) Close() error {
}
return nil
}
C.inflateEnd(&z.strm)
z.strm.inflateEnd()
z.err = io.EOF
return nil
}
55 changes: 12 additions & 43 deletions go/cgzip/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,9 @@

package cgzip

// See http://www.zlib.net/zlib_how.html for more information on this

/*
#cgo CFLAGS: -Werror=implicit
#cgo pkg-config: zlib

#include "zlib.h"

// deflateInit2 is a macro, so using a wrapper function
// using deflateInit2 instead of deflateInit to be able to specify gzip format
int cgzipDeflateInit(z_stream *strm, int level) {
strm->zalloc = Z_NULL;
strm->zfree = Z_NULL;
strm->opaque = Z_NULL;
return deflateInit2(strm, level, Z_DEFLATED,
16+15, // 16 makes it a gzip file, 15 is default
8, Z_DEFAULT_STRATEGY); // default values
}
*/
import "C"

import (
"fmt"
"io"
"unsafe"
)

const (
Expand Down Expand Up @@ -71,7 +49,7 @@ const (
type Writer struct {
w io.Writer
out []byte
strm C.z_stream
strm zstream
err error
}

Expand All @@ -86,9 +64,8 @@ func NewWriterLevel(w io.Writer, level int) (*Writer, error) {

func NewWriterLevelBuffer(w io.Writer, level, bufferSize int) (*Writer, error) {
z := &Writer{w: w, out: make([]byte, bufferSize)}
result := C.cgzipDeflateInit(&z.strm, (C.int)(level))
if result != Z_OK {
return nil, fmt.Errorf("cgzip: failed to initialize (%v): %v", result, C.GoString(z.strm.msg))
if err := z.strm.deflateInit(level); err != nil {
return nil, err
}
return z, nil
}
Expand All @@ -97,47 +74,39 @@ func NewWriterLevelBuffer(w io.Writer, level, bufferSize int) (*Writer, error) {
// new data or something else to do, like a flush
func (z *Writer) write(p []byte, flush int) int {
if len(p) == 0 {
z.strm.next_in = (*C.Bytef)(unsafe.Pointer(nil))
z.strm.avail_in = 0
z.strm.setInBuf(nil, 0)
} else {
z.strm.next_in = (*C.Bytef)(unsafe.Pointer(&p[0]))
z.strm.avail_in = (C.uInt)(len(p))
z.strm.setInBuf(p, len(p))
}
// we loop until we don't get a full output buffer
// each loop completely writes the output buffer to the underlying
// writer
for {
// deflate one buffer
z.strm.next_out = (*C.Bytef)(unsafe.Pointer(&z.out[0]))
z.strm.avail_out = (C.uInt)(len(z.out))
ret := C.deflate(&z.strm, (C.int)(flush))
if ret == Z_STREAM_ERROR {
// all the other error cases are normal,
// and this should never happen
panic(fmt.Errorf("cgzip: Unexpected error (1)"))
}
z.strm.setOutBuf(z.out, len(z.out))
z.strm.deflate(flush)

// write everything
from := 0
have := len(z.out) - int(z.strm.avail_out)
have := len(z.out) - int(z.strm.availOut())
for have > 0 {
var n int
n, z.err = z.w.Write(z.out[from:have])
if z.err != nil {
C.deflateEnd(&z.strm)
z.strm.deflateEnd()
return 0
}
from += n
have -= n
}

// we stop trying if we get a partial response
if z.strm.avail_out != 0 {
if z.strm.availOut() != 0 {
break
}
}
// the library guarantees this
if z.strm.avail_in != 0 {
if z.strm.availIn() != 0 {
panic(fmt.Errorf("cgzip: Unexpected error (2)"))
}
return len(p)
Expand Down Expand Up @@ -169,7 +138,7 @@ func (z *Writer) Close() error {
if z.err != nil {
return z.err
}
C.deflateEnd(&z.strm)
z.strm.deflateEnd()
z.err = io.EOF
return nil
}
163 changes: 163 additions & 0 deletions go/cgzip/zstream.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Copyright 2015, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cgzip

// See http://www.zlib.net/zlib_how.html for more information on this

/*
#cgo CFLAGS: -Werror=implicit
#cgo pkg-config: zlib

#include "zlib.h"

// inflateInit2 is a macro, so using a wrapper function
int zstream_inflate_init(char *strm) {
((z_stream*)strm)->zalloc = Z_NULL;
((z_stream*)strm)->zfree = Z_NULL;
((z_stream*)strm)->opaque = Z_NULL;
((z_stream*)strm)->avail_in = 0;
((z_stream*)strm)->next_in = Z_NULL;
return inflateInit2((z_stream*)strm,
16+15); // 16 makes it understand only gzip files
}

// deflateInit2 is a macro, so using a wrapper function
// using deflateInit2 instead of deflateInit to be able to specify gzip format
int zstream_deflate_init(char *strm, int level) {
((z_stream*)strm)->zalloc = Z_NULL;
((z_stream*)strm)->zfree = Z_NULL;
((z_stream*)strm)->opaque = Z_NULL;
return deflateInit2((z_stream*)strm, level, Z_DEFLATED,
16+15, // 16 makes it a gzip file, 15 is default
8, Z_DEFAULT_STRATEGY); // default values
}

unsigned int zstream_avail_in(char *strm) {
return ((z_stream*)strm)->avail_in;
}

unsigned int zstream_avail_out(char *strm) {
return ((z_stream*)strm)->avail_out;
}

char* zstream_msg(char *strm) {
return ((z_stream*)strm)->msg;
}

void zstream_set_in_buf(char *strm, void *buf, unsigned int len) {
((z_stream*)strm)->next_in = (Bytef*)buf;
((z_stream*)strm)->avail_in = len;
}

void zstream_set_out_buf(char *strm, void *buf, unsigned int len) {
((z_stream*)strm)->next_out = (Bytef*)buf;
((z_stream*)strm)->avail_out = len;
}

int zstream_inflate(char *strm, int flag) {
return inflate((z_stream*)strm, flag);
}

int zstream_deflate(char *strm, int flag) {
return deflate((z_stream*)strm, flag);
}

void zstream_inflate_end(char *strm) {
inflateEnd((z_stream*)strm);
}

void zstream_deflate_end(char *strm) {
deflateEnd((z_stream*)strm);
}
*/
import "C"

import (
"fmt"
"unsafe"
)

const (
zNoFlush = C.Z_NO_FLUSH
)

// z_stream is a buffer that's big enough to fit a C.z_stream.
// This lets us allocate a C.z_stream within Go, while keeping the contents
// opaque to the Go GC. Otherwise, the GC would look inside and complain that
// the pointers are invalid, since they point to objects allocated by C code.
type zstream [unsafe.Sizeof(C.z_stream{})]C.char

func (strm *zstream) inflateInit() error {
result := C.zstream_inflate_init(&strm[0])
if result != Z_OK {
return fmt.Errorf("cgzip: failed to initialize inflate (%v): %v", result, strm.msg())
}
return nil
}

func (strm *zstream) deflateInit(level int) error {
result := C.zstream_deflate_init(&strm[0], C.int(level))
if result != Z_OK {
return fmt.Errorf("cgzip: failed to initialize deflate (%v): %v", result, strm.msg())
}
return nil
}

func (strm *zstream) inflateEnd() {
C.zstream_inflate_end(&strm[0])
}

func (strm *zstream) deflateEnd() {
C.zstream_deflate_end(&strm[0])
}

func (strm *zstream) availIn() int {
return int(C.zstream_avail_in(&strm[0]))
}

func (strm *zstream) availOut() int {
return int(C.zstream_avail_out(&strm[0]))
}

func (strm *zstream) msg() string {
return C.GoString(C.zstream_msg(&strm[0]))
}

func (strm *zstream) setInBuf(buf []byte, size int) {
if buf == nil {
C.zstream_set_in_buf(&strm[0], nil, C.uint(size))
} else {
C.zstream_set_in_buf(&strm[0], unsafe.Pointer(&buf[0]), C.uint(size))
}
}

func (strm *zstream) setOutBuf(buf []byte, size int) {
if buf == nil {
C.zstream_set_out_buf(&strm[0], nil, C.uint(size))
} else {
C.zstream_set_out_buf(&strm[0], unsafe.Pointer(&buf[0]), C.uint(size))
}
}

func (strm *zstream) inflate(flag int) (int, error) {
ret := C.zstream_inflate(&strm[0], C.int(flag))
switch ret {
case Z_NEED_DICT:
ret = Z_DATA_ERROR
fallthrough
case Z_DATA_ERROR, Z_MEM_ERROR:
return int(ret), fmt.Errorf("cgzip: failed to inflate (%v): %v", ret, strm.msg())
}
return int(ret), nil
}

func (strm *zstream) deflate(flag int) {
ret := C.zstream_deflate(&strm[0], C.int(flag))
if ret == Z_STREAM_ERROR {
// all the other error cases are normal,
// and this should never happen
panic(fmt.Errorf("cgzip: Unexpected error (1)"))
}
}