diff options
Diffstat (limited to 'libgo/go/compress/zlib/writer.go')
-rw-r--r-- | libgo/go/compress/zlib/writer.go | 103 |
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 } |