diff --git a/cmd/npy2root/main_test.go b/cmd/npy2root/main_test.go index 6384f92d8..2c8d05479 100644 --- a/cmd/npy2root/main_test.go +++ b/cmd/npy2root/main_test.go @@ -5,6 +5,7 @@ package main import ( + "fmt" "io/ioutil" "math" "os" @@ -280,21 +281,16 @@ func TestConvert(t *testing.T) { tree := obj.(rtree.Tree) rvars := rtree.NewReadVars(tree) - scan, err := rtree.NewScannerVars(tree, rvars...) + r, err := rtree.NewReader(tree, rvars) if err != nil { - t.Fatalf("could not create tree scanner: %+v", err) + t.Fatalf("could not create tree reader: %+v", err) } - defer scan.Close() + defer r.Close() want := reflect.ValueOf(tc.want) n := 0 - for scan.Next() { - err := scan.Scan() - if err != nil { - t.Fatalf("could not read entry %d: %+v", scan.Entry(), err) - } - - i := int(scan.Entry()) + err = r.Read(func(ctx rtree.RCtx) error { + i := int(ctx.Entry) want := want.Index(i).Interface() got := reflect.ValueOf(rvars[0].Value).Elem().Interface() ok := false @@ -316,9 +312,13 @@ func TestConvert(t *testing.T) { } if !ok { - t.Fatalf("invalid value for entry %d:\ngot= %v\nwant=%v", scan.Entry(), got, want) + return fmt.Errorf("invalid value for entry %d:\ngot= %v\nwant=%v", ctx.Entry, got, want) } n++ + return nil + }) + if err != nil { + t.Fatalf("could not read tree: %+v", err) } if got, want := n, want.Len(); got != want { diff --git a/cmd/root2csv/main.go b/cmd/root2csv/main.go index 1688bc7e8..54d84027a 100644 --- a/cmd/root2csv/main.go +++ b/cmd/root2csv/main.go @@ -116,20 +116,20 @@ func process(oname, fname, tname string) error { } log.Printf("scanning leaves... [done]") - sc, err := rtree.NewTreeScannerVars(tree, nt.args...) + r, err := rtree.NewReader(tree, nt.args) if err != nil { - return fmt.Errorf("could not create tree scanner: %w", err) + return fmt.Errorf("could not create tree reader: %w", err) } - defer sc.Close() + defer r.Close() nrows := 0 - for sc.Next() { - err = sc.Scan(nt.vars...) - if err != nil { - return fmt.Errorf("could not scan entry %d: %w", nrows, err) - } + err = r.Read(func(ctx rtree.RCtx) error { nt.fill() nrows++ + return nil + }) + if err != nil { + return fmt.Errorf("could not read tree: %w", err) } tbl, err := csvutil.Create(oname) diff --git a/cmd/root2fits/main.go b/cmd/root2fits/main.go index 92f70fbb7..3ccfb9025 100644 --- a/cmd/root2fits/main.go +++ b/cmd/root2fits/main.go @@ -122,21 +122,22 @@ func process(oname, tname, fname string) error { for i, rvar := range rvars { wvars[i] = rvar.Value } - scan, err := rtree.NewScannerVars(tree, rvars...) + + r, err := rtree.NewReader(tree, rvars) if err != nil { - return fmt.Errorf("could not create ROOT scanner: %w", err) + return fmt.Errorf("could not create ROOT reader: %w", err) } - defer scan.Close() + defer r.Close() - for scan.Next() { - err = scan.Scan() - if err != nil { - return fmt.Errorf("could not read entry %d from ROOT tree: %w", scan.Entry(), err) - } + err = r.Read(func(ctx rtree.RCtx) error { err = tbl.Write(wvars...) if err != nil { - return fmt.Errorf("could not write entry %d to FITS table: %w", scan.Entry(), err) + return fmt.Errorf("could not write entry %d to FITS table: %w", ctx.Entry, err) } + return nil + }) + if err != nil { + return fmt.Errorf("could not read input ROOT file: %w", err) } err = fits.Write(tbl) diff --git a/groot/rtree/chain_test.go b/groot/rtree/chain_test.go index 3c78b7436..f64eac64f 100644 --- a/groot/rtree/chain_test.go +++ b/groot/rtree/chain_test.go @@ -6,7 +6,6 @@ package rtree_test import ( "fmt" - "io" "reflect" "testing" @@ -352,7 +351,7 @@ func TestChainOf(t *testing.T) { } } -func TestChainScanStruct(t *testing.T) { +func TestChainReaderStruct(t *testing.T) { files := []string{ "../testdata/chain.1.root", "../testdata/chain.2.root", @@ -422,421 +421,26 @@ func TestChainScanStruct(t *testing.T) { return data } - sc, err := rtree.NewTreeScanner(chain, &Data{}) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - - for sc.Next() { - var d1 Data - err := sc.Scan(&d1) - if err != nil { - t.Fatal(err) - } - i := sc.Entry() - if !reflect.DeepEqual(d1, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v\nwant=%#v\n", i, d1, want(i)) - } - - var d2 Data - err = sc.Scan(&d2) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(d2, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v\nwant=%#v\n", i, d2, want(i)) - } - total.got++ - } - - if err := sc.Err(); err != nil && err != io.EOF { - t.Fatal(err) - } - - if total.got != total.want { - t.Fatalf("entries scanned differ: got=%d want=%d", total.got, total.want) - } -} - -var SumF64 float64 // global variable to prevent unwanted compiler optimization - -func BenchmarkChainTreeScannerStruct(b *testing.B) { - files := []string{ - "../testdata/chain.1.root", - "../testdata/chain.2.root", - } - - trees := make([]rtree.Tree, len(files)) - for i, fname := range files { - f, err := riofs.Open(fname) - if err != nil { - b.Fatalf("could not open ROOT file %q: %v", fname, err) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - b.Fatal(err) - } - - trees[i] = obj.(rtree.Tree) - } - chain := rtree.Chain(trees...) - - type Data struct { - Event struct { - Beg string `groot:"Beg"` - F64 float64 `groot:"F64"` - ArrF64 [10]float64 `groot:"ArrayF64"` - N int32 `groot:"N"` - SliF64 []float64 `groot:"SliceF64"` - StdStr string `groot:"StdStr"` - StlVecF64 []float64 `groot:"StlVecF64"` - StlVecStr []string `groot:"StlVecStr"` - End string `groot:"End"` - } `groot:"evt"` - } - - sc, err := rtree.NewTreeScanner(chain, &Data{}) - if err != nil { - b.Fatal(err) - } - defer sc.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - err := sc.SeekEntry(0) - if err != nil { - b.Fatal(err) - } - for sc.Next() { - var data Data - err := sc.Scan(&data) - if err != nil { - b.Fatal(err) - } - SumF64 += data.Event.F64 - } - } - if err := sc.Err(); err != nil && err != io.EOF { - b.Fatal(err) - } -} - -func TestChainSeekEntryTreeScannerPtr(t *testing.T) { - files := []string{ - "../testdata/chain.flat.1.root", - "../testdata/chain.flat.2.root", - } - - trees := make([]rtree.Tree, len(files)) - for i, fname := range files { - f, err := riofs.Open(fname) - if err != nil { - t.Fatalf("could not open ROOT file %q: %v", fname, err) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - - trees[i] = obj.(rtree.Tree) - } - chain := rtree.Chain(trees...) - - type Data struct { - F64 float64 `groot:"F64"` - ArrF64 [10]float64 `groot:"ArrF64"` - N int32 `groot:"N"` - SliF64 []float64 `groot:"SliF64"` - } - - sc, err := rtree.NewTreeScanner(chain, &Data{}) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - - var entries = []int64{0, 1, 4, 2, 5, 6, 7, 2, 7, 3, 0, 9} - for _, entry := range entries { - err := sc.SeekEntry(entry) - if err != nil { - t.Fatalf("Could not seek to entry %d: %v", entry, err) - } - - if !sc.Next() { - t.Fatalf("Could not read entry %d", entry) - } - - var data Data - - err = sc.Scan(&data) - if err != nil { - t.Fatal(err) - } - - i := sc.Entry() - - if i != entry { - t.Fatalf("did not seek to entry %d. got=%d, want=%d", entry, i, entry) - } - if data.F64 != float64(i) { - t.Fatalf("entry [%d] : got= %#v want=%#v\n", i, data.F64, float64(i)) - } - var arr [10]float64 - for ii := range arr { - arr[ii] = float64(i) - } - if data.ArrF64 != arr { - t.Fatalf("entry [%d] : got= %#v want=%#v\n", i, data.ArrF64, arr) - } - sli := arr[:int(data.N)] - if !reflect.DeepEqual(sli, data.SliF64) { - t.Fatalf("entry [%d] : got= %#v want=%#v\n", i, data.SliF64, sli) - } - } -} - -func TestChainSeekEntryTreeScannerVars(t *testing.T) { - files := []string{ - "../testdata/chain.flat.1.root", - "../testdata/chain.flat.2.root", - } - - trees := make([]rtree.Tree, len(files)) - for i, fname := range files { - f, err := riofs.Open(fname) - if err != nil { - t.Fatalf("could not open ROOT file %q: %v", fname, err) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - - trees[i] = obj.(rtree.Tree) - } - chain := rtree.Chain(trees...) - - type Data struct { - F64 float64 `groot:"F64"` - ArrF64 [10]float64 `groot:"ArrF64"` - N int32 `groot:"N"` - SliF64 []float64 `groot:"SliF64"` - } - - var data Data - rvars := []rtree.ReadVar{ - {Name: "F64", Value: &data.F64}, - {Name: "ArrF64", Value: &data.ArrF64}, - {Name: "N", Value: &data.N}, - {Name: "SliF64", Value: &data.SliF64}, - } - - sc, err := rtree.NewTreeScannerVars(chain, rvars...) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - - var entries = []int64{0, 1, 4, 2, 5, 6, 7, 2, 7, 3, 0, 9} - for _, entry := range entries { - err := sc.SeekEntry(entry) - if err != nil { - t.Fatalf("Could not seek to entry %d: %v", entry, err) - } - - if !sc.Next() { - t.Fatalf("Could not read entry %d", entry) - } - - var data Data - - err = sc.Scan(&data.F64, &data.ArrF64, &data.N, &data.SliF64) - if err != nil { - t.Fatal(err) - } - - i := sc.Entry() - - if i != entry { - t.Fatalf("did not seek to entry %d. got=%d, want=%d", entry, i, entry) - } - if data.F64 != float64(i) { - t.Fatalf("entry [%d] : got= %#v want=%#v\n", i, data.F64, float64(i)) - } - var arr [10]float64 - for ii := range arr { - arr[ii] = float64(i) - } - if data.ArrF64 != arr { - t.Fatalf("entry [%d] : got= %#v want=%#v\n", i, data.ArrF64, arr) - } - sli := arr[:int(data.N)] - if !reflect.DeepEqual(sli, data.SliF64) { - t.Fatalf("entry [%d] : got= %#v want=%#v\n", i, data.SliF64, sli) - } - } -} - -func TestChainSeekEntryScannerPtr(t *testing.T) { - files := []string{ - "../testdata/chain.flat.1.root", - "../testdata/chain.flat.2.root", - } - - trees := make([]rtree.Tree, len(files)) - for i, fname := range files { - f, err := riofs.Open(fname) - if err != nil { - t.Fatalf("could not open ROOT file %q: %v", fname, err) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - - trees[i] = obj.(rtree.Tree) - } - chain := rtree.Chain(trees...) - - type Data struct { - F64 float64 `groot:"F64"` - ArrF64 [10]float64 `groot:"ArrF64"` - N int32 `groot:"N"` - SliF64 []float64 `groot:"SliF64"` - } - var data Data - sc, err := rtree.NewScanner(chain, &data) + r, err := rtree.NewReader(chain, rtree.ReadVarsFromStruct(&data)) if err != nil { t.Fatal(err) } - defer sc.Close() - - var entries = []int64{0, 1, 4, 2, 5, 6, 7, 2, 7, 3, 0, 9} - for _, entry := range entries { - err := sc.SeekEntry(entry) - if err != nil { - t.Fatalf("Could not seek to entry %d: %v", entry, err) - } - - if !sc.Next() { - t.Fatalf("Could not read entry %d", entry) - } - - err = sc.Scan() - if err != nil { - t.Fatal(err) - } - - i := sc.Entry() - - if i != entry { - t.Fatalf("did not seek to entry %d. got=%d, want=%d", entry, i, entry) - } - if data.F64 != float64(i) { - t.Fatalf("entry [%d] : got= %#v want=%#v\n", i, data.F64, float64(i)) - } - var arr [10]float64 - for ii := range arr { - arr[ii] = float64(i) - } - if data.ArrF64 != arr { - t.Fatalf("entry [%d] : got= %#v want=%#v\n", i, data.ArrF64, arr) - } - sli := arr[:int(data.N)] - if !reflect.DeepEqual(sli, data.SliF64) { - t.Fatalf("entry [%d] : got= %#v want=%#v\n", i, data.SliF64, sli) - } - } -} - -func TestChainSeekEntryScannerVars(t *testing.T) { - files := []string{ - "../testdata/chain.flat.1.root", - "../testdata/chain.flat.2.root", - } - - trees := make([]rtree.Tree, len(files)) - for i, fname := range files { - f, err := riofs.Open(fname) - if err != nil { - t.Fatalf("could not open ROOT file %q: %v", fname, err) - } - defer f.Close() + defer r.Close() - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) + err = r.Read(func(ctx rtree.RCtx) error { + i := ctx.Entry + if !reflect.DeepEqual(data, want(i)) { + return fmt.Errorf("entry[%d]:\ngot= %#v\nwant=%#v\n", i, data, want(i)) } - - trees[i] = obj.(rtree.Tree) - } - chain := rtree.Chain(trees...) - - type Data struct { - F64 float64 `groot:"F64"` - ArrF64 [10]float64 `groot:"ArrF64"` - N int32 `groot:"N"` - SliF64 []float64 `groot:"SliF64"` - } - - var data Data - rvars := []rtree.ReadVar{ - {Name: "F64", Value: &data.F64}, - {Name: "ArrF64", Value: &data.ArrF64}, - {Name: "N", Value: &data.N}, - {Name: "SliF64", Value: &data.SliF64}, - } - - sc, err := rtree.NewScannerVars(chain, rvars...) + total.got++ + return nil + }) if err != nil { t.Fatal(err) } - defer sc.Close() - var entries = []int64{0, 1, 4, 2, 5, 6, 7, 2, 7, 3, 0, 9} - for _, entry := range entries { - err := sc.SeekEntry(entry) - if err != nil { - t.Fatalf("Could not seek to entry %d: %v", entry, err) - } - - if !sc.Next() { - t.Fatalf("Could not read entry %d", entry) - } - - err = sc.Scan() - if err != nil { - t.Fatal(err) - } - - i := sc.Entry() - - if i != entry { - t.Fatalf("did not seek to entry %d. got=%d, want=%d", entry, i, entry) - } - if data.F64 != float64(i) { - t.Fatalf("entry [%d] : got= %#v want=%#v\n", i, data.F64, float64(i)) - } - var arr [10]float64 - for ii := range arr { - arr[ii] = float64(i) - } - if data.ArrF64 != arr { - t.Fatalf("entry [%d] : got= %#v want=%#v\n", i, data.ArrF64, arr) - } - sli := arr[:int(data.N)] - if !reflect.DeepEqual(sli, data.SliF64) { - t.Fatalf("entry [%d] : got= %#v want=%#v\n", i, data.SliF64, sli) - } + if total.got != total.want { + t.Fatalf("entries differ: got=%d want=%d", total.got, total.want) } } diff --git a/groot/rtree/example_scanner_test.go b/groot/rtree/example_scanner_test.go deleted file mode 100644 index 32c4c329f..000000000 --- a/groot/rtree/example_scanner_test.go +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright ©2017 The go-hep 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 rtree_test - -import ( - "fmt" - "io" - "log" - - "go-hep.org/x/hep/groot/riofs" - "go-hep.org/x/hep/groot/rtree" -) - -func ExampleTreeScanner() { - log.SetPrefix("groot: ") - log.SetFlags(0) - - f, err := riofs.Open("../testdata/small-flat-tree.root") - if err != nil { - log.Fatal(err) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - log.Fatal(err) - } - - tree := obj.(rtree.Tree) - - // like for the encoding/json package, struct fields need to - // be exported to be properly handled by rtree.Scanner. - // Thus, if the ROOT branch name is lower-case, use the "groot" - // struct-tag like shown below. - type Data struct { - I64 int64 `groot:"Int64"` - F64 float64 `groot:"Float64"` - Str string `groot:"Str"` - ArrF64 [10]float64 `groot:"ArrayFloat64"` - N int32 `groot:"N"` - SliF64 []float64 `groot:"SliceFloat64"` - } - - sc, err := rtree.NewTreeScanner(tree, &Data{}) - if err != nil { - log.Fatal(err) - } - defer sc.Close() - - for sc.Next() { - var data Data - err := sc.Scan(&data) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("entry[%d]: %+v\n", sc.Entry(), data) - if sc.Entry() == 9 { - break - } - } - - if err := sc.Err(); err != nil && err != io.EOF { - log.Fatal(err) - } - - // Output: - // entry[0]: {I64:0 F64:0 Str:evt-000 ArrF64:[0 0 0 0 0 0 0 0 0 0] N:0 SliF64:[]} - // entry[1]: {I64:1 F64:1 Str:evt-001 ArrF64:[1 1 1 1 1 1 1 1 1 1] N:1 SliF64:[1]} - // entry[2]: {I64:2 F64:2 Str:evt-002 ArrF64:[2 2 2 2 2 2 2 2 2 2] N:2 SliF64:[2 2]} - // entry[3]: {I64:3 F64:3 Str:evt-003 ArrF64:[3 3 3 3 3 3 3 3 3 3] N:3 SliF64:[3 3 3]} - // entry[4]: {I64:4 F64:4 Str:evt-004 ArrF64:[4 4 4 4 4 4 4 4 4 4] N:4 SliF64:[4 4 4 4]} - // entry[5]: {I64:5 F64:5 Str:evt-005 ArrF64:[5 5 5 5 5 5 5 5 5 5] N:5 SliF64:[5 5 5 5 5]} - // entry[6]: {I64:6 F64:6 Str:evt-006 ArrF64:[6 6 6 6 6 6 6 6 6 6] N:6 SliF64:[6 6 6 6 6 6]} - // entry[7]: {I64:7 F64:7 Str:evt-007 ArrF64:[7 7 7 7 7 7 7 7 7 7] N:7 SliF64:[7 7 7 7 7 7 7]} - // entry[8]: {I64:8 F64:8 Str:evt-008 ArrF64:[8 8 8 8 8 8 8 8 8 8] N:8 SliF64:[8 8 8 8 8 8 8 8]} - // entry[9]: {I64:9 F64:9 Str:evt-009 ArrF64:[9 9 9 9 9 9 9 9 9 9] N:9 SliF64:[9 9 9 9 9 9 9 9 9]} -} - -func ExampleTreeScanner_withVars() { - log.SetPrefix("groot: ") - log.SetFlags(0) - - f, err := riofs.Open("../testdata/small-flat-tree.root") - if err != nil { - log.Fatal(err) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - log.Fatal(err) - } - - tree := obj.(rtree.Tree) - - rvars := []rtree.ReadVar{ - {Name: "Int64"}, - {Name: "Float64"}, - {Name: "Str"}, - {Name: "ArrayFloat64"}, - {Name: "N"}, - {Name: "SliceFloat64"}, - } - sc, err := rtree.NewTreeScannerVars(tree, rvars...) - if err != nil { - log.Fatal(err) - } - defer sc.Close() - - for sc.Next() { - var ( - i64 int64 - f64 float64 - str string - arr [10]float64 - n int32 - sli []float64 - ) - err := sc.Scan(&i64, &f64, &str, &arr, &n, &sli) - if err != nil { - log.Fatal(err) - } - - fmt.Printf( - "entry[%d]: i64=%v f64=%v str=%q arr=%v n=%d sli=%v\n", - sc.Entry(), - i64, f64, str, arr, n, sli, - ) - if sc.Entry() == 9 { - break - } - } - - if err := sc.Err(); err != nil && err != io.EOF { - log.Fatal(err) - } - - // Output: - // entry[0]: i64=0 f64=0 str="evt-000" arr=[0 0 0 0 0 0 0 0 0 0] n=0 sli=[] - // entry[1]: i64=1 f64=1 str="evt-001" arr=[1 1 1 1 1 1 1 1 1 1] n=1 sli=[1] - // entry[2]: i64=2 f64=2 str="evt-002" arr=[2 2 2 2 2 2 2 2 2 2] n=2 sli=[2 2] - // entry[3]: i64=3 f64=3 str="evt-003" arr=[3 3 3 3 3 3 3 3 3 3] n=3 sli=[3 3 3] - // entry[4]: i64=4 f64=4 str="evt-004" arr=[4 4 4 4 4 4 4 4 4 4] n=4 sli=[4 4 4 4] - // entry[5]: i64=5 f64=5 str="evt-005" arr=[5 5 5 5 5 5 5 5 5 5] n=5 sli=[5 5 5 5 5] - // entry[6]: i64=6 f64=6 str="evt-006" arr=[6 6 6 6 6 6 6 6 6 6] n=6 sli=[6 6 6 6 6 6] - // entry[7]: i64=7 f64=7 str="evt-007" arr=[7 7 7 7 7 7 7 7 7 7] n=7 sli=[7 7 7 7 7 7 7] - // entry[8]: i64=8 f64=8 str="evt-008" arr=[8 8 8 8 8 8 8 8 8 8] n=8 sli=[8 8 8 8 8 8 8 8] - // entry[9]: i64=9 f64=9 str="evt-009" arr=[9 9 9 9 9 9 9 9 9 9] n=9 sli=[9 9 9 9 9 9 9 9 9] -} - -func ExampleScanner_withStruct() { - log.SetPrefix("groot: ") - log.SetFlags(0) - - f, err := riofs.Open("../testdata/small-flat-tree.root") - if err != nil { - log.Fatal(err) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - log.Fatal(err) - } - - tree := obj.(rtree.Tree) - - // like for the encoding/json package, struct fields need to - // be exported to be properly handled by rtree.Scanner. - // Thus, if the ROOT branch name is lower-case, use the "groot" - // struct-tag like shown below. - type Data struct { - I64 int64 `groot:"Int64"` - F64 float64 `groot:"Float64"` - Str string `groot:"Str"` - ArrF64 [10]float64 `groot:"ArrayFloat64"` - N int32 `groot:"N"` - SliF64 []float64 `groot:"SliceFloat64"` - } - - var data Data - sc, err := rtree.NewScanner(tree, &data) - if err != nil { - log.Fatal(err) - } - defer sc.Close() - - for sc.Next() { - err := sc.Scan() - if err != nil { - log.Fatal(err) - } - - fmt.Printf("entry[%d]: %+v\n", sc.Entry(), data) - if sc.Entry() == 9 { - break - } - } - - if err := sc.Err(); err != nil && err != io.EOF { - log.Fatal(err) - } - - // Output: - // entry[0]: {I64:0 F64:0 Str:evt-000 ArrF64:[0 0 0 0 0 0 0 0 0 0] N:0 SliF64:[]} - // entry[1]: {I64:1 F64:1 Str:evt-001 ArrF64:[1 1 1 1 1 1 1 1 1 1] N:1 SliF64:[1]} - // entry[2]: {I64:2 F64:2 Str:evt-002 ArrF64:[2 2 2 2 2 2 2 2 2 2] N:2 SliF64:[2 2]} - // entry[3]: {I64:3 F64:3 Str:evt-003 ArrF64:[3 3 3 3 3 3 3 3 3 3] N:3 SliF64:[3 3 3]} - // entry[4]: {I64:4 F64:4 Str:evt-004 ArrF64:[4 4 4 4 4 4 4 4 4 4] N:4 SliF64:[4 4 4 4]} - // entry[5]: {I64:5 F64:5 Str:evt-005 ArrF64:[5 5 5 5 5 5 5 5 5 5] N:5 SliF64:[5 5 5 5 5]} - // entry[6]: {I64:6 F64:6 Str:evt-006 ArrF64:[6 6 6 6 6 6 6 6 6 6] N:6 SliF64:[6 6 6 6 6 6]} - // entry[7]: {I64:7 F64:7 Str:evt-007 ArrF64:[7 7 7 7 7 7 7 7 7 7] N:7 SliF64:[7 7 7 7 7 7 7]} - // entry[8]: {I64:8 F64:8 Str:evt-008 ArrF64:[8 8 8 8 8 8 8 8 8 8] N:8 SliF64:[8 8 8 8 8 8 8 8]} - // entry[9]: {I64:9 F64:9 Str:evt-009 ArrF64:[9 9 9 9 9 9 9 9 9 9] N:9 SliF64:[9 9 9 9 9 9 9 9 9]} -} - -func ExampleScanner_withVars() { - log.SetPrefix("groot: ") - log.SetFlags(0) - - f, err := riofs.Open("../testdata/small-flat-tree.root") - if err != nil { - log.Fatal(err) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - log.Fatal(err) - } - - tree := obj.(rtree.Tree) - - var ( - i64 int64 - f64 float64 - str string - arr [10]float64 - n int32 - sli []float64 - ) - rvars := []rtree.ReadVar{ - {Name: "Int64", Value: &i64}, - {Name: "Float64", Value: &f64}, - {Name: "Str", Value: &str}, - {Name: "ArrayFloat64", Value: &arr}, - {Name: "N", Value: &n}, - {Name: "SliceFloat64", Value: &sli}, - } - sc, err := rtree.NewScannerVars(tree, rvars...) - if err != nil { - log.Fatal(err) - } - defer sc.Close() - - for sc.Next() { - err := sc.Scan() - if err != nil { - log.Fatal(err) - } - - fmt.Printf( - "entry[%d]: i64=%v f64=%v str=%q arr=%v n=%d sli=%v\n", - sc.Entry(), - i64, f64, str, arr, n, sli, - ) - if sc.Entry() == 9 { - break - } - } - - if err := sc.Err(); err != nil && err != io.EOF { - log.Fatal(err) - } - - // Output: - // entry[0]: i64=0 f64=0 str="evt-000" arr=[0 0 0 0 0 0 0 0 0 0] n=0 sli=[] - // entry[1]: i64=1 f64=1 str="evt-001" arr=[1 1 1 1 1 1 1 1 1 1] n=1 sli=[1] - // entry[2]: i64=2 f64=2 str="evt-002" arr=[2 2 2 2 2 2 2 2 2 2] n=2 sli=[2 2] - // entry[3]: i64=3 f64=3 str="evt-003" arr=[3 3 3 3 3 3 3 3 3 3] n=3 sli=[3 3 3] - // entry[4]: i64=4 f64=4 str="evt-004" arr=[4 4 4 4 4 4 4 4 4 4] n=4 sli=[4 4 4 4] - // entry[5]: i64=5 f64=5 str="evt-005" arr=[5 5 5 5 5 5 5 5 5 5] n=5 sli=[5 5 5 5 5] - // entry[6]: i64=6 f64=6 str="evt-006" arr=[6 6 6 6 6 6 6 6 6 6] n=6 sli=[6 6 6 6 6 6] - // entry[7]: i64=7 f64=7 str="evt-007" arr=[7 7 7 7 7 7 7 7 7 7] n=7 sli=[7 7 7 7 7 7 7] - // entry[8]: i64=8 f64=8 str="evt-008" arr=[8 8 8 8 8 8 8 8 8 8] n=8 sli=[8 8 8 8 8 8 8 8] - // entry[9]: i64=9 f64=9 str="evt-009" arr=[9 9 9 9 9 9 9 9 9 9] n=9 sli=[9 9 9 9 9 9 9 9 9] -} diff --git a/groot/rtree/reader_test.go b/groot/rtree/reader_test.go index 19d1b39cd..86d2ac357 100644 --- a/groot/rtree/reader_test.go +++ b/groot/rtree/reader_test.go @@ -7,9 +7,14 @@ package rtree import ( "fmt" "io" + "path/filepath" + "reflect" "testing" + "go-hep.org/x/hep/groot/internal/rtests" "go-hep.org/x/hep/groot/riofs" + _ "go-hep.org/x/hep/groot/riofs/plugin/xrootd" + "go-hep.org/x/hep/groot/root" ) func TestReader(t *testing.T) { @@ -161,3 +166,736 @@ func TestReader(t *testing.T) { }) } } + +type ScannerData struct { + B bool `groot:"B"` + Str string `groot:"Str"` + I8 int8 `groot:"I8"` + I16 int16 `groot:"I16"` + I32 int32 `groot:"I32"` + I64 int64 `groot:"I64"` + U8 uint8 `groot:"U8"` + U16 uint16 `groot:"U16"` + U32 uint32 `groot:"U32"` + U64 uint64 `groot:"U64"` + F32 float32 `groot:"F32"` + F64 float64 `groot:"F64"` + D16 root.Float16 `groot:"D16"` + D32 root.Double32 `groot:"D32"` + ArrBs [10]bool `groot:"ArrBs[10]"` + ArrI8 [10]int8 `groot:"ArrI8[10]"` + ArrI16 [10]int16 `groot:"ArrI16[10]"` + ArrI32 [10]int32 `groot:"ArrI32[10]"` + ArrI64 [10]int64 `groot:"ArrI64[10]"` + ArrU8 [10]uint8 `groot:"ArrU8[10]"` + ArrU16 [10]uint16 `groot:"ArrU16[10]"` + ArrU32 [10]uint32 `groot:"ArrU32[10]"` + ArrU64 [10]uint64 `groot:"ArrU64[10]"` + ArrF32 [10]float32 `groot:"ArrF32[10]"` + ArrF64 [10]float64 `groot:"ArrF64[10]"` + ArrD16 [10]root.Float16 `groot:"ArrD16[10]"` + ArrD32 [10]root.Double32 `groot:"ArrD32[10]"` + N int32 `groot:"N"` + SliBs []bool `groot:"SliBs[N]"` + SliI8 []int8 `groot:"SliI8[N]"` + SliI16 []int16 `groot:"SliI16[N]"` + SliI32 []int32 `groot:"SliI32[N]"` + SliI64 []int64 `groot:"SliI64[N]"` + SliU8 []uint8 `groot:"SliU8[N]"` + SliU16 []uint16 `groot:"SliU16[N]"` + SliU32 []uint32 `groot:"SliU32[N]"` + SliU64 []uint64 `groot:"SliU64[N]"` + SliF32 []float32 `groot:"SliF32[N]"` + SliF64 []float64 `groot:"SliF64[N]"` + SliD16 []root.Float16 `groot:"SliD16[N]"` + SliD32 []root.Double32 `groot:"SliD32[N]"` +} + +func (ScannerData) want(i int64) (data ScannerData) { + data.B = i%2 == 0 + data.Str = fmt.Sprintf("str-%d", i) + data.I8 = int8(-i) + data.I16 = int16(-i) + data.I32 = int32(-i) + data.I64 = int64(-i) + data.U8 = uint8(i) + data.U16 = uint16(i) + data.U32 = uint32(i) + data.U64 = uint64(i) + data.F32 = float32(i) + data.F64 = float64(i) + data.D16 = root.Float16(i) + data.D32 = root.Double32(i) + for ii := range data.ArrI32 { + data.ArrBs[ii] = ii == int(i) + data.ArrI8[ii] = int8(-i) + data.ArrI16[ii] = int16(-i) + data.ArrI32[ii] = int32(-i) + data.ArrI64[ii] = int64(-i) + data.ArrU8[ii] = uint8(i) + data.ArrU16[ii] = uint16(i) + data.ArrU32[ii] = uint32(i) + data.ArrU64[ii] = uint64(i) + data.ArrF32[ii] = float32(i) + data.ArrF64[ii] = float64(i) + data.ArrD16[ii] = root.Float16(i) + data.ArrD32[ii] = root.Double32(i) + } + data.N = int32(i) % 10 + data.SliBs = make([]bool, int(data.N)) + data.SliI8 = make([]int8, int(data.N)) + data.SliI16 = make([]int16, int(data.N)) + data.SliI32 = make([]int32, int(data.N)) + data.SliI64 = make([]int64, int(data.N)) + data.SliU8 = make([]uint8, int(data.N)) + data.SliU16 = make([]uint16, int(data.N)) + data.SliU32 = make([]uint32, int(data.N)) + data.SliU64 = make([]uint64, int(data.N)) + data.SliF32 = make([]float32, int(data.N)) + data.SliF64 = make([]float64, int(data.N)) + data.SliD16 = make([]root.Float16, int(data.N)) + data.SliD32 = make([]root.Double32, int(data.N)) + for ii := 0; ii < int(data.N); ii++ { + data.SliBs[ii] = (ii + 1) == int(i) + data.SliI8[ii] = int8(-i) + data.SliI16[ii] = int16(-i) + data.SliI32[ii] = int32(-i) + data.SliI64[ii] = int64(-i) + data.SliU8[ii] = uint8(i) + data.SliU16[ii] = uint16(i) + data.SliU32[ii] = uint32(i) + data.SliU64[ii] = uint64(i) + data.SliF32[ii] = float32(i) + data.SliF64[ii] = float64(i) + data.SliD16[ii] = root.Float16(i) + data.SliD32[ii] = root.Double32(i) + } + return data +} + +func TestReaderStruct(t *testing.T) { + for _, fname := range []string{ + "../testdata/x-flat-tree.root", + rtests.XrdRemote("testdata/x-flat-tree.root"), + } { + t.Run(fname, func(t *testing.T) { + t.Parallel() + + f, err := riofs.Open(fname) + if err != nil { + t.Fatal(err.Error()) + } + defer f.Close() + + obj, err := f.Get("tree") + if err != nil { + t.Fatal(err) + } + tree := obj.(Tree) + + var ( + want = ScannerData{}.want + data ScannerData + ) + r, err := NewReader(tree, ReadVarsFromStruct(&data)) + if err != nil { + t.Fatal(err) + } + defer r.Close() + err = r.Read(func(ctx RCtx) error { + if got, want := data, want(ctx.Entry); !reflect.DeepEqual(got, want) { + return fmt.Errorf( + "entry[%d]:\ngot= %#v\nwant=%#v\n", + ctx.Entry, got, want, + ) + } + return nil + }) + if err != nil && err != io.EOF { + t.Fatal(err) + } + }) + } +} + +func TestReaderVars(t *testing.T) { + for _, fname := range []string{ + "../testdata/x-flat-tree.root", + rtests.XrdRemote("testdata/x-flat-tree.root"), + } { + t.Run(fname, func(t *testing.T) { + t.Parallel() + + f, err := riofs.Open(fname) + if err != nil { + t.Fatal(err.Error()) + } + defer f.Close() + + obj, err := f.Get("tree") + if err != nil { + t.Fatal(err) + } + + tree := obj.(Tree) + + want := ScannerData{}.want + + var ( + data ScannerData + rvars = ReadVarsFromStruct(&data) + ) + r, err := NewReader(tree, rvars) + if err != nil { + t.Fatal(err) + } + defer r.Close() + + err = r.Read(func(ctx RCtx) error { + if got, want := data, want(ctx.Entry); !reflect.DeepEqual(got, want) { + return fmt.Errorf( + "entry[%d]:\ngot= %#v\nwant=%#v\n", + ctx.Entry, got, want, + ) + } + return nil + }) + if err != nil && err != io.EOF { + t.Fatal(err) + } + }) + } +} + +func TestReaderVarsMultipleTimes(t *testing.T) { + for _, fname := range []string{ + "../testdata/mc_105986.ZZ.root", + rtests.XrdRemote("testdata/mc_105986.ZZ.root"), + } { + t.Run(fname, func(t *testing.T) { + t.Parallel() + + f, err := riofs.Open(fname) + if err != nil { + t.Skip(err) + } + + obj, err := f.Get("mini") + if err != nil { + t.Fatal(err) + } + tree := obj.(Tree) + + for i := 0; i < 2; i++ { + var ( + data []float32 + rvars = []ReadVar{ + {Name: "lep_pt", Value: &data}, + } + ) + r, err := NewReader(tree, rvars) + if err != nil { + t.Fatal(err) + } + defer r.Close() + + err = r.Read(func(ctx RCtx) error { + return nil + }) + if err != nil { + t.Error(err) + } + } + }) + } +} + +func TestReaderStructWithCounterLeaf(t *testing.T) { + for _, fname := range []string{ + "../testdata/x-flat-tree.root", + rtests.XrdRemote("testdata/x-flat-tree.root"), + } { + t.Run(fname, func(t *testing.T) { + t.Parallel() + + f, err := riofs.Open(fname) + if err != nil { + t.Fatal(err.Error()) + } + defer f.Close() + + obj, err := f.Get("tree") + if err != nil { + t.Fatal(err) + } + + tree := obj.(Tree) + + type Data struct { + Sli []int32 `groot:"SliI32"` + } + var data Data + + want := func(i int64) Data { + var data Data + n := int32(i) % 10 + data.Sli = make([]int32, int(n)) + for ii := 0; ii < int(n); ii++ { + data.Sli[ii] = int32(-i) + } + return data + } + + r, err := NewReader(tree, ReadVarsFromStruct(&data)) + if err != nil { + t.Fatal(err) + } + defer r.Close() + + err = r.Read(func(ctx RCtx) error { + if got, want := data, want(ctx.Entry); !reflect.DeepEqual(got, want) { + return fmt.Errorf( + "entry[%d]:\ngot= %#v\nwant=%#v\n", + ctx.Entry, got, want, + ) + } + return nil + }) + if err != nil && err != io.EOF { + t.Fatal(err) + } + }) + } +} + +func TestReaderVarsWithCounterLeaf(t *testing.T) { + for _, fname := range []string{ + "../testdata/x-flat-tree.root", + rtests.XrdRemote("testdata/x-flat-tree.root"), + } { + t.Run(fname, func(t *testing.T) { + t.Parallel() + + f, err := riofs.Open(fname) + if err != nil { + t.Fatal(err.Error()) + } + defer f.Close() + + obj, err := f.Get("tree") + if err != nil { + t.Fatal(err) + } + + tree := obj.(Tree) + + want := func(i int64) []int32 { + n := int32(i) % 10 + data := make([]int32, int(n)) + for ii := 0; ii < int(n); ii++ { + data[ii] = int32(-i) + } + return data + } + + var ( + data []int32 + rvars = []ReadVar{ + {Name: "SliI32", Value: &data}, + } + ) + r, err := NewReader(tree, rvars) + if err != nil { + t.Fatal(err) + } + defer r.Close() + + err = r.Read(func(ctx RCtx) error { + if got, want := data, want(ctx.Entry); !reflect.DeepEqual(got, want) { + return fmt.Errorf( + "entry[%d]:\ngot= %#v\nwant=%#v\n", + ctx.Entry, got, want, + ) + } + return nil + }) + if err != nil && err != io.EOF { + t.Fatal(err) + } + }) + } +} + +func TestScannerStructWithStdVectorBool(t *testing.T) { + files, err := filepath.Glob("../testdata/stdvec-bool-*.root") + if err != nil { + t.Fatal(err) + } + + for _, fname := range files { + t.Run(fname, func(t *testing.T) { + t.Parallel() + + f, err := riofs.Open(fname) + if err != nil { + t.Fatal(err.Error()) + } + defer f.Close() + + obj, err := f.Get("tree") + if err != nil { + t.Fatal(err) + } + tree := obj.(Tree) + + type Data struct { + Bool bool `groot:"Bool"` + ArrBool [10]bool `groot:"ArrayBool"` + N int32 `groot:"N"` + SliBool []bool `groot:"SliceBool[N]"` + StlBool []bool `groot:"StlVecBool"` + } + type Event struct { + Data Data `groot:"evt"` + } + + want := func(i int64) Event { + var data Data + data.Bool = i%2 == 0 + for ii := range data.ArrBool { + data.ArrBool[ii] = i%2 == 0 + } + data.N = int32(i) % 10 + switch i { + case 0: + data.SliBool = nil + data.StlBool = nil + default: + data.SliBool = make([]bool, int(data.N)) + data.StlBool = make([]bool, int(data.N)) + } + for ii := 0; ii < int(data.N); ii++ { + data.SliBool[ii] = i%2 == 0 + data.StlBool[ii] = i%2 == 0 + } + return Event{data} + } + + var data Event + r, err := NewReader(tree, ReadVarsFromStruct(&data)) + if err != nil { + t.Fatal(err) + } + defer r.Close() + + err = r.Read(func(ctx RCtx) error { + if got, want := data, want(ctx.Entry); !reflect.DeepEqual(got, want) { + return fmt.Errorf( + "entry[%d]:\ngot= %#v\nwant=%#v\n", + ctx.Entry, got, want, + ) + } + return nil + + }) + if err != nil && err != io.EOF { + t.Fatal(err) + } + }) + } +} + +func TestNewReadVarsLeaves(t *testing.T) { + f, err := riofs.Open("../testdata/leaves.root") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + o, err := f.Get("tree") + if err != nil { + t.Fatal(err) + } + + tree := o.(Tree) + + vars := NewReadVars(tree) + want := []ReadVar{ + {Name: "B", Leaf: "B", Value: new(bool)}, + {Name: "Str", Leaf: "Str", Value: new(string)}, + {Name: "I8", Leaf: "I8", Value: new(int8)}, + {Name: "I16", Leaf: "I16", Value: new(int16)}, + {Name: "I32", Leaf: "I32", Value: new(int32)}, + {Name: "I64", Leaf: "I64", Value: new(int64)}, + {Name: "U8", Leaf: "U8", Value: new(uint8)}, + {Name: "U16", Leaf: "U16", Value: new(uint16)}, + {Name: "U32", Leaf: "U32", Value: new(uint32)}, + {Name: "U64", Leaf: "U64", Value: new(uint64)}, + {Name: "F32", Leaf: "F32", Value: new(float32)}, + {Name: "F64", Leaf: "F64", Value: new(float64)}, + {Name: "D16", Leaf: "D16", Value: new(root.Float16)}, + {Name: "D32", Leaf: "D32", Value: new(root.Double32)}, + // arrays + {Name: "ArrBs", Leaf: "ArrBs", Value: new([10]bool)}, + {Name: "ArrI8", Leaf: "ArrI8", Value: new([10]int8)}, + {Name: "ArrI16", Leaf: "ArrI16", Value: new([10]int16)}, + {Name: "ArrI32", Leaf: "ArrI32", Value: new([10]int32)}, + {Name: "ArrI64", Leaf: "ArrI64", Value: new([10]int64)}, + {Name: "ArrU8", Leaf: "ArrU8", Value: new([10]uint8)}, + {Name: "ArrU16", Leaf: "ArrU16", Value: new([10]uint16)}, + {Name: "ArrU32", Leaf: "ArrU32", Value: new([10]uint32)}, + {Name: "ArrU64", Leaf: "ArrU64", Value: new([10]uint64)}, + {Name: "ArrF32", Leaf: "ArrF32", Value: new([10]float32)}, + {Name: "ArrF64", Leaf: "ArrF64", Value: new([10]float64)}, + {Name: "ArrD16", Leaf: "ArrD16", Value: new([10]root.Float16)}, + {Name: "ArrD32", Leaf: "ArrD32", Value: new([10]root.Double32)}, + // slices + {Name: "N", Leaf: "N", Value: new(int32)}, + {Name: "SliBs", Leaf: "SliBs", Value: new([]bool)}, + {Name: "SliI8", Leaf: "SliI8", Value: new([]int8)}, + {Name: "SliI16", Leaf: "SliI16", Value: new([]int16)}, + {Name: "SliI32", Leaf: "SliI32", Value: new([]int32)}, + {Name: "SliI64", Leaf: "SliI64", Value: new([]int64)}, + {Name: "SliU8", Leaf: "SliU8", Value: new([]uint8)}, + {Name: "SliU16", Leaf: "SliU16", Value: new([]uint16)}, + {Name: "SliU32", Leaf: "SliU32", Value: new([]uint32)}, + {Name: "SliU64", Leaf: "SliU64", Value: new([]uint64)}, + {Name: "SliF32", Leaf: "SliF32", Value: new([]float32)}, + {Name: "SliF64", Leaf: "SliF64", Value: new([]float64)}, + {Name: "SliD16", Leaf: "SliD16", Value: new([]root.Float16)}, + {Name: "SliD32", Leaf: "SliD32", Value: new([]root.Double32)}, + } + + n := len(want) + if len(vars) < n { + n = len(vars) + } + + for i := 0; i < n; i++ { + got := vars[i] + if got.Name != want[i].Name { + t.Fatalf("invalid read-var name[%d]: got=%q, want=%q", i, got.Name, want[i].Name) + } + if got.Leaf != want[i].Leaf { + t.Fatalf("invalid read-var (name=%q) leaf-name[%d]: got=%q, want=%q", got.Name, i, got.Leaf, want[i].Leaf) + } + if got, want := reflect.TypeOf(got.Value), reflect.TypeOf(want[i].Value); got != want { + t.Fatalf("invalid read-var (name=%q) type[%d]: got=%v, want=%v", vars[i].Name, i, got, want) + } + } + + if len(want) != len(vars) { + t.Fatalf("invalid lengths. got=%d, want=%d", len(vars), len(want)) + } +} + +func TestG4LikeTree(t *testing.T) { + t.Parallel() + fname := rtests.XrdRemote("testdata/g4-like.root") + + f, err := riofs.Open(fname) + if err != nil { + t.Fatal(err.Error()) + } + defer f.Close() + + obj, err := f.Get("mytree") + if err != nil { + t.Fatal(err) + } + + tree := obj.(Tree) + + type EventData struct { + I32 int32 `groot:"i32"` + F64 float64 `groot:"f64"` + Sli []float64 `groot:"slif64"` + } + + want := func(i int64) (data EventData) { + data.I32 = int32(i + 1) + data.F64 = float64(i + 1) + data.Sli = make([]float64, i) + for ii := range data.Sli { + data.Sli[ii] = float64(ii) + float64(i) + } + return data + } + + data := EventData{ + Sli: make([]float64, 0), + } + rvars := []ReadVar{ + {Name: "i32", Value: &data.I32}, + {Name: "f64", Value: &data.F64}, + {Name: "slif64", Value: &data.Sli}, + } + r, err := NewReader(tree, rvars) + if err != nil { + t.Fatal(err) + } + defer r.Close() + + err = r.Read(func(ctx RCtx) error { + if got, want := data, want(ctx.Entry); !reflect.DeepEqual(got, want) { + return fmt.Errorf( + "entry[%d]:\ngot= %#v\nwant=%#v\n", + ctx.Entry, got, want, + ) + } + return nil + }) + if err != nil && err != io.EOF { + t.Fatal(err) + } +} + +func TestMultiLeafBranchWithReadVars(t *testing.T) { + t.Parallel() + + f, err := riofs.Open("../testdata/root_numpy_struct.root") + if err != nil { + t.Fatalf("%+v", err) + } + defer f.Close() + + obj, err := f.Get("test") + if err != nil { + t.Fatalf("%+v", err) + } + + tree := obj.(Tree) + + type Data struct { + b1l1 int32 + b1l2 float32 + b2l1 int32 + b2l2 float32 + } + + var ( + data Data + want = []Data{ + {10, 15.5, 20, 781.2}, + } + ) + + rvars := []ReadVar{ + { + Name: "branch1", + Leaf: "intleaf", + Value: &data.b1l1, + }, + { + Name: "branch1", + Leaf: "floatleaf", + Value: &data.b1l2, + }, + { + Name: "branch2", + Leaf: "intleaf", + Value: &data.b2l1, + }, + { + Name: "branch2", + Leaf: "floatleaf", + Value: &data.b2l2, + }, + } + + r, err := NewReader(tree, rvars) + if err != nil { + t.Fatalf("could not create reader: %+v", err) + } + defer r.Close() + + err = r.Read(func(ctx RCtx) error { + if got, want := data, want[ctx.Entry]; !reflect.DeepEqual(got, want) { + return fmt.Errorf( + "entry[%d]:\ngot= %#v\nwant=%#v\n", + ctx.Entry, got, want, + ) + } + return nil + }) + + if err != nil { + t.Fatal(err) + } +} + +func TestMultiLeafBranchWithTreeReadVars(t *testing.T) { + t.Parallel() + + f, err := riofs.Open("../testdata/root_numpy_struct.root") + if err != nil { + t.Fatalf("%+v", err) + } + defer f.Close() + + obj, err := f.Get("test") + if err != nil { + t.Fatalf("%+v", err) + } + + tree := obj.(Tree) + + type B struct { + L1 int32 `groot:"intleaf"` + L2 float32 `groot:"floatleaf"` + } + + type Data struct { + B1 B `groot:"branch1"` + B2 B `groot:"branch2"` + } + + var ( + data Data + want = []Data{ + {B{10, 15.5}, B{20, 781.2}}, + } + ) + + rvars := []ReadVar{ + { + Name: "branch1", + Leaf: "intleaf", + Value: &data.B1.L1, + }, + { + Name: "branch1", + Leaf: "floatleaf", + Value: &data.B1.L2, + }, + { + Name: "branch2", + Leaf: "intleaf", + Value: &data.B2.L1, + }, + { + Name: "branch2", + Leaf: "floatleaf", + Value: &data.B2.L2, + }, + } + + r, err := NewReader(tree, rvars) + if err != nil { + t.Fatalf("could not create reader: %+v", err) + } + defer r.Close() + + err = r.Read(func(ctx RCtx) error { + if got, want := data, want[ctx.Entry]; !reflect.DeepEqual(got, want) { + return fmt.Errorf( + "entry[%d]:\ngot= %#v\nwant=%#v\n", + ctx.Entry, got, want, + ) + } + return nil + }) + if err != nil { + t.Fatal(err) + } +} diff --git a/groot/rtree/scanner.go b/groot/rtree/scanner.go deleted file mode 100644 index 293db9f61..000000000 --- a/groot/rtree/scanner.go +++ /dev/null @@ -1,586 +0,0 @@ -// Copyright ©2017 The go-hep 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 rtree - -import ( - "fmt" - "reflect" -) - -type baseScanner struct { - tree Tree - i int64 // number of entries iterated over - n int64 // number of entries to iterate over - cur int64 // current entry index - chain bool // whether we are scanning through a chain - off int64 // entry offset. 0 for TTree. - tot int64 // tot-entries - err error // last error - - mbr []Branch // activated branches - cbr []Branch // branches activated because holding slice index - ibr []scanField // indices of activated branches - - closed bool -} - -// Close closes the Scanner, preventing further iteration. -// Close is idempotent and does not affect the result of Err. -func (s *baseScanner) Close() error { - if s.closed { - return nil - } - s.closed = true - s.tree = nil - s.mbr = nil - s.ibr = nil - return nil -} - -// Err returns the error, if any, that was encountered during iteration. -func (s *baseScanner) Err() error { - return s.err -} - -// Entry returns the entry number of the last read row. -func (s *baseScanner) Entry() int64 { - return s.cur -} - -// SeekEntry points the scanner to the i-th entry, ready to call Next. -func (s *baseScanner) SeekEntry(i int64) error { - if s.err != nil { - return s.err - } - if s.chain { - ch := s.tree.(*chain) - if i >= ch.off+ch.tree.Entries() || i < ch.off { - itree := s.findTree(i) - if itree == -1 { - s.err = fmt.Errorf("rtree: could not find Tree containing entry %d", i) - return s.err - } - s.loadTree(itree) - } - } - s.i = i - s.cur = i - 1 - return s.err -} - -// findTree finds the tree number in the chain that contains entry i -func (s *baseScanner) findTree(i int64) int { - ch := s.tree.(*chain) - for j := range ch.trees { - if i <= ch.tots[j] { - return j - } - } - return -1 -} - -// Next prepares the next result row for reading with the Scan method. -// It returns true on success, false if there is no next result row. -// Every call to Scan, even the first one, must be preceded by a call to Next. -func (s *baseScanner) Next() bool { - if s.closed { - return false - } - next := s.i < s.n - s.cur++ - s.i++ - - if s.chain { - if s.cur >= s.tot { - ch := s.tree.(*chain) - s.loadTree(ch.cur + 1) - } - } - - return next -} - -func (s *baseScanner) loadTree(i int) { - ch := s.tree.(*chain) - ch.loadTree(i) - s.off = ch.off - s.tot = ch.tot - if ch.tree == nil { - // tchain exhausted. - return - } - // reconnect branches - for i, v := range s.ibr { - name := v.br.Name() - br := ch.Branch(name) - _ = br.setAddress(v.ptr) - s.ibr[i].br = br - s.mbr[i] = br - if v.lcnt >= 0 { - leaf := br.Leaves()[0] - lcnt := leaf.LeafCount() - lbr := ch.Branch(lcnt.Name()) - s.cbr[v.lcnt] = lbr - } - } -} - -func (s *baseScanner) icur() int64 { - return s.cur - s.off -} - -// scanField associates a Branch with a struct's field index -type scanField struct { - br Branch - i int // field index - ptr interface{} // field address - lcnt int // index of dependant leaf-count (if any) - dup bool // whether the field is already read via leaf-count -} - -// TreeScanner scans, selects and iterates over Tree entries. -type TreeScanner struct { - scan baseScanner - - typ reflect.Type // type bound to this scanner (a struct, a map, a slice of types) - ptr reflect.Value // pointer to value bound to this scanner -} - -// NewTreeScanner creates a new Scanner connecting the pointer to some -// user provided type to the given Tree. -func NewTreeScanner(t Tree, ptr interface{}) (*TreeScanner, error) { - rvars := ReadVarsFromStruct(ptr) - sc, err := NewTreeScannerVars(t, rvars...) - if err != nil { - return nil, err - } - sc.ptr = reflect.ValueOf(ptr) - sc.typ = reflect.TypeOf(ptr).Elem() - for i := range sc.scan.ibr { - br := &sc.scan.ibr[i] - n := br.br.Name() - fields: - for j := 0; j < sc.typ.NumField(); j++ { - ft := sc.typ.Field(j) - if ft.Tag.Get("groot") == n || ft.Name == n { - br.i = j - break fields - } - } - } - return sc, nil -} - -// ScanVar describes a variable to be read out of a tree. -// -// DEPRECATED: please use ReadVar instead. -type ScanVar = ReadVar - -// NewScanVars returns the complete set of ReadVars to read all the data -// contained in the provided Tree. -// -// DEPRECATED: please use NewReadVars instead. -func NewScanVars(t Tree) []ScanVar { return NewReadVars(t) } - -// NewTreeScannerVars creates a new Scanner from a list of branches. -// It will return an error if the provided type does not match the -// type stored in the corresponding branch. -func NewTreeScannerVars(t Tree, vars ...ReadVar) (*TreeScanner, error) { - if len(vars) <= 0 { - return nil, fmt.Errorf("rtree: NewTreeScannerVars expects at least one branch name") - } - - mbr := make([]Branch, len(vars)) - ibr := make([]scanField, cap(mbr)) - cbr := make([]Branch, 0) - cbrset := make(map[string]bool) - lset := make(map[Leaf]struct{}) - clset := make(map[Leaf]struct{}) - - for i, sv := range vars { - br := t.Branch(sv.Name) - if br == nil { - return nil, fmt.Errorf("rtree: Tree %q has no branch named %q", t.Name(), sv.Name) - } - mbr[i] = br - ibr[i] = scanField{br: br, i: 0, lcnt: -1, ptr: sv.Value} - leaf := br.Leaves()[0] - if sv.Leaf != "" { - leaf = br.Leaf(sv.Leaf) - } - if leaf == nil { - return nil, fmt.Errorf("rtree: Tree %q has no leaf named %q", t.Name(), sv.Leaf) - } - lset[leaf] = struct{}{} - if lcnt := leaf.LeafCount(); lcnt != nil { - lbr := t.Leaf(lcnt.Name()) - if lbr == nil { - return nil, fmt.Errorf("rtree: Tree %q has no (count) branch named %q", t.Name(), lcnt.Name()) - } - bbr := lbr.Branch() - if !cbrset[bbr.Name()] { - cbr = append(cbr, bbr) - cbrset[bbr.Name()] = true - clset[lcnt] = struct{}{} - } - } - if sv.Value == nil { - sv.Value = newValue(leaf) - } - arg := sv.Value - if rv := reflect.ValueOf(arg); rv.Kind() != reflect.Ptr { - return nil, fmt.Errorf("rtree: ReadVar %d (name=%v) has non pointer Value", i, sv.Name) - } - err := br.setAddress(arg) - if err != nil { - return nil, fmt.Errorf("rtree: could not set branch address for %q: %w", br.Name(), err) - } - } - - // setup addresses for leaf-count not explicitly requested by user - for leaf := range clset { - _, ok := lset[leaf] - if ok { - continue - } - err := leaf.setAddress(nil) - if err != nil { - return nil, fmt.Errorf("rtree: could not set leaf-count address for %q: %w", leaf.Name(), err) - } - } - - // remove branches already loaded via leaf-count - for i, ib := range ibr { - if _, dup := cbrset[ib.br.Name()]; dup { - ibr[i].dup = true - } - } - - base := baseScanner{ - tree: t, - i: 0, - n: t.Entries(), - cur: -1, - err: nil, - ibr: ibr, - mbr: mbr, - cbr: cbr, - } - - if ch, ok := t.(*chain); ok { - base.chain = ok - base.off = ch.off - base.tot = ch.tot - } - - return &TreeScanner{ - scan: base, - typ: nil, - }, nil -} - -// Close closes the TreeScanner, preventing further iteration. -// Close is idempotent and does not affect the result of Err. -func (s *TreeScanner) Close() error { - return s.scan.Close() -} - -// Err returns the error, if any, that was encountered during iteration. -func (s *TreeScanner) Err() error { - return s.scan.Err() -} - -// Entry returns the entry number of the last read row. -func (s *TreeScanner) Entry() int64 { - return s.scan.Entry() -} - -// SeekEntry points the scanner to the i-th entry, ready to call Next. -func (s *TreeScanner) SeekEntry(i int64) error { - return s.scan.SeekEntry(i) -} - -// Next prepares the next result row for reading with the Scan method. -// It returns true on success, false if there is no next result row. -// Every call to Scan, even the first one, must be preceded by a call to Next. -func (s *TreeScanner) Next() bool { - return s.scan.Next() -} - -// Scan copies data loaded from the underlying Tree into the values pointed at by args. -func (s *TreeScanner) Scan(args ...interface{}) (err error) { - defer func(err error) { - if err != nil && s.scan.err == nil { - s.scan.err = err - } - }(err) - - switch len(args) { - case 0: - return fmt.Errorf("rtree: TreeScanner.Scan needs at least one argument") - - case 1: - // maybe special case: map? struct? - rt := reflect.TypeOf(args[0]).Elem() - switch rt.Kind() { - case reflect.Map: - err = s.scanMap(*args[0].(*map[string]interface{})) - return err - case reflect.Struct: - err = s.scanStruct(args[0]) - return err - } - } - - err = s.scanArgs(args...) - return err -} - -func (s *TreeScanner) scanMap(data map[string]interface{}) error { - panic("not implemented") -} - -func (s *TreeScanner) scanArgs(args ...interface{}) error { - var err error - - ientry := s.scan.icur() - - // load leaf count data - for _, br := range s.scan.cbr { - err = br.loadEntry(ientry) - if err != nil { - // FIXME(sbinet): properly decorate error - return err - } - } - - for i, ptr := range args { - br := s.scan.ibr[i] - fv := reflect.ValueOf(ptr).Elem() - err = br.br.loadEntry(ientry) - if err != nil { - // FIXME(sbinet): properly decorate error - return err - } - err = br.br.scan(fv.Addr().Interface()) - if err != nil { - return err - } - } - return err -} - -func (s *TreeScanner) scanStruct(data interface{}) error { - var err error - - ientry := s.scan.icur() - - // load leaf count data - for _, br := range s.scan.cbr { - s.scan.err = br.loadEntry(ientry) - if s.scan.err != nil { - // FIXME(sbinet): properly decorate error - return s.scan.err - } - } - - rt := reflect.TypeOf(data).Elem() - rv := reflect.ValueOf(data).Elem() - if rt != s.typ { - return fmt.Errorf("rtree: Scanner.Scan: types do not match (got: %v, want: %v)", rt, s.typ) - } - for _, br := range s.scan.ibr { - fv := rv.Field(br.i) - err = br.br.loadEntry(ientry) - if err != nil { - // FIXME(sbinet): properly decorate error - return err - } - err = br.br.scan(fv.Addr().Interface()) - if err != nil { - return err - } - } - return err -} - -// Scanner scans, selects and iterates over Tree entries. -// Scanner is bound to values the user provides, Scanner will -// then read data into these values during the tree scan. -type Scanner struct { - scan baseScanner - args []interface{} // a slice of pointers to read data into -} - -// NewScannerVars creates a new Scanner from a list of pairs (branch-name, target-address). -// Scanner will read the branches' data during Scan() and load them into these target-addresses. -func NewScannerVars(t Tree, vars ...ReadVar) (*Scanner, error) { - mbr := make([]Branch, len(vars)) - ibr := make([]scanField, cap(mbr)) - cbr := make([]Branch, 0) - cbrset := make(map[string]bool) - lset := make(map[Leaf]struct{}) - clset := make(map[Leaf]struct{}) - - args := make([]interface{}, len(vars)) - for i, sv := range vars { - br := t.Branch(sv.Name) - if br == nil { - return nil, fmt.Errorf("rtree: Tree %q has no branch named %q", t.Name(), sv.Name) - } - mbr[i] = br - ibr[i] = scanField{br: br, i: 0, lcnt: -1} - - leaf := br.Leaves()[0] - if sv.Leaf != "" { - leaf = br.Leaf(sv.Leaf) - } - if leaf == nil { - return nil, fmt.Errorf("rtree: Tree %q has no leaf named %q", t.Name(), sv.Leaf) - } - lset[leaf] = struct{}{} - if lcnt := leaf.LeafCount(); lcnt != nil { - lbr := t.Leaf(lcnt.Name()) - if lbr == nil { - return nil, fmt.Errorf("rtree: Tree %q has no (count) branch named %q", t.Name(), lcnt.Name()) - } - bbr := lbr.Branch() - if !cbrset[bbr.Name()] { - cbr = append(cbr, bbr) - cbrset[bbr.Name()] = true - clset[lcnt] = struct{}{} - } - } - arg := sv.Value - if arg == nil { - return nil, fmt.Errorf("rtree: ReadVar %d (name=%v) has nil Value", i, sv.Name) - } - if rv := reflect.ValueOf(arg); rv.Kind() != reflect.Ptr { - return nil, fmt.Errorf("rtree: ReadVar %d (name=%v) has non pointer Value", i, sv.Name) - } - var err error - switch br := br.(type) { - case *tbranchElement: - err = br.setAddress(arg) - case *tbranch: - err = leaf.setAddress(arg) - default: - panic(fmt.Errorf("rtree: unknown Branch type %T", br)) - } - if err != nil { - panic(err) - } - args[i] = arg - ibr[i].ptr = arg - } - - // setup addresses for leaf-count not explicitly requested by user - for leaf := range clset { - _, ok := lset[leaf] - if ok { - continue - } - err := leaf.setAddress(nil) - if err != nil { - return nil, fmt.Errorf("rtree: could not set leaf-count address for %q: %w", leaf.Name(), err) - } - } - - // remove branches already loaded via leaf-count - for i, ib := range ibr { - if _, dup := cbrset[ib.br.Name()]; dup { - ibr[i].dup = true - } - } - - base := baseScanner{ - tree: t, - i: 0, - n: t.Entries(), - cur: -1, - err: nil, - ibr: ibr, - mbr: mbr, - cbr: cbr, - } - - if ch, ok := t.(*chain); ok { - base.chain = ok - base.off = ch.off - base.tot = ch.tot - } - - return &Scanner{ - scan: base, - args: args, - }, nil -} - -// NewScanner creates a new Scanner bound to a (pointer to a) struct value. -// Scanner will read the branches' data during Scan() and load them into the fields of the struct value. -func NewScanner(t Tree, ptr interface{}) (*Scanner, error) { - rvars := ReadVarsFromStruct(ptr) - return NewScannerVars(t, rvars...) -} - -// Close closes the Scanner, preventing further iteration. -// Close is idempotent and does not affect the result of Err. -func (s *Scanner) Close() error { - return s.scan.Close() -} - -// Err returns the error, if any, that was encountered during iteration. -func (s *Scanner) Err() error { - return s.scan.Err() -} - -// Entry returns the entry number of the last read row. -func (s *Scanner) Entry() int64 { - return s.scan.Entry() -} - -// SeekEntry points the scanner to the i-th entry, ready to call Next. -func (s *Scanner) SeekEntry(i int64) error { - return s.scan.SeekEntry(i) -} - -// Next prepares the next result row for reading with the Scan method. -// It returns true on success, false if there is no next result row. -// Every call to Scan, even the first one, must be preceded by a call to Next. -func (s *Scanner) Next() bool { - return s.scan.Next() -} - -// Scan copies data loaded from the underlying Tree into the values the Scanner is bound to. -// The values bound to the Scanner are valid until the next call to Scan. -func (s *Scanner) Scan() error { - if s.scan.err != nil { - return s.scan.err - } - - ientry := s.scan.icur() - - // load leaf count data - for _, br := range s.scan.cbr { - s.scan.err = br.loadEntry(ientry) - if s.scan.err != nil { - // FIXME(sbinet): properly decorate error - return s.scan.err - } - } - - for i := range s.scan.ibr { - br := &s.scan.ibr[i] - if br.dup { - continue - } - s.scan.err = br.br.loadEntry(ientry) - if s.scan.err != nil { - // FIXME(sbinet): properly decorate error - return s.scan.err - } - } - return s.scan.err -} diff --git a/groot/rtree/scanner_test.go b/groot/rtree/scanner_test.go deleted file mode 100644 index 76ee4df83..000000000 --- a/groot/rtree/scanner_test.go +++ /dev/null @@ -1,1455 +0,0 @@ -// Copyright ©2017 The go-hep 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 rtree - -import ( - "fmt" - "io" - "path/filepath" - "reflect" - "testing" - - "go-hep.org/x/hep/groot/internal/rtests" - "go-hep.org/x/hep/groot/riofs" - _ "go-hep.org/x/hep/groot/riofs/plugin/xrootd" - "go-hep.org/x/hep/groot/root" -) - -type ScannerData struct { - B bool `groot:"B"` - Str string `groot:"Str"` - I8 int8 `groot:"I8"` - I16 int16 `groot:"I16"` - I32 int32 `groot:"I32"` - I64 int64 `groot:"I64"` - U8 uint8 `groot:"U8"` - U16 uint16 `groot:"U16"` - U32 uint32 `groot:"U32"` - U64 uint64 `groot:"U64"` - F32 float32 `groot:"F32"` - F64 float64 `groot:"F64"` - D16 root.Float16 `groot:"D16"` - D32 root.Double32 `groot:"D32"` - ArrBs [10]bool `groot:"ArrBs[10]"` - ArrI8 [10]int8 `groot:"ArrI8[10]"` - ArrI16 [10]int16 `groot:"ArrI16[10]"` - ArrI32 [10]int32 `groot:"ArrI32[10]"` - ArrI64 [10]int64 `groot:"ArrI64[10]"` - ArrU8 [10]uint8 `groot:"ArrU8[10]"` - ArrU16 [10]uint16 `groot:"ArrU16[10]"` - ArrU32 [10]uint32 `groot:"ArrU32[10]"` - ArrU64 [10]uint64 `groot:"ArrU64[10]"` - ArrF32 [10]float32 `groot:"ArrF32[10]"` - ArrF64 [10]float64 `groot:"ArrF64[10]"` - ArrD16 [10]root.Float16 `groot:"ArrD16[10]"` - ArrD32 [10]root.Double32 `groot:"ArrD32[10]"` - N int32 `groot:"N"` - SliBs []bool `groot:"SliBs[N]"` - SliI8 []int8 `groot:"SliI8[N]"` - SliI16 []int16 `groot:"SliI16[N]"` - SliI32 []int32 `groot:"SliI32[N]"` - SliI64 []int64 `groot:"SliI64[N]"` - SliU8 []uint8 `groot:"SliU8[N]"` - SliU16 []uint16 `groot:"SliU16[N]"` - SliU32 []uint32 `groot:"SliU32[N]"` - SliU64 []uint64 `groot:"SliU64[N]"` - SliF32 []float32 `groot:"SliF32[N]"` - SliF64 []float64 `groot:"SliF64[N]"` - SliD16 []root.Float16 `groot:"SliD16[N]"` - SliD32 []root.Double32 `groot:"SliD32[N]"` -} - -func (ScannerData) want(i int64) (data ScannerData) { - data.B = i%2 == 0 - data.Str = fmt.Sprintf("str-%d", i) - data.I8 = int8(-i) - data.I16 = int16(-i) - data.I32 = int32(-i) - data.I64 = int64(-i) - data.U8 = uint8(i) - data.U16 = uint16(i) - data.U32 = uint32(i) - data.U64 = uint64(i) - data.F32 = float32(i) - data.F64 = float64(i) - data.D16 = root.Float16(i) - data.D32 = root.Double32(i) - for ii := range data.ArrI32 { - data.ArrBs[ii] = ii == int(i) - data.ArrI8[ii] = int8(-i) - data.ArrI16[ii] = int16(-i) - data.ArrI32[ii] = int32(-i) - data.ArrI64[ii] = int64(-i) - data.ArrU8[ii] = uint8(i) - data.ArrU16[ii] = uint16(i) - data.ArrU32[ii] = uint32(i) - data.ArrU64[ii] = uint64(i) - data.ArrF32[ii] = float32(i) - data.ArrF64[ii] = float64(i) - data.ArrD16[ii] = root.Float16(i) - data.ArrD32[ii] = root.Double32(i) - } - data.N = int32(i) % 10 - data.SliBs = make([]bool, int(data.N)) - data.SliI8 = make([]int8, int(data.N)) - data.SliI16 = make([]int16, int(data.N)) - data.SliI32 = make([]int32, int(data.N)) - data.SliI64 = make([]int64, int(data.N)) - data.SliU8 = make([]uint8, int(data.N)) - data.SliU16 = make([]uint16, int(data.N)) - data.SliU32 = make([]uint32, int(data.N)) - data.SliU64 = make([]uint64, int(data.N)) - data.SliF32 = make([]float32, int(data.N)) - data.SliF64 = make([]float64, int(data.N)) - data.SliD16 = make([]root.Float16, int(data.N)) - data.SliD32 = make([]root.Double32, int(data.N)) - for ii := 0; ii < int(data.N); ii++ { - data.SliBs[ii] = (ii + 1) == int(i) - data.SliI8[ii] = int8(-i) - data.SliI16[ii] = int16(-i) - data.SliI32[ii] = int32(-i) - data.SliI64[ii] = int64(-i) - data.SliU8[ii] = uint8(i) - data.SliU16[ii] = uint16(i) - data.SliU32[ii] = uint32(i) - data.SliU64[ii] = uint64(i) - data.SliF32[ii] = float32(i) - data.SliF64[ii] = float64(i) - data.SliD16[ii] = root.Float16(i) - data.SliD32[ii] = root.Double32(i) - } - return data -} - -func TestTreeScannerStruct(t *testing.T) { - for _, fname := range []string{ - "../testdata/x-flat-tree.root", - rtests.XrdRemote("testdata/x-flat-tree.root"), - } { - t.Run(fname, func(t *testing.T) { - t.Parallel() - - f, err := riofs.Open(fname) - if err != nil { - t.Fatal(err.Error()) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - tree := obj.(Tree) - - want := ScannerData{}.want - - sc, err := NewTreeScanner(tree, &ScannerData{}) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - var d1 ScannerData - for sc.Next() { - err := sc.Scan(&d1) - if err != nil { - t.Fatal(err) - } - i := sc.Entry() - if !reflect.DeepEqual(d1, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, d1, want(i)) - } - - var d2 ScannerData - err = sc.Scan(&d2) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(d2, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, d2, want(i)) - } - } - if err := sc.Err(); err != nil && err != io.EOF { - t.Fatal(err) - } - }) - } -} - -func TestScannerStruct(t *testing.T) { - for _, fname := range []string{ - "../testdata/x-flat-tree.root", - rtests.XrdRemote("testdata/x-flat-tree.root"), - } { - t.Run(fname, func(t *testing.T) { - t.Parallel() - - f, err := riofs.Open(fname) - if err != nil { - t.Fatal(err.Error()) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - tree := obj.(Tree) - - var ( - want = ScannerData{}.want - data ScannerData - ) - sc, err := NewScanner(tree, &data) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - for sc.Next() { - err := sc.Scan() - if err != nil { - t.Fatal(err) - } - i := sc.Entry() - if !reflect.DeepEqual(data, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, data, want(i)) - } - - // test a second time - err = sc.Scan() - if err != nil { - t.Fatal(err) - } - i = sc.Entry() - if !reflect.DeepEqual(data, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, data, want(i)) - } - } - if err := sc.Err(); err != nil && err != io.EOF { - t.Fatal(err) - } - }) - } -} - -func TestScannerVars(t *testing.T) { - for _, fname := range []string{ - "../testdata/x-flat-tree.root", - rtests.XrdRemote("testdata/x-flat-tree.root"), - } { - t.Run(fname, func(t *testing.T) { - t.Parallel() - - f, err := riofs.Open(fname) - if err != nil { - t.Fatal(err.Error()) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - - tree := obj.(Tree) - - want := ScannerData{}.want - - var ( - data ScannerData - rvars = ReadVarsFromStruct(&data) - ) - sc, err := NewScannerVars(tree, rvars...) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - for sc.Next() { - err := sc.Scan() - if err != nil { - t.Fatal(err) - } - i := sc.Entry() - if !reflect.DeepEqual(data, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, data, want(i)) - } - } - if err := sc.Err(); err != nil && err != io.EOF { - t.Fatal(err) - } - }) - } -} - -func TestTreeScannerVarsMultipleTimes(t *testing.T) { - for _, fname := range []string{ - "../testdata/mc_105986.ZZ.root", - rtests.XrdRemote("testdata/mc_105986.ZZ.root"), - } { - t.Run(fname, func(t *testing.T) { - t.Parallel() - - f, err := riofs.Open(fname) - if err != nil { - t.Skip(err) - } - - obj, err := f.Get("mini") - if err != nil { - t.Fatal(err) - } - tree := obj.(Tree) - - for i := 0; i < 10; i++ { - sc, err := NewTreeScannerVars(tree, ReadVar{Name: "lep_pt"}) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - - for sc.Next() { - var data []float32 - err := sc.Scan(&data) - if err != nil { - t.Fatalf("could not scan data i=%d evt=%v err=%v", i, sc.Entry(), err) - } - } - err = sc.Err() - if err != nil { - t.Error(err) - } - } - }) - } -} - -func TestTreeScannerVars(t *testing.T) { - for _, fname := range []string{ - "../testdata/x-flat-tree.root", - rtests.XrdRemote("testdata/x-flat-tree.root"), - } { - t.Run(fname, func(t *testing.T) { - t.Parallel() - - f, err := riofs.Open(fname) - if err != nil { - t.Fatal(err.Error()) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - - tree := obj.(Tree) - want := ScannerData{}.want - rvars := ReadVarsFromStruct(new(ScannerData)) - sc, err := NewTreeScannerVars(tree, rvars...) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - var d1 ScannerData - for sc.Next() { - err := sc.Scan( - &d1.B, - &d1.Str, - &d1.I8, - &d1.I16, - &d1.I32, - &d1.I64, - &d1.U8, - &d1.U16, - &d1.U32, - &d1.U64, - &d1.F32, - &d1.F64, - &d1.D16, - &d1.D32, - &d1.ArrBs, - &d1.ArrI8, - &d1.ArrI16, - &d1.ArrI32, - &d1.ArrI64, - &d1.ArrU8, - &d1.ArrU16, - &d1.ArrU32, - &d1.ArrU64, - &d1.ArrF32, - &d1.ArrF64, - &d1.ArrD16, - &d1.ArrD32, - &d1.N, - &d1.SliBs, - &d1.SliI8, - &d1.SliI16, - &d1.SliI32, - &d1.SliI64, - &d1.SliU8, - &d1.SliU16, - &d1.SliU32, - &d1.SliU64, - &d1.SliF32, - &d1.SliF64, - &d1.SliD16, - &d1.SliD32, - ) - if err != nil { - t.Fatal(err) - } - i := sc.Entry() - if !reflect.DeepEqual(d1, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, d1, want(i)) - } - - var d2 ScannerData - err = sc.Scan( - &d2.B, - &d2.Str, - &d2.I8, - &d2.I16, - &d2.I32, - &d2.I64, - &d2.U8, - &d2.U16, - &d2.U32, - &d2.U64, - &d2.F32, - &d2.F64, - &d2.D16, - &d2.D32, - &d2.ArrBs, - &d2.ArrI8, - &d2.ArrI16, - &d2.ArrI32, - &d2.ArrI64, - &d2.ArrU8, - &d2.ArrU16, - &d2.ArrU32, - &d2.ArrU64, - &d2.ArrF32, - &d2.ArrF64, - &d2.ArrD16, - &d2.ArrD32, - &d2.N, - &d2.SliBs, - &d2.SliI8, - &d2.SliI16, - &d2.SliI32, - &d2.SliI64, - &d2.SliU8, - &d2.SliU16, - &d2.SliU32, - &d2.SliU64, - &d2.SliF32, - &d2.SliF64, - &d2.SliD16, - &d2.SliD32, - ) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(d2, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, d2, want(i)) - } - } - if err := sc.Err(); err != nil && err != io.EOF { - t.Fatal(err) - } - }) - } -} - -func TestScannerVarsMultipleTimes(t *testing.T) { - for _, fname := range []string{ - "../testdata/mc_105986.ZZ.root", - rtests.XrdRemote("testdata/mc_105986.ZZ.root"), - } { - t.Run(fname, func(t *testing.T) { - t.Parallel() - - f, err := riofs.Open(fname) - if err != nil { - t.Skip(err) - } - - obj, err := f.Get("mini") - if err != nil { - t.Fatal(err) - } - tree := obj.(Tree) - - var pt []float32 - for i := 0; i < 10; i++ { - sc, err := NewScannerVars(tree, ReadVar{Name: "lep_pt", Value: &pt}) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - - for sc.Next() { - err := sc.Scan() - if err != nil { - t.Error(err) - } - } - err = sc.Err() - if err != nil { - t.Error(err) - } - } - }) - } -} - -func TestTreeScannerStructWithCounterLeaf(t *testing.T) { - for _, fname := range []string{ - "../testdata/x-flat-tree.root", - rtests.XrdRemote("testdata/x-flat-tree.root"), - } { - t.Run(fname, func(t *testing.T) { - t.Parallel() - - f, err := riofs.Open(fname) - if err != nil { - t.Fatal(err.Error()) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - - tree := obj.(Tree) - - type Data struct { - Sli []int32 `groot:"SliI32"` - } - var data Data - - want := func(i int64) Data { - var data Data - n := int32(i) % 10 - data.Sli = make([]int32, int(n)) - for ii := 0; ii < int(n); ii++ { - data.Sli[ii] = int32(-i) - } - return data - } - - sc, err := NewTreeScanner(tree, &data) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - for sc.Next() { - err := sc.Scan(&data) - if err != nil { - t.Fatal(err) - } - i := sc.Entry() - if !reflect.DeepEqual(data, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, data, want(i)) - } - } - if err := sc.Err(); err != nil && err != io.EOF { - t.Fatal(err) - } - }) - } -} - -func TestScannerStructWithCounterLeaf(t *testing.T) { - for _, fname := range []string{ - "../testdata/x-flat-tree.root", - rtests.XrdRemote("testdata/x-flat-tree.root"), - } { - t.Run(fname, func(t *testing.T) { - t.Parallel() - - f, err := riofs.Open(fname) - if err != nil { - t.Fatal(err.Error()) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - - tree := obj.(Tree) - - type Data struct { - Sli []int32 `groot:"SliI32"` - } - var data Data - - want := func(i int64) Data { - var data Data - n := int32(i) % 10 - data.Sli = make([]int32, int(n)) - for ii := 0; ii < int(n); ii++ { - data.Sli[ii] = int32(-i) - } - return data - } - - sc, err := NewScanner(tree, &data) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - for sc.Next() { - err := sc.Scan() - if err != nil { - t.Fatal(err) - } - i := sc.Entry() - if !reflect.DeepEqual(data, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, data, want(i)) - } - } - if err := sc.Err(); err != nil && err != io.EOF { - t.Fatal(err) - } - }) - } -} - -func TestTreeScannerVarsWithCounterLeaf(t *testing.T) { - for _, fname := range []string{ - "../testdata/x-flat-tree.root", - rtests.XrdRemote("testdata/x-flat-tree.root"), - } { - t.Run(fname, func(t *testing.T) { - t.Parallel() - - f, err := riofs.Open(fname) - if err != nil { - t.Fatal(err.Error()) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - - tree := obj.(Tree) - - want := func(i int64) []int32 { - n := int32(i) % 10 - data := make([]int32, int(n)) - for ii := 0; ii < int(n); ii++ { - data[ii] = int32(-i) - } - return data - } - - rvar := ReadVar{Name: "SliI32"} - sc, err := NewTreeScannerVars(tree, rvar) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - for sc.Next() { - var data []int32 - err := sc.Scan(&data) - if err != nil { - t.Fatal(err) - } - i := sc.Entry() - if !reflect.DeepEqual(data, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, data, want(i)) - } - } - if err := sc.Err(); err != nil && err != io.EOF { - t.Fatal(err) - } - }) - } -} - -func TestScannerVarsWithCounterLeaf(t *testing.T) { - for _, fname := range []string{ - "../testdata/x-flat-tree.root", - rtests.XrdRemote("testdata/x-flat-tree.root"), - } { - t.Run(fname, func(t *testing.T) { - t.Parallel() - - f, err := riofs.Open(fname) - if err != nil { - t.Fatal(err.Error()) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - - tree := obj.(Tree) - - want := func(i int64) []int32 { - n := int32(i) % 10 - data := make([]int32, int(n)) - for ii := 0; ii < int(n); ii++ { - data[ii] = int32(-i) - } - return data - } - - var data []int32 - rvar := ReadVar{Name: "SliI32", Value: &data} - sc, err := NewScannerVars(tree, rvar) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - for sc.Next() { - err := sc.Scan() - if err != nil { - t.Fatal(err) - } - i := sc.Entry() - if !reflect.DeepEqual(data, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, data, want(i)) - } - } - if err := sc.Err(); err != nil && err != io.EOF { - t.Fatal(err) - } - }) - } -} - -func TestScannerStructWithStdVectorBool(t *testing.T) { - files, err := filepath.Glob("../testdata/stdvec-bool-*.root") - if err != nil { - t.Fatal(err) - } - - for _, fname := range files { - t.Run(fname, func(t *testing.T) { - t.Parallel() - - f, err := riofs.Open(fname) - if err != nil { - t.Fatal(err.Error()) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - tree := obj.(Tree) - - type Data struct { - Bool bool `groot:"Bool"` - ArrBool [10]bool `groot:"ArrayBool"` - N int32 `groot:"N"` - SliBool []bool `groot:"SliceBool[N]"` - StlBool []bool `groot:"StlVecBool"` - } - type Event struct { - Data Data `groot:"evt"` - } - - want := func(i int64) Event { - var data Data - data.Bool = i%2 == 0 - for ii := range data.ArrBool { - data.ArrBool[ii] = i%2 == 0 - } - data.N = int32(i) % 10 - switch i { - case 0: - data.SliBool = nil - data.StlBool = nil - default: - data.SliBool = make([]bool, int(data.N)) - data.StlBool = make([]bool, int(data.N)) - } - for ii := 0; ii < int(data.N); ii++ { - data.SliBool[ii] = i%2 == 0 - data.StlBool[ii] = i%2 == 0 - } - return Event{data} - } - - var data Event - sc, err := NewScanner(tree, &data) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - for sc.Next() { - err := sc.Scan() - if err != nil { - t.Fatal(err) - } - i := sc.Entry() - if !reflect.DeepEqual(data, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, data, want(i)) - } - - // test a second time - err = sc.Scan() - if err != nil { - t.Fatal(err) - } - i = sc.Entry() - if !reflect.DeepEqual(data, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, data, want(i)) - } - } - if err := sc.Err(); err != nil && err != io.EOF { - t.Fatal(err) - } - }) - } -} - -func BenchmarkTreeScannerStruct(b *testing.B) { - f, err := riofs.Open("../testdata/x-flat-tree.root") - if err != nil { - b.Fatal(err.Error()) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - b.Fatal(err) - } - tree := obj.(Tree) - - type Data struct { - F64 float64 `groot:"F64"` - } - - var data Data - s, err := NewTreeScanner(tree, &data) - if err != nil { - b.Fatal(err) - } - defer s.Close() - - var sum float64 - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = s.SeekEntry(0) - for s.Next() { - err = s.Scan(&data) - if err != nil { - b.Fatal(err) - } - sum += data.F64 - } - } -} - -func BenchmarkScannerStruct(b *testing.B) { - f, err := riofs.Open("../testdata/x-flat-tree.root") - if err != nil { - b.Fatal(err.Error()) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - b.Fatal(err) - } - tree := obj.(Tree) - - type Data struct { - F64 float64 `groot:"F64"` - } - - var data Data - s, err := NewScanner(tree, &data) - if err != nil { - b.Fatal(err) - } - defer s.Close() - - var sum float64 - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = s.SeekEntry(0) - for s.Next() { - err = s.Scan() - if err != nil { - b.Fatal(err) - } - sum += data.F64 - } - } -} - -func BenchmarkTreeScannerVars(b *testing.B) { - f, err := riofs.Open("../testdata/x-flat-tree.root") - if err != nil { - b.Fatal(err) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - b.Fatal(err) - } - - tree := obj.(Tree) - - rvars := []ReadVar{ - {Name: "F64"}, - } - s, err := NewTreeScannerVars(tree, rvars...) - if err != nil { - b.Fatal(err) - } - defer s.Close() - - var data ScannerData - var sum float64 - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _ = s.SeekEntry(0) - for s.Next() { - err := s.Scan(&data.F64) - if err != nil { - b.Fatal(err) - } - sum += data.F64 - } - } -} - -func BenchmarkScannerVars(b *testing.B) { - f, err := riofs.Open("../testdata/x-flat-tree.root") - if err != nil { - b.Fatal(err) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - b.Fatal(err) - } - - tree := obj.(Tree) - - var f64 float64 - rvars := []ReadVar{ - {Name: "F64", Value: &f64}, - } - s, err := NewScannerVars(tree, rvars...) - if err != nil { - b.Fatal(err) - } - defer s.Close() - - var sum float64 - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _ = s.SeekEntry(0) - for s.Next() { - err := s.Scan() - if err != nil { - b.Fatal(err) - } - sum += f64 - } - } -} - -func BenchmarkTreeScannerVarsBigFileScalar(b *testing.B) { - f, err := riofs.Open("../testdata/mc_105986.ZZ.root") - if err != nil { - b.Skip(err) - } - - obj, err := f.Get("mini") - if err != nil { - b.Fatal(err) - } - tree := obj.(Tree) - - sc, err := NewTreeScannerVars(tree, ReadVar{Name: "mcWeight"}) - if err != nil { - b.Fatal(err) - } - defer sc.Close() - - var sum float32 - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _ = sc.SeekEntry(0) - for sc.Next() { - var data float32 - err := sc.Scan(&data) - if err != nil { - b.Error(err) - } - sum += data - } - } -} - -func BenchmarkScannerVarsBigFileScalar(b *testing.B) { - f, err := riofs.Open("../testdata/mc_105986.ZZ.root") - if err != nil { - b.Skip(err) - } - - obj, err := f.Get("mini") - if err != nil { - b.Fatal(err) - } - tree := obj.(Tree) - - var mc float32 - sc, err := NewScannerVars(tree, ReadVar{Name: "mcWeight", Value: &mc}) - if err != nil { - b.Fatal(err) - } - defer sc.Close() - - var sum float32 - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _ = sc.SeekEntry(0) - for sc.Next() { - err := sc.Scan() - if err != nil { - b.Error(err) - } - sum += mc - } - } -} - -func BenchmarkTreeScannerVarsBigFileSlice(b *testing.B) { - f, err := riofs.Open("../testdata/mc_105986.ZZ.root") - if err != nil { - b.Skip(err) - } - - obj, err := f.Get("mini") - if err != nil { - b.Fatal(err) - } - tree := obj.(Tree) - - sc, err := NewTreeScannerVars(tree, ReadVar{Name: "lep_pt"}) - if err != nil { - b.Fatal(err) - } - defer sc.Close() - - var sum float32 - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _ = sc.SeekEntry(0) - for sc.Next() { - var data []float32 - err := sc.Scan(&data) - if err != nil { - b.Error(err) - } - sum += data[0] - } - } -} - -func BenchmarkScannerVarsBigFileSlice(b *testing.B) { - f, err := riofs.Open("../testdata/mc_105986.ZZ.root") - if err != nil { - b.Skip(err) - } - - obj, err := f.Get("mini") - if err != nil { - b.Fatal(err) - } - tree := obj.(Tree) - - var pt []float32 - sc, err := NewScannerVars(tree, ReadVar{Name: "lep_pt", Value: &pt}) - if err != nil { - b.Fatal(err) - } - defer sc.Close() - - var sum float32 - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _ = sc.SeekEntry(0) - for sc.Next() { - err := sc.Scan() - if err != nil { - b.Error(err) - } - sum += pt[0] - } - } -} - -func TestTreeScannerSeekEntry(t *testing.T) { - t.Parallel() - - fname := "../testdata/chain.1.root" - f, err := riofs.Open(fname) - if err != nil { - t.Fatalf("could not open ROOT file %q: %v", fname, err) - } - defer f.Close() - - obj, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - - tree := obj.(Tree) - - type Data struct { - Event struct { - Beg string `groot:"Beg"` - F64 float64 `groot:"F64"` - ArrF64 [10]float64 `groot:"ArrayF64"` - N int32 `groot:"N"` - SliF64 []float64 `groot:"SliceF64"` - StdStr string `groot:"StdStr"` - StlVecF64 []float64 `groot:"StlVecF64"` - StlVecStr []string `groot:"StlVecStr"` - End string `groot:"End"` - } `groot:"evt"` - } - - sc, err := NewTreeScanner(tree, &Data{}) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - - for _, entry := range []int64{0, 1, 2, 0, 1, 2, 9, 0, 9, 1} { - err := sc.SeekEntry(entry) - if err != nil { - t.Fatalf("could not seek to entry %d: %v", entry, err) - } - if !sc.Next() { - t.Fatalf("could not read entry %d", entry) - } - var d Data - err = sc.Scan(&d) - if err != nil { - t.Fatal(err) - } - i := sc.Entry() - if i != entry { - t.Fatalf("did not seek to entry %d. got=%d, want=%d", entry, i, entry) - } - if d.Event.F64 != float64(i) { - t.Fatalf("entry[%d]:\ngot= %#v\nwant=%#v\n", i, d.Event.F64, float64(i)) - } - } - - if err := sc.Err(); err != nil && err != io.EOF { - t.Fatal(err) - } -} - -func TestNewScanVars(t *testing.T) { - f, err := riofs.Open("../testdata/leaves.root") - if err != nil { - t.Fatal(err) - } - defer f.Close() - - o, err := f.Get("tree") - if err != nil { - t.Fatal(err) - } - - tree := o.(Tree) - - vars := NewScanVars(tree) - want := []ReadVar{ - {Name: "B", Leaf: "B", Value: new(bool)}, - {Name: "Str", Leaf: "Str", Value: new(string)}, - {Name: "I8", Leaf: "I8", Value: new(int8)}, - {Name: "I16", Leaf: "I16", Value: new(int16)}, - {Name: "I32", Leaf: "I32", Value: new(int32)}, - {Name: "I64", Leaf: "I64", Value: new(int64)}, - {Name: "U8", Leaf: "U8", Value: new(uint8)}, - {Name: "U16", Leaf: "U16", Value: new(uint16)}, - {Name: "U32", Leaf: "U32", Value: new(uint32)}, - {Name: "U64", Leaf: "U64", Value: new(uint64)}, - {Name: "F32", Leaf: "F32", Value: new(float32)}, - {Name: "F64", Leaf: "F64", Value: new(float64)}, - {Name: "D16", Leaf: "D16", Value: new(root.Float16)}, - {Name: "D32", Leaf: "D32", Value: new(root.Double32)}, - // arrays - {Name: "ArrBs", Leaf: "ArrBs", Value: new([10]bool)}, - {Name: "ArrI8", Leaf: "ArrI8", Value: new([10]int8)}, - {Name: "ArrI16", Leaf: "ArrI16", Value: new([10]int16)}, - {Name: "ArrI32", Leaf: "ArrI32", Value: new([10]int32)}, - {Name: "ArrI64", Leaf: "ArrI64", Value: new([10]int64)}, - {Name: "ArrU8", Leaf: "ArrU8", Value: new([10]uint8)}, - {Name: "ArrU16", Leaf: "ArrU16", Value: new([10]uint16)}, - {Name: "ArrU32", Leaf: "ArrU32", Value: new([10]uint32)}, - {Name: "ArrU64", Leaf: "ArrU64", Value: new([10]uint64)}, - {Name: "ArrF32", Leaf: "ArrF32", Value: new([10]float32)}, - {Name: "ArrF64", Leaf: "ArrF64", Value: new([10]float64)}, - {Name: "ArrD16", Leaf: "ArrD16", Value: new([10]root.Float16)}, - {Name: "ArrD32", Leaf: "ArrD32", Value: new([10]root.Double32)}, - // slices - {Name: "N", Leaf: "N", Value: new(int32)}, - {Name: "SliBs", Leaf: "SliBs", Value: new([]bool)}, - {Name: "SliI8", Leaf: "SliI8", Value: new([]int8)}, - {Name: "SliI16", Leaf: "SliI16", Value: new([]int16)}, - {Name: "SliI32", Leaf: "SliI32", Value: new([]int32)}, - {Name: "SliI64", Leaf: "SliI64", Value: new([]int64)}, - {Name: "SliU8", Leaf: "SliU8", Value: new([]uint8)}, - {Name: "SliU16", Leaf: "SliU16", Value: new([]uint16)}, - {Name: "SliU32", Leaf: "SliU32", Value: new([]uint32)}, - {Name: "SliU64", Leaf: "SliU64", Value: new([]uint64)}, - {Name: "SliF32", Leaf: "SliF32", Value: new([]float32)}, - {Name: "SliF64", Leaf: "SliF64", Value: new([]float64)}, - {Name: "SliD16", Leaf: "SliD16", Value: new([]root.Float16)}, - {Name: "SliD32", Leaf: "SliD32", Value: new([]root.Double32)}, - } - - n := len(want) - if len(vars) < n { - n = len(vars) - } - - for i := 0; i < n; i++ { - got := vars[i] - if got.Name != want[i].Name { - t.Fatalf("invalid read-var name[%d]: got=%q, want=%q", i, got.Name, want[i].Name) - } - if got.Leaf != want[i].Leaf { - t.Fatalf("invalid read-var (name=%q) leaf-name[%d]: got=%q, want=%q", got.Name, i, got.Leaf, want[i].Leaf) - } - if got, want := reflect.TypeOf(got.Value), reflect.TypeOf(want[i].Value); got != want { - t.Fatalf("invalid read-var (name=%q) type[%d]: got=%v, want=%v", vars[i].Name, i, got, want) - } - } - - if len(want) != len(vars) { - t.Fatalf("invalid lengths. got=%d, want=%d", len(vars), len(want)) - } -} - -func TestG4LikeTree(t *testing.T) { - t.Parallel() - fname := rtests.XrdRemote("testdata/g4-like.root") - - f, err := riofs.Open(fname) - if err != nil { - t.Fatal(err.Error()) - } - defer f.Close() - - obj, err := f.Get("mytree") - if err != nil { - t.Fatal(err) - } - - tree := obj.(Tree) - - type EventData struct { - I32 int32 `groot:"i32"` - F64 float64 `groot:"f64"` - Sli []float64 `groot:"slif64"` - } - - want := func(i int64) (data EventData) { - data.I32 = int32(i + 1) - data.F64 = float64(i + 1) - data.Sli = make([]float64, i) - for ii := range data.Sli { - data.Sli[ii] = float64(ii) + float64(i) - } - return data - } - - data := EventData{ - Sli: make([]float64, 0), - } - rvars := []ReadVar{ - {Name: "i32", Value: &data.I32}, - {Name: "f64", Value: &data.F64}, - {Name: "slif64", Value: &data.Sli}, - } - sc, err := NewScannerVars(tree, rvars...) - if err != nil { - t.Fatal(err) - } - defer sc.Close() - for sc.Next() { - err := sc.Scan() - if err != nil { - t.Fatal(err) - } - i := sc.Entry() - if !reflect.DeepEqual(data, want(i)) { - t.Fatalf("entry[%d]:\ngot= %#v.\nwant=%#v\n", i, data, want(i)) - } - } - if err := sc.Err(); err != nil && err != io.EOF { - t.Fatal(err) - } -} - -func TestMultiLeafBranchWithReadVars(t *testing.T) { - t.Parallel() - - f, err := riofs.Open("../testdata/root_numpy_struct.root") - if err != nil { - t.Fatalf("%+v", err) - } - defer f.Close() - - obj, err := f.Get("test") - if err != nil { - t.Fatalf("%+v", err) - } - - tree := obj.(Tree) - - type Data struct { - b1l1 int32 - b1l2 float32 - b2l1 int32 - b2l2 float32 - } - - var ( - data Data - want = []Data{ - {10, 15.5, 20, 781.2}, - } - ) - - rvars := []ReadVar{ - { - Name: "branch1", - Leaf: "intleaf", - Value: &data.b1l1, - }, - { - Name: "branch1", - Leaf: "floatleaf", - Value: &data.b1l2, - }, - { - Name: "branch2", - Leaf: "intleaf", - Value: &data.b2l1, - }, - { - Name: "branch2", - Leaf: "floatleaf", - Value: &data.b2l2, - }, - } - - sc, err := NewScannerVars(tree, rvars...) - if err != nil { - t.Fatalf("could not create scanner: %+v", err) - } - defer sc.Close() - - for sc.Next() { - err = sc.Scan() - if err != nil { - t.Fatalf("could not scan entry %d: %+v", sc.Entry(), err) - } - - if got, want := data, want[sc.Entry()]; !reflect.DeepEqual(got, want) { - t.Fatalf("invalid entry %d:\ngot= %#v\nwant=%#v", sc.Entry(), got, want) - } - } -} - -func TestMultiLeafBranchWithTreeReadVars(t *testing.T) { - t.Parallel() - - f, err := riofs.Open("../testdata/root_numpy_struct.root") - if err != nil { - t.Fatalf("%+v", err) - } - defer f.Close() - - obj, err := f.Get("test") - if err != nil { - t.Fatalf("%+v", err) - } - - tree := obj.(Tree) - - type B struct { - L1 int32 `groot:"intleaf"` - L2 float32 `groot:"floatleaf"` - } - - type Data struct { - B1 B `groot:"branch1"` - B2 B `groot:"branch2"` - } - - var ( - data Data - want = []Data{ - {B{10, 15.5}, B{20, 781.2}}, - } - ) - - rvars := []ReadVar{ - { - Name: "branch1", - Value: &data.B1, - }, - { - Name: "branch2", - Value: &data.B2, - }, - } - - sc, err := NewTreeScannerVars(tree, rvars...) - if err != nil { - t.Fatalf("could not create scanner: %+v", err) - } - defer sc.Close() - - for sc.Next() { - err = sc.Scan(&data.B1, &data.B2) - if err != nil { - t.Fatalf("could not scan entry %d: %+v", sc.Entry(), err) - } - - if got, want := data, want[sc.Entry()]; !reflect.DeepEqual(got, want) { - t.Fatalf("invalid entry %d:\ngot= %#v\nwant=%#v", sc.Entry(), got, want) - } - } -}