summaryrefslogtreecommitdiff
path: root/libgo/go/compress/zlib/writer.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/compress/zlib/writer.go')
-rw-r--r--libgo/go/compress/zlib/writer.go103
1 files changed, 70 insertions, 33 deletions
diff --git a/libgo/go/compress/zlib/writer.go b/libgo/go/compress/zlib/writer.go
index bbff6375ea1..cd8dea460a4 100644
--- a/libgo/go/compress/zlib/writer.go
+++ b/libgo/go/compress/zlib/writer.go
@@ -6,7 +6,7 @@ package zlib
import (
"compress/flate"
- "errors"
+ "fmt"
"hash"
"hash/adler32"
"io"
@@ -24,30 +24,55 @@ const (
// A Writer takes data written to it and writes the compressed
// form of that data to an underlying writer (see NewWriter).
type Writer struct {
- w io.Writer
- compressor *flate.Writer
- digest hash.Hash32
- err error
- scratch [4]byte
+ w io.Writer
+ level int
+ dict []byte
+ compressor *flate.Writer
+ digest hash.Hash32
+ err error
+ scratch [4]byte
+ wroteHeader bool
}
-// NewWriter calls NewWriterLevel with the default compression level.
-func NewWriter(w io.Writer) (*Writer, error) {
- return NewWriterLevel(w, DefaultCompression)
+// NewWriter creates a new Writer that satisfies writes by compressing data
+// written to w.
+//
+// It is the caller's responsibility to call Close on the WriteCloser when done.
+// Writes may be buffered and not flushed until Close.
+func NewWriter(w io.Writer) *Writer {
+ z, _ := NewWriterLevelDict(w, DefaultCompression, nil)
+ return z
}
-// NewWriterLevel calls NewWriterDict with no dictionary.
+// NewWriterLevel is like NewWriter but specifies the compression level instead
+// of assuming DefaultCompression.
+//
+// The compression level can be DefaultCompression, NoCompression, or any
+// integer value between BestSpeed and BestCompression inclusive. The error
+// returned will be nil if the level is valid.
func NewWriterLevel(w io.Writer, level int) (*Writer, error) {
- return NewWriterDict(w, level, nil)
+ return NewWriterLevelDict(w, level, nil)
}
-// NewWriterDict 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).
-// dict is the preset dictionary to compress with, or nil to use no dictionary.
-func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) {
- z := new(Writer)
+// NewWriterLevelDict is like NewWriterLevel but specifies a dictionary to
+// compress with.
+//
+// The dictionary may be nil. If not, its contents should not be modified until
+// the Writer is closed.
+func NewWriterLevelDict(w io.Writer, level int, dict []byte) (*Writer, error) {
+ if level < DefaultCompression || level > BestCompression {
+ return nil, fmt.Errorf("zlib: invalid compression level: %d", level)
+ }
+ return &Writer{
+ w: w,
+ level: level,
+ dict: dict,
+ }, nil
+}
+
+// writeHeader writes the ZLIB header.
+func (z *Writer) writeHeader() (err error) {
+ z.wroteHeader = true
// 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.
@@ -56,7 +81,7 @@ func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) {
// 0=fastest, 1=fast, 2=default, 3=best.
// The next bit, FDICT, is set if a dictionary is given.
// The final five FCHECK bits form a mod-31 checksum.
- switch level {
+ switch z.level {
case 0, 1:
z.scratch[1] = 0 << 6
case 2, 3, 4, 5:
@@ -66,35 +91,41 @@ func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) {
case 7, 8, 9:
z.scratch[1] = 3 << 6
default:
- return nil, errors.New("level out of range")
+ panic("unreachable")
}
- if dict != nil {
+ if z.dict != nil {
z.scratch[1] |= 1 << 5
}
z.scratch[1] += uint8(31 - (uint16(z.scratch[0])<<8+uint16(z.scratch[1]))%31)
- _, err := w.Write(z.scratch[0:2])
- if err != nil {
- return nil, err
+ if _, err = z.w.Write(z.scratch[0:2]); err != nil {
+ return err
}
- if dict != nil {
+ if z.dict != nil {
// The next four bytes are the Adler-32 checksum of the dictionary.
- checksum := adler32.Checksum(dict)
+ checksum := adler32.Checksum(z.dict)
z.scratch[0] = uint8(checksum >> 24)
z.scratch[1] = uint8(checksum >> 16)
z.scratch[2] = uint8(checksum >> 8)
z.scratch[3] = uint8(checksum >> 0)
- _, err = w.Write(z.scratch[0:4])
- if err != nil {
- return nil, err
+ if _, err = z.w.Write(z.scratch[0:4]); err != nil {
+ return err
}
}
- z.w = w
- z.compressor = flate.NewWriterDict(w, level, dict)
+ z.compressor, err = flate.NewWriterDict(z.w, z.level, z.dict)
+ if err != nil {
+ return err
+ }
z.digest = adler32.New()
- return z, nil
+ return nil
}
+// Write writes a compressed form of p to the underlying io.Writer. The
+// compressed bytes are not necessarily flushed until the Writer is closed or
+// explicitly flushed.
func (z *Writer) Write(p []byte) (n int, err error) {
+ if !z.wroteHeader {
+ z.err = z.writeHeader()
+ }
if z.err != nil {
return 0, z.err
}
@@ -110,8 +141,11 @@ func (z *Writer) Write(p []byte) (n int, err error) {
return
}
-// Flush flushes the underlying compressor.
+// Flush flushes the Writer to its underlying io.Writer.
func (z *Writer) Flush() error {
+ if !z.wroteHeader {
+ z.err = z.writeHeader()
+ }
if z.err != nil {
return z.err
}
@@ -121,6 +155,9 @@ func (z *Writer) Flush() error {
// Calling Close does not close the wrapped io.Writer originally passed to NewWriter.
func (z *Writer) Close() error {
+ if !z.wroteHeader {
+ z.err = z.writeHeader()
+ }
if z.err != nil {
return z.err
}