src/pkg/compress/gzip/gzip.go - The Go Programming Language

Golang

Source file src/pkg/compress/gzip/gzip.go

     1	// Copyright 2010 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	package gzip
     6	
     7	import (
     8		"compress/flate"
     9		"errors"
    10		"fmt"
    11		"hash"
    12		"hash/crc32"
    13		"io"
    14	)
    15	
    16	// These constants are copied from the flate package, so that code that imports
    17	// "compress/gzip" does not also have to import "compress/flate".
    18	const (
    19		NoCompression      = flate.NoCompression
    20		BestSpeed          = flate.BestSpeed
    21		BestCompression    = flate.BestCompression
    22		DefaultCompression = flate.DefaultCompression
    23	)
    24	
    25	// A Writer is an io.WriteCloser that satisfies writes by compressing data written
    26	// to its wrapped io.Writer.
    27	type Writer struct {
    28		Header
    29		w          io.Writer
    30		level      int
    31		compressor io.WriteCloser
    32		digest     hash.Hash32
    33		size       uint32
    34		closed     bool
    35		buf        [10]byte
    36		err        error
    37	}
    38	
    39	// NewWriter creates a new Writer that satisfies writes by compressing data
    40	// written to w.
    41	//
    42	// It is the caller's responsibility to call Close on the WriteCloser when done.
    43	// Writes may be buffered and not flushed until Close.
    44	//
    45	// Callers that wish to set the fields in Writer.Header must do so before
    46	// the first call to Write or Close. The Comment and Name header fields are
    47	// UTF-8 strings in Go, but the underlying format requires NUL-terminated ISO
    48	// 8859-1 (Latin-1). NUL or non-Latin-1 runes in those strings will lead to an
    49	// error on Write.
    50	func NewWriter(w io.Writer) *Writer {
    51		z, _ := NewWriterLevel(w, DefaultCompression)
    52		return z
    53	}
    54	
    55	// NewWriterLevel is like NewWriter but specifies the compression level instead
    56	// of assuming DefaultCompression.
    57	//
    58	// The compression level can be DefaultCompression, NoCompression, or any
    59	// integer value between BestSpeed and BestCompression inclusive. The error
    60	// returned will be nil if the level is valid.
    61	func NewWriterLevel(w io.Writer, level int) (*Writer, error) {
    62		if level < DefaultCompression || level > BestCompression {
    63			return nil, fmt.Errorf("gzip: invalid compression level: %d", level)
    64		}
    65		return &Writer{
    66			Header: Header{
    67				OS: 255, // unknown
    68			},
    69			w:      w,
    70			level:  level,
    71			digest: crc32.NewIEEE(),
    72		}, nil
    73	}
    74	
    75	// GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950).
    76	func put2(p []byte, v uint16) {
    77		p[0] = uint8(v >> 0)
    78		p[1] = uint8(v >> 8)
    79	}
    80	
    81	func put4(p []byte, v uint32) {
    82		p[0] = uint8(v >> 0)
    83		p[1] = uint8(v >> 8)
    84		p[2] = uint8(v >> 16)
    85		p[3] = uint8(v >> 24)
    86	}
    87	
    88	// writeBytes writes a length-prefixed byte slice to z.w.
    89	func (z *Writer) writeBytes(b []byte) error {
    90		if len(b) > 0xffff {
    91			return errors.New("gzip.Write: Extra data is too large")
    92		}
    93		put2(z.buf[0:2], uint16(len(b)))
    94		_, err := z.w.Write(z.buf[0:2])
    95		if err != nil {
    96			return err
    97		}
    98		_, err = z.w.Write(b)
    99		return err
   100	}
   101	
   102	// writeString writes a UTF-8 string s in GZIP's format to z.w.
   103	// GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1).
   104	func (z *Writer) writeString(s string) (err error) {
   105		// GZIP stores Latin-1 strings; error if non-Latin-1; convert if non-ASCII.
   106		needconv := false
   107		for _, v := range s {
   108			if v == 0 || v > 0xff {
   109				return errors.New("gzip.Write: non-Latin-1 header string")
   110			}
   111			if v > 0x7f {
   112				needconv = true
   113			}
   114		}
   115		if needconv {
   116			b := make([]byte, 0, len(s))
   117			for _, v := range s {
   118				b = append(b, byte(v))
   119			}
   120			_, err = z.w.Write(b)
   121		} else {
   122			_, err = io.WriteString(z.w, s)
   123		}
   124		if err != nil {
   125			return err
   126		}
   127		// GZIP strings are NUL-terminated.
   128		z.buf[0] = 0
   129		_, err = z.w.Write(z.buf[0:1])
   130		return err
   131	}
   132	
   133	// Write writes a compressed form of p to the underlying io.Writer. The
   134	// compressed bytes are not necessarily flushed until the Writer is closed.
   135	func (z *Writer) Write(p []byte) (int, error) {
   136		if z.err != nil {
   137			return 0, z.err
   138		}
   139		var n int
   140		// Write the GZIP header lazily.
   141		if z.compressor == nil {
   142			z.buf[0] = gzipID1
   143			z.buf[1] = gzipID2
   144			z.buf[2] = gzipDeflate
   145			z.buf[3] = 0
   146			if z.Extra != nil {
   147				z.buf[3] |= 0x04
   148			}
   149			if z.Name != "" {
   150				z.buf[3] |= 0x08
   151			}
   152			if z.Comment != "" {
   153				z.buf[3] |= 0x10
   154			}
   155			put4(z.buf[4:8], uint32(z.ModTime.Unix()))
   156			if z.level == BestCompression {
   157				z.buf[8] = 2
   158			} else if z.level == BestSpeed {
   159				z.buf[8] = 4
   160			} else {
   161				z.buf[8] = 0
   162			}
   163			z.buf[9] = z.OS
   164			n, z.err = z.w.Write(z.buf[0:10])
   165			if z.err != nil {
   166				return n, z.err
   167			}
   168			if z.Extra != nil {
   169				z.err = z.writeBytes(z.Extra)
   170				if z.err != nil {
   171					return n, z.err
   172				}
   173			}
   174			if z.Name != "" {
   175				z.err = z.writeString(z.Name)
   176				if z.err != nil {
   177					return n, z.err
   178				}
   179			}
   180			if z.Comment != "" {
   181				z.err = z.writeString(z.Comment)
   182				if z.err != nil {
   183					return n, z.err
   184				}
   185			}
   186			z.compressor, _ = flate.NewWriter(z.w, z.level)
   187		}
   188		z.size += uint32(len(p))
   189		z.digest.Write(p)
   190		n, z.err = z.compressor.Write(p)
   191		return n, z.err
   192	}
   193	
   194	// Close closes the Writer. It does not close the underlying io.Writer.
   195	func (z *Writer) Close() error {
   196		if z.err != nil {
   197			return z.err
   198		}
   199		if z.closed {
   200			return nil
   201		}
   202		z.closed = true
   203		if z.compressor == nil {
   204			z.Write(nil)
   205			if z.err != nil {
   206				return z.err
   207			}
   208		}
   209		z.err = z.compressor.Close()
   210		if z.err != nil {
   211			return z.err
   212		}
   213		put4(z.buf[0:4], z.digest.Sum32())
   214		put4(z.buf[4:8], z.size)
   215		_, z.err = z.w.Write(z.buf[0:8])
   216		return z.err
   217	}