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 }