Skip to content

Commit

Permalink
compress/lzw: fix hi code overflow.
Browse files Browse the repository at this point in the history
Change-Id: I2d3c3c715d857305944cd96c45554a16cb7967e9
Reviewed-on: https://go-review.googlesource.com/42032
Reviewed-by: David Symonds <[email protected]>
  • Loading branch information
nigeltao committed Apr 28, 2017
1 parent 4fcceca commit 642a1cc
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 1 deletion.
12 changes: 11 additions & 1 deletion src/compress/lzw/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,14 @@ type decoder struct {
// The next two codes mean clear and EOF.
// Other valid codes are in the range [lo, hi] where lo := clear + 2,
// with the upper bound incrementing on each code seen.
// overflow is the code at which hi overflows the code width.
//
// overflow is the code at which hi overflows the code width. It always
// equals 1 << width.
//
// last is the most recently seen code, or decoderInvalidCode.
//
// An invariant is that
// (hi < overflow) || (hi == overflow && last == decoderInvalidCode)
clear, eof, hi, overflow, last uint16

// Each code c in [lo, hi] expands to two or more bytes. For c != hi:
Expand Down Expand Up @@ -196,6 +202,10 @@ loop:
if d.hi >= d.overflow {
if d.width == maxWidth {
d.last = decoderInvalidCode
// Undo the d.hi++ a few lines above, so that (1) we maintain
// the invariant that d.hi <= d.overflow, and (2) d.hi does not
// eventually overflow a uint16.
d.hi--
} else {
d.width++
d.overflow <<= 1
Expand Down
26 changes: 26 additions & 0 deletions src/compress/lzw/reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,32 @@ func TestReader(t *testing.T) {
}
}

type devZero struct{}

func (devZero) Read(p []byte) (int, error) {
for i := range p {
p[i] = 0
}
return len(p), nil
}

func TestHiCodeDoesNotOverflow(t *testing.T) {
r := NewReader(devZero{}, LSB, 8)
d := r.(*decoder)
buf := make([]byte, 1024)
oldHi := uint16(0)
for i := 0; i < 100; i++ {
if _, err := io.ReadFull(r, buf); err != nil {
t.Fatalf("i=%d: %v", i, err)
}
// The hi code should never decrease.
if d.hi < oldHi {
t.Fatalf("i=%d: hi=%d decreased from previous value %d", i, d.hi, oldHi)
}
oldHi = d.hi
}
}

func BenchmarkDecoder(b *testing.B) {
buf, err := ioutil.ReadFile("../testdata/e.txt")
if err != nil {
Expand Down

0 comments on commit 642a1cc

Please sign in to comment.