The Go Programming Language

Source file src/pkg/compress/zlib/reader.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	/*
     6	Package zlib implements reading and writing of zlib format compressed data,
     7	as specified in RFC 1950.
     8	
     9	The implementation provides filters that uncompress during reading
    10	and compress during writing.  For example, to write compressed data
    11	to a buffer:
    12	
    13		var b bytes.Buffer
    14		w, err := zlib.NewWriter(&b)
    15		w.Write([]byte("hello, world\n"))
    16		w.Close()
    17	
    18	and to read that data back:
    19	
    20		r, err := zlib.NewReader(&b)
    21		io.Copy(os.Stdout, r)
    22		r.Close()
    23	*/
    24	package zlib
    25	
    26	import (
    27		"bufio"
    28		"compress/flate"
    29		"hash"
    30		"hash/adler32"
    31		"io"
    32		"os"
    33	)
    34	
    35	const zlibDeflate = 8
    36	
    37	var ChecksumError = os.NewError("zlib checksum error")
    38	var HeaderError = os.NewError("invalid zlib header")
    39	var DictionaryError = os.NewError("invalid zlib dictionary")
    40	
    41	type reader struct {
    42		r            flate.Reader
    43		decompressor io.ReadCloser
    44		digest       hash.Hash32
    45		err          os.Error
    46		scratch      [4]byte
    47	}
    48	
    49	// NewReader creates a new io.ReadCloser that satisfies reads by decompressing data read from r.
    50	// The implementation buffers input and may read more data than necessary from r.
    51	// It is the caller's responsibility to call Close on the ReadCloser when done.
    52	func NewReader(r io.Reader) (io.ReadCloser, os.Error) {
    53		return NewReaderDict(r, nil)
    54	}
    55	
    56	// NewReaderDict is like NewReader but uses a preset dictionary.
    57	// NewReaderDict ignores the dictionary if the compressed data does not refer to it.
    58	func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, os.Error) {
    59		z := new(reader)
    60		if fr, ok := r.(flate.Reader); ok {
    61			z.r = fr
    62		} else {
    63			z.r = bufio.NewReader(r)
    64		}
    65		_, err := io.ReadFull(z.r, z.scratch[0:2])
    66		if err != nil {
    67			return nil, err
    68		}
    69		h := uint(z.scratch[0])<<8 | uint(z.scratch[1])
    70		if (z.scratch[0]&0x0f != zlibDeflate) || (h%31 != 0) {
    71			return nil, HeaderError
    72		}
    73		if z.scratch[1]&0x20 != 0 {
    74			_, err = io.ReadFull(z.r, z.scratch[0:4])
    75			if err != nil {
    76				return nil, err
    77			}
    78			checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3])
    79			if checksum != adler32.Checksum(dict) {
    80				return nil, DictionaryError
    81			}
    82			z.decompressor = flate.NewReaderDict(z.r, dict)
    83		} else {
    84			z.decompressor = flate.NewReader(z.r)
    85		}
    86		z.digest = adler32.New()
    87		return z, nil
    88	}
    89	
    90	func (z *reader) Read(p []byte) (n int, err os.Error) {
    91		if z.err != nil {
    92			return 0, z.err
    93		}
    94		if len(p) == 0 {
    95			return 0, nil
    96		}
    97	
    98		n, err = z.decompressor.Read(p)
    99		z.digest.Write(p[0:n])
   100		if n != 0 || err != os.EOF {
   101			z.err = err
   102			return
   103		}
   104	
   105		// Finished file; check checksum.
   106		if _, err := io.ReadFull(z.r, z.scratch[0:4]); err != nil {
   107			z.err = err
   108			return 0, err
   109		}
   110		// ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
   111		checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3])
   112		if checksum != z.digest.Sum32() {
   113			z.err = ChecksumError
   114			return 0, z.err
   115		}
   116		return
   117	}
   118	
   119	// Calling Close does not close the wrapped io.Reader originally passed to NewReader.
   120	func (z *reader) Close() os.Error {
   121		if z.err != nil {
   122			return z.err
   123		}
   124		z.err = z.decompressor.Close()
   125		return z.err
   126	}

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