1
0
mirror of https://github.com/golang/go synced 2024-11-26 02:07:57 -07:00

compress/flate: dictionary support

R=bradfitzwork
CC=golang-dev
https://golang.org/cl/4397043
This commit is contained in:
Russ Cox 2011-04-14 10:41:55 -04:00
parent bd77eedacf
commit 36713a2a53
3 changed files with 100 additions and 0 deletions

View File

@ -477,6 +477,33 @@ func NewWriter(w io.Writer, level int) *Writer {
return &Writer{pw, &d} 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 // A Writer takes data written to it and writes the compressed
// form of that data to an underlying writer (see NewWriter). // form of that data to an underlying writer (see NewWriter).
type Writer struct { type Writer struct {

View File

@ -275,3 +275,49 @@ func TestDeflateInflateString(t *testing.T) {
} }
testToFromWithLevel(t, 1, gold, "2.718281828...") 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())
}
}

View File

@ -526,6 +526,20 @@ func (f *decompressor) dataBlock() os.Error {
return nil 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 { func (f *decompressor) moreBits() os.Error {
c, err := f.r.ReadByte() c, err := f.r.ReadByte()
if err != nil { if err != nil {
@ -618,3 +632,16 @@ func NewReader(r io.Reader) io.ReadCloser {
go func() { pw.CloseWithError(f.decompress(r, pw)) }() go func() { pw.CloseWithError(f.decompress(r, pw)) }()
return pr 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
}