70 lines
1.6 KiB
Go
70 lines
1.6 KiB
Go
package sereal
|
|
|
|
import (
|
|
"compress/zlib"
|
|
"math"
|
|
)
|
|
|
|
// ZlibCompressor compresses a Sereal document using the zlib format.
|
|
type ZlibCompressor struct {
|
|
Level int // compression level, set to ZlibDefaultCompression by default
|
|
}
|
|
|
|
// Zlib constants
|
|
const (
|
|
ZlibBestSpeed = zlib.BestSpeed
|
|
ZlibBestCompression = zlib.BestCompression
|
|
ZlibDefaultCompression = zlib.DefaultCompression
|
|
)
|
|
|
|
func (c ZlibCompressor) compress(buf []byte) ([]byte, error) {
|
|
// Prepend a compressed block with its length, i.e.:
|
|
//
|
|
// <Varint><Varint><Zlib Blob>
|
|
// 1st varint indicates the length of the uncompressed document,
|
|
// 2nd varint indicates the length of the compressed document.
|
|
//
|
|
// XXX It's the naive implementation, better to rework as described in the spec:
|
|
// https://github.com/Sereal/Sereal/blob/master/sereal_spec.pod#encoding-the-length-of-compressed-documents
|
|
|
|
if c.Level == 0 {
|
|
c.Level = ZlibDefaultCompression
|
|
}
|
|
|
|
tail, err := zlibEncode(buf, c.Level)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var head []byte
|
|
head = varint(head, uint(len(buf)))
|
|
head = varint(head, uint(len(tail)))
|
|
|
|
return append(head, tail...), nil
|
|
}
|
|
|
|
func (c ZlibCompressor) decompress(buf []byte) ([]byte, error) {
|
|
// Read the claimed length of the uncompressed document
|
|
uln, usz, err := varintdecode(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buf = buf[usz:]
|
|
|
|
// Read the claimed length of the compressed document
|
|
cln, csz, err := varintdecode(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if cln < 0 || cln > math.MaxInt32 || csz+cln > len(buf) {
|
|
return nil, ErrCorrupt{errBadOffset}
|
|
}
|
|
|
|
buf = buf[csz : csz+cln]
|
|
|
|
// XXX Perhaps check if len(buf) == cln
|
|
|
|
return zlibDecode(uln, buf)
|
|
}
|