|  | // Copyright 2010 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 gzip | 
|  |  | 
|  | import ( | 
|  | "compress/flate" | 
|  | "errors" | 
|  | "fmt" | 
|  | "hash/crc32" | 
|  | "io" | 
|  | "time" | 
|  | ) | 
|  |  | 
|  | // These constants are copied from the flate package, so that code that imports | 
|  | // "compress/gzip" does not also have to import "compress/flate". | 
|  | const ( | 
|  | NoCompression      = flate.NoCompression | 
|  | BestSpeed          = flate.BestSpeed | 
|  | BestCompression    = flate.BestCompression | 
|  | DefaultCompression = flate.DefaultCompression | 
|  | HuffmanOnly        = flate.HuffmanOnly | 
|  | ) | 
|  |  | 
|  | // A Writer is an io.WriteCloser. | 
|  | // Writes to a Writer are compressed and written to w. | 
|  | type Writer struct { | 
|  | Header      // written at first call to Write, Flush, or Close | 
|  | w           io.Writer | 
|  | level       int | 
|  | wroteHeader bool | 
|  | compressor  *flate.Writer | 
|  | digest      uint32 // CRC-32, IEEE polynomial (section 8) | 
|  | size        uint32 // Uncompressed size (section 2.3.1) | 
|  | closed      bool | 
|  | buf         [10]byte | 
|  | err         error | 
|  | } | 
|  |  | 
|  | // NewWriter returns a new Writer. | 
|  | // Writes to the returned writer are compressed and 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. | 
|  | // | 
|  | // Callers that wish to set the fields in Writer.Header must do so before | 
|  | // the first call to Write, Flush, or Close. | 
|  | func NewWriter(w io.Writer) *Writer { | 
|  | z, _ := NewWriterLevel(w, DefaultCompression) | 
|  | return z | 
|  | } | 
|  |  | 
|  | // NewWriterLevel is like NewWriter but specifies the compression level instead | 
|  | // of assuming DefaultCompression. | 
|  | // | 
|  | // The compression level can be DefaultCompression, NoCompression, HuffmanOnly | 
|  | // 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) { | 
|  | if level < HuffmanOnly || level > BestCompression { | 
|  | return nil, fmt.Errorf("gzip: invalid compression level: %d", level) | 
|  | } | 
|  | z := new(Writer) | 
|  | z.init(w, level) | 
|  | return z, nil | 
|  | } | 
|  |  | 
|  | func (z *Writer) init(w io.Writer, level int) { | 
|  | compressor := z.compressor | 
|  | if compressor != nil { | 
|  | compressor.Reset(w) | 
|  | } | 
|  | *z = Writer{ | 
|  | Header: Header{ | 
|  | OS: 255, // unknown | 
|  | }, | 
|  | w:          w, | 
|  | level:      level, | 
|  | compressor: compressor, | 
|  | } | 
|  | } | 
|  |  | 
|  | // Reset discards the Writer z's state and makes it equivalent to the | 
|  | // result of its original state from NewWriter or NewWriterLevel, but | 
|  | // writing to w instead. This permits reusing a Writer rather than | 
|  | // allocating a new one. | 
|  | func (z *Writer) Reset(w io.Writer) { | 
|  | z.init(w, z.level) | 
|  | } | 
|  |  | 
|  | // writeBytes writes a length-prefixed byte slice to z.w. | 
|  | func (z *Writer) writeBytes(b []byte) error { | 
|  | if len(b) > 0xffff { | 
|  | return errors.New("gzip.Write: Extra data is too large") | 
|  | } | 
|  | le.PutUint16(z.buf[:2], uint16(len(b))) | 
|  | _, err := z.w.Write(z.buf[:2]) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | _, err = z.w.Write(b) | 
|  | return err | 
|  | } | 
|  |  | 
|  | // writeString writes a UTF-8 string s in GZIP's format to z.w. | 
|  | // GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1). | 
|  | func (z *Writer) writeString(s string) (err error) { | 
|  | // GZIP stores Latin-1 strings; error if non-Latin-1; convert if non-ASCII. | 
|  | needconv := false | 
|  | for _, v := range s { | 
|  | if v == 0 || v > 0xff { | 
|  | return errors.New("gzip.Write: non-Latin-1 header string") | 
|  | } | 
|  | if v > 0x7f { | 
|  | needconv = true | 
|  | } | 
|  | } | 
|  | if needconv { | 
|  | b := make([]byte, 0, len(s)) | 
|  | for _, v := range s { | 
|  | b = append(b, byte(v)) | 
|  | } | 
|  | _, err = z.w.Write(b) | 
|  | } else { | 
|  | _, err = io.WriteString(z.w, s) | 
|  | } | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | // GZIP strings are NUL-terminated. | 
|  | z.buf[0] = 0 | 
|  | _, err = z.w.Write(z.buf[:1]) | 
|  | return err | 
|  | } | 
|  |  | 
|  | // Write writes a compressed form of p to the underlying io.Writer. The | 
|  | // compressed bytes are not necessarily flushed until the Writer is closed. | 
|  | func (z *Writer) Write(p []byte) (int, error) { | 
|  | if z.err != nil { | 
|  | return 0, z.err | 
|  | } | 
|  | var n int | 
|  | // Write the GZIP header lazily. | 
|  | if !z.wroteHeader { | 
|  | z.wroteHeader = true | 
|  | z.buf = [10]byte{0: gzipID1, 1: gzipID2, 2: gzipDeflate} | 
|  | if z.Extra != nil { | 
|  | z.buf[3] |= 0x04 | 
|  | } | 
|  | if z.Name != "" { | 
|  | z.buf[3] |= 0x08 | 
|  | } | 
|  | if z.Comment != "" { | 
|  | z.buf[3] |= 0x10 | 
|  | } | 
|  | if z.ModTime.After(time.Unix(0, 0)) { | 
|  | // Section 2.3.1, the zero value for MTIME means that the | 
|  | // modified time is not set. | 
|  | le.PutUint32(z.buf[4:8], uint32(z.ModTime.Unix())) | 
|  | } | 
|  | if z.level == BestCompression { | 
|  | z.buf[8] = 2 | 
|  | } else if z.level == BestSpeed { | 
|  | z.buf[8] = 4 | 
|  | } | 
|  | z.buf[9] = z.OS | 
|  | n, z.err = z.w.Write(z.buf[:10]) | 
|  | if z.err != nil { | 
|  | return n, z.err | 
|  | } | 
|  | if z.Extra != nil { | 
|  | z.err = z.writeBytes(z.Extra) | 
|  | if z.err != nil { | 
|  | return n, z.err | 
|  | } | 
|  | } | 
|  | if z.Name != "" { | 
|  | z.err = z.writeString(z.Name) | 
|  | if z.err != nil { | 
|  | return n, z.err | 
|  | } | 
|  | } | 
|  | if z.Comment != "" { | 
|  | z.err = z.writeString(z.Comment) | 
|  | if z.err != nil { | 
|  | return n, z.err | 
|  | } | 
|  | } | 
|  | if z.compressor == nil { | 
|  | z.compressor, _ = flate.NewWriter(z.w, z.level) | 
|  | } | 
|  | } | 
|  | z.size += uint32(len(p)) | 
|  | z.digest = crc32.Update(z.digest, crc32.IEEETable, p) | 
|  | n, z.err = z.compressor.Write(p) | 
|  | return n, z.err | 
|  | } | 
|  |  | 
|  | // Flush flushes any pending compressed data to the underlying writer. | 
|  | // | 
|  | // It is useful mainly in compressed network protocols, to ensure that | 
|  | // a remote reader has enough data to reconstruct a packet. Flush does | 
|  | // not return until the data has been written. If the underlying | 
|  | // writer returns an error, Flush returns that error. | 
|  | // | 
|  | // In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH. | 
|  | func (z *Writer) Flush() error { | 
|  | if z.err != nil { | 
|  | return z.err | 
|  | } | 
|  | if z.closed { | 
|  | return nil | 
|  | } | 
|  | if !z.wroteHeader { | 
|  | z.Write(nil) | 
|  | if z.err != nil { | 
|  | return z.err | 
|  | } | 
|  | } | 
|  | z.err = z.compressor.Flush() | 
|  | return z.err | 
|  | } | 
|  |  | 
|  | // Close closes the Writer by flushing any unwritten data to the underlying | 
|  | // io.Writer and writing the GZIP footer. | 
|  | // It does not close the underlying io.Writer. | 
|  | func (z *Writer) Close() error { | 
|  | if z.err != nil { | 
|  | return z.err | 
|  | } | 
|  | if z.closed { | 
|  | return nil | 
|  | } | 
|  | z.closed = true | 
|  | if !z.wroteHeader { | 
|  | z.Write(nil) | 
|  | if z.err != nil { | 
|  | return z.err | 
|  | } | 
|  | } | 
|  | z.err = z.compressor.Close() | 
|  | if z.err != nil { | 
|  | return z.err | 
|  | } | 
|  | le.PutUint32(z.buf[:4], z.digest) | 
|  | le.PutUint32(z.buf[4:8], z.size) | 
|  | _, z.err = z.w.Write(z.buf[:8]) | 
|  | return z.err | 
|  | } |