summaryrefslogtreecommitdiff
path: root/libgo/go/compress/flate
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/compress/flate')
-rw-r--r--libgo/go/compress/flate/deflate.go56
-rw-r--r--libgo/go/compress/flate/deflate_test.go46
-rw-r--r--libgo/go/compress/flate/inflate.go33
3 files changed, 127 insertions, 8 deletions
diff --git a/libgo/go/compress/flate/deflate.go b/libgo/go/compress/flate/deflate.go
index 591b35c4463..a02a5e8d94b 100644
--- a/libgo/go/compress/flate/deflate.go
+++ b/libgo/go/compress/flate/deflate.go
@@ -143,10 +143,18 @@ func (d *compressor) fillWindow(index int) (int, os.Error) {
d.blockStart = math.MaxInt32
}
for i, h := range d.hashHead {
- d.hashHead[i] = max(h-wSize, -1)
+ v := h - wSize
+ if v < -1 {
+ v = -1
+ }
+ d.hashHead[i] = v
}
for i, h := range d.hashPrev {
- d.hashPrev[i] = max(h-wSize, -1)
+ v := -h - wSize
+ if v < -1 {
+ v = -1
+ }
+ d.hashPrev[i] = v
}
}
count, err := d.r.Read(d.window[d.windowEnd:])
@@ -177,10 +185,18 @@ func (d *compressor) writeBlock(tokens []token, index int, eof bool) os.Error {
// Try to find a match starting at index whose length is greater than prevSize.
// We only look at chainCount possibilities before giving up.
func (d *compressor) findMatch(pos int, prevHead int, prevLength int, lookahead int) (length, offset int, ok bool) {
- win := d.window[0 : pos+min(maxMatchLength, lookahead)]
+ minMatchLook := maxMatchLength
+ if lookahead < minMatchLook {
+ minMatchLook = lookahead
+ }
+
+ win := d.window[0 : pos+minMatchLook]
// We quit when we get a match that's at least nice long
- nice := min(d.niceMatch, len(win)-pos)
+ nice := len(win) - pos
+ if d.niceMatch < nice {
+ nice = d.niceMatch
+ }
// If we've got a match that's good enough, only look in 1/4 the chain.
tries := d.maxChainLength
@@ -344,9 +360,12 @@ Loop:
}
prevLength := length
prevOffset := offset
- minIndex := max(index-maxOffset, 0)
length = minMatchLength - 1
offset = 0
+ minIndex := index - maxOffset
+ if minIndex < 0 {
+ minIndex = 0
+ }
if chainHead >= minIndex &&
(isFastDeflate && lookahead > minMatchLength-1 ||
@@ -477,6 +496,33 @@ func NewWriter(w io.Writer, level int) *Writer {
return &Writer{pw, &d}
}
+// NewWriterDict is like NewWriter but initializes the new
+// Writer with a preset dictionary. The returned Writer behaves
+// as if the dictionary had been written to it without producing
+// any compressed output. The compressed data written to w
+// can only be decompressed by a Reader initialized with the
+// same dictionary.
+func NewWriterDict(w io.Writer, level int, dict []byte) *Writer {
+ dw := &dictWriter{w, false}
+ zw := NewWriter(dw, level)
+ zw.Write(dict)
+ zw.Flush()
+ dw.enabled = true
+ return zw
+}
+
+type dictWriter struct {
+ w io.Writer
+ enabled bool
+}
+
+func (w *dictWriter) Write(b []byte) (n int, err os.Error) {
+ if w.enabled {
+ return w.w.Write(b)
+ }
+ return len(b), nil
+}
+
// A Writer takes data written to it and writes the compressed
// form of that data to an underlying writer (see NewWriter).
type Writer struct {
diff --git a/libgo/go/compress/flate/deflate_test.go b/libgo/go/compress/flate/deflate_test.go
index ed5884a4b78..650a8059ace 100644
--- a/libgo/go/compress/flate/deflate_test.go
+++ b/libgo/go/compress/flate/deflate_test.go
@@ -275,3 +275,49 @@ func TestDeflateInflateString(t *testing.T) {
}
testToFromWithLevel(t, 1, gold, "2.718281828...")
}
+
+func TestReaderDict(t *testing.T) {
+ const (
+ dict = "hello world"
+ text = "hello again world"
+ )
+ var b bytes.Buffer
+ w := NewWriter(&b, 5)
+ w.Write([]byte(dict))
+ w.Flush()
+ b.Reset()
+ w.Write([]byte(text))
+ w.Close()
+
+ r := NewReaderDict(&b, []byte(dict))
+ data, err := ioutil.ReadAll(r)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(data) != "hello again world" {
+ t.Fatalf("read returned %q want %q", string(data), text)
+ }
+}
+
+func TestWriterDict(t *testing.T) {
+ const (
+ dict = "hello world"
+ text = "hello again world"
+ )
+ var b bytes.Buffer
+ w := NewWriter(&b, 5)
+ w.Write([]byte(dict))
+ w.Flush()
+ b.Reset()
+ w.Write([]byte(text))
+ w.Close()
+
+ var b1 bytes.Buffer
+ w = NewWriterDict(&b1, 5, []byte(dict))
+ w.Write([]byte(text))
+ w.Close()
+
+ if !bytes.Equal(b1.Bytes(), b.Bytes()) {
+ t.Fatalf("writer wrote %q want %q", b1.Bytes(), b.Bytes())
+ }
+}
diff --git a/libgo/go/compress/flate/inflate.go b/libgo/go/compress/flate/inflate.go
index 7dc8cf93bd9..320b80d0699 100644
--- a/libgo/go/compress/flate/inflate.go
+++ b/libgo/go/compress/flate/inflate.go
@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// The flate package implements the DEFLATE compressed data
-// format, described in RFC 1951. The gzip and zlib packages
-// implement access to DEFLATE-based file formats.
+// Package flate implements the DEFLATE compressed data format, described in
+// RFC 1951. The gzip and zlib packages implement access to DEFLATE-based file
+// formats.
package flate
import (
@@ -526,6 +526,20 @@ func (f *decompressor) dataBlock() os.Error {
return nil
}
+func (f *decompressor) setDict(dict []byte) {
+ if len(dict) > len(f.hist) {
+ // Will only remember the tail.
+ dict = dict[len(dict)-len(f.hist):]
+ }
+
+ f.hp = copy(f.hist[:], dict)
+ if f.hp == len(f.hist) {
+ f.hp = 0
+ f.hfull = true
+ }
+ f.hw = f.hp
+}
+
func (f *decompressor) moreBits() os.Error {
c, err := f.r.ReadByte()
if err != nil {
@@ -618,3 +632,16 @@ func NewReader(r io.Reader) io.ReadCloser {
go func() { pw.CloseWithError(f.decompress(r, pw)) }()
return pr
}
+
+// NewReaderDict is like NewReader but initializes the reader
+// with a preset dictionary. The returned Reader behaves as if
+// the uncompressed data stream started with the given dictionary,
+// which has already been read. NewReaderDict is typically used
+// to read data compressed by NewWriterDict.
+func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser {
+ var f decompressor
+ f.setDict(dict)
+ pr, pw := io.Pipe()
+ go func() { pw.CloseWithError(f.decompress(r, pw)) }()
+ return pr
+}