Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
4kills committed Jul 27, 2020
2 parents b92692f + 96dded3 commit 9c10abf
Show file tree
Hide file tree
Showing 11 changed files with 405 additions and 21 deletions.
9 changes: 4 additions & 5 deletions compress.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package libdeflate
// If you pass nil for out, this function will allocate a fitting buffer and return it (not preferred though).
//
// IF YOU WANT TO COMPRESS MORE THAN ONCE, PLEASE REFER TO NewCompressor(),
// as this function creates a new Compressor which is then closed at the end of the function.
// as this function creates a new Compressor (alloc 32KiB) which is then closed at the end of the function.
//
// See Compress for further information.
func CompressZlib(in, out []byte) (int, []byte, error) {
Expand All @@ -17,7 +17,7 @@ func CompressZlib(in, out []byte) (int, []byte, error) {
// If you pass nil for out, this function will allocate a fitting buffer and return it (not preferred though).
//
// IF YOU WANT TO COMPRESS MORE THAN ONCE, PLEASE REFER TO NewCompressorLevel(),
// as this function creates a new Compressor which is then closed at the end of the function.
// as this function creates a new Compressor (alloc 32KiB) which is then closed at the end of the function.
//
// See CompressLevel for further information.
func CompressZlibLevel(in, out []byte, level int) (int, []byte, error) {
Expand All @@ -31,7 +31,7 @@ func CompressZlibLevel(in, out []byte, level int) (int, []byte, error) {
// m specifies which compression format should be used (e.g. ModeZlib). Uses default compression level.
//
// IF YOU WANT TO COMPRESS MORE THAN ONCE, PLEASE REFER TO NewCompressor(),
// as this function creates a new Compressor which is then closed at the end of the function.
// as this function creates a new Compressor (alloc 32KiB) which is then closed at the end of the function.
//
// Notice that for extremely small or already highly compressed data,
// the compressed data could be larger than uncompressed.
Expand All @@ -48,7 +48,7 @@ func Compress(in, out []byte, m Mode) (int, []byte, error) {
// Level defines the compression level.
//
// IF YOU WANT TO COMPRESS MORE THAN ONCE, PLEASE REFER TO NewCompressorLevel(),
// as this function creates a new Compressor which is then closed at the end of the function.
// as this function creates a new Compressor (alloc 32KiB) which is then closed at the end of the function.
//
// Notice that for extremely small or already highly compressed data,
// the compressed data could be larger than uncompressed.
Expand All @@ -62,4 +62,3 @@ func CompressLevel(in, out []byte, m Mode, level int) (int, []byte, error) {

return c.Compress(in, out, m)
}

57 changes: 57 additions & 0 deletions compressing_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package libdeflate

import (
"bytes"
"compress/zlib"
"io"
"testing"
)

/*---------------------
UNIT TESTS
-----------------------*/

func TestCompressZlibConvenience(t *testing.T) {
_, comp, err := CompressZlib(shortString, nil)
if err != nil {
t.Error(err)
}

b := bytes.NewBuffer(comp)
r, err := zlib.NewReader(b)
if err != nil {
t.Error(err)
}
defer r.Close()

dc := &bytes.Buffer{}
io.Copy(dc, r)

slicesEqual(shortString, dc.Bytes(), t)
}

/*---------------------
BENCHMARKS
-----------------------*/

func BenchmarkCompressZlib(b *testing.B) {
out := make([]byte, len(shortString))

b.ResetTimer()

for i := 0; i < b.N; i++ {
CompressZlib(shortString, out)
}
}

func BenchmarkCompressor_CompressZlib(b *testing.B) {
c, _ := NewCompressor()
defer c.Close()
out := make([]byte, len(shortString))

b.ResetTimer()

for i := 0; i < b.N; i++ {
c.CompressZlib(shortString, out)
}
}
22 changes: 14 additions & 8 deletions compressor.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,22 @@ import "github.com/4kills/libdeflate/native"
// If you want to compress concurrently, create a compressor for each thread.
//
// Always Close() the decompressor to free c memory.
// One Compressor allocates at least 32 KiB.
type Compressor struct {
c *native.Compressor
c *native.Compressor
lvl int
}

// NewCompressor returns a new Compressor used to compress data with compression level DefaultCompressionLevel.
// Errors if out of memory.
// Errors if out of memory. Allocates 32KiB.
// See NewCompressorLevel for custom compression level
func NewCompressor() (Compressor, error) {
return NewCompressorLevel(DefaultCompressionLevel)
}

// NewCompressor returns a new Compressor used to compress data.
// NewCompressorLevel returns a new Compressor used to compress data.
// Errors if out of memory or if an invalid compression level was passed.
// Allocates 32KiB.
//
// The compression level is legal if and only if:
// MinCompressionLevel <= level <= MaxCompressionLevel
Expand Down Expand Up @@ -50,10 +52,14 @@ func (c Compressor) CompressZlib(in, out []byte) (int, []byte, error) {
// If out == nil: For a too large discrepancy (len(out) > 1000 + 2 * len(in)) Compress will error
func (c Compressor) Compress(in, out []byte, m Mode) (int, []byte, error) {
switch m {
case ModeZlib: return c.c.Compress(in, out, native.CompressZlib)
case ModeDEFLATE: return c.c.Compress(in, out, native.CompressDEFLATE)
case ModeGzip: return c.c.Compress(in, out, native.CompressGzip)
default: panic("libdeflate: compress: invalid mode")
case ModeZlib:
return c.c.Compress(in, out, native.CompressZlib)
case ModeDEFLATE:
return c.c.Compress(in, out, native.CompressDEFLATE)
case ModeGzip:
return c.c.Compress(in, out, native.CompressGzip)
default:
panic("libdeflate: compress: invalid mode")
}
}

Expand All @@ -70,4 +76,4 @@ func (c Compressor) Level() int {
// After closing, the compressor must not be used anymore, as the methods will panic (except for the c.Level() method).
func (c Compressor) Close() {
c.c.Close()
}
}
166 changes: 166 additions & 0 deletions compressor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package libdeflate

import (
"bytes"
"compress/flate"
"compress/gzip"
"compress/zlib"
"io"
"testing"
)

var shortString = []byte("hello, world\nhello, world\nhello, world\nhello, world\nhello, world\nhello, world\nhello, world\nhello, world\nhello, world\nhello, world\nhello, world\n")

/*---------------------
UNIT TESTS
-----------------------*/

func TestNewCompressor(t *testing.T) {
c, err := NewCompressor()
if err != nil {
t.Error(err)
}
defer c.Close()

c, err = NewCompressorLevel(30)
if err == nil {
t.Fail()
}
}

func TestCompressDEFLATE(t *testing.T) {
c, _ := NewCompressor()
defer c.Close()

_, comp, err := c.Compress(shortString, nil, ModeDEFLATE)
if err != nil {
t.Error(err)
}

b := bytes.NewBuffer(comp)
r := flate.NewReader(b)
defer r.Close()

dc := &bytes.Buffer{}
io.Copy(dc, r)

slicesEqual(shortString, dc.Bytes(), t)
}

func TestCompressGzip(t *testing.T) {
c, _ := NewCompressor()
defer c.Close()

_, comp, err := c.Compress(shortString, nil, ModeGzip)
if err != nil {
t.Error(err)
}

b := bytes.NewBuffer(comp)
r, err := gzip.NewReader(b)
if err != nil {
t.Error(err)
}
defer r.Close()

dc := &bytes.Buffer{}
io.Copy(dc, r)

slicesEqual(shortString, dc.Bytes(), t)
}

func TestCompressZlibMaxComp(t *testing.T) {
c, _ := NewCompressorLevel(MaxStdZlibCompressionLevel)
defer c.Close()
_, comp, err := c.CompressZlib(shortString, nil)
if err != nil {
t.Error(err)
}

r, _ := zlib.NewReader(bytes.NewBuffer(comp))
defer r.Close()
decomp := make([]byte, len(shortString))
r.Read(decomp)

slicesEqual(shortString, decomp, t)
}

func TestCompressZlib(t *testing.T) {
c, _ := NewCompressor()
defer c.Close()
_, comp, err := c.CompressZlib(shortString, nil)
if err != nil {
t.Error(err)
}

r, _ := zlib.NewReader(bytes.NewBuffer(comp))
defer r.Close()
decomp := make([]byte, len(shortString))
r.Read(decomp)

slicesEqual([]byte(shortString), decomp, t)
}

// this test doesn't really say as much as TestCompress
func TestCompressMeta(t *testing.T) {
c, _ := NewCompressor()
defer c.Close()

if _, _, err := c.CompressZlib(make([]byte, 0), nil); err == nil {
t.Error("expected error")
}

n, out, err := c.CompressZlib(shortString, nil)
if err != nil || n == 0 || n >= len(shortString) || n != len(out) {
t.Error(err)
t.Error(n)
}

out2 := make([]byte, len(shortString))
n, _, err = c.CompressZlib(shortString, out2)
if err != nil || n == 0 {
t.Error(err)
t.Error(n)
}

slicesEqual(out, out2[:n], t) //tests if rep produces same results
}

/*---------------------
INTEGRATION TESTS
-----------------------*/

func TestCompressDecompress(t *testing.T) {
c, _ := NewCompressor()
defer c.Close()
_, comp, err := c.CompressZlib(shortString, nil)
if err != nil {
t.Error(err)
}

out := make([]byte, len(shortString))
dc, _ := NewDecompressor()
defer dc.Close()
if _, err := dc.DecompressZlib(comp, out); err != nil {
t.Error(err)
}
slicesEqual([]byte(shortString), out, t)
}

/*---------------------
HELPER
-----------------------*/

func slicesEqual(expected, actual []byte, t *testing.T) {
if len(expected) != len(actual) {
t.Error("len of slices unequal")
t.FailNow()
}

for i := range expected {
if expected[i] != actual[i] {
t.Errorf("slices differ at %d: want: %d, got: %d", i, expected[i], actual[i])
t.FailNow()
}
}
}
1 change: 1 addition & 0 deletions consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package libdeflate

import "github.com/4kills/libdeflate/native"

// These constants specify several special compression levels
const (
MinCompressionLevel = native.MinCompressionLevel
MaxStdZlibCompressionLevel = native.MaxStdZlibCompressionLevel
Expand Down
8 changes: 4 additions & 4 deletions decompress.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package libdeflate

// Decompress decompresses the given data from in (zlib formatted) to out and returns out
// DecompressZlib decompresses the given data from in (zlib formatted) to out and returns out
// or an error if something went wrong.
//
// If you pass a buffer to out, the size of this buffer must exactly match the length of the decompressed data.
// If you pass nil to out, this function will allocate a sufficient buffer and return it.
//
// IF YOU WANT TO DECOMPRESS MORE THAN ONCE, PLEASE REFER TO NewDecompressor(),
// as this function creates a new Decompressor which is then closed at the end of the function.
// as this function creates a new Decompressor (alloc 32KiB) which is then closed at the end of the function.
//
// If error != nil, the data in out is undefined.
func DecompressZlib(in, out []byte) ([]byte, error) {
Expand All @@ -21,7 +21,7 @@ func DecompressZlib(in, out []byte) ([]byte, error) {
// If you pass nil to out, this function will allocate a sufficient buffer and return it.
//
// IF YOU WANT TO DECOMPRESS MORE THAN ONCE, PLEASE REFER TO NewDecompressor(),
// as this function creates a new Decompressor which is then closed at the end of the function.
// as this function creates a new Decompressor (alloc 32KiB) which is then closed at the end of the function.
//
// If error != nil, the data in out is undefined.
func Decompress(in, out []byte, m Mode) ([]byte, error) {
Expand All @@ -32,4 +32,4 @@ func Decompress(in, out []byte, m Mode) ([]byte, error) {
defer dc.Close()

return dc.Decompress(in, out, m)
}
}
Loading

0 comments on commit 9c10abf

Please sign in to comment.