Skip to content

Commit

Permalink
Upgrade zlib to upstream (#971)
Browse files Browse the repository at this point in the history
Mostly cosmetic changes.
  • Loading branch information
klauspost authored Jun 12, 2024
1 parent 0396178 commit 7ae2138
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 30 deletions.
32 changes: 18 additions & 14 deletions zlib/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,18 @@ package zlib
import (
"bufio"
"compress/zlib"
"encoding/binary"
"hash"
"hash/adler32"
"io"

"github.com/klauspost/compress/flate"
)

const zlibDeflate = 8
const (
zlibDeflate = 8
zlibMaxWindow = 7
)

var (
// ErrChecksum is returned when reading ZLIB data that has an invalid checksum.
Expand All @@ -52,7 +56,7 @@ type reader struct {
scratch [4]byte
}

// Resetter resets a ReadCloser returned by NewReader or NewReaderDict to
// Resetter resets a ReadCloser returned by [NewReader] or [NewReaderDict]
// to switch to a new underlying Reader. This permits reusing a ReadCloser
// instead of allocating a new one.
type Resetter interface {
Expand All @@ -63,20 +67,20 @@ type Resetter interface {

// NewReader creates a new ReadCloser.
// Reads from the returned ReadCloser read and decompress data from r.
// If r does not implement io.ByteReader, the decompressor may read more
// If r does not implement [io.ByteReader], the decompressor may read more
// data than necessary from r.
// It is the caller's responsibility to call Close on the ReadCloser when done.
//
// The ReadCloser returned by NewReader also implements Resetter.
// The [io.ReadCloser] returned by NewReader also implements [Resetter].
func NewReader(r io.Reader) (io.ReadCloser, error) {
return NewReaderDict(r, nil)
}

// NewReaderDict is like NewReader but uses a preset dictionary.
// NewReaderDict is like [NewReader] but uses a preset dictionary.
// NewReaderDict ignores the dictionary if the compressed data does not refer to it.
// If the compressed data refers to a different dictionary, NewReaderDict returns ErrDictionary.
// If the compressed data refers to a different dictionary, NewReaderDict returns [ErrDictionary].
//
// The ReadCloser returned by NewReaderDict also implements Resetter.
// The ReadCloser returned by NewReaderDict also implements [Resetter].
func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, error) {
z := new(reader)
err := z.Reset(r, dict)
Expand Down Expand Up @@ -108,17 +112,17 @@ func (z *reader) Read(p []byte) (int, error) {
return n, z.err
}
// ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3])
checksum := binary.BigEndian.Uint32(z.scratch[:4])
if checksum != z.digest.Sum32() {
z.err = ErrChecksum
return n, z.err
}
return n, io.EOF
}

// Calling Close does not close the wrapped io.Reader originally passed to NewReader.
// Calling Close does not close the wrapped [io.Reader] originally passed to [NewReader].
// In order for the ZLIB checksum to be verified, the reader must be
// fully consumed until the io.EOF.
// fully consumed until the [io.EOF].
func (z *reader) Close() error {
if z.err != nil && z.err != io.EOF {
return z.err
Expand All @@ -128,7 +132,7 @@ func (z *reader) Close() error {
}

func (z *reader) Reset(r io.Reader, dict []byte) error {
*z = reader{decompressor: z.decompressor, digest: z.digest}
*z = reader{decompressor: z.decompressor}
if fr, ok := r.(flate.Reader); ok {
z.r = fr
} else {
Expand All @@ -143,8 +147,8 @@ func (z *reader) Reset(r io.Reader, dict []byte) error {
}
return z.err
}
h := uint(z.scratch[0])<<8 | uint(z.scratch[1])
if (z.scratch[0]&0x0f != zlibDeflate) || (h%31 != 0) {
h := binary.BigEndian.Uint16(z.scratch[:2])
if (z.scratch[0]&0x0f != zlibDeflate) || (z.scratch[0]>>4 > zlibMaxWindow) || (h%31 != 0) {
z.err = ErrHeader
return z.err
}
Expand All @@ -157,7 +161,7 @@ func (z *reader) Reset(r io.Reader, dict []byte) error {
}
return z.err
}
checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3])
checksum := binary.BigEndian.Uint32(z.scratch[:4])
if checksum != adler32.Checksum(dict) {
z.err = ErrDictionary
return z.err
Expand Down
11 changes: 9 additions & 2 deletions zlib/reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type zlibTest struct {
}

// Compare-to-golden test data was generated by the ZLIB example program at
// http://www.zlib.net/zpipe.c
// https://www.zlib.net/zpipe.c

var zlibTests = []zlibTest{
{
Expand Down Expand Up @@ -65,7 +65,14 @@ var zlibTests = []zlibTest{
nil,
},
{
"bad header",
"bad header (CINFO)",
"",
[]byte{0x88, 0x98, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01},
nil,
ErrHeader,
},
{
"bad header (FCHECK)",
"",
[]byte{0x78, 0x9f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01},
nil,
Expand Down
18 changes: 6 additions & 12 deletions zlib/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package zlib

import (
"encoding/binary"
"fmt"
"hash"
"hash/adler32"
Expand All @@ -20,7 +21,7 @@ const (
BestSpeed = flate.BestSpeed
BestCompression = flate.BestCompression
DefaultCompression = flate.DefaultCompression
ConstantCompression = flate.ConstantCompression
ConstantCompression = flate.ConstantCompression // Deprecated: Use HuffmanOnly.
HuffmanOnly = flate.HuffmanOnly
)

Expand All @@ -40,7 +41,7 @@ type Writer struct {
// NewWriter creates a new Writer.
// Writes to the returned Writer are compressed and written to w.
//
// It is the caller's responsibility to call Close on the WriteCloser when done.
// It is the caller's responsibility to call Close on the Writer when done.
// Writes may be buffered and not flushed until Close.
func NewWriter(w io.Writer) *Writer {
z, _ := NewWriterLevelDict(w, DefaultCompression, nil)
Expand Down Expand Up @@ -116,17 +117,13 @@ func (z *Writer) writeHeader() (err error) {
if z.dict != nil {
z.scratch[1] |= 1 << 5
}
z.scratch[1] += uint8(31 - (uint16(z.scratch[0])<<8+uint16(z.scratch[1]))%31)
z.scratch[1] += uint8(31 - binary.BigEndian.Uint16(z.scratch[:2])%31)
if _, err = z.w.Write(z.scratch[0:2]); err != nil {
return err
}
if z.dict != nil {
// The next four bytes are the Adler-32 checksum of the dictionary.
checksum := adler32.Checksum(z.dict)
z.scratch[0] = uint8(checksum >> 24)
z.scratch[1] = uint8(checksum >> 16)
z.scratch[2] = uint8(checksum >> 8)
z.scratch[3] = uint8(checksum >> 0)
binary.BigEndian.PutUint32(z.scratch[:], adler32.Checksum(z.dict))
if _, err = z.w.Write(z.scratch[0:4]); err != nil {
return err
}
Expand Down Expand Up @@ -192,10 +189,7 @@ func (z *Writer) Close() error {
}
checksum := z.digest.Sum32()
// ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
z.scratch[0] = uint8(checksum >> 24)
z.scratch[1] = uint8(checksum >> 16)
z.scratch[2] = uint8(checksum >> 8)
z.scratch[3] = uint8(checksum >> 0)
binary.BigEndian.PutUint32(z.scratch[:], checksum)
_, z.err = z.w.Write(z.scratch[0:4])
return z.err
}
16 changes: 14 additions & 2 deletions zlib/writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,24 +153,36 @@ func TestWriter(t *testing.T) {
}

func TestWriterBig(t *testing.T) {
for _, fn := range filenames {
for i, fn := range filenames {
testFileLevelDict(t, fn, DefaultCompression, "")
testFileLevelDict(t, fn, NoCompression, "")
testFileLevelDict(t, fn, HuffmanOnly, "")
for level := BestSpeed; level <= BestCompression; level++ {
testFileLevelDict(t, fn, level, "")
if level >= 1 && testing.Short() {
break
}
}
if i == 0 && testing.Short() {
break
}
}
}

func TestWriterDict(t *testing.T) {
const dictionary = "0123456789."
for _, fn := range filenames {
for i, fn := range filenames {
testFileLevelDict(t, fn, DefaultCompression, dictionary)
testFileLevelDict(t, fn, NoCompression, dictionary)
testFileLevelDict(t, fn, HuffmanOnly, dictionary)
for level := BestSpeed; level <= BestCompression; level++ {
testFileLevelDict(t, fn, level, dictionary)
if level >= 1 && testing.Short() {
break
}
}
if i == 0 && testing.Short() {
break
}
}
}
Expand Down

0 comments on commit 7ae2138

Please sign in to comment.