Skip to content

Commit 83274b1

Browse files
committed
add Ctx.DecompressInto for decompression of payloads with known sizes
Adds DecompressInto() to zstd_context to match the functionality previously implemented in the zstd file. In some systems, compressed payloads are prefixed or tagged with the size of the original payload. This allows allocation of a perfectly-sized buffer to hold the decompressed payload. This method provides a way to decompress directly into a pre-allocated buffer. DecompressInto accepts a destination buffer and requires that the decompressed payload fit into the buffer. If it does not, it returns the original error returned by zstd. Relates to/Follows up on: DataDog#130
1 parent 091c219 commit 83274b1

File tree

2 files changed

+51
-8
lines changed

2 files changed

+51
-8
lines changed

zstd_ctx.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ type Ctx interface {
2424
// prevent allocation. If it is too small, or if nil is passed, a new buffer
2525
// will be allocated and returned.
2626
Decompress(dst, src []byte) ([]byte, error)
27+
28+
// DecompressInto decompresses src into dst. Unlike Decompress, DecompressInto
29+
// requires that dst be sufficiently large to hold the decompressed payload.
30+
// DecompressInto may be used when the caller knows the size of the decompressed
31+
// payload before attempting decompression.
32+
//
33+
// It returns the number of bytes copied and an error if any is encountered. If
34+
// dst is too small, DecompressInto errors.
35+
DecompressInto(dst, src []byte) (int, error)
2736
}
2837

2938
type ctx struct {
@@ -104,14 +113,7 @@ func (c *ctx) Decompress(dst, src []byte) ([]byte, error) {
104113
dst = make([]byte, bound)
105114
}
106115

107-
written := int(C.ZSTD_decompressDCtx(
108-
c.dctx,
109-
unsafe.Pointer(&dst[0]),
110-
C.size_t(len(dst)),
111-
unsafe.Pointer(&src[0]),
112-
C.size_t(len(src))))
113-
114-
err := getError(written)
116+
written, err := DecompressInto(dst, src)
115117
if err == nil {
116118
return dst[:written], nil
117119
}
@@ -125,6 +127,17 @@ func (c *ctx) Decompress(dst, src []byte) ([]byte, error) {
125127
return ioutil.ReadAll(r)
126128
}
127129

130+
func (c *ctx) DecompressInto(dst, src []byte) (int, error) {
131+
written := int(C.ZSTD_decompressDCtx(
132+
c.dctx,
133+
unsafe.Pointer(&dst[0]),
134+
C.size_t(len(dst)),
135+
unsafe.Pointer(&src[0]),
136+
C.size_t(len(src))))
137+
err := getError(written)
138+
return written, err
139+
}
140+
128141
func finalizeCtx(c *ctx) {
129142
C.ZSTD_freeCCtx(c.cctx)
130143
C.ZSTD_freeDCtx(c.dctx)

zstd_ctx_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,36 @@ func TestCtxDecompressZeroLengthBuf(t *testing.T) {
118118
}
119119
}
120120

121+
func TestCtxCompressDecompressInto(t *testing.T) {
122+
ctx := NewCtx()
123+
payload := []byte("Hello World!")
124+
compressed, err := ctx.Compress(make([]byte, CompressBound(len(payload))), payload)
125+
if err != nil {
126+
t.Fatalf("Error while compressing: %v", err)
127+
}
128+
t.Logf("Compressed: %v", compressed)
129+
130+
// We know the size of the payload; construct a buffer that perfectly fits
131+
// the payload and use DecompressInto.
132+
decompressed := make([]byte, len(payload))
133+
if n, err := ctx.DecompressInto(decompressed, compressed); err != nil {
134+
t.Fatalf("error while decompressing into buffer of size %d: %v",
135+
len(decompressed), err)
136+
} else if n != len(decompressed) {
137+
t.Errorf("Ctx.DecompressedInto = (%d, nil), want (%d, nil)", n, len(decompressed))
138+
}
139+
if !bytes.Equal(payload, decompressed) {
140+
t.Fatalf("Ctx.DecompressInto(_, Compress(_, %q)) yielded %q, want %q", payload, decompressed, payload)
141+
}
142+
143+
// Ensure that decompressing into a buffer too small errors appropriately.
144+
smallBuffer := make([]byte, len(payload)-1)
145+
if _, err := ctx.DecompressInto(smallBuffer, compressed); !IsDstSizeTooSmallError(err) {
146+
t.Fatalf("Ctx.DecompressInto(<%d-sized buffer>, Compress(_, %q)) = %v, want 'Destination buffer is too small'",
147+
len(smallBuffer), payload, err)
148+
}
149+
}
150+
121151
func TestCtxTooSmall(t *testing.T) {
122152
ctx := NewCtx()
123153

0 commit comments

Comments
 (0)