diff --git a/src/pkg/compress/flate/copy.go b/src/pkg/compress/flate/copy.go new file mode 100644 index 0000000000..06e5d2e66d --- /dev/null +++ b/src/pkg/compress/flate/copy.go @@ -0,0 +1,17 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +// forwardCopy is like the built-in copy function except that it always goes +// forward from the start, even if the dst and src overlap. +func forwardCopy(dst, src []byte) int { + if len(src) > len(dst) { + src = src[:len(dst)] + } + for i, x := range src { + dst[i] = x + } + return len(src) +} diff --git a/src/pkg/compress/flate/copy_test.go b/src/pkg/compress/flate/copy_test.go new file mode 100644 index 0000000000..d13941cf1c --- /dev/null +++ b/src/pkg/compress/flate/copy_test.go @@ -0,0 +1,42 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +import ( + "testing" +) + +func TestForwardCopy(t *testing.T) { + testCases := []struct { + dst0, dst1 int + src0, src1 int + want string + }{ + {0, 9, 0, 9, "012345678"}, + {0, 5, 4, 9, "45678"}, + {4, 9, 0, 5, "01230"}, + {1, 6, 3, 8, "34567"}, + {3, 8, 1, 6, "12121"}, + {0, 9, 3, 6, "345"}, + {3, 6, 0, 9, "012"}, + {1, 6, 0, 9, "00000"}, + {0, 4, 7, 8, "7"}, + {0, 1, 6, 8, "6"}, + {4, 4, 6, 9, ""}, + {2, 8, 6, 6, ""}, + {0, 0, 0, 0, ""}, + } + for _, tc := range testCases { + b := []byte("012345678") + dst := b[tc.dst0:tc.dst1] + src := b[tc.src0:tc.src1] + n := forwardCopy(dst, src) + got := string(dst[:n]) + if got != tc.want { + t.Errorf("dst=b[%d:%d], src=b[%d:%d]: got %q, want %q", + tc.dst0, tc.dst1, tc.src0, tc.src1, got, tc.want) + } + } +} diff --git a/src/pkg/compress/flate/inflate.go b/src/pkg/compress/flate/inflate.go index 3f2042bfe9..a4be91b6f7 100644 --- a/src/pkg/compress/flate/inflate.go +++ b/src/pkg/compress/flate/inflate.go @@ -505,51 +505,49 @@ func (f *decompressor) huffmanBlock() { return } - p := f.hp - dist - if p < 0 { - p += len(f.hist) - } - for i := 0; i < length; i++ { - f.hist[f.hp] = f.hist[p] - f.hp++ - p++ - if f.hp == len(f.hist) { - // After flush continue copying out of history. - f.copyLen = length - (i + 1) - f.copyDist = dist - f.flush((*decompressor).copyHuff) - return - } - if p == len(f.hist) { - p = 0 - } + f.copyLen, f.copyDist = length, dist + if f.copyHist() { + return } } panic("unreached") } -func (f *decompressor) copyHuff() { - length := f.copyLen - dist := f.copyDist - p := f.hp - dist +// copyHist copies f.copyLen bytes from f.hist (f.copyDist bytes ago) to itself. +// It reports whether the f.hist buffer is full. +func (f *decompressor) copyHist() bool { + p := f.hp - f.copyDist if p < 0 { p += len(f.hist) } - for i := 0; i < length; i++ { - f.hist[f.hp] = f.hist[p] - f.hp++ - p++ + for f.copyLen > 0 { + n := f.copyLen + if x := len(f.hist) - f.hp; n > x { + n = x + } + if x := len(f.hist) - p; n > x { + n = x + } + forwardCopy(f.hist[f.hp:f.hp+n], f.hist[p:p+n]) + p += n + f.hp += n + f.copyLen -= n if f.hp == len(f.hist) { - f.copyLen = length - (i + 1) + // After flush continue copying out of history. f.flush((*decompressor).copyHuff) - return + return true } if p == len(f.hist) { p = 0 } } + return false +} - // Continue processing Huffman block. +func (f *decompressor) copyHuff() { + if f.copyHist() { + return + } f.huffmanBlock() } @@ -584,9 +582,9 @@ func (f *decompressor) dataBlock() { f.copyData() } +// copyData copies f.copyLen bytes from the underlying reader into f.hist. +// It pauses for reads when f.hist is full. func (f *decompressor) copyData() { - // Read f.dataLen bytes into history, - // pausing for reads as history fills. n := f.copyLen for n > 0 { m := len(f.hist) - f.hp