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  }