Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add full zero payload block encoding option. #155

Merged
merged 1 commit into from
Sep 5, 2019
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
21 changes: 20 additions & 1 deletion zstd/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,12 +393,31 @@ func (e *Encoder) Close() error {

// EncodeAll will encode all input in src and append it to dst.
// This function can be called concurrently, but each call will only run on a single goroutine.
// If empty input is given, nothing is returned.
// If empty input is given, nothing is returned, unless WithZeroFrames is specified.
// Encoded blocks can be concatenated and the result will be the combined input stream.
// Data compressed with EncodeAll can be decoded with the Decoder,
// using either a stream or DecodeAll.
func (e *Encoder) EncodeAll(src, dst []byte) []byte {
if len(src) == 0 {
if e.o.fullZero {
// Add frame header.
fh := frameHeader{
ContentSize: 0,
WindowSize: minWindowSize,
SingleSegment: true,
// Adding a checksum would be a waste of space.
Checksum: false,
DictID: 0,
}
dst, _ = fh.appendTo(dst)

// Write raw block as last one only.
var blk blockHeader
blk.setSize(0)
blk.setType(blockTypeRaw)
blk.setLast(true)
dst = blk.appendTo(dst)
}
return dst
}
e.init.Do(func() {
Expand Down
11 changes: 11 additions & 0 deletions zstd/encoder_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type encoderOptions struct {
blockSize int
windowSize int
level EncoderLevel
fullZero bool
}

func (o *encoderOptions) setDefault() {
Expand Down Expand Up @@ -166,6 +167,16 @@ func WithEncoderLevel(l EncoderLevel) EOption {
}
}

// WithZeroFrames will encode 0 length input as full frames.
// This can be needed for compatibility with zstandard usage,
// but is not needed for this package.
func WithZeroFrames(b bool) EOption {
return func(o *encoderOptions) error {
o.fullZero = b
return nil
}
}

// WithSingleSegment will set the "single segment" flag when EncodeAll is used.
// If this flag is set, data must be regenerated within a single continuous memory segment.
// In this case, Window_Descriptor byte is skipped, but Frame_Content_Size is necessarily present.
Expand Down
49 changes: 49 additions & 0 deletions zstd/encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,55 @@ func TestEncoder_EncodeAllSilesia(t *testing.T) {
t.Log("Encoded content matched")
}

func TestEncoder_EncodeAllEmpty(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
var in []byte

e, err := NewWriter(nil, WithZeroFrames(true))
if err != nil {
t.Fatal(err)
}
dst := e.EncodeAll(in, nil)
if len(dst) == 0 {
t.Fatal("Requested zero frame, but got nothing.")
}
t.Log("Block Encoder len", len(in), "-> zstd len", len(dst), dst)

dec, err := NewReader(nil, WithDecoderMaxMemory(220<<20))
if err != nil {
t.Fatal(err)
}
decoded, err := dec.DecodeAll(dst, nil)
if err != nil {
t.Error(err, len(decoded))
}
if !bytes.Equal(decoded, in) {
t.Fatal("Decoded does not match")
}

// Test buffer writer.
var buf bytes.Buffer
e.Reset(&buf)
err = e.Close()
dst = buf.Bytes()
if len(dst) == 0 {
t.Fatal("Requested zero frame, but got nothing.")
}
t.Log("Buffer Encoder len", len(in), "-> zstd len", len(dst))

decoded, err = dec.DecodeAll(dst, nil)
if err != nil {
t.Error(err, len(decoded))
}
if !bytes.Equal(decoded, in) {
t.Fatal("Decoded does not match")
}

t.Log("Encoded content matched")
}

func TestEncoder_EncodeAllEnwik9(t *testing.T) {
if false || testing.Short() {
t.SkipNow()
Expand Down
5 changes: 1 addition & 4 deletions zstd/frameenc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
package zstd

import (
"errors"
"fmt"
"io"
"math"
Expand Down Expand Up @@ -49,9 +48,7 @@ func (f frameHeader) appendTo(dst []byte) ([]byte, error) {
windowLog := (bits.Len32(f.WindowSize-1) - winLogMin) << 3
dst = append(dst, uint8(windowLog))
}
if f.SingleSegment && f.ContentSize == 0 {
return nil, errors.New("single segment, but no size set")
}

switch fcs {
case 0:
if f.SingleSegment {
Expand Down