Skip to content

Commit

Permalink
compress/flate: fix another deflate Reset inconsistency
Browse files Browse the repository at this point in the history
While investigating #34121, fixed by CL 193605,
I discovered another case where Reset was not quite
resetting enough.

This specific case is not a problem in Reset itself but
rather that the Huffman bit writer in one code path
is using uninitialized memory left over from a previous
block, making the compression not choose the optimal
compression method.

Fixes #34121.

Change-Id: I29245b28214d924e382f91e2c56b4b8a9b7da13d
Reviewed-on: https://go-review.googlesource.com/c/go/+/243140
Run-TryBot: Russ Cox <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Joe Tsai <[email protected]>
  • Loading branch information
rsc committed Jul 16, 2020
1 parent 8d43307 commit 4469f54
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 16 deletions.
56 changes: 40 additions & 16 deletions src/compress/flate/deflate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,33 +512,57 @@ func TestWriterReset(t *testing.T) {
t.Errorf("level %d Writer not reset after Reset", level)
}
}
testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, NoCompression) })
testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, DefaultCompression) })
testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, BestCompression) })
dict := []byte("we are the world")
testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, NoCompression, dict) })
testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, DefaultCompression, dict) })
testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, BestCompression, dict) })

levels := []int{0, 1, 2, 5, 9}
for _, level := range levels {
t.Run(fmt.Sprint(level), func(t *testing.T) {
testResetOutput(t, level, nil)
})
}

t.Run("dict", func(t *testing.T) {
for _, level := range levels {
t.Run(fmt.Sprint(level), func(t *testing.T) {
testResetOutput(t, level, nil)
})
}
})
}

func testResetOutput(t *testing.T, newWriter func(w io.Writer) (*Writer, error)) {
func testResetOutput(t *testing.T, level int, dict []byte) {
writeData := func(w *Writer) {
msg := []byte("now is the time for all good gophers")
w.Write(msg)
w.Flush()

hello := []byte("hello world")
for i := 0; i < 1024; i++ {
w.Write(hello)
}

fill := bytes.Repeat([]byte("x"), 65000)
w.Write(fill)
}

buf := new(bytes.Buffer)
w, err := newWriter(buf)
var w *Writer
var err error
if dict == nil {
w, err = NewWriter(buf, level)
} else {
w, err = NewWriterDict(buf, level, dict)
}
if err != nil {
t.Fatalf("NewWriter: %v", err)
}
b := []byte("hello world")
for i := 0; i < 1024; i++ {
w.Write(b)
}

writeData(w)
w.Close()
out1 := buf.Bytes()

buf2 := new(bytes.Buffer)
w.Reset(buf2)
for i := 0; i < 1024; i++ {
w.Write(b)
}
writeData(w)
w.Close()
out2 := buf2.Bytes()

Expand Down
1 change: 1 addition & 0 deletions src/compress/flate/huffman_bit_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) {
w.literalFreq[endBlockMarker] = 1

const numLiterals = endBlockMarker + 1
w.offsetFreq[0] = 1
const numOffsets = 1

w.literalEncoding.generate(w.literalFreq, 15)
Expand Down

0 comments on commit 4469f54

Please sign in to comment.