// readString reads a NUL-terminated string from z.r. // It treats the bytes read as being encoded as ISO 8859-1 (Latin-1) and // will output a string encoded using UTF-8. // This method always updates z.digest with the data read. func (z *Reader) readString() (string, error) { var err error needConv := false for i := 0; ; i++ { if i >= len(z.buf) { return "", ErrHeader } z.buf[i], err = z.r.ReadByte() if err != nil { return "", err } if z.buf[i] > 0x7f { needConv = true } if z.buf[i] == 0 { // Digest covers the NUL terminator. z.digest = crc32.Update(z.digest, crc32.IEEETable, z.buf[:i+1]) // Strings are ISO 8859-1, Latin-1 (RFC 1952, section 2.3.1). if needConv { s := make([]rune, 0, i) for _, v := range z.buf[:i] { s = append(s, rune(v)) } return string(s), nil } return string(z.buf[:i]), nil } } }
// Read implements io.Reader, reading uncompressed bytes from its underlying Reader. func (z *Reader) Read(p []byte) (n int, err error) { if z.err != nil { return 0, z.err } n, z.err = z.decompressor.Read(p) z.digest = crc32.Update(z.digest, crc32.IEEETable, p[:n]) z.size += uint32(n) if z.err != io.EOF { // In the normal case we return here. return n, z.err } // Finished file; check checksum and size. if _, err := io.ReadFull(z.r, z.buf[:8]); err != nil { z.err = noEOF(err) return n, z.err } digest := le.Uint32(z.buf[:4]) size := le.Uint32(z.buf[4:8]) if digest != z.digest || size != z.size { z.err = ErrChecksum return n, z.err } z.digest, z.size = 0, 0 // File is ok; check if there is another. if !z.multistream { return n, io.EOF } z.err = nil // Remove io.EOF if _, z.err = z.readHeader(); z.err != nil { return n, z.err } // Read from next file, if necessary. if n > 0 { return n, nil } return z.Read(p) }
// crc implements the checksum specified in section 3 of // https://github.com/google/snappy/blob/master/framing_format.txt func crc(b []byte) uint32 { c := crc32.Update(0, crcTable, b) return uint32(c>>15|c<<17) + 0xa282ead8 }
func (c CRC) Update(b []byte) CRC { return CRC(crc32.Update(uint32(c), table, b)) }
// Write writes a compressed form of p to the underlying io.Writer. The // compressed bytes are not necessarily flushed until the Writer is closed. func (z *Writer) Write(p []byte) (int, error) { if z.err != nil { return 0, z.err } var n int // Write the GZIP header lazily. if !z.wroteHeader { z.wroteHeader = true z.buf[0] = gzipID1 z.buf[1] = gzipID2 z.buf[2] = gzipDeflate z.buf[3] = 0 if z.Extra != nil { z.buf[3] |= 0x04 } if z.Name != "" { z.buf[3] |= 0x08 } if z.Comment != "" { z.buf[3] |= 0x10 } le.PutUint32(z.buf[4:8], uint32(z.ModTime.Unix())) if z.level == BestCompression { z.buf[8] = 2 } else if z.level == BestSpeed { z.buf[8] = 4 } else { z.buf[8] = 0 } z.buf[9] = z.OS n, z.err = z.w.Write(z.buf[:10]) if z.err != nil { return n, z.err } if z.Extra != nil { z.err = z.writeBytes(z.Extra) if z.err != nil { return n, z.err } } if z.Name != "" { z.err = z.writeString(z.Name) if z.err != nil { return n, z.err } } if z.Comment != "" { z.err = z.writeString(z.Comment) if z.err != nil { return n, z.err } } if z.compressor == nil { z.compressor, _ = flate.NewWriter(z.w, z.level) } } z.size += uint32(len(p)) z.digest = crc32.Update(z.digest, crc32.IEEETable, p) n, z.err = z.compressor.Write(p) return n, z.err }
// readHeader reads the GZIP header according to section 2.3.1. // This method does not set z.err. func (z *Reader) readHeader() (hdr Header, err error) { if _, err = io.ReadFull(z.r, z.buf[:10]); err != nil { // RFC 1952, section 2.2, says the following: // A gzip file consists of a series of "members" (compressed data sets). // // Other than this, the specification does not clarify whether a // "series" is defined as "one or more" or "zero or more". To err on the // side of caution, Go interprets this to mean "zero or more". // Thus, it is okay to return io.EOF here. return hdr, err } if z.buf[0] != gzipID1 || z.buf[1] != gzipID2 || z.buf[2] != gzipDeflate { return hdr, ErrHeader } flg := z.buf[3] hdr.ModTime = time.Unix(int64(le.Uint32(z.buf[4:8])), 0) // z.buf[8] is XFL and is currently ignored. hdr.OS = z.buf[9] z.digest = crc32.ChecksumIEEE(z.buf[:10]) if flg&flagExtra != 0 { if _, err = io.ReadFull(z.r, z.buf[:2]); err != nil { return hdr, noEOF(err) } z.digest = crc32.Update(z.digest, crc32.IEEETable, z.buf[:2]) data := make([]byte, le.Uint16(z.buf[:2])) if _, err = io.ReadFull(z.r, data); err != nil { return hdr, noEOF(err) } z.digest = crc32.Update(z.digest, crc32.IEEETable, data) hdr.Extra = data } var s string if flg&flagName != 0 { if s, err = z.readString(); err != nil { return hdr, err } hdr.Name = s } if flg&flagComment != 0 { if s, err = z.readString(); err != nil { return hdr, err } hdr.Comment = s } if flg&flagHdrCrc != 0 { if _, err = io.ReadFull(z.r, z.buf[:2]); err != nil { return hdr, noEOF(err) } digest := le.Uint16(z.buf[:2]) if digest != uint16(z.digest) { return hdr, ErrHeader } } z.digest = 0 if z.decompressor == nil { z.decompressor = flate.NewReader(z.r) } else { z.decompressor.(flate.Resetter).Reset(z.r, nil) } return hdr, nil }