Source file src/pkg/compress/gzip/gunzip.go
1
2
3
4
5
6
7 package gzip
8
9 import (
10 "bufio"
11 "compress/flate"
12 "errors"
13 "hash"
14 "hash/crc32"
15 "io"
16 "time"
17 )
18
19 const (
20 gzipID1 = 0x1f
21 gzipID2 = 0x8b
22 gzipDeflate = 8
23 flagText = 1 << 0
24 flagHdrCrc = 1 << 1
25 flagExtra = 1 << 2
26 flagName = 1 << 3
27 flagComment = 1 << 4
28 )
29
30 func makeReader(r io.Reader) flate.Reader {
31 if rr, ok := r.(flate.Reader); ok {
32 return rr
33 }
34 return bufio.NewReader(r)
35 }
36
37 var (
38
39 ErrChecksum = errors.New("gzip: invalid checksum")
40
41 ErrHeader = errors.New("gzip: invalid header")
42 )
43
44
45
46 type Header struct {
47 Comment string
48 Extra []byte
49 ModTime time.Time
50 Name string
51 OS byte
52 }
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 type Reader struct {
69 Header
70 r flate.Reader
71 decompressor io.ReadCloser
72 digest hash.Hash32
73 size uint32
74 flg byte
75 buf [512]byte
76 err error
77 }
78
79
80
81
82 func NewReader(r io.Reader) (*Reader, error) {
83 z := new(Reader)
84 z.r = makeReader(r)
85 z.digest = crc32.NewIEEE()
86 if err := z.readHeader(true); err != nil {
87 return nil, err
88 }
89 return z, nil
90 }
91
92
93 func get4(p []byte) uint32 {
94 return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24
95 }
96
97 func (z *Reader) readString() (string, error) {
98 var err error
99 needconv := false
100 for i := 0; ; i++ {
101 if i >= len(z.buf) {
102 return "", ErrHeader
103 }
104 z.buf[i], err = z.r.ReadByte()
105 if err != nil {
106 return "", err
107 }
108 if z.buf[i] > 0x7f {
109 needconv = true
110 }
111 if z.buf[i] == 0 {
112
113 if needconv {
114 s := make([]rune, 0, i)
115 for _, v := range z.buf[0:i] {
116 s = append(s, rune(v))
117 }
118 return string(s), nil
119 }
120 return string(z.buf[0:i]), nil
121 }
122 }
123 }
124
125 func (z *Reader) read2() (uint32, error) {
126 _, err := io.ReadFull(z.r, z.buf[0:2])
127 if err != nil {
128 return 0, err
129 }
130 return uint32(z.buf[0]) | uint32(z.buf[1])<<8, nil
131 }
132
133 func (z *Reader) readHeader(save bool) error {
134 _, err := io.ReadFull(z.r, z.buf[0:10])
135 if err != nil {
136 return err
137 }
138 if z.buf[0] != gzipID1 || z.buf[1] != gzipID2 || z.buf[2] != gzipDeflate {
139 return ErrHeader
140 }
141 z.flg = z.buf[3]
142 if save {
143 z.ModTime = time.Unix(int64(get4(z.buf[4:8])), 0)
144
145 z.OS = z.buf[9]
146 }
147 z.digest.Reset()
148 z.digest.Write(z.buf[0:10])
149
150 if z.flg&flagExtra != 0 {
151 n, err := z.read2()
152 if err != nil {
153 return err
154 }
155 data := make([]byte, n)
156 if _, err = io.ReadFull(z.r, data); err != nil {
157 return err
158 }
159 if save {
160 z.Extra = data
161 }
162 }
163
164 var s string
165 if z.flg&flagName != 0 {
166 if s, err = z.readString(); err != nil {
167 return err
168 }
169 if save {
170 z.Name = s
171 }
172 }
173
174 if z.flg&flagComment != 0 {
175 if s, err = z.readString(); err != nil {
176 return err
177 }
178 if save {
179 z.Comment = s
180 }
181 }
182
183 if z.flg&flagHdrCrc != 0 {
184 n, err := z.read2()
185 if err != nil {
186 return err
187 }
188 sum := z.digest.Sum32() & 0xFFFF
189 if n != sum {
190 return ErrHeader
191 }
192 }
193
194 z.digest.Reset()
195 z.decompressor = flate.NewReader(z.r)
196 return nil
197 }
198
199 func (z *Reader) Read(p []byte) (n int, err error) {
200 if z.err != nil {
201 return 0, z.err
202 }
203 if len(p) == 0 {
204 return 0, nil
205 }
206
207 n, err = z.decompressor.Read(p)
208 z.digest.Write(p[0:n])
209 z.size += uint32(n)
210 if n != 0 || err != io.EOF {
211 z.err = err
212 return
213 }
214
215
216 if _, err := io.ReadFull(z.r, z.buf[0:8]); err != nil {
217 z.err = err
218 return 0, err
219 }
220 crc32, isize := get4(z.buf[0:4]), get4(z.buf[4:8])
221 sum := z.digest.Sum32()
222 if sum != crc32 || isize != z.size {
223 z.err = ErrChecksum
224 return 0, z.err
225 }
226
227
228 if err = z.readHeader(false); err != nil {
229 z.err = err
230 return
231 }
232
233
234 z.digest.Reset()
235 z.size = 0
236 return z.Read(p)
237 }
238
239
240 func (z *Reader) Close() error { return z.decompressor.Close() }
View as plain text