go-hep.org/x/hep@v0.38.1/groot/rdict/type.go (about)

     1  // Copyright ©2020 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  	"reflect"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"go-hep.org/x/hep/groot/rbase"
    14  	"go-hep.org/x/hep/groot/rbytes"
    15  	"go-hep.org/x/hep/groot/rmeta"
    16  	"go-hep.org/x/hep/groot/root"
    17  	"go-hep.org/x/hep/groot/rtypes"
    18  )
    19  
    20  // TypeFromSI returns a Go type corresponding to the provided StreamerInfo.
    21  // TypeFromSI first reaches out to the known groot types (via groot/rtypes) and
    22  // then resorts to building a new type with reflect.
    23  func TypeFromSI(ctx rbytes.StreamerInfoContext, si rbytes.StreamerInfo) (reflect.Type, error) {
    24  	name := si.Name()
    25  	if rtypes.Factory.HasKey(name) {
    26  		fct := rtypes.Factory.Get(name)
    27  		v := fct()
    28  		return v.Type().Elem(), nil
    29  	}
    30  
    31  	switch {
    32  	case name == "TString":
    33  		if len(si.Elements()) == 0 {
    34  			sinfo := si.(*StreamerInfo)
    35  			sinfo.elems = append(sinfo.elems, &StreamerBasicType{
    36  				StreamerElement: Element{
    37  					Name:   *rbase.NewNamed("This", ""),
    38  					Type:   rmeta.TString,
    39  					Size:   25,
    40  					MaxIdx: [5]int32{0, 0, 0, 0, 0},
    41  					EName:  "TString",
    42  				}.New(),
    43  			})
    44  		}
    45  		return gotypes[reflect.String], nil
    46  
    47  	case name == "string", name == "std::string":
    48  		if len(si.Elements()) == 0 {
    49  			// fix for old (v=2) streamer for string
    50  			sinfo := si.(*StreamerInfo)
    51  			sinfo.elems = append(sinfo.elems, &StreamerSTLstring{
    52  				StreamerSTL: StreamerSTL{
    53  					StreamerElement: Element{
    54  						Name:   *rbase.NewNamed("This", "Used to call the proper TStreamerInfo case"),
    55  						Type:   rmeta.STLstring,
    56  						Size:   32,
    57  						MaxIdx: [5]int32{0, 0, 0, 0, 0},
    58  						EName:  "string",
    59  					}.New(),
    60  					vtype: rmeta.ESTLType(rmeta.STLstring),
    61  					ctype: rmeta.STLstring,
    62  				},
    63  			})
    64  		}
    65  		return gotypes[reflect.String], nil
    66  
    67  	case hasStdPrefix(name,
    68  		"vector", "list", "deque",
    69  		"set", "multiset", "unordered_set", "unordered_multiset",
    70  		"map", "multimap", "unordered_map", "unordered_multimap"):
    71  		var (
    72  			se      = si.Elements()[0]
    73  			rt, err = TypeFromSE(ctx, se)
    74  		)
    75  		if err != nil {
    76  			return nil, fmt.Errorf(
    77  				"rdict: could not build element %q type for %q: %w",
    78  				se.Name(), si.Name(), err,
    79  			)
    80  		}
    81  		return rt, nil
    82  	}
    83  
    84  	fields := make([]reflect.StructField, 0, len(si.Elements()))
    85  	for _, se := range si.Elements() {
    86  		rt, err := TypeFromSE(ctx, se)
    87  		if err != nil {
    88  			return nil, fmt.Errorf(
    89  				"rdict: could not build element %q type for %q: %w",
    90  				se.Name(), si.Name(), err,
    91  			)
    92  		}
    93  		et := se.Title()
    94  		if !strings.HasPrefix(et, "[") {
    95  			et = ""
    96  		}
    97  		if rt.Kind() == reflect.Array {
    98  			et = ndimsFromType(rt)
    99  		}
   100  		ft := reflect.StructField{
   101  			Name: "ROOT_" + cxxNameSanitizer.Replace(se.Name()),
   102  			Type: rt,
   103  			Tag:  reflect.StructTag(fmt.Sprintf("groot:%q", se.Name()+et)),
   104  		}
   105  		fields = append(fields, ft)
   106  	}
   107  
   108  	return reflect.StructOf(fields), nil
   109  }
   110  
   111  // TypeFromSE returns a Go type corresponding to the provided StreamerElement.
   112  // TypeFromSE first reaches out to the known groot types (via groot/rtypes) and
   113  // then resorts to building a new type with reflect.
   114  func TypeFromSE(ctx rbytes.StreamerInfoContext, se rbytes.StreamerElement) (reflect.Type, error) {
   115  	name := se.TypeName()
   116  	name = strings.TrimRight(name, "*")
   117  	if rtypes.Factory.HasKey(name) {
   118  		var (
   119  			fct = rtypes.Factory.Get(name)
   120  			v   = fct()
   121  			typ = v.Elem().Type()
   122  		)
   123  
   124  		return typeFromDescr(typ, se.TypeName(), se.ArrayLen(), se.ArrayDims()), nil
   125  	}
   126  
   127  	switch se := se.(type) {
   128  	default:
   129  		return nil, fmt.Errorf("rdict: unknown streamer element: %#v (%T)", se, se)
   130  
   131  	case *StreamerBase:
   132  		var (
   133  			typename = se.Name()
   134  			typevers = se.vbase
   135  		)
   136  
   137  		si, err := ctx.StreamerInfo(se.Name(), int(typevers))
   138  		if err != nil {
   139  			return nil, fmt.Errorf("rdict: could not find streamer info for base %q: %w", typename, err)
   140  		}
   141  		return TypeFromSI(ctx, si)
   142  
   143  	case *StreamerBasicType:
   144  		return typeFrom(ctx, se.TypeName(), se.Type(), se.Size(), se.ArrayLen(), se.ArrayDims())
   145  
   146  	case *StreamerString:
   147  		return typeFrom(ctx, se.TypeName(), se.Type(), se.Size(), se.ArrayLen(), se.ArrayDims())
   148  
   149  	case *StreamerBasicPointer:
   150  		return typeFrom(ctx, se.TypeName(), se.Type(), se.Size(), -1, se.ArrayDims())
   151  
   152  	case *StreamerSTLstring:
   153  		return gotypes[reflect.String], nil
   154  
   155  	case *StreamerLoop:
   156  		var (
   157  			typename = se.TypeName()
   158  			typevers = int16(-1)
   159  		)
   160  		typename = typename[:len(typename)-1] // drop final '*'
   161  		elt, err := typeFromTypeName(ctx, typename, typevers, se.Type(), se, 1)
   162  		if err != nil {
   163  			return nil, fmt.Errorf(
   164  				"rdict: could not find type of looper %q: %w",
   165  				typename, err,
   166  			)
   167  		}
   168  		return reflect.SliceOf(elt), nil
   169  
   170  	case *StreamerObject, *StreamerObjectAny:
   171  		var (
   172  			alen     = se.ArrayLen()
   173  			typename = se.TypeName()
   174  			typevers = -1
   175  			si, err  = ctx.StreamerInfo(typename, typevers)
   176  		)
   177  		if err != nil {
   178  			return nil, fmt.Errorf("rdict: could not find streamer info for type %q: %w", typename, err)
   179  		}
   180  
   181  		typ, err := TypeFromSI(ctx, si)
   182  		if err != nil {
   183  			return nil, fmt.Errorf("rdict: could not build type for %q: %w", typename, err)
   184  		}
   185  		return typeFromDescr(typ, typename, alen, se.ArrayDims()), nil
   186  
   187  	case *StreamerObjectPointer, *StreamerObjectAnyPointer:
   188  		var (
   189  			alen     = se.ArrayLen()
   190  			typename = se.TypeName()
   191  			typevers = -1
   192  		)
   193  		typename = typename[:len(typename)-1] // drop final '*'
   194  
   195  		si, err := ctx.StreamerInfo(typename, typevers)
   196  		if err != nil {
   197  			return nil, fmt.Errorf("rdict: could not find streamer info for ptr-to-object %q: %w", typename, err)
   198  		}
   199  
   200  		typ, err := TypeFromSI(ctx, si)
   201  		if err != nil {
   202  			return nil, fmt.Errorf("rdict: could not create type for ptr-to-object %q: %w", typename, err)
   203  		}
   204  		typ = reflect.PointerTo(typ)
   205  		return typeFromDescr(typ, typename, alen, se.ArrayDims()), nil
   206  
   207  	case *StreamerSTL:
   208  		switch se.STLType() {
   209  		case rmeta.STLvector, rmeta.STLlist, rmeta.STLdeque, rmeta.STLend:
   210  			var (
   211  				ct       = se.ContainedType()
   212  				typevers = int16(-1)
   213  				ename    string
   214  			)
   215  			switch {
   216  			case hasThisHandling(se):
   217  				ename = se.Title()
   218  				ename = ename[:strings.Index(ename, "> Used to call the proper TStreamerInfo case")+1]
   219  				ename = strings.TrimSpace(ename)
   220  				ename = rmeta.CxxTemplateFrom("Type" + ename).Args[0]
   221  			default:
   222  				ename = rmeta.CxxTemplateFrom(se.TypeName()).Args[0]
   223  			}
   224  			elt, err := typeFromTypeName(ctx, ename, typevers, ct, se, 1)
   225  			if err != nil {
   226  				return nil, fmt.Errorf("rdict: could not create type for %q: %w", se.TypeName(), err)
   227  			}
   228  			return reflect.SliceOf(elt), nil
   229  
   230  		case rmeta.STLset, rmeta.STLunorderedset, rmeta.STLmultiset, rmeta.STLunorderedmultiset:
   231  			var (
   232  				ct       = se.ContainedType()
   233  				typename = se.TypeName()
   234  				typevers = int16(-1)
   235  				kname    string
   236  			)
   237  			switch {
   238  			case hasThisHandling(se):
   239  				kname = se.Title()
   240  				kname = kname[:strings.Index(kname, "> Used to call the proper TStreamerInfo case")+1]
   241  				kname = strings.TrimSpace(kname)
   242  				kname = rmeta.CxxTemplateFrom("Type" + kname).Args[0]
   243  			default:
   244  				kname = rmeta.CxxTemplateFrom(typename).Args[0]
   245  			}
   246  
   247  			key, err := typeFromTypeName(ctx, kname, typevers, ct, se, 1)
   248  			if err != nil {
   249  				return nil, fmt.Errorf(
   250  					"could not find key type %q for std::{,multi,unordered_}set %q: %w", kname, typename, err,
   251  				)
   252  			}
   253  			return reflect.SliceOf(key), nil
   254  
   255  		case rmeta.STLmap, rmeta.STLunorderedmap, rmeta.STLmultimap, rmeta.STLunorderedmultimap:
   256  			var (
   257  				ct       = se.ContainedType()
   258  				typename = se.TypeName()
   259  				typevers = int16(-1)
   260  				enames   []string
   261  			)
   262  			switch {
   263  			case hasThisHandling(se):
   264  				ename := se.Title()
   265  				ename = ename[1:] // drop leading '<'
   266  				ename = ename[:strings.Index(ename, "> Used to call the proper TStreamerInfo case")]
   267  				ename = strings.TrimSpace(ename)
   268  				enames = rmeta.CxxTemplateFrom(ename).Args
   269  			default:
   270  				enames = rmeta.CxxTemplateFrom(typename).Args
   271  			}
   272  			kname := enames[0]
   273  			vname := enames[1]
   274  
   275  			key, err := typeFromTypeName(ctx, kname, typevers, ct, se, 1)
   276  			if err != nil {
   277  				return nil, fmt.Errorf(
   278  					"could not find key type %q for std::{,multi,unordered_}map %q: %w", kname, typename, err,
   279  				)
   280  			}
   281  			val, err := typeFromTypeName(ctx, vname, typevers, ct, se, 1)
   282  			if err != nil {
   283  				return nil, fmt.Errorf(
   284  					"could not find val type %q for std::{,multi,unordered_}map %q: %w", vname, typename, err,
   285  				)
   286  			}
   287  			return reflect.MapOf(key, val), nil
   288  
   289  		case rmeta.STLbitset:
   290  			var (
   291  				typename = se.TypeName()
   292  				enames   = rmeta.CxxTemplateFrom(typename).Args
   293  				_, err   = strconv.Atoi(enames[0])
   294  			)
   295  			if err != nil {
   296  				return nil, fmt.Errorf(
   297  					"could not infer bitset argument (type=%q): %w", typename, err,
   298  				)
   299  			}
   300  			// FIXME(sbinet): use a fixed-sized array rounded-up to n/8-bytes
   301  			//	bits2bytes := func(v int) int {
   302  			//		const len = 8-1
   303  			//		return (v + (8-(sz&len))&len)/8
   304  			//	}
   305  			//	n := bits2bytes(v)
   306  			return reflect.SliceOf(gotypes[reflect.Uint8]), nil
   307  
   308  		default:
   309  			return nil, fmt.Errorf("rdict: STL container not implemented: %#v (vtype=%+v)", se, se.STLType())
   310  		}
   311  	}
   312  }
   313  
   314  func typeFrom(ctx rbytes.StreamerInfoContext, typename string, enum rmeta.Enum, size uintptr, n int, dims []int32) (reflect.Type, error) {
   315  	var rt reflect.Type
   316  
   317  	switch enum {
   318  	case rmeta.Bool:
   319  		rt = gotypes[reflect.Bool]
   320  	case rmeta.Uint8:
   321  		rt = gotypes[reflect.Uint8]
   322  	case rmeta.Uint16:
   323  		rt = gotypes[reflect.Uint16]
   324  	case rmeta.Uint32, rmeta.Bits:
   325  		rt = gotypes[reflect.Uint32]
   326  	case rmeta.Uint64, rmeta.ULong64:
   327  		rt = gotypes[reflect.Uint64]
   328  	case rmeta.Int8:
   329  		rt = gotypes[reflect.Int8]
   330  	case rmeta.Int16:
   331  		rt = gotypes[reflect.Int16]
   332  	case rmeta.Int32:
   333  		rt = gotypes[reflect.Int32]
   334  	case rmeta.Int64, rmeta.Long64:
   335  		rt = gotypes[reflect.Int64]
   336  	case rmeta.Float32:
   337  		rt = gotypes[reflect.Float32]
   338  	case rmeta.Float64:
   339  		rt = gotypes[reflect.Float64]
   340  	case rmeta.Float16:
   341  		rt = reflect.TypeOf((*root.Float16)(nil)).Elem()
   342  	case rmeta.Double32:
   343  		rt = reflect.TypeOf((*root.Double32)(nil)).Elem()
   344  	case rmeta.TString, rmeta.STLstring:
   345  		rt = gotypes[reflect.String]
   346  
   347  	case rmeta.CharStar:
   348  		rt = gotypes[reflect.String]
   349  
   350  	case rmeta.Counter:
   351  		switch size {
   352  		case 4:
   353  			rt = gotypes[reflect.Int32]
   354  		case 8:
   355  			rt = gotypes[reflect.Int64]
   356  		default:
   357  			return nil, fmt.Errorf("rdict: invalid counter size=%d", size)
   358  		}
   359  
   360  	case rmeta.TObject:
   361  		rt = reflect.TypeOf((*rbase.Object)(nil)).Elem()
   362  
   363  	case rmeta.TNamed:
   364  		rt = reflect.TypeOf((*rbase.Named)(nil)).Elem()
   365  
   366  	case rmeta.OffsetL + rmeta.Bool:
   367  		// dim handled by typeFromDescr.
   368  		rt = gotypes[reflect.Bool]
   369  	case rmeta.OffsetL + rmeta.Uint8:
   370  		// dim handled by typeFromDescr.
   371  		rt = gotypes[reflect.Uint8]
   372  	case rmeta.OffsetL + rmeta.Uint16:
   373  		// dim handled by typeFromDescr.
   374  		rt = gotypes[reflect.Uint16]
   375  	case rmeta.OffsetL + rmeta.Uint32:
   376  		// dim handled by typeFromDescr.
   377  		rt = gotypes[reflect.Uint32]
   378  	case rmeta.OffsetL + rmeta.Uint64, rmeta.OffsetL + rmeta.ULong64:
   379  		// dim handled by typeFromDescr.
   380  		rt = gotypes[reflect.Uint64]
   381  	case rmeta.OffsetL + rmeta.Int8:
   382  		// dim handled by typeFromDescr.
   383  		rt = gotypes[reflect.Int8]
   384  	case rmeta.OffsetL + rmeta.Int16:
   385  		// dim handled by typeFromDescr.
   386  		rt = gotypes[reflect.Int16]
   387  	case rmeta.OffsetL + rmeta.Int32:
   388  		// dim handled by typeFromDescr.
   389  		rt = gotypes[reflect.Int32]
   390  	case rmeta.OffsetL + rmeta.Int64, rmeta.OffsetL + rmeta.Long64:
   391  		// dim handled by typeFromDescr.
   392  		rt = gotypes[reflect.Int64]
   393  	case rmeta.OffsetL + rmeta.Float32:
   394  		// dim handled by typeFromDescr.
   395  		rt = gotypes[reflect.Float32]
   396  	case rmeta.OffsetL + rmeta.Float64:
   397  		// dim handled by typeFromDescr.
   398  		rt = gotypes[reflect.Float64]
   399  	case rmeta.OffsetL + rmeta.Float16:
   400  		// dim handled by typeFromDescr.
   401  		rt = reflect.TypeOf((*root.Float16)(nil)).Elem()
   402  	case rmeta.OffsetL + rmeta.Double32:
   403  		// dim handled by typeFromDescr.
   404  		rt = reflect.TypeOf((*root.Double32)(nil)).Elem()
   405  	case rmeta.OffsetL + rmeta.TString,
   406  		rmeta.OffsetL + rmeta.CharStar,
   407  		rmeta.OffsetL + rmeta.STLstring:
   408  		// dim handled by typeFromDescr.
   409  		rt = gotypes[reflect.String]
   410  
   411  	case rmeta.OffsetP + rmeta.Bool:
   412  		rt = reflect.SliceOf(gotypes[reflect.Bool])
   413  	case rmeta.OffsetP + rmeta.Uint8:
   414  		rt = reflect.SliceOf(gotypes[reflect.Uint8])
   415  	case rmeta.OffsetP + rmeta.Uint16:
   416  		rt = reflect.SliceOf(gotypes[reflect.Uint16])
   417  	case rmeta.OffsetP + rmeta.Uint32:
   418  		rt = reflect.SliceOf(gotypes[reflect.Uint32])
   419  	case rmeta.OffsetP + rmeta.Uint64, rmeta.OffsetP + rmeta.ULong64:
   420  		rt = reflect.SliceOf(gotypes[reflect.Uint64])
   421  	case rmeta.OffsetP + rmeta.Int8:
   422  		rt = reflect.SliceOf(gotypes[reflect.Int8])
   423  	case rmeta.OffsetP + rmeta.Int16:
   424  		rt = reflect.SliceOf(gotypes[reflect.Int16])
   425  	case rmeta.OffsetP + rmeta.Int32:
   426  		rt = reflect.SliceOf(gotypes[reflect.Int32])
   427  	case rmeta.OffsetP + rmeta.Int64, rmeta.OffsetP + rmeta.Long64:
   428  		rt = reflect.SliceOf(gotypes[reflect.Int64])
   429  	case rmeta.OffsetP + rmeta.Float32:
   430  		rt = reflect.SliceOf(gotypes[reflect.Float32])
   431  	case rmeta.OffsetP + rmeta.Float64:
   432  		rt = reflect.SliceOf(gotypes[reflect.Float64])
   433  	case rmeta.OffsetP + rmeta.Float16:
   434  		rt = reflect.SliceOf(reflect.TypeOf((*root.Float16)(nil)).Elem())
   435  	case rmeta.OffsetP + rmeta.Double32:
   436  		rt = reflect.SliceOf(reflect.TypeOf((*root.Double32)(nil)).Elem())
   437  	case rmeta.OffsetP + rmeta.STLstring,
   438  		rmeta.OffsetP + rmeta.CharStar:
   439  		rt = reflect.SliceOf(gotypes[reflect.String])
   440  	}
   441  
   442  	if rt == nil {
   443  		return nil, fmt.Errorf("rmeta=%d (%v) not implemented (size=%d, n=%v)", enum, enum, size, n)
   444  	}
   445  
   446  	return typeFromDescr(rt, typename, n, dims), nil
   447  }
   448  
   449  func typeFromTypeName(ctx rbytes.StreamerInfoContext, typename string, typevers int16, enum rmeta.Enum, se rbytes.StreamerElement, n int) (reflect.Type, error) {
   450  	e, ok := rmeta.TypeName2Enum(typename)
   451  	if ok {
   452  		return typeFrom(ctx, typename, e, se.Size(), n, se.ArrayDims())
   453  	}
   454  
   455  	switch {
   456  	case hasStdPrefix(typename, "vector", "list", "deque"):
   457  		enames := rmeta.CxxTemplateFrom(typename).Args
   458  		et, err := typeFromTypeName(ctx, enames[0], -1, -1, se, n)
   459  		if err != nil {
   460  			return nil, err
   461  		}
   462  		return reflect.SliceOf(et), nil
   463  
   464  	case hasStdPrefix(typename, "set", "multiset", "unordered_set", "unordered_multiset"):
   465  		enames := rmeta.CxxTemplateFrom(typename).Args
   466  		kname := enames[0]
   467  
   468  		kt, err := typeFromTypeName(ctx, kname, -1, -1, se, n)
   469  		if err != nil {
   470  			return nil, err
   471  		}
   472  		return reflect.SliceOf(kt), nil
   473  
   474  	case hasStdPrefix(typename, "map", "multimap", "unordered_map", "unordered_multimap"):
   475  		enames := rmeta.CxxTemplateFrom(typename).Args
   476  		kname := enames[0]
   477  		vname := enames[1]
   478  
   479  		kt, err := typeFromTypeName(ctx, kname, -1, -1, se, n)
   480  		if err != nil {
   481  			return nil, err
   482  		}
   483  		vt, err := typeFromTypeName(ctx, vname, -1, -1, se, n)
   484  		if err != nil {
   485  			return nil, err
   486  		}
   487  		return reflect.MapOf(kt, vt), nil
   488  
   489  	case hasStdPrefix(typename, "pair"):
   490  		enames := rmeta.CxxTemplateFrom(typename).Args
   491  		p0 := enames[0]
   492  		p1 := enames[1]
   493  		t0, err := typeFromTypeName(ctx, p0, -1, -1, se, n)
   494  		if err != nil {
   495  			return nil, err
   496  		}
   497  		t1, err := typeFromTypeName(ctx, p1, -1, -1, se, n)
   498  		if err != nil {
   499  			return nil, err
   500  		}
   501  		return reflect.StructOf([]reflect.StructField{
   502  			{
   503  				Name: "ROOT_first",
   504  				Type: t0,
   505  				Tag:  reflect.StructTag(`groot:"first"`),
   506  			},
   507  			{
   508  				Name: "ROOT_second",
   509  				Type: t1,
   510  				Tag:  reflect.StructTag(`groot:"second"`),
   511  			},
   512  		}), nil
   513  
   514  	case hasStdPrefix(typename, "bitset"):
   515  		var (
   516  			enames = rmeta.CxxTemplateFrom(typename).Args
   517  			_, err = strconv.Atoi(enames[0])
   518  		)
   519  
   520  		if err != nil {
   521  			return nil, fmt.Errorf("rdict: invalid STL bitset argument (type=%q): %+v", typename, err)
   522  		}
   523  		return reflect.SliceOf(gotypes[reflect.Uint8]), nil
   524  	}
   525  
   526  	osi, err := ctx.StreamerInfo(typename, int(typevers))
   527  	if err != nil {
   528  		return nil, fmt.Errorf("rdict: could not find streamer info for %q (version=%d): %w", typename, typevers, err)
   529  	}
   530  
   531  	return TypeFromSI(ctx, osi)
   532  }
   533  
   534  func typeFromDescr(typ reflect.Type, typename string, alen int, dims []int32) reflect.Type {
   535  	if alen > 0 {
   536  		// handle [n][m][u][v][w]T
   537  		ndim := len(dims)
   538  		for i := range dims {
   539  			typ = reflect.ArrayOf(int(dims[ndim-1-i]), typ)
   540  		}
   541  		return typ
   542  	}
   543  
   544  	if alen < 0 {
   545  		// slice. drop one '*' from typename.
   546  		typename = strings.TrimSuffix(typename, "*")
   547  	}
   548  	if typename == "char*" {
   549  		// slice. drop one '*' from typename.
   550  		typename = strings.TrimSuffix(typename, "*")
   551  	}
   552  
   553  	// handle T***
   554  	for i := range typename {
   555  		if typename[len(typename)-1-i] != '*' {
   556  			break
   557  		}
   558  		typ = reflect.PointerTo(typ)
   559  	}
   560  
   561  	return typ
   562  }
   563  
   564  func ndimsFromType(rt reflect.Type) string {
   565  	var dims []int
   566  	for rt.Kind() == reflect.Array {
   567  		dims = append(dims, rt.Len())
   568  		rt = rt.Elem()
   569  	}
   570  	var o strings.Builder
   571  	for _, v := range dims {
   572  		o.WriteString("[" + strconv.Itoa(v) + "]")
   573  	}
   574  	return o.String()
   575  }
   576  
   577  var (
   578  	gotypes = map[reflect.Kind]reflect.Type{
   579  		reflect.Bool:    reflect.TypeOf(false),
   580  		reflect.Uint8:   reflect.TypeOf(uint8(0)),
   581  		reflect.Uint16:  reflect.TypeOf(uint16(0)),
   582  		reflect.Uint32:  reflect.TypeOf(uint32(0)),
   583  		reflect.Uint64:  reflect.TypeOf(uint64(0)),
   584  		reflect.Int8:    reflect.TypeOf(int8(0)),
   585  		reflect.Int16:   reflect.TypeOf(int16(0)),
   586  		reflect.Int32:   reflect.TypeOf(int32(0)),
   587  		reflect.Int64:   reflect.TypeOf(int64(0)),
   588  		reflect.Uint:    reflect.TypeOf(uint(0)),
   589  		reflect.Int:     reflect.TypeOf(int(0)),
   590  		reflect.Float32: reflect.TypeOf(float32(0)),
   591  		reflect.Float64: reflect.TypeOf(float64(0)),
   592  		reflect.String:  reflect.TypeOf(""),
   593  	}
   594  )
   595  
   596  func hasStdPrefix(typename string, ps ...string) bool {
   597  	for _, p := range ps {
   598  		switch {
   599  		case strings.HasPrefix(typename, p+"<"),
   600  			strings.HasPrefix(typename, "std::"+p+"<"):
   601  			return true
   602  		}
   603  	}
   604  	return false
   605  }
   606  
   607  func hasThisHandling(se rbytes.StreamerElement) bool {
   608  	return se.Name() == "This" && strings.HasPrefix(se.Title(), "<")
   609  }