mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-12-01 02:28:56 +00:00
112 lines
2.9 KiB
Go
112 lines
2.9 KiB
Go
|
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||
|
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||
|
|
||
|
// See page 362.
|
||
|
//
|
||
|
// The version of this program that appeared in the first and second
|
||
|
// printings did not comply with the proposed rules for passing
|
||
|
// pointers between Go and C, described here:
|
||
|
// https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md
|
||
|
//
|
||
|
// The rules forbid a C function like bz2compress from storing 'in'
|
||
|
// and 'out' (pointers to variables allocated by Go) into the Go
|
||
|
// variable 's', even temporarily.
|
||
|
//
|
||
|
// The version below, which appears in the third printing, has been
|
||
|
// corrected. To comply with the rules, the bz_stream variable must
|
||
|
// be allocated by C code. We have introduced two C functions,
|
||
|
// bz2alloc and bz2free, to allocate and free instances of the
|
||
|
// bz_stream type. Also, we have changed bz2compress so that before
|
||
|
// it returns, it clears the fields of the bz_stream that contain
|
||
|
// pointers to Go variables.
|
||
|
|
||
|
//!+
|
||
|
|
||
|
// Package bzip provides a writer that uses bzip2 compression (bzip.org).
|
||
|
package bzip
|
||
|
|
||
|
/*
|
||
|
#cgo CFLAGS: -I/usr/include
|
||
|
#cgo LDFLAGS: -L/usr/lib -lbz2
|
||
|
#include <bzlib.h>
|
||
|
#include <stdlib.h>
|
||
|
bz_stream* bz2alloc() { return calloc(1, sizeof(bz_stream)); }
|
||
|
int bz2compress(bz_stream *s, int action,
|
||
|
char *in, unsigned *inlen, char *out, unsigned *outlen);
|
||
|
void bz2free(bz_stream* s) { free(s); }
|
||
|
*/
|
||
|
import "C"
|
||
|
|
||
|
import (
|
||
|
"io"
|
||
|
"unsafe"
|
||
|
)
|
||
|
|
||
|
type writer struct {
|
||
|
w io.Writer // underlying output stream
|
||
|
stream *C.bz_stream
|
||
|
outbuf [64 * 1024]byte
|
||
|
}
|
||
|
|
||
|
// NewWriter returns a writer for bzip2-compressed streams.
|
||
|
func NewWriter(out io.Writer) io.WriteCloser {
|
||
|
const blockSize = 9
|
||
|
const verbosity = 0
|
||
|
const workFactor = 30
|
||
|
w := &writer{w: out, stream: C.bz2alloc()}
|
||
|
C.BZ2_bzCompressInit(w.stream, blockSize, verbosity, workFactor)
|
||
|
return w
|
||
|
}
|
||
|
|
||
|
//!-
|
||
|
|
||
|
//!+write
|
||
|
func (w *writer) Write(data []byte) (int, error) {
|
||
|
if w.stream == nil {
|
||
|
panic("closed")
|
||
|
}
|
||
|
var total int // uncompressed bytes written
|
||
|
|
||
|
for len(data) > 0 {
|
||
|
inlen, outlen := C.uint(len(data)), C.uint(cap(w.outbuf))
|
||
|
C.bz2compress(w.stream, C.BZ_RUN,
|
||
|
(*C.char)(unsafe.Pointer(&data[0])), &inlen,
|
||
|
(*C.char)(unsafe.Pointer(&w.outbuf)), &outlen)
|
||
|
total += int(inlen)
|
||
|
data = data[inlen:]
|
||
|
if _, err := w.w.Write(w.outbuf[:outlen]); err != nil {
|
||
|
return total, err
|
||
|
}
|
||
|
}
|
||
|
return total, nil
|
||
|
}
|
||
|
|
||
|
//!-write
|
||
|
|
||
|
//!+close
|
||
|
// Close flushes the compressed data and closes the stream.
|
||
|
// It does not close the underlying io.Writer.
|
||
|
func (w *writer) Close() error {
|
||
|
if w.stream == nil {
|
||
|
panic("closed")
|
||
|
}
|
||
|
defer func() {
|
||
|
C.BZ2_bzCompressEnd(w.stream)
|
||
|
C.bz2free(w.stream)
|
||
|
w.stream = nil
|
||
|
}()
|
||
|
for {
|
||
|
inlen, outlen := C.uint(0), C.uint(cap(w.outbuf))
|
||
|
r := C.bz2compress(w.stream, C.BZ_FINISH, nil, &inlen,
|
||
|
(*C.char)(unsafe.Pointer(&w.outbuf)), &outlen)
|
||
|
if _, err := w.w.Write(w.outbuf[:outlen]); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if r == C.BZ_STREAM_END {
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//!-close
|