1
2
3
4
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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
50
51
52 func NewReader(r io.Reader) (io.ReadCloser, os.Error) {
53 return NewReaderDict(r, nil)
54 }
55
56
57
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
106 if _, err := io.ReadFull(z.r, z.scratch[0:4]); err != nil {
107 z.err = err
108 return 0, err
109 }
110
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
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 }