1
2
3
4
5 package gzip
6
7 import (
8 "errors"
9 "fmt"
10 "hash/crc32"
11 "io"
12
13 "github.com/klauspost/compress/flate"
14 )
15
16
17
18 const (
19 NoCompression = flate.NoCompression
20 BestSpeed = flate.BestSpeed
21 BestCompression = flate.BestCompression
22 DefaultCompression = flate.DefaultCompression
23 ConstantCompression = flate.ConstantCompression
24 HuffmanOnly = flate.HuffmanOnly
25
26
27
28
29
30
31 StatelessCompression = -3
32 )
33
34
35
36 type Writer struct {
37 Header
38 w io.Writer
39 level int
40 err error
41 compressor *flate.Writer
42 digest uint32
43 size uint32
44 wroteHeader bool
45 closed bool
46 buf [10]byte
47 }
48
49
50
51
52
53
54
55
56
57 func NewWriter(w io.Writer) *Writer {
58 z, _ := NewWriterLevel(w, DefaultCompression)
59 return z
60 }
61
62
63
64
65
66
67
68 func NewWriterLevel(w io.Writer, level int) (*Writer, error) {
69 if level < StatelessCompression || level > BestCompression {
70 return nil, fmt.Errorf("gzip: invalid compression level: %d", level)
71 }
72 z := new(Writer)
73 z.init(w, level)
74 return z, nil
75 }
76
77
78 const MinCustomWindowSize = flate.MinCustomWindowSize
79
80
81 const MaxCustomWindowSize = flate.MaxCustomWindowSize
82
83
84
85 func NewWriterWindow(w io.Writer, windowSize int) (*Writer, error) {
86 if windowSize < MinCustomWindowSize {
87 return nil, errors.New("gzip: requested window size less than MinWindowSize")
88 }
89 if windowSize > MaxCustomWindowSize {
90 return nil, errors.New("gzip: requested window size bigger than MaxCustomWindowSize")
91 }
92
93 z := new(Writer)
94 z.init(w, -windowSize)
95 return z, nil
96 }
97
98 func (z *Writer) init(w io.Writer, level int) {
99 compressor := z.compressor
100 if level != StatelessCompression {
101 if compressor != nil {
102 compressor.Reset(w)
103 }
104 }
105
106 *z = Writer{
107 Header: Header{
108 OS: 255,
109 },
110 w: w,
111 level: level,
112 compressor: compressor,
113 }
114 }
115
116
117
118
119
120 func (z *Writer) Reset(w io.Writer) {
121 z.init(w, z.level)
122 }
123
124
125 func (z *Writer) writeBytes(b []byte) error {
126 if len(b) > 0xffff {
127 return errors.New("gzip.Write: Extra data is too large")
128 }
129 le.PutUint16(z.buf[:2], uint16(len(b)))
130 _, err := z.w.Write(z.buf[:2])
131 if err != nil {
132 return err
133 }
134 _, err = z.w.Write(b)
135 return err
136 }
137
138
139
140 func (z *Writer) writeString(s string) (err error) {
141
142 needconv := false
143 for _, v := range s {
144 if v == 0 || v > 0xff {
145 return errors.New("gzip.Write: non-Latin-1 header string")
146 }
147 if v > 0x7f {
148 needconv = true
149 }
150 }
151 if needconv {
152 b := make([]byte, 0, len(s))
153 for _, v := range s {
154 b = append(b, byte(v))
155 }
156 _, err = z.w.Write(b)
157 } else {
158 _, err = io.WriteString(z.w, s)
159 }
160 if err != nil {
161 return err
162 }
163
164 z.buf[0] = 0
165 _, err = z.w.Write(z.buf[:1])
166 return err
167 }
168
169
170
171 func (z *Writer) Write(p []byte) (int, error) {
172 if z.err != nil {
173 return 0, z.err
174 }
175 var n int
176
177 if !z.wroteHeader {
178 z.wroteHeader = true
179 z.buf[0] = gzipID1
180 z.buf[1] = gzipID2
181 z.buf[2] = gzipDeflate
182 z.buf[3] = 0
183 if z.Extra != nil {
184 z.buf[3] |= 0x04
185 }
186 if z.Name != "" {
187 z.buf[3] |= 0x08
188 }
189 if z.Comment != "" {
190 z.buf[3] |= 0x10
191 }
192 le.PutUint32(z.buf[4:8], uint32(z.ModTime.Unix()))
193 if z.level == BestCompression {
194 z.buf[8] = 2
195 } else if z.level == BestSpeed {
196 z.buf[8] = 4
197 } else {
198 z.buf[8] = 0
199 }
200 z.buf[9] = z.OS
201 n, z.err = z.w.Write(z.buf[:10])
202 if z.err != nil {
203 return n, z.err
204 }
205 if z.Extra != nil {
206 z.err = z.writeBytes(z.Extra)
207 if z.err != nil {
208 return n, z.err
209 }
210 }
211 if z.Name != "" {
212 z.err = z.writeString(z.Name)
213 if z.err != nil {
214 return n, z.err
215 }
216 }
217 if z.Comment != "" {
218 z.err = z.writeString(z.Comment)
219 if z.err != nil {
220 return n, z.err
221 }
222 }
223
224 if z.compressor == nil && z.level != StatelessCompression {
225 z.compressor, _ = flate.NewWriter(z.w, z.level)
226 }
227 }
228 z.size += uint32(len(p))
229 z.digest = crc32.Update(z.digest, crc32.IEEETable, p)
230 if z.level == StatelessCompression {
231 return len(p), flate.StatelessDeflate(z.w, p, false, nil)
232 }
233 n, z.err = z.compressor.Write(p)
234 return n, z.err
235 }
236
237
238
239
240
241
242
243
244
245 func (z *Writer) Flush() error {
246 if z.err != nil {
247 return z.err
248 }
249 if z.closed || z.level == StatelessCompression {
250 return nil
251 }
252 if !z.wroteHeader {
253 z.Write(nil)
254 if z.err != nil {
255 return z.err
256 }
257 }
258 z.err = z.compressor.Flush()
259 return z.err
260 }
261
262
263
264 func (z *Writer) Close() error {
265 if z.err != nil {
266 return z.err
267 }
268 if z.closed {
269 return nil
270 }
271 z.closed = true
272 if !z.wroteHeader {
273 z.Write(nil)
274 if z.err != nil {
275 return z.err
276 }
277 }
278 if z.level == StatelessCompression {
279 z.err = flate.StatelessDeflate(z.w, nil, true, nil)
280 } else {
281 z.err = z.compressor.Close()
282 }
283 if z.err != nil {
284 return z.err
285 }
286 le.PutUint32(z.buf[:4], z.digest)
287 le.PutUint32(z.buf[4:8], z.size)
288 _, z.err = z.w.Write(z.buf[:8])
289 return z.err
290 }
291
View as plain text