safing-structures/dsd/compression.go
2024-06-24 09:20:00 +02:00

103 lines
2.2 KiB
Go

package dsd
import (
"bytes"
"compress/gzip"
"errors"
"github.com/safing/structures/varint"
)
// DumpAndCompress stores the interface as a dsd formatted data structure and compresses the resulting data.
func DumpAndCompress(t interface{}, format uint8, compression uint8) ([]byte, error) {
// Check if compression format is valid.
compression, ok := ValidateCompressionFormat(compression)
if !ok {
return nil, ErrIncompatibleFormat
}
// Dump the given data with the given format.
data, err := Dump(t, format)
if err != nil {
return nil, err
}
// prepare writer
packetFormat := varint.Pack8(compression)
buf := bytes.NewBuffer(nil)
buf.Write(packetFormat)
// compress
switch compression {
case GZIP:
// create gzip writer
gzipWriter, err := gzip.NewWriterLevel(buf, gzip.BestCompression)
if err != nil {
return nil, err
}
// write data
n, err := gzipWriter.Write(data)
if err != nil {
return nil, err
}
if n != len(data) {
return nil, errors.New("failed to fully write to gzip compressor")
}
// flush and write gzip footer
err = gzipWriter.Close()
if err != nil {
return nil, err
}
default:
return nil, ErrIncompatibleFormat
}
return buf.Bytes(), nil
}
// DecompressAndLoad decompresses the data using the specified compression format and then loads the resulting data blob into the interface.
func DecompressAndLoad(data []byte, compression uint8, t interface{}) (format uint8, err error) {
// Check if compression format is valid.
_, ok := ValidateCompressionFormat(compression)
if !ok {
return 0, ErrIncompatibleFormat
}
// prepare reader
buf := bytes.NewBuffer(nil)
// decompress
switch compression {
case GZIP:
// create gzip reader
gzipReader, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return 0, err
}
// read uncompressed data
_, err = buf.ReadFrom(gzipReader)
if err != nil {
return 0, err
}
// flush and verify gzip footer
err = gzipReader.Close()
if err != nil {
return 0, err
}
default:
return 0, ErrIncompatibleFormat
}
// assign decompressed data
data = buf.Bytes()
format, read, err := loadFormat(data)
if err != nil {
return 0, err
}
return format, LoadAsFormat(data[read:], format, t)
}