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 }