aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/compress
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2015-01-15 00:27:56 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2015-01-15 00:27:56 +0000
commitf8d9fa9e80b57f89e7877ce6cad8a3464879009b (patch)
tree58a1724fee16d2b03c65678c4dd9b50bb97137a9 /libgo/go/compress
parent6bd3f109d8d8fa58eeccd6b3504721b4f20c00c2 (diff)
downloadgcc-f8d9fa9e80b57f89e7877ce6cad8a3464879009b.zip
gcc-f8d9fa9e80b57f89e7877ce6cad8a3464879009b.tar.gz
gcc-f8d9fa9e80b57f89e7877ce6cad8a3464879009b.tar.bz2
libgo, compiler: Upgrade libgo to Go 1.4, except for runtime.
This upgrades all of libgo other than the runtime package to the Go 1.4 release. In Go 1.4 much of the runtime was rewritten into Go. Merging that code will take more time and will not change the API, so I'm putting it off for now. There are a few runtime changes anyhow, to accomodate other packages that rely on minor modifications to the runtime support. The compiler changes slightly to add a one-bit flag to each type descriptor kind that is stored directly in an interface, which for gccgo is currently only pointer types. Another one-bit flag (gcprog) is reserved because it is used by the gc compiler, but gccgo does not currently use it. There is another error check in the compiler since I ran across it during testing. gotools/: * Makefile.am (go_cmd_go_files): Sort entries. Add generate.go. * Makefile.in: Rebuild. From-SVN: r219627
Diffstat (limited to 'libgo/go/compress')
-rw-r--r--libgo/go/compress/bzip2/bzip2.go23
-rw-r--r--libgo/go/compress/bzip2/bzip2_test.go56
-rw-r--r--libgo/go/compress/bzip2/move_to_front.go79
-rw-r--r--libgo/go/compress/flate/fixedhuff.go2
-rw-r--r--libgo/go/compress/flate/gen.go55
-rw-r--r--libgo/go/compress/flate/inflate.go37
-rw-r--r--libgo/go/compress/flate/inflate_test.go39
-rw-r--r--libgo/go/compress/gzip/gunzip.go36
-rw-r--r--libgo/go/compress/gzip/gunzip_test.go41
-rw-r--r--libgo/go/compress/lzw/reader.go10
-rw-r--r--libgo/go/compress/zlib/reader.go81
11 files changed, 346 insertions, 113 deletions
diff --git a/libgo/go/compress/bzip2/bzip2.go b/libgo/go/compress/bzip2/bzip2.go
index 82e30c7..15575d2 100644
--- a/libgo/go/compress/bzip2/bzip2.go
+++ b/libgo/go/compress/bzip2/bzip2.go
@@ -42,6 +42,8 @@ type reader struct {
}
// NewReader returns an io.Reader which decompresses bzip2 data from r.
+// If r does not also implement io.ByteReader,
+// the decompressor may read more data than necessary from r.
func NewReader(r io.Reader) io.Reader {
bz2 := new(reader)
bz2.br = newBitReader(r)
@@ -261,6 +263,11 @@ func (bz2 *reader) readBlock() (err error) {
}
}
+ if numSymbols == 0 {
+ // There must be an EOF symbol.
+ return StructuralError("no symbols in input")
+ }
+
// A block uses between two and six different Huffman trees.
numHuffmanTrees := br.ReadBits(3)
if numHuffmanTrees < 2 || numHuffmanTrees > 6 {
@@ -307,10 +314,10 @@ func (bz2 *reader) readBlock() (err error) {
// Now we decode the arrays of code-lengths for each tree.
lengths := make([]uint8, numSymbols)
- for i := 0; i < numHuffmanTrees; i++ {
+ for i := range huffmanTrees {
// The code lengths are delta encoded from a 5-bit base value.
length := br.ReadBits(5)
- for j := 0; j < numSymbols; j++ {
+ for j := range lengths {
for {
if !br.ReadBit() {
break
@@ -333,6 +340,12 @@ func (bz2 *reader) readBlock() (err error) {
}
selectorIndex := 1 // the next tree index to use
+ if len(treeIndexes) == 0 {
+ return StructuralError("no tree selectors given")
+ }
+ if int(treeIndexes[0]) >= len(huffmanTrees) {
+ return StructuralError("tree selector out of range")
+ }
currentHuffmanTree := huffmanTrees[treeIndexes[0]]
bufIndex := 0 // indexes bz2.buf, the output buffer.
// The output of the move-to-front transform is run-length encoded and
@@ -350,6 +363,12 @@ func (bz2 *reader) readBlock() (err error) {
decoded := 0 // counts the number of symbols decoded by the current tree.
for {
if decoded == 50 {
+ if selectorIndex >= numSelectors {
+ return StructuralError("insufficient selector indices for number of symbols")
+ }
+ if int(treeIndexes[selectorIndex]) >= len(huffmanTrees) {
+ return StructuralError("tree selector out of range")
+ }
currentHuffmanTree = huffmanTrees[treeIndexes[selectorIndex]]
selectorIndex++
decoded = 0
diff --git a/libgo/go/compress/bzip2/bzip2_test.go b/libgo/go/compress/bzip2/bzip2_test.go
index 727249d..fb79d08 100644
--- a/libgo/go/compress/bzip2/bzip2_test.go
+++ b/libgo/go/compress/bzip2/bzip2_test.go
@@ -208,6 +208,52 @@ func TestBufferOverrun(t *testing.T) {
ioutil.ReadAll(decompressor)
}
+func TestOutOfRangeSelector(t *testing.T) {
+ // Tests https://code.google.com/p/go/issues/detail?id=8363.
+ buffer := bytes.NewReader(outOfRangeSelector)
+ decompressor := NewReader(buffer)
+ // This shouldn't panic.
+ ioutil.ReadAll(decompressor)
+}
+
+func TestMTF(t *testing.T) {
+ mtf := newMTFDecoderWithRange(5)
+
+ // 0 1 2 3 4
+ expect := byte(1)
+ x := mtf.Decode(1)
+ if x != expect {
+ t.Errorf("expected %v, got %v", expect, x)
+ }
+
+ // 1 0 2 3 4
+ x = mtf.Decode(0)
+ if x != expect {
+ t.Errorf("expected %v, got %v", expect, x)
+ }
+
+ // 1 0 2 3 4
+ expect = byte(0)
+ x = mtf.Decode(1)
+ if x != expect {
+ t.Errorf("expected %v, got %v", expect, x)
+ }
+
+ // 0 1 2 3 4
+ expect = byte(4)
+ x = mtf.Decode(4)
+ if x != expect {
+ t.Errorf("expected %v, got %v", expect, x)
+ }
+
+ // 4 0 1 2 3
+ expect = byte(0)
+ x = mtf.Decode(1)
+ if x != expect {
+ t.Errorf("expected %v, got %v", expect, x)
+ }
+}
+
var bufferOverrunBase64 string = `
QlpoNTFBWSZTWTzyiGcACMP/////////////////////////////////3/7f3///
////4N/fCZODak2Xo44GIHZgkGzDRbFAuwAAKoFV7T6AO6qwA6APb6s2rOoAkAAD
@@ -361,3 +407,13 @@ O0A8s/iua5oFdNZTWvbVI4FUH9sKcLiB3/fIAF+sB4n8q6L+UCfmbPcAo/crQ6b3
HqhDBMY9J0q/jdz9GNYZ/1fbXdkUqAQKFePhtzJDRBZba27+LPQNMCcrHMq06F1T
4QmLmkHt7LxB2pAczUO+T2O9bHEw/HWw+dYf2MoRDUw=
`
+
+var outOfRangeSelector = []byte{
+ 0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26,
+ 0x53, 0x59, 0x4e, 0xec, 0xe8, 0x36, 0x00, 0x00,
+ 0x02, 0x51, 0x80, 0x00, 0x10, 0x40, 0x00, 0x06,
+ 0x44, 0x90, 0x80, 0x20, 0x00, 0x31, 0x06, 0x4c,
+ 0x41, 0x01, 0xa7, 0xa9, 0xa5, 0x80, 0xbb, 0x94,
+ 0x31, 0x17, 0x72, 0x45, 0x38, 0x50, 0x90, 0x00,
+ 0x00, 0x00, 0x00,
+}
diff --git a/libgo/go/compress/bzip2/move_to_front.go b/libgo/go/compress/bzip2/move_to_front.go
index b7e75a7..526dfb3 100644
--- a/libgo/go/compress/bzip2/move_to_front.go
+++ b/libgo/go/compress/bzip2/move_to_front.go
@@ -11,88 +11,43 @@ package bzip2
// index into that list. When a symbol is referenced, it's moved to the front
// of the list. Thus, a repeated symbol ends up being encoded with many zeros,
// as the symbol will be at the front of the list after the first access.
-type moveToFrontDecoder struct {
- // Rather than actually keep the list in memory, the symbols are stored
- // as a circular, double linked list with the symbol indexed by head
- // at the front of the list.
- symbols [256]byte
- next [256]uint8
- prev [256]uint8
- head uint8
- len int
-}
+type moveToFrontDecoder []byte
// newMTFDecoder creates a move-to-front decoder with an explicit initial list
// of symbols.
-func newMTFDecoder(symbols []byte) *moveToFrontDecoder {
+func newMTFDecoder(symbols []byte) moveToFrontDecoder {
if len(symbols) > 256 {
panic("too many symbols")
}
-
- m := new(moveToFrontDecoder)
- copy(m.symbols[:], symbols)
- m.len = len(symbols)
- m.threadLinkedList()
- return m
+ return moveToFrontDecoder(symbols)
}
// newMTFDecoderWithRange creates a move-to-front decoder with an initial
// symbol list of 0...n-1.
-func newMTFDecoderWithRange(n int) *moveToFrontDecoder {
+func newMTFDecoderWithRange(n int) moveToFrontDecoder {
if n > 256 {
panic("newMTFDecoderWithRange: cannot have > 256 symbols")
}
- m := new(moveToFrontDecoder)
+ m := make([]byte, n)
for i := 0; i < n; i++ {
- m.symbols[byte(i)] = byte(i)
- }
- m.len = n
- m.threadLinkedList()
- return m
-}
-
-// threadLinkedList creates the initial linked-list pointers.
-func (m *moveToFrontDecoder) threadLinkedList() {
- if m.len == 0 {
- return
- }
-
- m.prev[0] = uint8(m.len - 1)
-
- for i := byte(0); int(i) < m.len-1; i++ {
- m.next[i] = uint8(i + 1)
- m.prev[i+1] = uint8(i)
+ m[i] = byte(i)
}
-
- m.next[m.len-1] = 0
+ return moveToFrontDecoder(m)
}
-func (m *moveToFrontDecoder) Decode(n int) (b byte) {
- // Most of the time, n will be zero so it's worth dealing with this
- // simple case.
- if n == 0 {
- return m.symbols[m.head]
- }
-
- i := m.head
- for j := 0; j < n; j++ {
- i = m.next[i]
- }
- b = m.symbols[i]
-
- m.next[m.prev[i]] = m.next[i]
- m.prev[m.next[i]] = m.prev[i]
- m.next[i] = m.head
- m.prev[i] = m.prev[m.head]
- m.next[m.prev[m.head]] = i
- m.prev[m.head] = i
- m.head = i
-
+func (m moveToFrontDecoder) Decode(n int) (b byte) {
+ // Implement move-to-front with a simple copy. This approach
+ // beats more sophisticated approaches in benchmarking, probably
+ // because it has high locality of reference inside of a
+ // single cache line (most move-to-front operations have n < 64).
+ b = m[n]
+ copy(m[1:], m[:n])
+ m[0] = b
return
}
// First returns the symbol at the front of the list.
-func (m *moveToFrontDecoder) First() byte {
- return m.symbols[m.head]
+func (m moveToFrontDecoder) First() byte {
+ return m[0]
}
diff --git a/libgo/go/compress/flate/fixedhuff.go b/libgo/go/compress/flate/fixedhuff.go
index 9be3d53..7df8b9a 100644
--- a/libgo/go/compress/flate/fixedhuff.go
+++ b/libgo/go/compress/flate/fixedhuff.go
@@ -4,7 +4,7 @@
package flate
-// autogenerated by gen.go, DO NOT EDIT
+// autogenerated by go run gen.go -output fixedhuff.go, DO NOT EDIT
var fixedHuffmanDecoder = huffmanDecoder{
7,
diff --git a/libgo/go/compress/flate/gen.go b/libgo/go/compress/flate/gen.go
index 1427557..6288ecd 100644
--- a/libgo/go/compress/flate/gen.go
+++ b/libgo/go/compress/flate/gen.go
@@ -7,14 +7,21 @@
// This program generates fixedhuff.go
// Invoke as
//
-// go run gen.go |gofmt >fixedhuff.go
+// go run gen.go -output fixedhuff.go
package main
import (
+ "bytes"
+ "flag"
"fmt"
+ "go/format"
+ "io/ioutil"
+ "log"
)
+var filename = flag.String("output", "fixedhuff.go", "output file name")
+
const maxCodeLen = 16
// Note: the definition of the huffmanDecoder struct is copied from
@@ -113,6 +120,8 @@ func (h *huffmanDecoder) init(bits []int) bool {
}
func main() {
+ flag.Parse()
+
var h huffmanDecoder
var bits [288]int
initReverseByte()
@@ -129,27 +138,43 @@ func main() {
bits[i] = 8
}
h.init(bits[:])
- fmt.Println("package flate")
- fmt.Println()
- fmt.Println("// autogenerated by gen.go, DO NOT EDIT")
- fmt.Println()
- fmt.Println("var fixedHuffmanDecoder = huffmanDecoder{")
- fmt.Printf("\t%d,\n", h.min)
- fmt.Println("\t[huffmanNumChunks]uint32{")
+
+ var buf bytes.Buffer
+
+ fmt.Fprintf(&buf, `// Copyright 2013 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.`+"\n\n")
+
+ fmt.Fprintln(&buf, "package flate")
+ fmt.Fprintln(&buf)
+ fmt.Fprintln(&buf, "// autogenerated by go run gen.go -output fixedhuff.go, DO NOT EDIT")
+ fmt.Fprintln(&buf)
+ fmt.Fprintln(&buf, "var fixedHuffmanDecoder = huffmanDecoder{")
+ fmt.Fprintf(&buf, "\t%d,\n", h.min)
+ fmt.Fprintln(&buf, "\t[huffmanNumChunks]uint32{")
for i := 0; i < huffmanNumChunks; i++ {
if i&7 == 0 {
- fmt.Printf("\t\t")
+ fmt.Fprintf(&buf, "\t\t")
} else {
- fmt.Printf(" ")
+ fmt.Fprintf(&buf, " ")
}
- fmt.Printf("0x%04x,", h.chunks[i])
+ fmt.Fprintf(&buf, "0x%04x,", h.chunks[i])
if i&7 == 7 {
- fmt.Println()
+ fmt.Fprintln(&buf)
}
}
- fmt.Println("\t},")
- fmt.Println("\tnil, 0,")
- fmt.Println("}")
+ fmt.Fprintln(&buf, "\t},")
+ fmt.Fprintln(&buf, "\tnil, 0,")
+ fmt.Fprintln(&buf, "}")
+
+ data, err := format.Source(buf.Bytes())
+ if err != nil {
+ log.Fatal(err)
+ }
+ err = ioutil.WriteFile(*filename, data, 0644)
+ if err != nil {
+ log.Fatal(err)
+ }
}
var reverseByte [256]byte
diff --git a/libgo/go/compress/flate/inflate.go b/libgo/go/compress/flate/inflate.go
index ce4923e..76519bb 100644
--- a/libgo/go/compress/flate/inflate.go
+++ b/libgo/go/compress/flate/inflate.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:generate go run gen.go -output fixedhuff.go
+
// Package flate implements the DEFLATE compressed data format, described in
// RFC 1951. The gzip and zlib packages implement access to DEFLATE-based file
// formats.
@@ -54,6 +56,15 @@ func (e *WriteError) Error() string {
return "flate: write error at offset " + strconv.FormatInt(e.Offset, 10) + ": " + e.Err.Error()
}
+// Resetter resets a ReadCloser returned by NewReader or NewReaderDict to
+// to switch to a new underlying Reader. This permits reusing a ReadCloser
+// instead of allocating a new one.
+type Resetter interface {
+ // Reset discards any buffered data and resets the Resetter as if it was
+ // newly initialized with the given reader.
+ Reset(r io.Reader, dict []byte) error
+}
+
// Note that much of the implementation of huffmanDecoder is also copied
// into gen.go (in package main) for the purpose of precomputing the
// fixed huffman tables so they can be included statically.
@@ -677,10 +688,28 @@ func makeReader(r io.Reader) Reader {
return bufio.NewReader(r)
}
+func (f *decompressor) Reset(r io.Reader, dict []byte) error {
+ *f = decompressor{
+ r: makeReader(r),
+ bits: f.bits,
+ codebits: f.codebits,
+ hist: f.hist,
+ step: (*decompressor).nextBlock,
+ }
+ if dict != nil {
+ f.setDict(dict)
+ }
+ return nil
+}
+
// NewReader returns a new ReadCloser that can be used
-// to read the uncompressed version of r. It is the caller's
-// responsibility to call Close on the ReadCloser when
-// finished reading.
+// to read the uncompressed version of r.
+// If r does not also 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 finished reading.
+//
+// The ReadCloser returned by NewReader also implements Resetter.
func NewReader(r io.Reader) io.ReadCloser {
var f decompressor
f.bits = new([maxLit + maxDist]int)
@@ -696,6 +725,8 @@ func NewReader(r io.Reader) io.ReadCloser {
// the uncompressed data stream started with the given dictionary,
// which has already been read. NewReaderDict is typically used
// to read data compressed by NewWriterDict.
+//
+// The ReadCloser returned by NewReader also implements Resetter.
func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser {
var f decompressor
f.r = makeReader(r)
diff --git a/libgo/go/compress/flate/inflate_test.go b/libgo/go/compress/flate/inflate_test.go
new file mode 100644
index 0000000..9f25d30
--- /dev/null
+++ b/libgo/go/compress/flate/inflate_test.go
@@ -0,0 +1,39 @@
+// Copyright 2014 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 (
+ "bytes"
+ "io"
+ "testing"
+)
+
+func TestReset(t *testing.T) {
+ ss := []string{
+ "lorem ipsum izzle fo rizzle",
+ "the quick brown fox jumped over",
+ }
+
+ deflated := make([]bytes.Buffer, 2)
+ for i, s := range ss {
+ w, _ := NewWriter(&deflated[i], 1)
+ w.Write([]byte(s))
+ w.Close()
+ }
+
+ inflated := make([]bytes.Buffer, 2)
+
+ f := NewReader(&deflated[0])
+ io.Copy(&inflated[0], f)
+ f.(Resetter).Reset(&deflated[1], nil)
+ io.Copy(&inflated[1], f)
+ f.Close()
+
+ for i, s := range ss {
+ if s != inflated[i].String() {
+ t.Errorf("inflated[%d]:\ngot %q\nwant %q", i, inflated[i], s)
+ }
+ }
+}
diff --git a/libgo/go/compress/gzip/gunzip.go b/libgo/go/compress/gzip/gunzip.go
index 4f398b1..72ee55c 100644
--- a/libgo/go/compress/gzip/gunzip.go
+++ b/libgo/go/compress/gzip/gunzip.go
@@ -74,14 +74,17 @@ type Reader struct {
flg byte
buf [512]byte
err error
+ multistream bool
}
// NewReader creates a new Reader reading the given reader.
-// The implementation buffers input and may read more data than necessary from r.
+// If r does not also implement io.ByteReader,
+// the decompressor may read more data than necessary from r.
// It is the caller's responsibility to call Close on the Reader when done.
func NewReader(r io.Reader) (*Reader, error) {
z := new(Reader)
z.r = makeReader(r)
+ z.multistream = true
z.digest = crc32.NewIEEE()
if err := z.readHeader(true); err != nil {
return nil, err
@@ -101,9 +104,30 @@ func (z *Reader) Reset(r io.Reader) error {
}
z.size = 0
z.err = nil
+ z.multistream = true
return z.readHeader(true)
}
+// Multistream controls whether the reader supports multistream files.
+//
+// If enabled (the default), the Reader expects the input to be a sequence
+// of individually gzipped data streams, each with its own header and
+// trailer, ending at EOF. The effect is that the concatenation of a sequence
+// of gzipped files is treated as equivalent to the gzip of the concatenation
+// of the sequence. This is standard behavior for gzip readers.
+//
+// Calling Multistream(false) disables this behavior; disabling the behavior
+// can be useful when reading file formats that distinguish individual gzip
+// data streams or mix gzip data streams with other data streams.
+// In this mode, when the Reader reaches the end of the data stream,
+// Read returns io.EOF. If the underlying reader implements io.ByteReader,
+// it will be left positioned just after the gzip stream.
+// To start the next stream, call z.Reset(r) followed by z.Multistream(false).
+// If there is no next stream, z.Reset(r) will return io.EOF.
+func (z *Reader) Multistream(ok bool) {
+ z.multistream = ok
+}
+
// GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950).
func get4(p []byte) uint32 {
return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24
@@ -207,7 +231,11 @@ func (z *Reader) readHeader(save bool) error {
}
z.digest.Reset()
- z.decompressor = flate.NewReader(z.r)
+ if z.decompressor == nil {
+ z.decompressor = flate.NewReader(z.r)
+ } else {
+ z.decompressor.(flate.Resetter).Reset(z.r, nil)
+ }
return nil
}
@@ -240,6 +268,10 @@ func (z *Reader) Read(p []byte) (n int, err error) {
}
// File is ok; is there another?
+ if !z.multistream {
+ return 0, io.EOF
+ }
+
if err = z.readHeader(false); err != nil {
z.err = err
return
diff --git a/libgo/go/compress/gzip/gunzip_test.go b/libgo/go/compress/gzip/gunzip_test.go
index 2471038..0636dec 100644
--- a/libgo/go/compress/gzip/gunzip_test.go
+++ b/libgo/go/compress/gzip/gunzip_test.go
@@ -9,6 +9,7 @@ import (
"io"
"io/ioutil"
"os"
+ "strings"
"testing"
"time"
)
@@ -367,3 +368,43 @@ func TestInitialReset(t *testing.T) {
t.Errorf("got %q want %q", s, gunzipTests[1].raw)
}
}
+
+func TestMultistreamFalse(t *testing.T) {
+ // Find concatenation test.
+ var tt gunzipTest
+ for _, tt = range gunzipTests {
+ if strings.HasSuffix(tt.desc, " x2") {
+ goto Found
+ }
+ }
+ t.Fatal("cannot find hello.txt x2 in gunzip tests")
+
+Found:
+ br := bytes.NewReader(tt.gzip)
+ var r Reader
+ if err := r.Reset(br); err != nil {
+ t.Fatalf("first reset: %v", err)
+ }
+
+ // Expect two streams with "hello world\n", then real EOF.
+ const hello = "hello world\n"
+
+ r.Multistream(false)
+ data, err := ioutil.ReadAll(&r)
+ if string(data) != hello || err != nil {
+ t.Fatalf("first stream = %q, %v, want %q, %v", string(data), err, hello, nil)
+ }
+
+ if err := r.Reset(br); err != nil {
+ t.Fatalf("second reset: %v", err)
+ }
+ r.Multistream(false)
+ data, err = ioutil.ReadAll(&r)
+ if string(data) != hello || err != nil {
+ t.Fatalf("second stream = %q, %v, want %q, %v", string(data), err, hello, nil)
+ }
+
+ if err := r.Reset(br); err != io.EOF {
+ t.Fatalf("third reset: err=%v, want io.EOF", err)
+ }
+}
diff --git a/libgo/go/compress/lzw/reader.go b/libgo/go/compress/lzw/reader.go
index ef59699..526620c 100644
--- a/libgo/go/compress/lzw/reader.go
+++ b/libgo/go/compress/lzw/reader.go
@@ -6,12 +6,16 @@
// described in T. A. Welch, ``A Technique for High-Performance Data
// Compression'', Computer, 17(6) (June 1984), pp 8-19.
//
-// In particular, it implements LZW as used by the GIF, TIFF and PDF file
+// In particular, it implements LZW as used by the GIF and PDF file
// formats, which means variable-width codes up to 12 bits and the first
// two non-literal codes are a clear code and an EOF code.
+//
+// The TIFF file format uses a similar but incompatible version of the LZW
+// algorithm. See the golang.org/x/image/tiff/lzw package for an
+// implementation.
package lzw
-// TODO(nigeltao): check that TIFF and PDF use LZW in the same way as GIF,
+// TODO(nigeltao): check that PDF uses LZW in the same way as GIF,
// modulo LSB/MSB packing order.
import (
@@ -218,6 +222,8 @@ func (d *decoder) Close() error {
// NewReader creates a new io.ReadCloser.
// Reads from the returned io.ReadCloser read and decompress data from r.
+// If r does not also 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
// finished reading.
// The number of bits to use for literal codes, litWidth, must be in the
diff --git a/libgo/go/compress/zlib/reader.go b/libgo/go/compress/zlib/reader.go
index 9e1aafd..816f1bf 100644
--- a/libgo/go/compress/zlib/reader.go
+++ b/libgo/go/compress/zlib/reader.go
@@ -51,45 +51,36 @@ type reader struct {
scratch [4]byte
}
-// NewReader creates a new io.ReadCloser.
-// Reads from the returned io.ReadCloser read and decompress data from r.
+// Resetter resets a ReadCloser returned by NewReader or NewReaderDict to
+// to switch to a new underlying Reader. This permits reusing a ReadCloser
+// instead of allocating a new one.
+type Resetter interface {
+ // Reset discards any buffered data and resets the Resetter as if it was
+ // newly initialized with the given reader.
+ Reset(r io.Reader, dict []byte) error
+}
+
+// NewReader creates a new ReadCloser.
+// Reads from the returned ReadCloser read and decompress data from r.
// The implementation buffers input and 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.
func NewReader(r io.Reader) (io.ReadCloser, error) {
return NewReaderDict(r, nil)
}
// 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.
+//
+// The ReadCloser returned by NewReaderDict also implements Resetter.
func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, error) {
z := new(reader)
- if fr, ok := r.(flate.Reader); ok {
- z.r = fr
- } else {
- z.r = bufio.NewReader(r)
- }
- _, err := io.ReadFull(z.r, z.scratch[0:2])
+ err := z.Reset(r, dict)
if err != nil {
return nil, err
}
- h := uint(z.scratch[0])<<8 | uint(z.scratch[1])
- if (z.scratch[0]&0x0f != zlibDeflate) || (h%31 != 0) {
- return nil, ErrHeader
- }
- if z.scratch[1]&0x20 != 0 {
- _, err = io.ReadFull(z.r, z.scratch[0:4])
- if err != nil {
- return nil, err
- }
- checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3])
- if checksum != adler32.Checksum(dict) {
- return nil, ErrDictionary
- }
- z.decompressor = flate.NewReaderDict(z.r, dict)
- } else {
- z.decompressor = flate.NewReader(z.r)
- }
- z.digest = adler32.New()
return z, nil
}
@@ -130,3 +121,41 @@ func (z *reader) Close() error {
z.err = z.decompressor.Close()
return z.err
}
+
+func (z *reader) Reset(r io.Reader, dict []byte) error {
+ if fr, ok := r.(flate.Reader); ok {
+ z.r = fr
+ } else {
+ z.r = bufio.NewReader(r)
+ }
+ _, err := io.ReadFull(z.r, z.scratch[0:2])
+ if err != nil {
+ return err
+ }
+ h := uint(z.scratch[0])<<8 | uint(z.scratch[1])
+ if (z.scratch[0]&0x0f != zlibDeflate) || (h%31 != 0) {
+ return ErrHeader
+ }
+ haveDict := z.scratch[1]&0x20 != 0
+ if haveDict {
+ _, err = io.ReadFull(z.r, z.scratch[0:4])
+ if err != nil {
+ return err
+ }
+ checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3])
+ if checksum != adler32.Checksum(dict) {
+ return ErrDictionary
+ }
+ }
+ if z.decompressor == nil {
+ if haveDict {
+ z.decompressor = flate.NewReaderDict(z.r, dict)
+ } else {
+ z.decompressor = flate.NewReader(z.r)
+ }
+ } else {
+ z.decompressor.(flate.Resetter).Reset(z.r, dict)
+ }
+ z.digest = adler32.New()
+ return nil
+}