1 | // Copyright 2010 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 gzip
|
---|
6 |
|
---|
7 | import (
|
---|
8 | "errors"
|
---|
9 | "fmt"
|
---|
10 | "hash/crc32"
|
---|
11 | "io"
|
---|
12 |
|
---|
13 | "github.com/klauspost/compress/flate"
|
---|
14 | )
|
---|
15 |
|
---|
16 | // These constants are copied from the flate package, so that code that imports
|
---|
17 | // "compress/gzip" does not also have to import "compress/flate".
|
---|
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 | // StatelessCompression will do compression but without maintaining any state
|
---|
27 | // between Write calls.
|
---|
28 | // There will be no memory kept between Write calls,
|
---|
29 | // but compression and speed will be suboptimal.
|
---|
30 | // Because of this, the size of actual Write calls will affect output size.
|
---|
31 | StatelessCompression = -3
|
---|
32 | )
|
---|
33 |
|
---|
34 | // A Writer is an io.WriteCloser.
|
---|
35 | // Writes to a Writer are compressed and written to w.
|
---|
36 | type Writer struct {
|
---|
37 | Header // written at first call to Write, Flush, or Close
|
---|
38 | w io.Writer
|
---|
39 | level int
|
---|
40 | err error
|
---|
41 | compressor *flate.Writer
|
---|
42 | digest uint32 // CRC-32, IEEE polynomial (section 8)
|
---|
43 | size uint32 // Uncompressed size (section 2.3.1)
|
---|
44 | wroteHeader bool
|
---|
45 | closed bool
|
---|
46 | buf [10]byte
|
---|
47 | }
|
---|
48 |
|
---|
49 | // NewWriter returns a new Writer.
|
---|
50 | // Writes to the returned writer are compressed and written to w.
|
---|
51 | //
|
---|
52 | // It is the caller's responsibility to call Close on the WriteCloser when done.
|
---|
53 | // Writes may be buffered and not flushed until Close.
|
---|
54 | //
|
---|
55 | // Callers that wish to set the fields in Writer.Header must do so before
|
---|
56 | // the first call to Write, Flush, or Close.
|
---|
57 | func NewWriter(w io.Writer) *Writer {
|
---|
58 | z, _ := NewWriterLevel(w, DefaultCompression)
|
---|
59 | return z
|
---|
60 | }
|
---|
61 |
|
---|
62 | // NewWriterLevel is like NewWriter but specifies the compression level instead
|
---|
63 | // of assuming DefaultCompression.
|
---|
64 | //
|
---|
65 | // The compression level can be DefaultCompression, NoCompression, or any
|
---|
66 | // integer value between BestSpeed and BestCompression inclusive. The error
|
---|
67 | // returned will be nil if the level is valid.
|
---|
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 | func (z *Writer) init(w io.Writer, level int) {
|
---|
78 | compressor := z.compressor
|
---|
79 | if level != StatelessCompression {
|
---|
80 | if compressor != nil {
|
---|
81 | compressor.Reset(w)
|
---|
82 | }
|
---|
83 | }
|
---|
84 |
|
---|
85 | *z = Writer{
|
---|
86 | Header: Header{
|
---|
87 | OS: 255, // unknown
|
---|
88 | },
|
---|
89 | w: w,
|
---|
90 | level: level,
|
---|
91 | compressor: compressor,
|
---|
92 | }
|
---|
93 | }
|
---|
94 |
|
---|
95 | // Reset discards the Writer z's state and makes it equivalent to the
|
---|
96 | // result of its original state from NewWriter or NewWriterLevel, but
|
---|
97 | // writing to w instead. This permits reusing a Writer rather than
|
---|
98 | // allocating a new one.
|
---|
99 | func (z *Writer) Reset(w io.Writer) {
|
---|
100 | z.init(w, z.level)
|
---|
101 | }
|
---|
102 |
|
---|
103 | // writeBytes writes a length-prefixed byte slice to z.w.
|
---|
104 | func (z *Writer) writeBytes(b []byte) error {
|
---|
105 | if len(b) > 0xffff {
|
---|
106 | return errors.New("gzip.Write: Extra data is too large")
|
---|
107 | }
|
---|
108 | le.PutUint16(z.buf[:2], uint16(len(b)))
|
---|
109 | _, err := z.w.Write(z.buf[:2])
|
---|
110 | if err != nil {
|
---|
111 | return err
|
---|
112 | }
|
---|
113 | _, err = z.w.Write(b)
|
---|
114 | return err
|
---|
115 | }
|
---|
116 |
|
---|
117 | // writeString writes a UTF-8 string s in GZIP's format to z.w.
|
---|
118 | // GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1).
|
---|
119 | func (z *Writer) writeString(s string) (err error) {
|
---|
120 | // GZIP stores Latin-1 strings; error if non-Latin-1; convert if non-ASCII.
|
---|
121 | needconv := false
|
---|
122 | for _, v := range s {
|
---|
123 | if v == 0 || v > 0xff {
|
---|
124 | return errors.New("gzip.Write: non-Latin-1 header string")
|
---|
125 | }
|
---|
126 | if v > 0x7f {
|
---|
127 | needconv = true
|
---|
128 | }
|
---|
129 | }
|
---|
130 | if needconv {
|
---|
131 | b := make([]byte, 0, len(s))
|
---|
132 | for _, v := range s {
|
---|
133 | b = append(b, byte(v))
|
---|
134 | }
|
---|
135 | _, err = z.w.Write(b)
|
---|
136 | } else {
|
---|
137 | _, err = io.WriteString(z.w, s)
|
---|
138 | }
|
---|
139 | if err != nil {
|
---|
140 | return err
|
---|
141 | }
|
---|
142 | // GZIP strings are NUL-terminated.
|
---|
143 | z.buf[0] = 0
|
---|
144 | _, err = z.w.Write(z.buf[:1])
|
---|
145 | return err
|
---|
146 | }
|
---|
147 |
|
---|
148 | // Write writes a compressed form of p to the underlying io.Writer. The
|
---|
149 | // compressed bytes are not necessarily flushed until the Writer is closed.
|
---|
150 | func (z *Writer) Write(p []byte) (int, error) {
|
---|
151 | if z.err != nil {
|
---|
152 | return 0, z.err
|
---|
153 | }
|
---|
154 | var n int
|
---|
155 | // Write the GZIP header lazily.
|
---|
156 | if !z.wroteHeader {
|
---|
157 | z.wroteHeader = true
|
---|
158 | z.buf[0] = gzipID1
|
---|
159 | z.buf[1] = gzipID2
|
---|
160 | z.buf[2] = gzipDeflate
|
---|
161 | z.buf[3] = 0
|
---|
162 | if z.Extra != nil {
|
---|
163 | z.buf[3] |= 0x04
|
---|
164 | }
|
---|
165 | if z.Name != "" {
|
---|
166 | z.buf[3] |= 0x08
|
---|
167 | }
|
---|
168 | if z.Comment != "" {
|
---|
169 | z.buf[3] |= 0x10
|
---|
170 | }
|
---|
171 | le.PutUint32(z.buf[4:8], uint32(z.ModTime.Unix()))
|
---|
172 | if z.level == BestCompression {
|
---|
173 | z.buf[8] = 2
|
---|
174 | } else if z.level == BestSpeed {
|
---|
175 | z.buf[8] = 4
|
---|
176 | } else {
|
---|
177 | z.buf[8] = 0
|
---|
178 | }
|
---|
179 | z.buf[9] = z.OS
|
---|
180 | n, z.err = z.w.Write(z.buf[:10])
|
---|
181 | if z.err != nil {
|
---|
182 | return n, z.err
|
---|
183 | }
|
---|
184 | if z.Extra != nil {
|
---|
185 | z.err = z.writeBytes(z.Extra)
|
---|
186 | if z.err != nil {
|
---|
187 | return n, z.err
|
---|
188 | }
|
---|
189 | }
|
---|
190 | if z.Name != "" {
|
---|
191 | z.err = z.writeString(z.Name)
|
---|
192 | if z.err != nil {
|
---|
193 | return n, z.err
|
---|
194 | }
|
---|
195 | }
|
---|
196 | if z.Comment != "" {
|
---|
197 | z.err = z.writeString(z.Comment)
|
---|
198 | if z.err != nil {
|
---|
199 | return n, z.err
|
---|
200 | }
|
---|
201 | }
|
---|
202 |
|
---|
203 | if z.compressor == nil && z.level != StatelessCompression {
|
---|
204 | z.compressor, _ = flate.NewWriter(z.w, z.level)
|
---|
205 | }
|
---|
206 | }
|
---|
207 | z.size += uint32(len(p))
|
---|
208 | z.digest = crc32.Update(z.digest, crc32.IEEETable, p)
|
---|
209 | if z.level == StatelessCompression {
|
---|
210 | return len(p), flate.StatelessDeflate(z.w, p, false, nil)
|
---|
211 | }
|
---|
212 | n, z.err = z.compressor.Write(p)
|
---|
213 | return n, z.err
|
---|
214 | }
|
---|
215 |
|
---|
216 | // Flush flushes any pending compressed data to the underlying writer.
|
---|
217 | //
|
---|
218 | // It is useful mainly in compressed network protocols, to ensure that
|
---|
219 | // a remote reader has enough data to reconstruct a packet. Flush does
|
---|
220 | // not return until the data has been written. If the underlying
|
---|
221 | // writer returns an error, Flush returns that error.
|
---|
222 | //
|
---|
223 | // In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH.
|
---|
224 | func (z *Writer) Flush() error {
|
---|
225 | if z.err != nil {
|
---|
226 | return z.err
|
---|
227 | }
|
---|
228 | if z.closed || z.level == StatelessCompression {
|
---|
229 | return nil
|
---|
230 | }
|
---|
231 | if !z.wroteHeader {
|
---|
232 | z.Write(nil)
|
---|
233 | if z.err != nil {
|
---|
234 | return z.err
|
---|
235 | }
|
---|
236 | }
|
---|
237 | z.err = z.compressor.Flush()
|
---|
238 | return z.err
|
---|
239 | }
|
---|
240 |
|
---|
241 | // Close closes the Writer, flushing any unwritten data to the underlying
|
---|
242 | // io.Writer, but does not close the underlying io.Writer.
|
---|
243 | func (z *Writer) Close() error {
|
---|
244 | if z.err != nil {
|
---|
245 | return z.err
|
---|
246 | }
|
---|
247 | if z.closed {
|
---|
248 | return nil
|
---|
249 | }
|
---|
250 | z.closed = true
|
---|
251 | if !z.wroteHeader {
|
---|
252 | z.Write(nil)
|
---|
253 | if z.err != nil {
|
---|
254 | return z.err
|
---|
255 | }
|
---|
256 | }
|
---|
257 | if z.level == StatelessCompression {
|
---|
258 | z.err = flate.StatelessDeflate(z.w, nil, true, nil)
|
---|
259 | } else {
|
---|
260 | z.err = z.compressor.Close()
|
---|
261 | }
|
---|
262 | if z.err != nil {
|
---|
263 | return z.err
|
---|
264 | }
|
---|
265 | le.PutUint32(z.buf[:4], z.digest)
|
---|
266 | le.PutUint32(z.buf[4:8], z.size)
|
---|
267 | _, z.err = z.w.Write(z.buf[:8])
|
---|
268 | return z.err
|
---|
269 | }
|
---|