source: code/trunk/vendor/github.com/klauspost/compress/gzip/gzip.go@ 145

Last change on this file since 145 was 145, checked in by Izuru Yakumo, 22 months ago

Updated the Makefile and vendored depedencies

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 6.8 KB
Line 
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
5package gzip
6
7import (
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".
18const (
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.
36type 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.
57func 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.
68func 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
77func (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.
99func (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.
104func (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).
119func (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.
150func (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.
224func (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.
243func (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}
Note: See TracBrowser for help on using the repository browser.