Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions zstd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,13 +329,13 @@ Data compressed with [dictionaries](https://github.com/facebook/zstd#the-case-fo

Dictionaries are added individually to Decoders.
Dictionaries are generated by the `zstd --train` command and contains an initial state for the decoder.
To add a dictionary use the `RegisterDict(data)` with the dictionary data before starting any decompression.
To add a dictionary use the `WithDecoderDicts(dicts ...[]byte)` option with the dictionary data.
Several dictionaries can be added at once.

The dictionary will be used automatically for the data that specifies them.

A re-used Decoder will still contain the dictionaries registered.

When registering a dictionary with the same ID it will override the existing.
When registering multiple dictionaries with the same ID, the last one will be used.

### Allocation-less operation

Expand Down
20 changes: 7 additions & 13 deletions zstd/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ func NewReader(r io.Reader, opts ...DOption) (*Decoder, error) {
d.current.output = make(chan decodeOutput, d.o.concurrent)
d.current.flushed = true

// Transfer option dicts.
d.dicts = make(map[uint32]dict, len(d.o.dicts))
for _, dc := range d.o.dicts {
d.dicts[dc.id] = dc
}
d.o.dicts = nil

// Create decoders
d.decoders = make(chan *blockDec, d.o.concurrent)
for i := 0; i < d.o.concurrent; i++ {
Expand Down Expand Up @@ -399,19 +406,6 @@ func (d *Decoder) Close() {
d.current.err = ErrDecoderClosed
}

// RegisterDict will load a dictionary
func (d *Decoder) RegisterDict(b []byte) error {
dc, err := loadDict(b)
if err != nil {
return err
}
if d.dicts == nil {
d.dicts = make(map[uint32]dict, 1)
}
d.dicts[dc.id] = *dc
return nil
}

// IOReadCloser returns the decoder as an io.ReadCloser for convenience.
// Any changes to the decoder will be reflected, so the returned ReadCloser
// can be reused along with the decoder.
Expand Down
16 changes: 16 additions & 0 deletions zstd/decoder_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type decoderOptions struct {
lowMem bool
concurrent int
maxDecodedSize uint64
dicts []dict
}

func (o *decoderOptions) setDefault() {
Expand Down Expand Up @@ -66,3 +67,18 @@ func WithDecoderMaxMemory(n uint64) DOption {
return nil
}
}

// WithDecoderDicts allows to register one or more dictionaries for the decoder.
// If several dictionaries with the same ID is provided the last one will be used.
func WithDecoderDicts(dicts ...[]byte) DOption {
return func(o *decoderOptions) error {
for _, b := range dicts {
d, err := loadDict(b)
if err != nil {
return err
}
o.dicts = append(o.dicts, *d)
}
return nil
}
}
33 changes: 15 additions & 18 deletions zstd/dict_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@ func TestDecoder_SmallDict(t *testing.T) {
if err != nil {
t.Fatal(err)
}
dec, err := NewReader(nil, WithDecoderConcurrency(1))
if err != nil {
t.Fatal(err)
return
}
var dicts [][]byte
for _, tt := range zr.File {
if !strings.HasSuffix(tt.Name, ".dict") {
continue
Expand All @@ -39,12 +35,14 @@ func TestDecoder_SmallDict(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = dec.RegisterDict(in)
if err != nil {
t.Fatal(tt.Name, err)
}
dicts = append(dicts, in)
}()
}
dec, err := NewReader(nil, WithDecoderConcurrency(1), WithDecoderDicts(dicts...))
if err != nil {
t.Fatal(err)
return
}
defer dec.Close()
for _, tt := range zr.File {
if !strings.HasSuffix(tt.Name, ".zst") {
Expand Down Expand Up @@ -84,11 +82,8 @@ func TestDecoder_MoreDicts(t *testing.T) {
if err != nil {
t.Fatal(err)
}
dec, err := NewReader(nil, WithDecoderConcurrency(1))
if err != nil {
t.Fatal(err)
return
}

var dicts [][]byte
for _, tt := range zr.File {
if !strings.HasSuffix(tt.Name, ".dict") {
continue
Expand All @@ -103,12 +98,14 @@ func TestDecoder_MoreDicts(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = dec.RegisterDict(in)
if err != nil {
t.Fatal(tt.Name, err)
}
dicts = append(dicts, in)
}()
}
dec, err := NewReader(nil, WithDecoderConcurrency(1), WithDecoderDicts(dicts...))
if err != nil {
t.Fatal(err)
return
}
defer dec.Close()
for _, tt := range zr.File {
if !strings.HasSuffix(tt.Name, ".zst") {
Expand Down