go-hep.org/x/hep@v0.38.1/groot/rdict/visit.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  package rdict
     6  
     7  import (
     8  	"fmt"
     9  	"strings"
    10  
    11  	"go-hep.org/x/hep/groot/rbytes"
    12  	"go-hep.org/x/hep/groot/rmeta"
    13  )
    14  
    15  // Visit inspects a streamer info and visits all its elements, once.
    16  func Visit(ctx rbytes.StreamerInfoContext, si rbytes.StreamerInfo, f func(depth int, se rbytes.StreamerElement) error) error {
    17  	v := newVisitor(ctx, f)
    18  	return v.run(0, si)
    19  }
    20  
    21  type visitor struct {
    22  	ctx rbytes.StreamerInfoContext
    23  	set map[rbytes.StreamerElement]struct{}
    24  	f   func(depth int, se rbytes.StreamerElement) error
    25  }
    26  
    27  func newVisitor(ctx rbytes.StreamerInfoContext, f func(depth int, se rbytes.StreamerElement) error) *visitor {
    28  	if ctx == nil {
    29  		ctx = StreamerInfos
    30  	}
    31  	return &visitor{
    32  		ctx: ctx,
    33  		set: make(map[rbytes.StreamerElement]struct{}),
    34  		f:   f,
    35  	}
    36  }
    37  
    38  func (v *visitor) seen(se rbytes.StreamerElement) bool {
    39  	if _, seen := v.set[se]; seen {
    40  		return true
    41  	}
    42  	v.set[se] = struct{}{}
    43  	return false
    44  }
    45  
    46  func (v *visitor) run(depth int, si rbytes.StreamerInfo) error {
    47  	for _, se := range si.Elements() {
    48  		err := v.visitSE(depth, se)
    49  		if err != nil {
    50  			return err
    51  		}
    52  	}
    53  	return nil
    54  }
    55  
    56  func (v *visitor) visitSE(depth int, se rbytes.StreamerElement) error {
    57  	if v.seen(se) {
    58  		return nil
    59  	}
    60  
    61  	err := v.f(depth, se)
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	switch se.TypeName() {
    67  	case "TVirtualIndex", "TVirtualIndex*":
    68  		return nil
    69  	}
    70  
    71  	switch se := se.(type) {
    72  	case *StreamerBasicType:
    73  		// no-op
    74  	case *StreamerBasicPointer:
    75  		// no-op
    76  
    77  	case *StreamerBase:
    78  		base, err := v.ctx.StreamerInfo(se.Name(), -1)
    79  		if err != nil {
    80  			return fmt.Errorf("could not find base %q: %w", se.Name(), err)
    81  		}
    82  		return v.run(depth+1, base)
    83  	case *StreamerObject:
    84  		si, err := v.ctx.StreamerInfo(se.TypeName(), -1)
    85  		if err != nil {
    86  			return fmt.Errorf("could not find object %q: %w", se.TypeName(), err)
    87  		}
    88  		return v.run(depth+1, si)
    89  
    90  	case *StreamerObjectPointer:
    91  		tname := strings.TrimRight(se.TypeName(), "*")
    92  		si, err := v.ctx.StreamerInfo(tname, -1)
    93  		if err != nil {
    94  			return fmt.Errorf("could not find object-pointer %q: %w", tname, err)
    95  		}
    96  		return v.run(depth+1, si)
    97  
    98  	case *StreamerObjectAny:
    99  		tname := se.TypeName()
   100  		si, err := v.ctx.StreamerInfo(tname, -1)
   101  		if err != nil {
   102  			return fmt.Errorf("could not find object-any %q: %w", tname, err)
   103  		}
   104  		return v.run(depth+1, si)
   105  
   106  	case *StreamerObjectAnyPointer:
   107  		tname := strings.TrimRight(se.TypeName(), "*")
   108  		si, err := v.ctx.StreamerInfo(tname, -1)
   109  		if err != nil {
   110  			return fmt.Errorf("could not find object-any-pointer %q: %w", tname, err)
   111  		}
   112  		return v.run(depth+1, si)
   113  
   114  	case *StreamerString, *StreamerSTLstring:
   115  		// no-op
   116  
   117  	case *StreamerSTL:
   118  		switch se.STLType() {
   119  		case rmeta.STLdeque, rmeta.STLforwardlist, rmeta.STLlist,
   120  			rmeta.STLset, rmeta.STLunorderedset, rmeta.STLunorderedmultiset,
   121  			rmeta.STLvector:
   122  
   123  			etn := se.ElemTypeName()
   124  			tname := strings.TrimRight(etn[0], "*")
   125  			if _, ok := rmeta.CxxBuiltins[tname]; ok {
   126  				// no-op: C++ builtin.
   127  				return nil
   128  			}
   129  			si, err := v.ctx.StreamerInfo(tname, -1)
   130  			if err != nil {
   131  				return fmt.Errorf("could not find std::container<T> element %q: %w", tname, err)
   132  			}
   133  			return v.run(depth+1, si)
   134  
   135  		default:
   136  			return fmt.Errorf("rdict: cant visit non-vector-like STL streamers %#v", se)
   137  		}
   138  
   139  	case *StreamerLoop:
   140  		tname := strings.TrimRight(se.TypeName(), "*")
   141  		if _, ok := rmeta.CxxBuiltins[tname]; ok {
   142  			// no-op: C++ builtin.
   143  			return nil
   144  		}
   145  		si, err := v.ctx.StreamerInfo(tname, -1)
   146  		if err != nil {
   147  			return fmt.Errorf("could not find looper %q: %w", tname, err)
   148  		}
   149  		return v.run(depth+1, si)
   150  
   151  	default:
   152  		panic(fmt.Errorf("rdict: unknown visit streamer %T", se))
   153  	}
   154  
   155  	return nil
   156  }