mirror of
https://github.com/golang/go
synced 2024-11-25 02:07:58 -07:00
ZLIB deflater.
R=rsc APPROVED=rsc DELTA=222 (219 added, 0 deleted, 3 changed) OCL=35031 CL=35129
This commit is contained in:
parent
0ba28329e4
commit
c51e184538
@ -7,5 +7,6 @@ include $(GOROOT)/src/Make.$(GOARCH)
|
||||
TARG=compress/zlib
|
||||
GOFILES=\
|
||||
reader.go\
|
||||
writer.go\
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
|
@ -2,8 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The zlib package implements reading (and eventually writing) of
|
||||
// zlib format compressed files, as specified in RFC 1950.
|
||||
// The zlib package implements reading and writing of zlib
|
||||
// format compressed files, as specified in RFC 1950.
|
||||
package zlib
|
||||
|
||||
import (
|
||||
@ -87,6 +87,10 @@ func (z *reader) Read(p []byte) (n int, err os.Error) {
|
||||
|
||||
// Calling Close does not close the wrapped io.Reader originally passed to NewInflater.
|
||||
func (z *reader) Close() os.Error {
|
||||
return z.inflater.Close();
|
||||
if z.err != nil {
|
||||
return z.err;
|
||||
}
|
||||
z.err = z.inflater.Close();
|
||||
return z.err;
|
||||
}
|
||||
|
||||
|
1
src/pkg/compress/zlib/testdata/e.txt
vendored
Normal file
1
src/pkg/compress/zlib/testdata/e.txt
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/pkg/compress/zlib/testdata/pi.txt
vendored
Normal file
1
src/pkg/compress/zlib/testdata/pi.txt
vendored
Normal file
File diff suppressed because one or more lines are too long
107
src/pkg/compress/zlib/writer.go
Normal file
107
src/pkg/compress/zlib/writer.go
Normal file
@ -0,0 +1,107 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package zlib
|
||||
|
||||
import (
|
||||
"compress/flate";
|
||||
"hash";
|
||||
"hash/adler32";
|
||||
"io";
|
||||
"os";
|
||||
)
|
||||
|
||||
// These constants are copied from the flate package, so that code that imports
|
||||
// "compress/zlib" does not also have to import "compress/flate".
|
||||
const (
|
||||
NoCompression = flate.NoCompression;
|
||||
BestSpeed = flate.BestSpeed;
|
||||
BestCompression = flate.BestCompression;
|
||||
DefaultCompression = flate.DefaultCompression;
|
||||
)
|
||||
|
||||
type writer struct {
|
||||
w io.Writer;
|
||||
deflater io.WriteCloser;
|
||||
digest hash.Hash32;
|
||||
err os.Error;
|
||||
scratch [4]byte;
|
||||
}
|
||||
|
||||
// NewDeflater calls NewDeflaterLevel with the default compression level.
|
||||
func NewDeflater(w io.Writer) (io.WriteCloser, os.Error) {
|
||||
return NewDeflaterLevel(w, DefaultCompression);
|
||||
}
|
||||
|
||||
// NewDeflater creates a new io.WriteCloser that satisfies writes by compressing data written to w.
|
||||
// It is the caller's responsibility to call Close on the WriteCloser when done.
|
||||
// level is the compression level, which can be DefaultCompression, NoCompression,
|
||||
// or any integer value between BestSpeed and BestCompression (inclusive).
|
||||
func NewDeflaterLevel(w io.Writer, level int) (io.WriteCloser, os.Error) {
|
||||
z := new(writer);
|
||||
// ZLIB has a two-byte header (as documented in RFC 1950).
|
||||
// The first four bits is the CINFO (compression info), which is 7 for the default deflate window size.
|
||||
// The next four bits is the CM (compression method), which is 8 for deflate.
|
||||
z.scratch[0] = 0x78;
|
||||
// The next two bits is the FLEVEL (compression level). The four values are:
|
||||
// 0=fastest, 1=fast, 2=default, 3=best.
|
||||
// The next bit, FDICT, is unused, in this implementation.
|
||||
// The final five FCHECK bits form a mod-31 checksum.
|
||||
switch level {
|
||||
case 0, 1:
|
||||
z.scratch[1] = 0x01;
|
||||
case 2, 3, 4, 5:
|
||||
z.scratch[1] = 0x5e;
|
||||
case 6, -1:
|
||||
z.scratch[1] = 0x9c;
|
||||
case 7, 8, 9:
|
||||
z.scratch[1] = 0xda;
|
||||
default:
|
||||
return nil, os.NewError("level out of range");
|
||||
}
|
||||
_, err := w.Write(z.scratch[0:2]);
|
||||
if err != nil {
|
||||
return nil, err;
|
||||
}
|
||||
z.w = w;
|
||||
z.deflater = flate.NewDeflater(w, level);
|
||||
z.digest = adler32.New();
|
||||
return z, nil;
|
||||
}
|
||||
|
||||
func (z *writer) Write(p []byte) (n int, err os.Error) {
|
||||
if z.err != nil {
|
||||
return 0, z.err;
|
||||
}
|
||||
if len(p) == 0 {
|
||||
return 0, nil;
|
||||
}
|
||||
n, err = z.deflater.Write(p);
|
||||
if err != nil {
|
||||
z.err = err;
|
||||
return;
|
||||
}
|
||||
z.digest.Write(p);
|
||||
return;
|
||||
}
|
||||
|
||||
// Calling Close does not close the wrapped io.Writer originally passed to NewDeflater.
|
||||
func (z *writer) Close() os.Error {
|
||||
if z.err != nil {
|
||||
return z.err;
|
||||
}
|
||||
z.err = z.deflater.Close();
|
||||
if z.err != nil {
|
||||
return z.err;
|
||||
}
|
||||
checksum := z.digest.Sum32();
|
||||
// ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
|
||||
z.scratch[0] = uint8(checksum>>24);
|
||||
z.scratch[1] = uint8(checksum>>16);
|
||||
z.scratch[2] = uint8(checksum>>8);
|
||||
z.scratch[3] = uint8(checksum>>0);
|
||||
_, z.err = z.w.Write(z.scratch[0:4]);
|
||||
return z.err;
|
||||
}
|
||||
|
105
src/pkg/compress/zlib/writer_test.go
Normal file
105
src/pkg/compress/zlib/writer_test.go
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package zlib
|
||||
|
||||
import (
|
||||
"io";
|
||||
"os";
|
||||
"testing";
|
||||
)
|
||||
|
||||
var filenames = []string {
|
||||
"testdata/e.txt",
|
||||
"testdata/pi.txt",
|
||||
}
|
||||
|
||||
// Tests that compressing and then decompressing the given file at the given compression level
|
||||
// yields equivalent bytes to the original file.
|
||||
func testFileLevel(t *testing.T, fn string, level int) {
|
||||
// Read the file, as golden output.
|
||||
golden, err := os.Open(fn, os.O_RDONLY, 0444);
|
||||
if err != nil {
|
||||
t.Errorf("%s (level=%d): %v", fn, level, err);
|
||||
return;
|
||||
}
|
||||
defer golden.Close();
|
||||
|
||||
// Read the file again, and push it through a pipe that compresses at the write end, and decompresses at the read end.
|
||||
raw, err := os.Open(fn, os.O_RDONLY, 0444);
|
||||
if err != nil {
|
||||
t.Errorf("%s (level=%d): %v", fn, level, err);
|
||||
return;
|
||||
}
|
||||
piper, pipew := io.Pipe();
|
||||
defer piper.Close();
|
||||
go func() {
|
||||
defer raw.Close();
|
||||
defer pipew.Close();
|
||||
zlibw, err := NewDeflaterLevel(pipew, level);
|
||||
if err != nil {
|
||||
t.Errorf("%s (level=%d): %v", fn, level, err);
|
||||
return;
|
||||
}
|
||||
defer zlibw.Close();
|
||||
var b [1024]byte;
|
||||
for {
|
||||
n, err0 := raw.Read(&b);
|
||||
if err0 != nil && err0 != os.EOF {
|
||||
t.Errorf("%s (level=%d): %v", fn, level, err0);
|
||||
return;
|
||||
}
|
||||
_, err1 := zlibw.Write(b[0:n]);
|
||||
if err1 == os.EPIPE {
|
||||
// Fail, but do not report the error, as some other (presumably reportable) error broke the pipe.
|
||||
return;
|
||||
}
|
||||
if err1 != nil {
|
||||
t.Errorf("%s (level=%d): %v", fn, level, err1);
|
||||
return;
|
||||
}
|
||||
if err0 == os.EOF {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}();
|
||||
zlibr, err := NewInflater(piper);
|
||||
if err != nil {
|
||||
t.Errorf("%s (level=%d): %v", fn, level, err);
|
||||
return;
|
||||
}
|
||||
defer zlibr.Close();
|
||||
|
||||
// Compare the two.
|
||||
b0, err0 := io.ReadAll(golden);
|
||||
b1, err1 := io.ReadAll(zlibr);
|
||||
if err0 != nil {
|
||||
t.Errorf("%s (level=%d): %v", fn, level, err0);
|
||||
return;
|
||||
}
|
||||
if err1 != nil {
|
||||
t.Errorf("%s (level=%d): %v", fn, level, err1);
|
||||
return;
|
||||
}
|
||||
if len(b0) != len(b1) {
|
||||
t.Errorf("%s (level=%d): length mismatch %d versus %d", fn, level, len(b0), len(b1));
|
||||
return;
|
||||
}
|
||||
for i := 0; i < len(b0); i++ {
|
||||
if b0[i] != b1[i] {
|
||||
t.Errorf("%s (level=%d): mismatch at %d, 0x%02x versus 0x%02x\n", fn, level, i, b0[i], b1[i]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriter(t *testing.T) {
|
||||
for _, fn := range filenames {
|
||||
testFileLevel(t, fn, DefaultCompression);
|
||||
testFileLevel(t, fn, NoCompression);
|
||||
for level := BestSpeed; level <= BestCompression; level++ {
|
||||
testFileLevel(t, fn, level);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user