123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 |
- // Copyright 2012 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package flate
- import (
- "archive/zip"
- "bytes"
- "compress/flate"
- "fmt"
- "io"
- "math"
- "math/rand"
- "os"
- "runtime"
- "strconv"
- "strings"
- "testing"
- )
- func TestWriterMemUsage(t *testing.T) {
- testMem := func(t *testing.T, fn func()) {
- var before, after runtime.MemStats
- runtime.GC()
- runtime.ReadMemStats(&before)
- fn()
- runtime.GC()
- runtime.ReadMemStats(&after)
- t.Logf("%s: Memory Used: %dKB, %d allocs", t.Name(), (after.HeapInuse-before.HeapInuse)/1024, after.HeapObjects-before.HeapObjects)
- }
- data := make([]byte, 100000)
- t.Run(fmt.Sprint("stateless"), func(t *testing.T) {
- testMem(t, func() {
- StatelessDeflate(io.Discard, data, false, nil)
- })
- })
- for level := HuffmanOnly; level <= BestCompression; level++ {
- t.Run(fmt.Sprint("level-", level), func(t *testing.T) {
- var zr *Writer
- var err error
- testMem(t, func() {
- zr, err = NewWriter(io.Discard, level)
- if err != nil {
- t.Fatal(err)
- }
- zr.Write(data)
- })
- zr.Close()
- })
- }
- for level := HuffmanOnly; level <= BestCompression; level++ {
- t.Run(fmt.Sprint("stdlib-", level), func(t *testing.T) {
- var zr *flate.Writer
- var err error
- testMem(t, func() {
- zr, err = flate.NewWriter(io.Discard, level)
- if err != nil {
- t.Fatal(err)
- }
- zr.Write(data)
- })
- zr.Close()
- })
- }
- }
- func TestWriterRegression(t *testing.T) {
- data, err := os.ReadFile("testdata/regression.zip")
- if err != nil {
- t.Fatal(err)
- }
- for level := HuffmanOnly; level <= BestCompression; level++ {
- t.Run(fmt.Sprint("level_", level), func(t *testing.T) {
- zr, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
- if err != nil {
- t.Fatal(err)
- }
- for _, tt := range zr.File {
- if !strings.HasSuffix(t.Name(), "") {
- continue
- }
- t.Run(tt.Name, func(t *testing.T) {
- if testing.Short() && tt.FileInfo().Size() > 10000 {
- t.SkipNow()
- }
- r, err := tt.Open()
- if err != nil {
- t.Error(err)
- return
- }
- in, err := io.ReadAll(r)
- if err != nil {
- t.Error(err)
- }
- msg := "level " + strconv.Itoa(level) + ":"
- buf := new(bytes.Buffer)
- fw, err := NewWriter(buf, level)
- if err != nil {
- t.Fatal(msg + err.Error())
- }
- n, err := fw.Write(in)
- if n != len(in) {
- t.Fatal(msg + "short write")
- }
- if err != nil {
- t.Fatal(msg + err.Error())
- }
- err = fw.Close()
- if err != nil {
- t.Fatal(msg + err.Error())
- }
- fr1 := NewReader(buf)
- data2, err := io.ReadAll(fr1)
- if err != nil {
- t.Fatal(msg + err.Error())
- }
- if !bytes.Equal(in, data2) {
- t.Fatal(msg + "not equal")
- }
- // Do it again...
- msg = "level " + strconv.Itoa(level) + " (reset):"
- buf.Reset()
- fw.Reset(buf)
- n, err = fw.Write(in)
- if n != len(in) {
- t.Fatal(msg + "short write")
- }
- if err != nil {
- t.Fatal(msg + err.Error())
- }
- err = fw.Close()
- if err != nil {
- t.Fatal(msg + err.Error())
- }
- fr1 = NewReader(buf)
- data2, err = io.ReadAll(fr1)
- if err != nil {
- t.Fatal(msg + err.Error())
- }
- if !bytes.Equal(in, data2) {
- t.Fatal(msg + "not equal")
- }
- })
- }
- })
- }
- }
- func benchmarkEncoder(b *testing.B, testfile, level, n int) {
- b.SetBytes(int64(n))
- buf0, err := os.ReadFile(testfiles[testfile])
- if err != nil {
- b.Fatal(err)
- }
- if len(buf0) == 0 {
- b.Fatalf("test file %q has no data", testfiles[testfile])
- }
- buf1 := make([]byte, n)
- for i := 0; i < n; i += len(buf0) {
- if len(buf0) > n-i {
- buf0 = buf0[:n-i]
- }
- copy(buf1[i:], buf0)
- }
- buf0 = nil
- runtime.GC()
- w, err := NewWriter(io.Discard, level)
- if err != nil {
- b.Fatal(err)
- }
- b.ResetTimer()
- b.ReportAllocs()
- for i := 0; i < b.N; i++ {
- w.Reset(io.Discard)
- _, err = w.Write(buf1)
- if err != nil {
- b.Fatal(err)
- }
- err = w.Close()
- if err != nil {
- b.Fatal(err)
- }
- }
- }
- func BenchmarkEncodeDigitsConstant1e4(b *testing.B) { benchmarkEncoder(b, digits, constant, 1e4) }
- func BenchmarkEncodeDigitsConstant1e5(b *testing.B) { benchmarkEncoder(b, digits, constant, 1e5) }
- func BenchmarkEncodeDigitsConstant1e6(b *testing.B) { benchmarkEncoder(b, digits, constant, 1e6) }
- func BenchmarkEncodeDigitsSpeed1e4(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e4) }
- func BenchmarkEncodeDigitsSpeed1e5(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e5) }
- func BenchmarkEncodeDigitsSpeed1e6(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e6) }
- func BenchmarkEncodeDigitsDefault1e4(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e4) }
- func BenchmarkEncodeDigitsDefault1e5(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e5) }
- func BenchmarkEncodeDigitsDefault1e6(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e6) }
- func BenchmarkEncodeDigitsCompress1e4(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e4) }
- func BenchmarkEncodeDigitsCompress1e5(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e5) }
- func BenchmarkEncodeDigitsCompress1e6(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e6) }
- func BenchmarkEncodeDigitsSL1e4(b *testing.B) { benchmarkStatelessEncoder(b, digits, 1e4) }
- func BenchmarkEncodeDigitsSL1e5(b *testing.B) { benchmarkStatelessEncoder(b, digits, 1e5) }
- func BenchmarkEncodeDigitsSL1e6(b *testing.B) { benchmarkStatelessEncoder(b, digits, 1e6) }
- func BenchmarkEncodeTwainConstant1e4(b *testing.B) { benchmarkEncoder(b, twain, constant, 1e4) }
- func BenchmarkEncodeTwainConstant1e5(b *testing.B) { benchmarkEncoder(b, twain, constant, 1e5) }
- func BenchmarkEncodeTwainConstant1e6(b *testing.B) { benchmarkEncoder(b, twain, constant, 1e6) }
- func BenchmarkEncodeTwainSpeed1e4(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e4) }
- func BenchmarkEncodeTwainSpeed1e5(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e5) }
- func BenchmarkEncodeTwainSpeed1e6(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e6) }
- func BenchmarkEncodeTwainDefault1e4(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e4) }
- func BenchmarkEncodeTwainDefault1e5(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e5) }
- func BenchmarkEncodeTwainDefault1e6(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e6) }
- func BenchmarkEncodeTwainCompress1e4(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e4) }
- func BenchmarkEncodeTwainCompress1e5(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e5) }
- func BenchmarkEncodeTwainCompress1e6(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e6) }
- func BenchmarkEncodeTwainSL1e4(b *testing.B) { benchmarkStatelessEncoder(b, twain, 1e4) }
- func BenchmarkEncodeTwainSL1e5(b *testing.B) { benchmarkStatelessEncoder(b, twain, 1e5) }
- func BenchmarkEncodeTwainSL1e6(b *testing.B) { benchmarkStatelessEncoder(b, twain, 1e6) }
- func benchmarkStatelessEncoder(b *testing.B, testfile, n int) {
- b.SetBytes(int64(n))
- buf0, err := os.ReadFile(testfiles[testfile])
- if err != nil {
- b.Fatal(err)
- }
- if len(buf0) == 0 {
- b.Fatalf("test file %q has no data", testfiles[testfile])
- }
- buf1 := make([]byte, n)
- for i := 0; i < n; i += len(buf0) {
- if len(buf0) > n-i {
- buf0 = buf0[:n-i]
- }
- copy(buf1[i:], buf0)
- }
- buf0 = nil
- runtime.GC()
- b.ResetTimer()
- b.ReportAllocs()
- for i := 0; i < b.N; i++ {
- w := NewStatelessWriter(io.Discard)
- _, err = w.Write(buf1)
- if err != nil {
- b.Fatal(err)
- }
- err = w.Close()
- if err != nil {
- b.Fatal(err)
- }
- }
- }
- // A writer that fails after N writes.
- type errorWriter struct {
- N int
- }
- func (e *errorWriter) Write(b []byte) (int, error) {
- if e.N <= 0 {
- return 0, io.ErrClosedPipe
- }
- e.N--
- return len(b), nil
- }
- // Test if errors from the underlying writer is passed upwards.
- func TestWriteError(t *testing.T) {
- buf := new(bytes.Buffer)
- n := 65536
- if !testing.Short() {
- n *= 4
- }
- for i := 0; i < n; i++ {
- fmt.Fprintf(buf, "asdasfasf%d%dfghfgujyut%dyutyu\n", i, i, i)
- }
- in := buf.Bytes()
- // We create our own buffer to control number of writes.
- copyBuf := make([]byte, 128)
- for l := 0; l < 10; l++ {
- for fail := 1; fail <= 256; fail *= 2 {
- // Fail after 'fail' writes
- ew := &errorWriter{N: fail}
- w, err := NewWriter(ew, l)
- if err != nil {
- t.Fatalf("NewWriter: level %d: %v", l, err)
- }
- n, err := copyBuffer(w, bytes.NewBuffer(in), copyBuf)
- if err == nil {
- t.Fatalf("Level %d: Expected an error, writer was %#v", l, ew)
- }
- n2, err := w.Write([]byte{1, 2, 2, 3, 4, 5})
- if n2 != 0 {
- t.Fatal("Level", l, "Expected 0 length write, got", n)
- }
- if err == nil {
- t.Fatal("Level", l, "Expected an error")
- }
- err = w.Flush()
- if err == nil {
- t.Fatal("Level", l, "Expected an error on flush")
- }
- err = w.Close()
- if err == nil {
- t.Fatal("Level", l, "Expected an error on close")
- }
- w.Reset(io.Discard)
- n2, err = w.Write([]byte{1, 2, 3, 4, 5, 6})
- if err != nil {
- t.Fatal("Level", l, "Got unexpected error after reset:", err)
- }
- if n2 == 0 {
- t.Fatal("Level", l, "Got 0 length write, expected > 0")
- }
- if testing.Short() {
- return
- }
- }
- }
- }
- // Test if errors from the underlying writer is passed upwards.
- func TestWriter_Reset(t *testing.T) {
- buf := new(bytes.Buffer)
- n := 65536
- if !testing.Short() {
- n *= 4
- }
- for i := 0; i < n; i++ {
- fmt.Fprintf(buf, "asdasfasf%d%dfghfgujyut%dyutyu\n", i, i, i)
- }
- in := buf.Bytes()
- for l := 0; l < 10; l++ {
- l := l
- if testing.Short() && l > 1 {
- continue
- }
- t.Run(fmt.Sprintf("level-%d", l), func(t *testing.T) {
- t.Parallel()
- offset := 1
- if testing.Short() {
- offset = 256
- }
- for ; offset <= 256; offset *= 2 {
- // Fail after 'fail' writes
- w, err := NewWriter(io.Discard, l)
- if err != nil {
- t.Fatalf("NewWriter: level %d: %v", l, err)
- }
- if w.d.fast == nil {
- t.Skip("Not Fast...")
- return
- }
- for i := 0; i < (bufferReset-len(in)-offset-maxMatchOffset)/maxMatchOffset; i++ {
- // skip ahead to where we are close to wrap around...
- w.d.fast.Reset()
- }
- w.d.fast.Reset()
- _, err = w.Write(in)
- if err != nil {
- t.Fatal(err)
- }
- for i := 0; i < 50; i++ {
- // skip ahead again... This should wrap around...
- w.d.fast.Reset()
- }
- w.d.fast.Reset()
- _, err = w.Write(in)
- if err != nil {
- t.Fatal(err)
- }
- for i := 0; i < (math.MaxUint32-bufferReset)/maxMatchOffset; i++ {
- // skip ahead to where we are close to wrap around...
- w.d.fast.Reset()
- }
- _, err = w.Write(in)
- if err != nil {
- t.Fatal(err)
- }
- err = w.Close()
- if err != nil {
- t.Fatal(err)
- }
- }
- })
- }
- }
- func TestDeterministicL1(t *testing.T) { testDeterministic(1, t) }
- func TestDeterministicL2(t *testing.T) { testDeterministic(2, t) }
- func TestDeterministicL3(t *testing.T) { testDeterministic(3, t) }
- func TestDeterministicL4(t *testing.T) { testDeterministic(4, t) }
- func TestDeterministicL5(t *testing.T) { testDeterministic(5, t) }
- func TestDeterministicL6(t *testing.T) { testDeterministic(6, t) }
- func TestDeterministicL7(t *testing.T) { testDeterministic(7, t) }
- func TestDeterministicL8(t *testing.T) { testDeterministic(8, t) }
- func TestDeterministicL9(t *testing.T) { testDeterministic(9, t) }
- func TestDeterministicL0(t *testing.T) { testDeterministic(0, t) }
- func TestDeterministicLM2(t *testing.T) { testDeterministic(-2, t) }
- func testDeterministic(i int, t *testing.T) {
- // Test so much we cross a good number of block boundaries.
- var length = maxStoreBlockSize*30 + 500
- if testing.Short() {
- length /= 10
- }
- // Create a random, but compressible stream.
- rng := rand.New(rand.NewSource(1))
- t1 := make([]byte, length)
- for i := range t1 {
- t1[i] = byte(rng.Int63() & 7)
- }
- // Do our first encode.
- var b1 bytes.Buffer
- br := bytes.NewBuffer(t1)
- w, err := NewWriter(&b1, i)
- if err != nil {
- t.Fatal(err)
- }
- // Use a very small prime sized buffer.
- cbuf := make([]byte, 787)
- _, err = copyBuffer(w, br, cbuf)
- if err != nil {
- t.Fatal(err)
- }
- w.Close()
- // We choose a different buffer size,
- // bigger than a maximum block, and also a prime.
- var b2 bytes.Buffer
- cbuf = make([]byte, 81761)
- br2 := bytes.NewBuffer(t1)
- w2, err := NewWriter(&b2, i)
- if err != nil {
- t.Fatal(err)
- }
- _, err = copyBuffer(w2, br2, cbuf)
- if err != nil {
- t.Fatal(err)
- }
- w2.Close()
- b1b := b1.Bytes()
- b2b := b2.Bytes()
- if !bytes.Equal(b1b, b2b) {
- t.Errorf("level %d did not produce deterministic result, result mismatch, len(a) = %d, len(b) = %d", i, len(b1b), len(b2b))
- }
- // Test using io.WriterTo interface.
- var b3 bytes.Buffer
- br = bytes.NewBuffer(t1)
- w, err = NewWriter(&b3, i)
- if err != nil {
- t.Fatal(err)
- }
- _, err = br.WriteTo(w)
- if err != nil {
- t.Fatal(err)
- }
- w.Close()
- b3b := b3.Bytes()
- if !bytes.Equal(b1b, b3b) {
- t.Errorf("level %d (io.WriterTo) did not produce deterministic result, result mismatch, len(a) = %d, len(b) = %d", i, len(b1b), len(b3b))
- }
- }
- // copyBuffer is a copy of io.CopyBuffer, since we want to support older go versions.
- // This is modified to never use io.WriterTo or io.ReaderFrom interfaces.
- func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) {
- if buf == nil {
- buf = make([]byte, 32*1024)
- }
- for {
- nr, er := src.Read(buf)
- if nr > 0 {
- nw, ew := dst.Write(buf[0:nr])
- if nw > 0 {
- written += int64(nw)
- }
- if ew != nil {
- err = ew
- break
- }
- if nr != nw {
- err = io.ErrShortWrite
- break
- }
- }
- if er == io.EOF {
- break
- }
- if er != nil {
- err = er
- break
- }
- }
- return written, err
- }
- func BenchmarkCompressAllocations(b *testing.B) {
- payload := []byte(strings.Repeat("Tiny payload", 20))
- for j := -2; j <= 9; j++ {
- b.Run("level("+strconv.Itoa(j)+")", func(b *testing.B) {
- b.Run("flate", func(b *testing.B) {
- b.ReportAllocs()
- for i := 0; i < b.N; i++ {
- w, err := NewWriter(io.Discard, j)
- if err != nil {
- b.Fatal(err)
- }
- w.Write(payload)
- w.Close()
- }
- })
- })
- }
- }
- func BenchmarkCompressAllocationsSingle(b *testing.B) {
- payload := []byte(strings.Repeat("Tiny payload", 20))
- const level = 2
- b.Run("flate", func(b *testing.B) {
- b.ReportAllocs()
- for i := 0; i < b.N; i++ {
- w, err := NewWriter(io.Discard, level)
- if err != nil {
- b.Fatal(err)
- }
- w.Write(payload)
- w.Close()
- }
- })
- }
|