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