go-hep.org/x/hep@v0.38.1/groot/rcmd/dump.go (about)

     1  // Copyright ©2019 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  package rcmd
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"reflect"
    12  	"strconv"
    13  
    14  	"go-hep.org/x/hep/groot"
    15  	"go-hep.org/x/hep/groot/rdict"
    16  	"go-hep.org/x/hep/groot/rhist"
    17  	"go-hep.org/x/hep/groot/riofs"
    18  	"go-hep.org/x/hep/groot/root"
    19  	"go-hep.org/x/hep/groot/rtree"
    20  	"go-hep.org/x/hep/hbook/rootcnv"
    21  	"go-hep.org/x/hep/hbook/yodacnv"
    22  )
    23  
    24  // Dump dumps the content of the fname ROOT file to the provided io.Writer.
    25  // If deep is true, Dump will recursively inspect directories and trees.
    26  // Dump only display the content of ROOT objects satisfying the provided filter function.
    27  //
    28  // If filter is nil, Dump will consider all ROOT objects.
    29  func Dump(w io.Writer, fname string, deep bool, filter func(name string) bool) error {
    30  	f, err := groot.Open(fname)
    31  	if err != nil {
    32  		return fmt.Errorf("could not open file with read-access: %w", err)
    33  	}
    34  	defer f.Close()
    35  
    36  	if filter == nil {
    37  		filter = func(string) bool { return true }
    38  	}
    39  
    40  	cmd := dumpCmd{
    41  		w:     w,
    42  		deep:  deep,
    43  		match: filter,
    44  	}
    45  	return cmd.dumpDir(f)
    46  }
    47  
    48  type dumpCmd struct {
    49  	w     io.Writer
    50  	deep  bool
    51  	match func(name string) bool
    52  }
    53  
    54  func (cmd *dumpCmd) dumpDir(dir riofs.Directory) error {
    55  	for i, key := range dir.Keys() {
    56  		fmt.Fprintf(cmd.w, "key[%03d]: %s;%d %q (%s)", i, key.Name(), key.Cycle(), key.Title(), key.ClassName())
    57  		if !(cmd.deep && cmd.match(key.Name())) {
    58  			fmt.Fprint(cmd.w, "\n")
    59  			continue
    60  		}
    61  		obj, err := key.Object()
    62  		if err != nil {
    63  			return fmt.Errorf("could not decode object %q from dir %q: %w", key.Name(), dir.(root.Named).Name(), err)
    64  		}
    65  		err = cmd.dumpObj(obj)
    66  		if errors.Is(err, errIgnoreKey) {
    67  			continue
    68  		}
    69  		if err != nil {
    70  			return fmt.Errorf("error dumping key %q: %w", key.Name(), err)
    71  		}
    72  	}
    73  	return nil
    74  }
    75  
    76  var errIgnoreKey = fmt.Errorf("rcmd: ignore key")
    77  
    78  func (cmd *dumpCmd) dumpObj(obj root.Object) error {
    79  	var err error
    80  	switch obj := obj.(type) {
    81  	case rtree.Tree:
    82  		fmt.Fprintf(cmd.w, "\n")
    83  		err = cmd.dumpTree(obj)
    84  	case riofs.Directory:
    85  		fmt.Fprintf(cmd.w, "\n")
    86  		err = cmd.dumpDir(obj)
    87  	case rhist.H2:
    88  		fmt.Fprintf(cmd.w, "\n")
    89  		err = cmd.dumpH2(obj)
    90  	case rhist.H1: // keep after rhist.H2
    91  		fmt.Fprintf(cmd.w, "\n")
    92  		err = cmd.dumpH1(obj)
    93  	case rhist.Graph:
    94  		fmt.Fprintf(cmd.w, "\n")
    95  		err = cmd.dumpGraph(obj)
    96  	case rhist.MultiGraph:
    97  		for _, g := range obj.Graphs() {
    98  			fmt.Fprintf(cmd.w, "\n")
    99  			err = cmd.dumpGraph(g)
   100  			if err != nil {
   101  				return err
   102  			}
   103  		}
   104  	case root.List:
   105  		fmt.Fprintf(cmd.w, "\n")
   106  		err = cmd.dumpList(obj)
   107  	case *rdict.Object:
   108  		fmt.Fprintf(cmd.w, " => %v\n", obj)
   109  	case fmt.Stringer:
   110  		fmt.Fprintf(cmd.w, " => %q\n", obj.String())
   111  	case root.ObjectFinder:
   112  		keys := obj.Keys()
   113  		if len(keys) == 0 {
   114  			return err
   115  		}
   116  		fmt.Fprintf(cmd.w, "\n")
   117  		for _, name := range keys {
   118  			sub, err := obj.Get(name)
   119  			if err != nil {
   120  				return err
   121  			}
   122  			err = cmd.dumpObj(sub)
   123  			if err != nil {
   124  				return err
   125  			}
   126  		}
   127  	default:
   128  		fmt.Fprintf(cmd.w, " => ignoring key of type %T\n", obj)
   129  		return errIgnoreKey
   130  	}
   131  	return err
   132  }
   133  
   134  func (cmd *dumpCmd) dumpList(lst root.List) error {
   135  	for i := range lst.Len() {
   136  		fmt.Fprintf(cmd.w, "lst[%s][%d]: ", lst.Name(), i)
   137  		err := cmd.dumpObj(lst.At(i))
   138  		if err != nil && !errors.Is(err, errIgnoreKey) {
   139  			return fmt.Errorf("could not dump list: %w", err)
   140  		}
   141  	}
   142  	return nil
   143  }
   144  
   145  func (cmd *dumpCmd) dumpTree(t rtree.Tree) error {
   146  	vars := rtree.NewReadVars(t)
   147  	r, err := rtree.NewReader(t, vars)
   148  	if err != nil {
   149  		return fmt.Errorf("could not create reader: %w", err)
   150  	}
   151  	defer r.Close()
   152  
   153  	names := make([][]byte, len(vars))
   154  	for i, v := range vars {
   155  		name := v.Name
   156  		if v.Leaf != "" && v.Leaf != v.Name {
   157  			name = v.Name + "." + v.Leaf
   158  		}
   159  		names[i] = []byte(name)
   160  	}
   161  
   162  	// FIXME(sbinet): don't use a "global" buffer for when rtree.Reader reads multiple
   163  	// events in parallel.
   164  	buf := make([]byte, 0, 8*1024)
   165  	hdr := make([]byte, 0, 6)
   166  	err = r.Read(func(rctx rtree.RCtx) error {
   167  		hdr = hdr[:0]
   168  		hdr = append(hdr, '[')
   169  		switch {
   170  		case rctx.Entry < 10:
   171  			hdr = append(hdr, '0', '0')
   172  		case rctx.Entry < 100:
   173  			hdr = append(hdr, '0')
   174  		}
   175  		hdr = strconv.AppendInt(hdr, rctx.Entry, 10)
   176  		hdr = append(hdr, ']', '[')
   177  		for i, v := range vars {
   178  			buf = buf[:0]
   179  			buf = append(buf, hdr...)
   180  			buf = append(buf, names[i]...)
   181  			buf = append(buf, ']', ':', ' ')
   182  			rv := reflect.Indirect(reflect.ValueOf(v.Value))
   183  			// All of this is a convoluted (but efficient) way to do:
   184  			//  fmt.Fprintf(cmd.w, "[%03d][%s]: %v\n", rctx.Entry, name, rv.Interface())
   185  			buf = append(buf, fmt.Sprintf("%v\n", rv.Interface())...)
   186  			_, err = cmd.w.Write(buf)
   187  			if err != nil {
   188  				return err
   189  			}
   190  		}
   191  		return nil
   192  	})
   193  	if err != nil {
   194  		return fmt.Errorf("rcmd: could not read through tree: %w", err)
   195  	}
   196  	return nil
   197  }
   198  
   199  func (cmd *dumpCmd) dumpH1(h1 rhist.H1) error {
   200  	h := rootcnv.H1D(h1)
   201  	return yodacnv.Write(cmd.w, h)
   202  }
   203  
   204  func (cmd *dumpCmd) dumpH2(h2 rhist.H2) error {
   205  	h := rootcnv.H2D(h2)
   206  	return yodacnv.Write(cmd.w, h)
   207  }
   208  
   209  func (cmd *dumpCmd) dumpGraph(gr rhist.Graph) error {
   210  	g := rootcnv.S2D(gr)
   211  	return yodacnv.Write(cmd.w, g)
   212  }