go-hep.org/x/hep@v0.38.1/groot/internal/rcompress/rcompress_test.go (about)

     1  // Copyright ©2018 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build !race
     6  
     7  package rcompress_test
     8  
     9  import (
    10  	"bytes"
    11  	"compress/flate"
    12  	"fmt"
    13  	"os"
    14  	"path/filepath"
    15  	"sort"
    16  	"strings"
    17  	"testing"
    18  
    19  	"go-hep.org/x/hep/groot/internal/rcompress"
    20  	"go-hep.org/x/hep/groot/internal/rtests"
    21  	"go-hep.org/x/hep/groot/rbase"
    22  	"go-hep.org/x/hep/groot/riofs"
    23  	"go-hep.org/x/hep/groot/root"
    24  )
    25  
    26  func TestCompress(t *testing.T) {
    27  	t.Parallel()
    28  
    29  	dir, err := os.MkdirTemp("", "groot-rcompress-")
    30  	if err != nil {
    31  		t.Fatal(err)
    32  	}
    33  	defer os.RemoveAll(dir)
    34  
    35  	wants := map[string]root.Object{
    36  		"small": rbase.NewObjString("hello"),
    37  		"10mb":  rbase.NewObjString(strings.Repeat("-+", 10*1024*1024)),
    38  		"16mb":  rbase.NewObjString(strings.Repeat("-+", 16*1024*1024)),
    39  	}
    40  
    41  	const macroROOT = `
    42  #include "TFile.h"
    43  #include "TObjString.h"
    44  #include <iostream>
    45  
    46  void testcompress(const char *fname, int size) {
    47  	auto f = TFile::Open(fname, "READ");
    48  	auto str = (TObjString*)f->Get("str");
    49  	if (str == nullptr) { exit(1); }
    50  	if (str->GetString().Length() != size) {
    51  		std::cerr << "invalid length: got=" << str->GetString().Length()
    52  				  << " want=" << size << "\n";
    53  		exit(2);
    54  	}
    55  
    56  	exit(0);
    57  }
    58  `
    59  	for _, tc := range []struct {
    60  		name string
    61  		opt  riofs.FileOption
    62  	}{
    63  		{name: "default", opt: func(*riofs.File) error { return nil }},
    64  		{name: "default-nil", opt: nil},
    65  		{name: "no-compr", opt: riofs.WithoutCompression()},
    66  		// lz4
    67  		{name: "lz4-default", opt: riofs.WithLZ4(flate.DefaultCompression)},
    68  		{name: "lz4-0", opt: riofs.WithLZ4(0)},
    69  		{name: "lz4-1", opt: riofs.WithLZ4(1)},
    70  		{name: "lz4-9", opt: riofs.WithLZ4(9)},
    71  		{name: "lz4-best-speed", opt: riofs.WithLZ4(flate.BestSpeed)},
    72  		{name: "lz4-best-compr", opt: riofs.WithLZ4(flate.BestCompression)},
    73  		// lzma
    74  		{name: "lzma-default", opt: riofs.WithLZMA(flate.DefaultCompression)},
    75  		{name: "lzma-0", opt: riofs.WithLZMA(0)},
    76  		{name: "lzma-1", opt: riofs.WithLZMA(1)},
    77  		{name: "lzma-9", opt: riofs.WithLZMA(9)},
    78  		{name: "lzma-best-speed", opt: riofs.WithLZMA(flate.BestSpeed)},
    79  		{name: "lzma-best-compr", opt: riofs.WithLZMA(flate.BestCompression)},
    80  		// zlib
    81  		{name: "zlib-default", opt: riofs.WithZlib(flate.DefaultCompression)},
    82  		{name: "zlib-0", opt: riofs.WithZlib(0)},
    83  		{name: "zlib-1", opt: riofs.WithZlib(1)},
    84  		{name: "zlib-9", opt: riofs.WithZlib(9)},
    85  		{name: "zlib-best-speed", opt: riofs.WithZlib(flate.BestSpeed)},
    86  		{name: "zlib-best-compr", opt: riofs.WithZlib(flate.BestCompression)},
    87  		// zstd
    88  		{name: "zstd-default", opt: riofs.WithZstd(flate.DefaultCompression)},
    89  		{name: "zstd-0", opt: riofs.WithZstd(0)},
    90  		{name: "zstd-1", opt: riofs.WithZstd(1)},
    91  		{name: "zstd-9", opt: riofs.WithZstd(9)},
    92  		{name: "zstd-best-speed", opt: riofs.WithZstd(flate.BestSpeed)},
    93  		{name: "zstd-best-compr", opt: riofs.WithZstd(flate.BestCompression)},
    94  	} {
    95  		for k, want := range wants {
    96  			if (k == "16mb" || k == "10mb") &&
    97  				!strings.HasSuffix(tc.name, "best-compr") &&
    98  				!strings.HasSuffix(tc.name, "-1") {
    99  				continue
   100  			}
   101  			tname := fmt.Sprintf("%s-%s", k, tc.name)
   102  			t.Run(tname, func(t *testing.T) {
   103  				fname := filepath.Join(dir, "test-"+tname+".root")
   104  				w, err := riofs.Create(fname, tc.opt)
   105  				if err != nil {
   106  					t.Fatalf("%+v", err)
   107  				}
   108  				defer w.Close()
   109  
   110  				err = w.Put("str", want)
   111  				if err != nil {
   112  					t.Fatalf("%+v", err)
   113  				}
   114  
   115  				err = w.Close()
   116  				if err != nil {
   117  					t.Fatalf("%+v", err)
   118  				}
   119  
   120  				r, err := riofs.Open(fname)
   121  				if err != nil {
   122  					t.Fatalf("%+v", err)
   123  				}
   124  				defer r.Close()
   125  
   126  				obj, err := r.Get("str")
   127  				if err != nil {
   128  					t.Fatalf("%+v", err)
   129  				}
   130  				str := obj.(root.ObjString)
   131  
   132  				if got, want := str.String(), want.(root.ObjString).String(); got != want {
   133  					t.Fatalf("got:\n%s\nwant:\n%s", got, want)
   134  				}
   135  
   136  				if !rtests.HasROOT {
   137  					return
   138  				}
   139  
   140  				out, err := rtests.RunCxxROOT("testcompress", []byte(macroROOT), fname, len(want.(root.ObjString).String()))
   141  				if err != nil {
   142  					t.Fatalf("error: %+v\n%s\n", err, out)
   143  				}
   144  			})
   145  		}
   146  	}
   147  }
   148  
   149  func TestRoundtrip(t *testing.T) {
   150  	wants := map[string][]byte{
   151  		"00-10kb": []byte(strings.Repeat("-+", 10*1024)),
   152  		"01-16mb": []byte(strings.Repeat("-+", 16*1024*1024-2)), // remove 2 so divisible by kMaxCompressedBlockSize
   153  	}
   154  	keysOf := func(kvs map[string][]byte) []string {
   155  		keys := make([]string, 0, len(kvs))
   156  		for k := range kvs {
   157  			keys = append(keys, k)
   158  		}
   159  		sort.Strings(keys)
   160  		return keys
   161  	}
   162  
   163  	for _, tc := range []struct {
   164  		name string
   165  		opt  rcompress.Settings
   166  	}{
   167  		// lz4
   168  		{name: "lz4-default", opt: rcompress.Settings{Alg: rcompress.LZ4, Lvl: flate.DefaultCompression}},
   169  		// lzma
   170  		{name: "lzma-default", opt: rcompress.Settings{Alg: rcompress.LZMA, Lvl: flate.DefaultCompression}},
   171  		// zlib
   172  		{name: "zlib-default", opt: rcompress.Settings{Alg: rcompress.ZLIB, Lvl: flate.DefaultCompression}},
   173  		// zstd
   174  		{name: "zstd-default", opt: rcompress.Settings{Alg: rcompress.ZSTD, Lvl: flate.DefaultCompression}},
   175  	} {
   176  		for _, k := range keysOf(wants) {
   177  			tname := fmt.Sprintf("%s-%s", tc.name, k)
   178  			t.Run(tname, func(t *testing.T) {
   179  				defer func() {
   180  					err := recover()
   181  					if err != nil {
   182  						t.Fatalf("test panicked: %q", err)
   183  					}
   184  				}()
   185  
   186  				want := wants[k]
   187  				xsrc, err := rcompress.Compress(nil, want, tc.opt.Compression())
   188  				if err != nil {
   189  					t.Fatalf("could not create compressed source: %+v", err)
   190  				}
   191  				xdst := make([]byte, len(want))
   192  				err = rcompress.Decompress(xdst, bytes.NewReader(xsrc))
   193  				if err != nil {
   194  					t.Fatalf("could not decompress xsrc: %+v", err)
   195  				}
   196  				if !bytes.Equal(xdst, want) {
   197  					t.Fatalf("round-trip failed: %+v", err)
   198  				}
   199  			})
   200  		}
   201  	}
   202  }
   203  func BenchmarkCompression(b *testing.B) {
   204  	b.ReportAllocs()
   205  
   206  	wants := map[string][]byte{
   207  		"00-10kb": []byte(strings.Repeat("-+", 10*1024)),
   208  		"01-10mb": []byte(strings.Repeat("-+", 10*1024*1024)),
   209  		"02-16mb": []byte(strings.Repeat("-+", 16*1024*1024)),
   210  	}
   211  	keysOf := func(kvs map[string][]byte) []string {
   212  		keys := make([]string, 0, len(kvs))
   213  		for k := range kvs {
   214  			keys = append(keys, k)
   215  		}
   216  		sort.Strings(keys)
   217  		return keys
   218  	}
   219  
   220  	for _, tc := range []struct {
   221  		name string
   222  		opt  rcompress.Settings
   223  	}{
   224  		{name: "default", opt: rcompress.DefaultSettings},
   225  		// lz4
   226  		{name: "lz4-default", opt: rcompress.Settings{Alg: rcompress.LZ4, Lvl: flate.DefaultCompression}},
   227  		{name: "lz4-1", opt: rcompress.Settings{Alg: rcompress.LZ4, Lvl: 1}},
   228  		{name: "lz4-9", opt: rcompress.Settings{Alg: rcompress.LZ4, Lvl: 9}},
   229  		{name: "lz4-best-speed", opt: rcompress.Settings{Alg: rcompress.LZ4, Lvl: flate.BestSpeed}},
   230  		{name: "lz4-best-compr", opt: rcompress.Settings{Alg: rcompress.LZ4, Lvl: flate.BestCompression}},
   231  		// lzma
   232  		{name: "lzma-default", opt: rcompress.Settings{Alg: rcompress.LZMA, Lvl: flate.DefaultCompression}},
   233  		{name: "lzma-1", opt: rcompress.Settings{Alg: rcompress.LZMA, Lvl: 1}},
   234  		{name: "lzma-9", opt: rcompress.Settings{Alg: rcompress.LZMA, Lvl: 9}},
   235  		{name: "lzma-best-speed", opt: rcompress.Settings{Alg: rcompress.LZMA, Lvl: flate.BestSpeed}},
   236  		{name: "lzma-best-compr", opt: rcompress.Settings{Alg: rcompress.LZMA, Lvl: flate.BestCompression}},
   237  		// zlib
   238  		{name: "zlib-default", opt: rcompress.Settings{Alg: rcompress.ZLIB, Lvl: flate.DefaultCompression}},
   239  		{name: "zlib-1", opt: rcompress.Settings{Alg: rcompress.ZLIB, Lvl: 1}},
   240  		{name: "zlib-9", opt: rcompress.Settings{Alg: rcompress.ZLIB, Lvl: 9}},
   241  		{name: "zlib-best-speed", opt: rcompress.Settings{Alg: rcompress.ZLIB, Lvl: flate.BestSpeed}},
   242  		{name: "zlib-best-compr", opt: rcompress.Settings{Alg: rcompress.ZLIB, Lvl: flate.BestCompression}},
   243  		// zstd
   244  		{name: "zstd-default", opt: rcompress.Settings{Alg: rcompress.ZSTD, Lvl: flate.DefaultCompression}},
   245  		{name: "zstd-1", opt: rcompress.Settings{Alg: rcompress.ZSTD, Lvl: 1}},
   246  		{name: "zstd-9", opt: rcompress.Settings{Alg: rcompress.ZSTD, Lvl: 9}},
   247  		{name: "zstd-best-speed", opt: rcompress.Settings{Alg: rcompress.ZSTD, Lvl: flate.BestSpeed}},
   248  		{name: "zstd-best-compr", opt: rcompress.Settings{Alg: rcompress.ZSTD, Lvl: flate.BestCompression}},
   249  	} {
   250  		for _, k := range keysOf(wants) {
   251  			want := wants[k]
   252  			tname := fmt.Sprintf("%s-%s", tc.name, k)
   253  			compr := tc.opt.Compression()
   254  			defer func() {
   255  				err := recover()
   256  				if err != nil {
   257  					b.Fatalf("%s panicked: %+v", tname, err)
   258  				}
   259  			}()
   260  			xsrc, err := rcompress.Compress(nil, want, compr)
   261  			if err != nil {
   262  				b.Fatalf("could not create compressed source: %+v", err)
   263  			}
   264  			xdst := make([]byte, len(want))
   265  			err = rcompress.Decompress(xdst, bytes.NewReader(xsrc))
   266  			if err != nil {
   267  				b.Fatalf("could not decompress xsrc: %+v", err)
   268  			}
   269  			if !bytes.Equal(xdst, want) {
   270  				b.Fatalf("round-trip failed: %+v", err)
   271  			}
   272  
   273  			b.Run("enc-"+tname, func(b *testing.B) {
   274  				src := want
   275  				dst := make([]byte, len(src))
   276  				b.ResetTimer()
   277  				for i := 0; i < b.N; i++ {
   278  					_, err := rcompress.Compress(dst, src, compr)
   279  					if err != nil {
   280  						b.Fatalf("%+v", err)
   281  					}
   282  				}
   283  			})
   284  
   285  			b.Run("dec-"+tname, func(b *testing.B) {
   286  				dst := make([]byte, len(want))
   287  				b.ResetTimer()
   288  				for i := 0; i < b.N; i++ {
   289  					src := bytes.NewReader(xsrc)
   290  					err := rcompress.Decompress(dst, src)
   291  					if err != nil {
   292  						b.Fatalf("%+v", err)
   293  					}
   294  				}
   295  			})
   296  		}
   297  	}
   298  }