The Go Programming Language

Source file src/pkg/compress/zlib/writer.go

     1	// Copyright 2009 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 zlib
     6	
     7	import (
     8		"compress/flate"
     9		"hash"
    10		"hash/adler32"
    11		"io"
    12		"os"
    13	)
    14	
    15	// These constants are copied from the flate package, so that code that imports
    16	// "compress/zlib" does not also have to import "compress/flate".
    17	const (
    18		NoCompression      = flate.NoCompression
    19		BestSpeed          = flate.BestSpeed
    20		BestCompression    = flate.BestCompression
    21		DefaultCompression = flate.DefaultCompression
    22	)
    23	
    24	// A Writer takes data written to it and writes the compressed
    25	// form of that data to an underlying writer (see NewWriter).
    26	type Writer struct {
    27		w          io.Writer
    28		compressor *flate.Writer
    29		digest     hash.Hash32
    30		err        os.Error
    31		scratch    [4]byte
    32	}
    33	
    34	// NewWriter calls NewWriterLevel with the default compression level.
    35	func NewWriter(w io.Writer) (*Writer, os.Error) {
    36		return NewWriterLevel(w, DefaultCompression)
    37	}
    38	
    39	// NewWriterLevel calls NewWriterDict with no dictionary.
    40	func NewWriterLevel(w io.Writer, level int) (*Writer, os.Error) {
    41		return NewWriterDict(w, level, nil)
    42	}
    43	
    44	// NewWriterDict creates a new io.WriteCloser that satisfies writes by compressing data written to w.
    45	// It is the caller's responsibility to call Close on the WriteCloser when done.
    46	// level is the compression level, which can be DefaultCompression, NoCompression,
    47	// or any integer value between BestSpeed and BestCompression (inclusive).
    48	// dict is the preset dictionary to compress with, or nil to use no dictionary.
    49	func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, os.Error) {
    50		z := new(Writer)
    51		// ZLIB has a two-byte header (as documented in RFC 1950).
    52		// The first four bits is the CINFO (compression info), which is 7 for the default deflate window size.
    53		// The next four bits is the CM (compression method), which is 8 for deflate.
    54		z.scratch[0] = 0x78
    55		// The next two bits is the FLEVEL (compression level). The four values are:
    56		// 0=fastest, 1=fast, 2=default, 3=best.
    57		// The next bit, FDICT, is set if a dictionary is given.
    58		// The final five FCHECK bits form a mod-31 checksum.
    59		switch level {
    60		case 0, 1:
    61			z.scratch[1] = 0 << 6
    62		case 2, 3, 4, 5:
    63			z.scratch[1] = 1 << 6
    64		case 6, -1:
    65			z.scratch[1] = 2 << 6
    66		case 7, 8, 9:
    67			z.scratch[1] = 3 << 6
    68		default:
    69			return nil, os.NewError("level out of range")
    70		}
    71		if dict != nil {
    72			z.scratch[1] |= 1 << 5
    73		}
    74		z.scratch[1] += uint8(31 - (uint16(z.scratch[0])<<8+uint16(z.scratch[1]))%31)
    75		_, err := w.Write(z.scratch[0:2])
    76		if err != nil {
    77			return nil, err
    78		}
    79		if dict != nil {
    80			// The next four bytes are the Adler-32 checksum of the dictionary.
    81			checksum := adler32.Checksum(dict)
    82			z.scratch[0] = uint8(checksum >> 24)
    83			z.scratch[1] = uint8(checksum >> 16)
    84			z.scratch[2] = uint8(checksum >> 8)
    85			z.scratch[3] = uint8(checksum >> 0)
    86			_, err = w.Write(z.scratch[0:4])
    87			if err != nil {
    88				return nil, err
    89			}
    90		}
    91		z.w = w
    92		z.compressor = flate.NewWriterDict(w, level, dict)
    93		z.digest = adler32.New()
    94		return z, nil
    95	}
    96	
    97	func (z *Writer) Write(p []byte) (n int, err os.Error) {
    98		if z.err != nil {
    99			return 0, z.err
   100		}
   101		if len(p) == 0 {
   102			return 0, nil
   103		}
   104		n, err = z.compressor.Write(p)
   105		if err != nil {
   106			z.err = err
   107			return
   108		}
   109		z.digest.Write(p)
   110		return
   111	}
   112	
   113	// Flush flushes the underlying compressor.
   114	func (z *Writer) Flush() os.Error {
   115		if z.err != nil {
   116			return z.err
   117		}
   118		z.err = z.compressor.Flush()
   119		return z.err
   120	}
   121	
   122	// Calling Close does not close the wrapped io.Writer originally passed to NewWriter.
   123	func (z *Writer) Close() os.Error {
   124		if z.err != nil {
   125			return z.err
   126		}
   127		z.err = z.compressor.Close()
   128		if z.err != nil {
   129			return z.err
   130		}
   131		checksum := z.digest.Sum32()
   132		// ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
   133		z.scratch[0] = uint8(checksum >> 24)
   134		z.scratch[1] = uint8(checksum >> 16)
   135		z.scratch[2] = uint8(checksum >> 8)
   136		z.scratch[3] = uint8(checksum >> 0)
   137		_, z.err = z.w.Write(z.scratch[0:4])
   138		return z.err
   139	}

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.