go-hep.org/x/hep@v0.38.1/groot/riofs/file.go (about) 1 // Copyright ©2017 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 riofs 6 7 import ( 8 "errors" 9 "fmt" 10 "io" 11 "math" 12 "os" 13 "strings" 14 "sync" 15 "time" 16 17 "go-hep.org/x/hep/groot/internal/rcompress" 18 "go-hep.org/x/hep/groot/rbase" 19 "go-hep.org/x/hep/groot/rbytes" 20 "go-hep.org/x/hep/groot/rcont" 21 "go-hep.org/x/hep/groot/rdict" 22 "go-hep.org/x/hep/groot/rmeta" 23 "go-hep.org/x/hep/groot/root" 24 ) 25 26 var ( 27 ErrReadOnly = errors.New("riofs: file read-only") 28 ) 29 30 type Reader interface { 31 io.Reader 32 io.ReaderAt 33 io.Closer 34 } 35 36 type Writer interface { 37 io.Writer 38 io.WriterAt 39 io.Closer 40 } 41 42 type syncer interface { 43 // Sync commits the current contents of the file to stable storage. 44 Sync() error 45 } 46 47 type stater interface { 48 // Stat returns a FileInfo describing the file. 49 Stat() (os.FileInfo, error) 50 } 51 52 // FileOption configures internal states of a ROOT file. 53 type FileOption func(f *File) error 54 55 // A ROOT file is a suite of consecutive data records (TKey's) with 56 // the following format (see also the TKey class). If the key is 57 // located past the 32 bit file limit (> 2 GB) then some fields will 58 // be 8 instead of 4 bytes: 59 // 60 // 1->4 Nbytes = Length of compressed object (in bytes) 61 // 5->6 Version = TKey version identifier 62 // 7->10 ObjLen = Length of uncompressed object 63 // 11->14 Datime = Date and time when object was written to file 64 // 15->16 KeyLen = Length of the key structure (in bytes) 65 // 17->18 Cycle = Cycle of key 66 // 19->22 [19->26] SeekKey = Pointer to record itself (consistency check) 67 // 23->26 [27->34] SeekPdir = Pointer to directory header 68 // 27->27 [35->35] lname = Number of bytes in the class name 69 // 28->.. [36->..] ClassName = Object Class Name 70 // ..->.. lname = Number of bytes in the object name 71 // ..->.. Name = lName bytes with the name of the object 72 // ..->.. lTitle = Number of bytes in the object title 73 // ..->.. Title = Title of the object 74 // -----> DATA = Data bytes associated to the object 75 // 76 // The first data record starts at byte fBEGIN (currently set to kBEGIN). 77 // Bytes 1->kBEGIN contain the file description, when fVersion >= 1000000 78 // it is a large file (> 2 GB) and the offsets will be 8 bytes long and 79 // fUnits will be set to 8: 80 // 81 // 1->4 "root" = Root file identifier 82 // 5->8 fVersion = File format version 83 // 9->12 fBEGIN = Pointer to first data record 84 // 13->16 [13->20] fEND = Pointer to first free word at the EOF 85 // 17->20 [21->28] fSeekFree = Pointer to FREE data record 86 // 21->24 [29->32] fNbytesFree = Number of bytes in FREE data record 87 // 25->28 [33->36] nfree = Number of free data records 88 // 29->32 [37->40] fNbytesName = Number of bytes in TNamed at creation time 89 // 33->33 [41->41] fUnits = Number of bytes for file pointers 90 // 34->37 [42->45] fCompress = Compression level and algorithm 91 // 38->41 [46->53] fSeekInfo = Pointer to TStreamerInfo record 92 // 42->45 [54->57] fNbytesInfo = Number of bytes in TStreamerInfo record 93 // 46->63 [58->75] fUUID = Universal Unique ID 94 type File struct { 95 r Reader 96 w Writer 97 closer io.Closer 98 99 id string //non-root, identifies filename, etc. 100 101 version int32 102 begin int64 103 104 // Remainder of record is variable length, 4 or 8 bytes per pointer 105 end int64 106 seekfree int64 // first available record 107 nbytesfree int32 // total bytes available 108 nfree int32 // total free bytes 109 nbytesname int32 // number of bytes in TNamed at creation time 110 units byte 111 compression int32 112 seekinfo int64 // pointer to TStreamerInfo 113 nbytesinfo int32 // sizeof(TStreamerInfo) 114 uuid rbase.UUID 115 116 dir tdirectoryFile // root directory of this file 117 siKey Key 118 sinfos []rbytes.StreamerInfo 119 simap map[rbytes.StreamerInfo]struct{} // local set of streamers, when writing 120 121 spans freeList // list of free spans on file 122 } 123 124 // Open opens the named ROOT file for reading. If successful, methods on the 125 // returned file can be used for reading; the associated file descriptor 126 // has mode os.O_RDONLY. 127 func Open(path string) (*File, error) { 128 fd, err := openFile(path) 129 if err != nil { 130 return nil, fmt.Errorf("riofs: unable to open %q: %w", path, err) 131 } 132 133 f := &File{ 134 r: fd, 135 closer: fd, 136 id: path, 137 } 138 f.dir.file = f 139 140 err = f.readHeader() 141 if err != nil { 142 return nil, fmt.Errorf("riofs: failed to read header %q: %w", path, err) 143 } 144 145 return f, nil 146 } 147 148 // NewReader creates a new ROOT file reader. 149 func NewReader(r Reader) (*File, error) { 150 f := &File{ 151 r: r, 152 closer: r, 153 } 154 f.dir.file = f 155 156 err := f.readHeader() 157 if err != nil { 158 return nil, fmt.Errorf("riofs: failed to read header: %w", err) 159 } 160 f.id = f.dir.Name() 161 162 return f, nil 163 } 164 165 // Create creates the named ROOT file for writing. 166 func Create(name string, opts ...FileOption) (*File, error) { 167 fd, err := os.Create(name) 168 if err != nil { 169 return nil, fmt.Errorf("riofs: unable to create %q: %w", name, err) 170 } 171 172 f := &File{ 173 w: fd, 174 closer: fd, 175 id: name, 176 version: root.Version, 177 begin: kBEGIN, 178 end: kBEGIN, 179 units: 4, 180 sinfos: nil, 181 simap: make(map[rbytes.StreamerInfo]struct{}), 182 } 183 f.dir = *newDirectoryFile(name, "", f, nil) 184 f.dir.dir.named.SetTitle("") 185 f.spans.add(f.begin, kStartBigFile) 186 187 { 188 cfg := rcompress.SettingsFrom(rcompress.DefaultSettings.Compression()) 189 f.setCompression(cfg.Alg, cfg.Lvl) 190 } 191 192 for _, opt := range opts { 193 if opt == nil { 194 continue 195 } 196 err := opt(f) 197 if err != nil { 198 return nil, fmt.Errorf("riofs: could not apply option to ROOT file: %w", err) 199 } 200 } 201 202 // write directory info 203 namelen := f.dir.dir.named.Sizeof() 204 objlen := namelen + int32(f.dir.recordSize(f.version)) 205 key := newKey(&f.dir, f.dir.Name(), f.dir.Title(), "TFile", objlen, f) 206 f.nbytesname = key.keylen + namelen 207 f.dir.nbytesname = key.keylen + namelen 208 f.dir.seekdir = key.seekkey 209 f.seekfree = 0 210 f.nbytesfree = 0 211 212 err = f.writeHeader() 213 if err != nil { 214 _ = fd.Close() 215 _ = os.RemoveAll(name) 216 return nil, fmt.Errorf("riofs: failed to write header %q: %w", name, err) 217 } 218 219 buf := rbytes.NewWBuffer(make([]byte, objlen), nil, 0, f) 220 buf.WriteString(f.id) 221 buf.WriteString(f.Title()) 222 223 _, err = f.dir.MarshalROOT(buf) 224 if err != nil { 225 return nil, fmt.Errorf("riofs: failed to write header: %w", err) 226 } 227 key.buf = buf.Bytes() 228 229 _, err = key.writeFile(f) 230 if err != nil { 231 return nil, fmt.Errorf("riofs: failed to write key header: %w", err) 232 } 233 234 return f, nil 235 } 236 237 func (f *File) setEnd(pos int64) error { 238 f.end = pos 239 if f.spans.Len() == 0 { 240 return fmt.Errorf("riofs: empty free segment list") 241 } 242 blk := f.spans.last() 243 if blk == nil { 244 return fmt.Errorf("riofs: last free segment is nil") 245 } 246 247 if blk.last != kStartBigFile { 248 return fmt.Errorf("riofs: last free segment is not the file ending") 249 } 250 251 blk.first = pos 252 return nil 253 } 254 255 // Stat returns the os.FileInfo structure describing this file. 256 func (f *File) Stat() (os.FileInfo, error) { 257 if f.r != nil { 258 if st, ok := f.r.(stater); ok { 259 return st.Stat() 260 } 261 } 262 if f.w != nil { 263 if st, ok := f.w.(stater); ok { 264 return st.Stat() 265 } 266 } 267 return nil, fmt.Errorf("riofs: underlying file w/o os.FileInfo") 268 } 269 270 // Read implements io.Reader 271 func (f *File) Read(p []byte) (int, error) { 272 return f.r.Read(p) 273 } 274 275 // ReadAt implements io.ReaderAt 276 func (f *File) ReadAt(p []byte, off int64) (int, error) { 277 return f.r.ReadAt(p, off) 278 } 279 280 // WriteAt implements io.WriterAt 281 func (f *File) WriteAt(p []byte, off int64) (int, error) { 282 return f.w.WriteAt(p, off) 283 } 284 285 // Version returns the ROOT version this file was created with. 286 func (f *File) Version() int { 287 return int(f.version) 288 } 289 290 func (f *File) readHeader() error { 291 292 buf := make([]byte, 64+12) // 64: small file + extra space for big file 293 if _, err := f.ReadAt(buf, 0); err != nil { 294 return err 295 } 296 297 r := rbytes.NewRBuffer(buf, nil, 0, nil) 298 299 // Header 300 301 var magic [4]byte 302 if _, err := io.ReadFull(r, magic[:]); err != nil || string(magic[:]) != string(rootMagic) { 303 if err != nil { 304 return fmt.Errorf("riofs: failed to read ROOT file magic header: %w", err) 305 } 306 return fmt.Errorf("riofs: %q is not a root file", f.id) 307 } 308 309 f.version = r.ReadI32() 310 f.begin = int64(r.ReadI32()) 311 if f.version < 1000000 { // small file 312 f.end = int64(r.ReadI32()) 313 f.seekfree = int64(r.ReadI32()) 314 f.nbytesfree = r.ReadI32() 315 f.nfree = r.ReadI32() 316 f.nbytesname = r.ReadI32() 317 f.units = r.ReadU8() 318 f.compression = r.ReadI32() 319 f.seekinfo = int64(r.ReadI32()) 320 f.nbytesinfo = r.ReadI32() 321 } else { // large files 322 f.end = r.ReadI64() 323 f.seekfree = r.ReadI64() 324 f.nbytesfree = r.ReadI32() 325 f.nfree = r.ReadI32() 326 f.nbytesname = r.ReadI32() 327 f.units = r.ReadU8() 328 f.compression = r.ReadI32() 329 f.seekinfo = r.ReadI64() 330 f.nbytesinfo = r.ReadI32() 331 } 332 f.version %= 1000000 333 334 if _, err := io.ReadFull(r, f.uuid[:]); err != nil || r.Err() != nil { 335 if err != nil { 336 return fmt.Errorf("riofs: failed to read ROOT's UUID file: %w", err) 337 } 338 return r.Err() 339 } 340 341 var err error 342 343 err = f.dir.readDirInfo() 344 if err != nil { 345 return fmt.Errorf("riofs: failed to read ROOT directory infos: %w", err) 346 } 347 348 if f.seekfree > 0 { 349 err = f.readFreeSegments() 350 if err != nil { 351 return fmt.Errorf("riofs: failed to read ROOT file free segments: %w", err) 352 } 353 } 354 355 if f.seekinfo > 0 { 356 err = f.readStreamerInfo() 357 if err != nil { 358 return fmt.Errorf("riofs: failed to read ROOT streamer infos: %w", err) 359 } 360 } 361 362 err = f.dir.readKeys() 363 if err != nil { 364 return fmt.Errorf("riofs: failed to read ROOT file keys: %w", err) 365 } 366 367 return nil 368 } 369 370 func (f *File) writeHeader() error { 371 var ( 372 err error 373 nfree = int32(len(f.spans)) 374 ) 375 376 buf := rbytes.NewWBuffer(make([]byte, f.begin), nil, 0, f) 377 _, err = buf.Write(rootMagic) 378 if err != nil { 379 return fmt.Errorf("riofs: could not write ROOT file magic header: %w", err) 380 } 381 382 version := f.version 383 if f.IsBigFile() || 384 f.seekfree > kStartBigFile || 385 f.seekinfo > kStartBigFile { 386 if version < 1000000 { 387 version += 1000000 388 } 389 f.units = 8 390 } 391 buf.WriteI32(version) 392 buf.WriteI32(int32(f.begin)) 393 switch { 394 case version < 1000000: 395 buf.WriteI32(int32(f.end)) 396 buf.WriteI32(int32(f.seekfree)) 397 buf.WriteI32(f.nbytesfree) 398 buf.WriteI32(nfree) 399 buf.WriteI32(f.nbytesname) 400 buf.WriteU8(f.units) 401 buf.WriteI32(f.compression) 402 buf.WriteI32(int32(f.seekinfo)) 403 buf.WriteI32(f.nbytesinfo) 404 default: 405 buf.WriteI64(f.end) 406 buf.WriteI64(f.seekfree) 407 buf.WriteI32(f.nbytesfree) 408 buf.WriteI32(nfree) 409 buf.WriteI32(f.nbytesname) 410 buf.WriteU8(f.units) 411 buf.WriteI32(f.compression) 412 buf.WriteI64(f.seekinfo) 413 buf.WriteI32(f.nbytesinfo) 414 } 415 416 _, err = f.uuid.MarshalROOT(buf) 417 if err != nil { 418 return fmt.Errorf("riofs: could not write UUID's file header: %w", err) 419 } 420 421 _, _ = f.w.WriteAt(make([]byte, f.begin), 0) 422 _, err = f.w.WriteAt(buf.Bytes(), 0) 423 if err != nil { 424 return fmt.Errorf("riofs: could not write file header: %w", err) 425 } 426 427 if w, ok := f.w.(syncer); ok { 428 err = w.Sync() 429 } 430 431 return err 432 } 433 434 // Close closes the File, rendering it unusable for I/O. 435 // It returns an error, if any. 436 func (f *File) Close() error { 437 if f.closer == nil { 438 return nil 439 } 440 441 var err error 442 443 err = f.dir.close() 444 if err != nil { 445 return err 446 } 447 448 if f.w != nil { 449 err = f.writeStreamerInfo() 450 if err != nil { 451 return err 452 } 453 454 err = f.writeFreeSegments() 455 if err != nil { 456 return err 457 } 458 459 err = f.writeHeader() 460 if err != nil { 461 return err 462 } 463 } 464 465 for i := range f.dir.keys { 466 k := &f.dir.keys[i] 467 k.f = nil 468 } 469 f.dir.keys = nil 470 f.dir.file = nil 471 472 err = f.closer.Close() 473 f.closer = nil 474 return err 475 } 476 477 // Keys returns the list of keys this File contains 478 func (f *File) Keys() []Key { 479 return f.dir.Keys() 480 } 481 482 func (f *File) Name() string { 483 return f.dir.Name() 484 } 485 486 func (f *File) Title() string { 487 return f.dir.Title() 488 } 489 490 func (f *File) Class() string { 491 return "TFile" 492 } 493 494 // Compression returns the compression-mechanism and compression-level 495 // used for this file. 496 func (f *File) Compression() int32 { 497 return f.compression 498 } 499 500 // readStreamerInfo reads the list of StreamerInfo from this file 501 func (f *File) readStreamerInfo() error { 502 if f.seekinfo <= 0 || f.seekinfo >= f.end { 503 return fmt.Errorf("riofs: invalid pointer to StreamerInfo (pos=%v end=%v)", f.seekinfo, f.end) 504 505 } 506 buf := make([]byte, int(f.nbytesinfo)) 507 nbytes, err := f.ReadAt(buf, f.seekinfo) 508 if err != nil { 509 return err 510 } 511 if nbytes != int(f.nbytesinfo) { 512 return fmt.Errorf("riofs: requested [%v] bytes. read [%v] bytes from file", f.nbytesinfo, nbytes) 513 } 514 515 err = f.siKey.UnmarshalROOT(rbytes.NewRBuffer(buf, nil, 0, nil)) 516 f.siKey.f = f 517 if err != nil { 518 return err 519 } 520 521 objs := f.siKey.Value().(root.List) 522 f.sinfos = make([]rbytes.StreamerInfo, 0, objs.Len()) 523 for i := range objs.Len() { 524 obj, ok := objs.At(i).(rbytes.StreamerInfo) 525 if !ok { 526 continue 527 } 528 f.sinfos = append(f.sinfos, obj) 529 rdict.StreamerInfos.Add(obj) 530 } 531 return nil 532 } 533 534 // muWriteStreamerInfo makes sure we serialize calls to File.writeStreamerInfo, 535 // as StreamerInfos are shared through the global rdict.StreamerInfos registry. 536 var muWriteStreamerInfo sync.Mutex 537 538 // writeStreamerInfo writes the list of StreamerInfos used in this file. 539 func (f *File) writeStreamerInfo() error { 540 muWriteStreamerInfo.Lock() 541 defer muWriteStreamerInfo.Unlock() 542 543 if f.w == nil { 544 return nil 545 } 546 547 var ( 548 err error 549 sinfos = rcont.NewList("", nil) 550 rules = rcont.NewList("listOfRules", nil) 551 ) 552 553 err = f.findDepStreamers() 554 if err != nil { 555 return fmt.Errorf("riofs: could not find dependent streamers: %w", err) 556 } 557 558 for _, si := range f.sinfos { 559 sinfos.Append(si) 560 } 561 562 if rules.Len() > 0 { 563 sinfos.Append(rules) 564 } 565 566 if f.seekinfo != 0 { 567 f.markFree(f.seekinfo, f.seekinfo+int64(f.nbytesinfo)-1) 568 } 569 570 key := newKey(&f.dir, "StreamerInfo", sinfos.Title(), sinfos.Class(), 0, f) 571 offset := uint32(key.keylen) 572 buf := rbytes.NewWBuffer(nil, nil, offset, f) 573 _, err = sinfos.MarshalROOT(buf) 574 if err != nil { 575 return fmt.Errorf("riofs: could not write StreamerInfo list: %w", err) 576 } 577 578 key, err = newKeyFromBuf(&f.dir, "StreamerInfo", sinfos.Title(), sinfos.Class(), 1, buf.Bytes(), f, nil) 579 if err != nil { 580 return fmt.Errorf("riofs: could not create StreamerInfo key: %w", err) 581 } 582 f.seekinfo = key.seekkey 583 f.nbytesinfo = key.nbytes 584 585 _, err = key.writeFile(f) 586 if err != nil { 587 return fmt.Errorf("riofs: could not write StreamerInfo list key: %w", err) 588 } 589 590 return nil 591 } 592 593 // findDepStreamers finds all the needed streamers for proper persistency. 594 func (f *File) findDepStreamers() error { 595 type depsType struct { 596 name string 597 vers int 598 } 599 600 var ( 601 deps []depsType 602 err error 603 ) 604 605 for _, si := range f.sinfos { 606 err = rdict.Visit(rdict.StreamerInfos, si, func(depth int, se rbytes.StreamerElement) error { 607 switch se := se.(type) { 608 case *rdict.StreamerBase: 609 deps = append(deps, depsType{se.Name(), se.Base()}) 610 case *rdict.StreamerObject, *rdict.StreamerObjectAny: 611 deps = append(deps, depsType{se.TypeName(), -1}) 612 case *rdict.StreamerObjectPointer, *rdict.StreamerObjectAnyPointer: 613 deps = append(deps, depsType{strings.TrimRight(se.TypeName(), "*"), -1}) 614 case *rdict.StreamerString, *rdict.StreamerSTLstring: 615 deps = append(deps, depsType{se.TypeName(), -1}) 616 617 case *rdict.StreamerSTL: 618 for _, etn := range se.ElemTypeName() { 619 deps = append(deps, depsType{etn, -1}) 620 } 621 } 622 return nil 623 }) 624 if err != nil { 625 return fmt.Errorf("riofs: could not visit all dependent streamers for %#v: %w", si, err) 626 } 627 } 628 629 for _, dep := range deps { 630 if isCoreType(dep.name) || isCxxBuiltin(dep.name) { 631 continue 632 } 633 sub, err := rdict.StreamerInfos.StreamerInfo(dep.name, dep.vers) 634 if err != nil { 635 return fmt.Errorf("riofs: could not find streamer for %q and version=%d: %w", dep.name, dep.vers, err) 636 } 637 f.addStreamer(sub) 638 } 639 640 return nil 641 } 642 643 // markFree marks unused bytes on the file. 644 // it's the equivalent of slice[beg:end] = nil. 645 func (f *File) markFree(beg, end int64) { 646 if len(f.spans) == 0 { 647 return 648 } 649 650 span := f.spans.add(beg, end) 651 if span == nil { 652 return 653 } 654 nbytes := min(span.free(), 2000000000) 655 buf := rbytes.NewWBuffer(make([]byte, 4), nil, 0, f) 656 buf.WriteI32(-int32(nbytes)) 657 if end == f.end-1 { 658 f.end = span.first 659 } 660 _, err := f.w.WriteAt(buf.Bytes(), span.first) 661 if err != nil { 662 panic(err) 663 } 664 } 665 666 func (f *File) readFreeSegments() error { 667 var err error 668 buf := make([]byte, f.nbytesfree) 669 nbytes, err := f.ReadAt(buf, f.seekfree) 670 if err == io.EOF { 671 err = nil 672 } 673 if err != nil { 674 return err 675 } 676 if nbytes != len(buf) { 677 return fmt.Errorf("riofs: requested [%v] bytes, read [%v] bytes from file", f.nbytesfree, nbytes) 678 } 679 680 var key = Key{f: f} 681 err = key.UnmarshalROOT(rbytes.NewRBuffer(buf, nil, 0, nil)) 682 if err != nil { 683 return fmt.Errorf("riofs: could not unmarshal free-segment key: %w", err) 684 } 685 buf, err = key.Bytes() 686 if err != nil { 687 return fmt.Errorf("riofs: could not read key payload: %w", err) 688 } 689 rbuf := rbytes.NewRBuffer(buf, nil, 0, nil) 690 for rbuf.Len() > 0 { 691 var span freeSegment 692 err = span.UnmarshalROOT(rbuf) 693 if err != nil { 694 if err == io.EOF { 695 err = nil 696 } 697 break 698 } 699 f.spans = append(f.spans, span) 700 } 701 702 return err 703 } 704 705 func (f *File) writeFreeSegments() error { 706 var err error 707 708 if f.seekfree != 0 { 709 f.markFree(f.seekfree, f.seekfree+int64(f.nbytesfree)-1) 710 } 711 712 key := func() *Key { 713 var nbytes int32 714 for _, span := range f.spans { 715 nbytes += span.sizeof() 716 } 717 if nbytes == 0 { 718 return nil 719 } 720 key := newKey(&f.dir, f.Name(), f.Title(), "TFile", nbytes, f) 721 if key.seekkey == 0 { 722 return nil 723 } 724 return &key 725 }() 726 727 if key == nil { 728 return nil 729 } 730 731 isBigFile := f.IsBigFile() 732 if !isBigFile && f.end > kStartBigFile { 733 // the free block list is large enough to bring the file over the 734 // 2Gb limit. 735 // The references and offsets are now 64b, so we need to redo the 736 // calculation since the list of free blocks will not fit in the 737 // original size. 738 panic("not implemented") 739 } 740 741 nbytes := key.objlen 742 buf := rbytes.NewWBuffer(make([]byte, nbytes), nil, 0, f) 743 for _, span := range f.spans { 744 _, err := span.MarshalROOT(buf) 745 if err != nil { 746 return fmt.Errorf("riofs: could not marshal free-block: %w", err) 747 } 748 } 749 if abytes := buf.Pos(); abytes != int64(nbytes) { 750 switch { 751 case abytes < int64(nbytes): 752 // most likely one of the 'free' segments was used 753 // to store this key. 754 // we thus have one less free-block to store than planned. 755 copy(buf.Bytes()[abytes:], make([]byte, int64(nbytes)-abytes)) 756 default: 757 panic("riofs: free block list larger than expected") 758 } 759 } 760 761 f.nbytesfree = key.nbytes 762 f.seekfree = key.seekkey 763 key.buf = buf.Bytes() 764 _, err = key.writeFile(f) 765 if err != nil { 766 return fmt.Errorf("riofs: could not write free-block list: %w", err) 767 } 768 return nil 769 } 770 771 // StreamerInfos returns the list of StreamerInfos of this file. 772 func (f *File) StreamerInfos() []rbytes.StreamerInfo { 773 return f.sinfos 774 } 775 776 // StreamerInfo returns the named StreamerInfo. 777 // If version is negative, the latest version should be returned. 778 func (f *File) StreamerInfo(name string, version int) (rbytes.StreamerInfo, error) { 779 if len(f.sinfos) == 0 { 780 return nil, fmt.Errorf("riofs: no streamer for %q (no streamerinfo list)", name) 781 } 782 783 for _, si := range f.sinfos { 784 if si.Name() == name { 785 return si, nil 786 } 787 if _, ok := rdict.Typename(name, si.Title()); ok { 788 return si, nil 789 } 790 } 791 792 si, ok := rdict.StreamerInfos.Get(name, version) 793 if ok { 794 return si, nil 795 } 796 797 return nil, fmt.Errorf("riofs: no streamer for %q", name) 798 } 799 800 // RegisterStreamer adds the given streamer info to the list of streamers 801 // that will be stored in the ROOT file. 802 func (f *File) RegisterStreamer(streamer rbytes.StreamerInfo) { 803 if old, err := f.StreamerInfo(streamer.Name(), streamer.ClassVersion()); err == nil && old != nil { 804 return 805 } 806 rdict.StreamerInfos.Add(streamer) 807 f.addStreamer(streamer) 808 } 809 810 func (f *File) addStreamer(streamer rbytes.StreamerInfo) { 811 if isCoreType(streamer.Name()) { 812 return 813 } 814 815 if _, dup := f.simap[streamer]; dup { 816 return 817 } 818 819 f.simap[streamer] = struct{}{} 820 f.sinfos = append(f.sinfos, streamer) 821 } 822 823 // Get returns the object identified by namecycle 824 // 825 // namecycle has the format name;cycle 826 // name = * is illegal, cycle = * is illegal 827 // cycle = "" or cycle = 9999 ==> apply to a memory object 828 // 829 // examples: 830 // foo : get object named foo in memory 831 // if object is not in memory, try with highest cycle from file 832 // foo;1 : get cycle 1 of foo on file 833 func (f *File) Get(namecycle string) (root.Object, error) { 834 return f.dir.Get(namecycle) 835 } 836 837 // Put puts the object v under the key with the given name. 838 func (f *File) Put(name string, v root.Object) error { 839 if f.w == nil { 840 return fmt.Errorf("could not put %q into file %q: %w", name, f.Name(), ErrReadOnly) 841 } 842 return f.dir.Put(name, v) 843 } 844 845 // Mkdir creates a new subdirectory 846 func (f *File) Mkdir(name string) (Directory, error) { 847 if f.w == nil { 848 return nil, fmt.Errorf("could not mkdir %q in file %q: %w", name, f.Name(), ErrReadOnly) 849 } 850 return f.dir.Mkdir(name) 851 } 852 853 // Parent returns the directory holding this directory. 854 // Parent returns nil if this is the top-level directory. 855 func (*File) Parent() Directory { return nil } 856 857 // SegmentMap displays to w the file's segments map. 858 func (f *File) SegmentMap(w io.Writer) (err error) { 859 const timefmt = "20060102/150405" 860 var ( 861 idcur = f.begin 862 sz = int64(64) 863 date time.Time 864 class string 865 ) 866 867 ndigits := int(math.Log10(float64(f.end))) + 1 868 for idcur < f.end { 869 var ( 870 buf = make([]byte, sz) 871 n int 872 ) 873 n, err = f.ReadAt(buf, idcur) 874 switch err { 875 case nil: 876 // ok 877 case io.EOF: 878 if n <= 0 { 879 return fmt.Errorf("could not read buffer at position %d: %w", idcur, err) 880 } 881 err = nil 882 default: 883 return fmt.Errorf("could not read buffer at position %d: %w", idcur, err) 884 } 885 886 var ( 887 k struct { 888 nbytes int32 889 rvers int16 890 objlen int32 891 date time.Time 892 keylen int16 893 cycle int16 894 seekkey int64 895 seekpdir int64 896 } 897 r = rbytes.NewRBuffer(buf, nil, 0, f) 898 ) 899 900 k.nbytes = r.ReadI32() 901 if r.Err() != nil || k.nbytes == 0 { 902 class = "=== [ERR] ===" 903 fmt.Fprintf(w, "%s At:%-*d N=%-8d %-14s\n", strings.Repeat("*", 15), ndigits+1, idcur, k.nbytes, class) 904 return fmt.Errorf("invalid key (nbytes=%d): %w)", k.nbytes, err) 905 } 906 if k.nbytes < 0 { 907 class = "=== [GAP] ===" 908 fmt.Fprintf(w, "%s At:%-*d N=%-8d %-14s\n", strings.Repeat("*", 15), ndigits+1, idcur, k.nbytes, class) 909 idcur += int64(-k.nbytes) 910 continue 911 } 912 913 k.rvers = r.ReadI16() 914 k.objlen = r.ReadI32() 915 k.date = datime2time(r.ReadU32()) 916 k.keylen = r.ReadI16() 917 k.cycle = r.ReadI16() 918 919 switch { 920 case k.rvers > 1000: 921 k.seekkey = r.ReadI64() 922 k.seekpdir = r.ReadI64() 923 default: 924 k.seekkey = int64(r.ReadI32()) 925 k.seekpdir = int64(r.ReadI32()) 926 } 927 928 class = r.ReadString() 929 date = k.date 930 931 switch idcur { 932 case f.seekfree: 933 class = "FreeSegments" 934 case f.seekinfo: 935 class = "StreamerInfo" 936 case f.dir.seekkeys: 937 class = "KeysList" 938 } 939 940 switch { 941 case k.objlen != k.nbytes-int32(k.keylen): 942 cx := float64(k.objlen+int32(k.keylen)) / float64(k.nbytes) 943 fmt.Fprintf(w, "%s At:%-*d N=%-8d %-14s CX = %5.2f\n", date.Format(timefmt), ndigits+1, idcur, k.nbytes, class, cx) 944 default: 945 fmt.Fprintf(w, "%s At:%-*d N=%-8d %-14s\n", date.Format(timefmt), ndigits+1, idcur, k.nbytes, class) 946 } 947 idcur += int64(k.nbytes) 948 } 949 950 class = "END" 951 fmt.Fprintf(w, "%s At:%-*d N=%-8d %-14s\n", date.Format(timefmt), ndigits+1, idcur, 1, class) 952 return err 953 } 954 955 // Records writes the records structure of the ROOT file to w. 956 func (f *File) Records(w io.Writer) error { 957 fmt.Fprintf(w, "=== file %q ===\n", f.id) 958 fmt.Fprintf(w, "begin: %d\n", f.begin) 959 fmt.Fprintf(w, "end: %d\n", f.end) 960 fmt.Fprintf(w, "seek-free: %d nbytes-free=%d nfree=%d\n", f.seekfree, f.nbytesfree, f.nfree) 961 fmt.Fprintf(w, "seek-info: %d nbytes-info=%d\n", f.seekinfo, f.nbytesinfo) 962 963 return f.dir.records(w, 0) 964 } 965 966 // IsBigFile returns whether the file will need 64b offsets. 967 func (f *File) IsBigFile() bool { 968 return f.end > kStartBigFile 969 } 970 971 var ( 972 _ root.Object = (*File)(nil) 973 _ root.Named = (*File)(nil) 974 _ Directory = (*File)(nil) 975 _ rbytes.StreamerInfoContext = (*File)(nil) 976 _ streamerInfoStore = (*File)(nil) 977 978 _ io.Reader = (*File)(nil) 979 _ io.ReaderAt = (*File)(nil) 980 _ io.WriterAt = (*File)(nil) 981 _ io.Closer = (*File)(nil) 982 ) 983 984 // autogenCtx implements StreamerInfoContext. 985 // autogenCtx automatically generates missing streamer infos when queried. 986 // 987 // We use a wrapper to break cycles in File.StreamerInfo and File.RegisterStreamer. 988 type autogenCtx struct { 989 sictx rbytes.StreamerInfoContext 990 } 991 992 var _ rbytes.StreamerInfoContext = (*autogenCtx)(nil) 993 994 func (agctx autogenCtx) StreamerInfo(name string, version int) (rbytes.StreamerInfo, error) { 995 si, err := agctx.sictx.StreamerInfo(name, version) 996 if err == nil { 997 return si, nil 998 } 999 1000 // no streamer for "name" in that file. 1001 // try whether "name" isn't actually std::vector<T> and a streamer 1002 // for T is in that file. 1003 if strings.Contains(name, "<") { 1004 cxx := rmeta.CxxTemplateFrom(name) 1005 switch cxx.Name { 1006 case "vector": 1007 si := stdvecSIFrom(name, cxx.Args[0], agctx.sictx) 1008 if si != nil { 1009 // agctx.f.sinfos = append(agctx.f.sinfos, si) 1010 rdict.StreamerInfos.Add(si) 1011 return si, nil 1012 } 1013 } 1014 } 1015 1016 if isCxxBuiltin(name) { 1017 switch name { 1018 case "string": 1019 return rdict.NewStreamerInfo(name, version, []rbytes.StreamerElement{ 1020 &rdict.StreamerSTLstring{ 1021 StreamerSTL: *rdict.NewCxxStreamerSTL( 1022 rdict.Element{ 1023 Name: *rbase.NewNamed(name, ""), 1024 Type: rmeta.STLstring, 1025 EName: "string", 1026 }.New(), 0, rmeta.STLstring), 1027 }, 1028 }), nil 1029 } 1030 } 1031 1032 return nil, fmt.Errorf("riofs: no streamer for %q", name) 1033 }