go-hep.org/x/hep@v0.38.1/groot/riofs/dir.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  	"fmt"
     9  	"io"
    10  	"reflect"
    11  	"sort"
    12  	"strings"
    13  	"time"
    14  
    15  	"go-hep.org/x/hep/groot/rbase"
    16  	"go-hep.org/x/hep/groot/rbytes"
    17  	"go-hep.org/x/hep/groot/rdict"
    18  	"go-hep.org/x/hep/groot/rmeta"
    19  	"go-hep.org/x/hep/groot/root"
    20  	"go-hep.org/x/hep/groot/rtypes"
    21  	"go-hep.org/x/hep/groot/rvers"
    22  )
    23  
    24  type tdirectory struct {
    25  	rvers  int16
    26  	named  rbase.Named // name+title of this directory
    27  	parent Directory
    28  	uuid   rbase.UUID
    29  }
    30  
    31  func (dir *tdirectory) RVersion() int16 { return dir.rvers }
    32  
    33  func (dir *tdirectory) Class() string {
    34  	return "TDirectory"
    35  }
    36  
    37  func (dir *tdirectory) Name() string {
    38  	return dir.named.Name()
    39  }
    40  
    41  func (dir *tdirectory) Title() string {
    42  	return dir.named.Title()
    43  }
    44  
    45  func (dir *tdirectory) MarshalROOT(w *rbytes.WBuffer) (int, error) {
    46  	if w.Err() != nil {
    47  		return 0, w.Err()
    48  	}
    49  
    50  	hdr := w.WriteHeader(dir.Class(), dir.RVersion())
    51  	w.WriteObject(&dir.named)
    52  	w.WriteObjectAny((*rbase.Object)(nil))
    53  
    54  	// FIXME(sbinet): stream list
    55  	w.WriteObject(&dir.uuid)
    56  
    57  	return w.SetHeader(hdr)
    58  }
    59  
    60  func (dir *tdirectory) UnmarshalROOT(r *rbytes.RBuffer) error {
    61  	if r.Err() != nil {
    62  		return r.Err()
    63  	}
    64  
    65  	hdr := r.ReadHeader(dir.Class(), -1)
    66  	dir.rvers = hdr.Vers
    67  
    68  	r.ReadObject(&dir.named)
    69  	obj := r.ReadObjectAny()
    70  	if obj != nil {
    71  		dir.parent = obj.(Directory)
    72  	}
    73  	// FIXME(sbinet): stream list
    74  	r.ReadObject(&dir.uuid)
    75  
    76  	r.CheckHeader(hdr)
    77  	return r.Err()
    78  }
    79  
    80  type tdirectoryFile struct {
    81  	dir tdirectory
    82  
    83  	ctime      time.Time // time of directory's creation
    84  	mtime      time.Time // time of directory's last modification
    85  	nbyteskeys int32     // number of bytes for the keys
    86  	nbytesname int32     // number of bytes in TNamed at creation time
    87  	seekdir    int64     // location of directory on file
    88  	seekparent int64     // location of parent directory on file
    89  	seekkeys   int64     // location of Keys record on file
    90  
    91  	classname string
    92  
    93  	file *File // pointer to current file in memory
    94  	keys []Key
    95  	dirs []*tdirectoryFile
    96  }
    97  
    98  func newDirectoryFile(name, title string, f *File, parent *tdirectoryFile) *tdirectoryFile {
    99  	now := nowUTC()
   100  	if title == "" {
   101  		title = name
   102  	}
   103  	dir := &tdirectoryFile{
   104  		dir: tdirectory{
   105  			rvers: rvers.DirectoryFile,
   106  			named: *rbase.NewNamed(name, title),
   107  		},
   108  		ctime: now,
   109  		mtime: now,
   110  		file:  f,
   111  	}
   112  	if parent == nil {
   113  		return dir
   114  	}
   115  
   116  	dir.dir.parent = parent
   117  	dir.seekparent = parent.seekdir
   118  
   119  	objlen := int32(dir.recordSize(f.version))
   120  	key := newKey(parent, name, title, "TDirectory", objlen, f)
   121  	dir.nbytesname = key.keylen
   122  	dir.seekdir = key.seekkey
   123  
   124  	buf := rbytes.NewWBuffer(make([]byte, objlen), nil, 0, f)
   125  	buf.WriteString(f.id)
   126  	buf.WriteString(f.Title())
   127  	// dir-marshal
   128  	_, err := dir.MarshalROOT(buf)
   129  	if err != nil {
   130  		panic(fmt.Errorf("riofs: failed to write header: %w", err))
   131  	}
   132  	key.buf = buf.Bytes()
   133  	key.obj = dir
   134  
   135  	parent.keys = append(parent.keys, key)
   136  
   137  	// key-write-file
   138  	_, err = key.writeFile(f)
   139  	if err != nil {
   140  		panic(fmt.Errorf("riofs: failed to write key header: %w", err))
   141  	}
   142  
   143  	return dir
   144  }
   145  
   146  func (dir *tdirectoryFile) isBigFile() bool {
   147  	return dir.dir.rvers > 1000
   148  }
   149  
   150  // recordSize returns the size of the directory header in bytes
   151  func (dir *tdirectoryFile) recordSize(version int32) int64 {
   152  	var nbytes int64
   153  	nbytes += 2 // fVersion
   154  	nbytes += 4 // ctime
   155  	nbytes += 4 // mtime
   156  	nbytes += 4 // nbyteskeys
   157  	nbytes += 4 // nbytesname
   158  	if version >= 40000 {
   159  		// assume that the file may be above 2 Gbytes if file version is > 4
   160  		nbytes += 8 // seekdir
   161  		nbytes += 8 // seekparent
   162  		nbytes += 8 // seekkeys
   163  	} else {
   164  		nbytes += 4 // seekdir
   165  		nbytes += 4 // seekparent
   166  		nbytes += 4 // seekkeys
   167  	}
   168  	nbytes += int64(dir.dir.uuid.Sizeof())
   169  
   170  	return nbytes
   171  }
   172  
   173  func (dir *tdirectoryFile) readDirInfo() error {
   174  	f := dir.file
   175  	nbytes := int64(f.nbytesname) + dir.recordSize(f.version)
   176  
   177  	if nbytes+f.begin > f.end {
   178  		return fmt.Errorf(
   179  			"riofs: file [%v] has an incorrect header length [%v] or incorrect end of file length [%v]",
   180  			f.id,
   181  			f.begin+nbytes,
   182  			f.end,
   183  		)
   184  	}
   185  
   186  	data := make([]byte, int(nbytes))
   187  	if _, err := f.ReadAt(data, f.begin); err != nil {
   188  		return err
   189  	}
   190  
   191  	r := rbytes.NewRBuffer(data[f.nbytesname:], nil, 0, nil)
   192  	r.ReadObject(dir)
   193  	if r.Err() != nil {
   194  		return r.Err()
   195  	}
   196  
   197  	nk := 4 // Key::fNumberOfBytes
   198  	r = rbytes.NewRBuffer(data[nk:], nil, 0, nil)
   199  	keyversion := r.ReadI16()
   200  	if r.Err() != nil {
   201  		return r.Err()
   202  	}
   203  
   204  	if keyversion > 1000 {
   205  		// large files
   206  		nk += 2     // Key::fVersion
   207  		nk += 2 * 4 // Key::fObjectSize, Date
   208  		nk += 2 * 2 // Key::fKeyLength, fCycle
   209  		nk += 2 * 8 // Key::fSeekKey, fSeekParentDirectory
   210  	} else {
   211  		nk += 2     // Key::fVersion
   212  		nk += 2 * 4 // Key::fObjectSize, Date
   213  		nk += 2 * 2 // Key::fKeyLength, fCycle
   214  		nk += 2 * 4 // Key::fSeekKey, fSeekParentDirectory
   215  	}
   216  
   217  	r = rbytes.NewRBuffer(data[nk:], nil, 0, nil)
   218  	dir.classname = r.ReadString()
   219  
   220  	dir.dir.named.SetName(r.ReadString())
   221  	dir.dir.named.SetTitle(r.ReadString())
   222  
   223  	if dir.nbytesname < 10 || dir.nbytesname > 1000 {
   224  		return fmt.Errorf("riofs: can't read directory info")
   225  	}
   226  
   227  	return r.Err()
   228  }
   229  
   230  func (dir *tdirectoryFile) readKeys() error {
   231  	var err error
   232  	if dir.seekkeys <= 0 {
   233  		return nil
   234  	}
   235  
   236  	buf := make([]byte, int(dir.nbyteskeys))
   237  	_, err = dir.file.ReadAt(buf, dir.seekkeys)
   238  	if err != nil {
   239  		return err
   240  	}
   241  
   242  	hdr := Key{f: dir.file}
   243  	err = hdr.UnmarshalROOT(rbytes.NewRBuffer(buf, nil, 0, dir))
   244  	if err != nil {
   245  		return err
   246  	}
   247  
   248  	buf = make([]byte, hdr.objlen)
   249  	_, err = dir.file.ReadAt(buf, dir.seekkeys+int64(hdr.keylen))
   250  	if err != nil {
   251  		return err
   252  	}
   253  
   254  	r := rbytes.NewRBuffer(buf, nil, 0, dir)
   255  	nkeys := r.ReadI32()
   256  	if r.Err() != nil {
   257  		return r.Err()
   258  	}
   259  	dir.keys = make([]Key, int(nkeys))
   260  	for i := range dir.keys {
   261  		k := &dir.keys[i]
   262  		k.f = dir.file
   263  		k.parent = dir
   264  		err := k.UnmarshalROOT(r)
   265  		if err != nil {
   266  			return err
   267  		}
   268  		// support old ROOT versions.
   269  		if k.class == "TDirectory" {
   270  			k.class = "TDirectoryFile"
   271  		}
   272  	}
   273  	return nil
   274  }
   275  
   276  func (dir *tdirectoryFile) close() error {
   277  	if dir.file.w == nil {
   278  		return nil
   279  	}
   280  
   281  	if dir.file != nil && dir.file.IsBigFile() {
   282  		if dir.dir.rvers < 1000 {
   283  			dir.dir.rvers += 1000
   284  		}
   285  	}
   286  
   287  	// FIXME(sbinet): ROOT applies this optimization. should we ?
   288  	//	if len(dir.dir.keys) == 0 || dir.dir.seekdir == 0 {
   289  	//		return nil
   290  	//	}
   291  
   292  	err := dir.save()
   293  	if err != nil {
   294  		return err
   295  	}
   296  
   297  	return nil
   298  }
   299  
   300  func (dir *tdirectoryFile) save() error {
   301  	err := dir.saveSelf()
   302  	if err != nil {
   303  		return err
   304  	}
   305  
   306  	for _, sub := range dir.dirs {
   307  		err = sub.save()
   308  		if err != nil {
   309  			return err
   310  		}
   311  	}
   312  
   313  	return nil
   314  }
   315  
   316  func (dir *tdirectoryFile) saveSelf() (err error) {
   317  	err = dir.writeKeys()
   318  	if err != nil {
   319  		return err
   320  	}
   321  
   322  	err = dir.writeHeader()
   323  	if err != nil {
   324  		return err
   325  	}
   326  
   327  	return nil
   328  }
   329  
   330  // writeDirHeader overwrites the Directory header record.
   331  func (dir *tdirectoryFile) writeHeader() error {
   332  	var (
   333  		err error
   334  	)
   335  	dir.mtime = nowUTC()
   336  
   337  	nbytes := int32(dir.recordSize(dir.file.version))
   338  	buf := rbytes.NewWBuffer(make([]byte, nbytes), nil, 0, nil)
   339  	_, err = dir.MarshalROOT(buf)
   340  	if err != nil {
   341  		return fmt.Errorf("riofs: could not marshal dir-info: %w", err)
   342  	}
   343  
   344  	_, err = dir.file.w.WriteAt(buf.Bytes(), dir.seekdir+int64(dir.nbytesname))
   345  	if err != nil {
   346  		return fmt.Errorf("riofs: could not write dir-info to file: %w", err)
   347  	}
   348  
   349  	return nil
   350  }
   351  
   352  // Get returns the object identified by namecycle
   353  //
   354  //	namecycle has the format name;cycle
   355  //	name  = * is illegal, cycle = * is illegal
   356  //	cycle = "" or cycle = 9999 ==> apply to a memory object
   357  //
   358  //	examples:
   359  //	  foo   : get object named foo in memory
   360  //	          if object is not in memory, try with highest cycle from file
   361  //	  foo;1 : get cycle 1 of foo on file
   362  func (dir *tdirectoryFile) Get(namecycle string) (root.Object, error) {
   363  	var keys []*Key
   364  	name, cycle := decodeNameCycle(namecycle)
   365  	for i := range dir.keys {
   366  		k := &dir.keys[i]
   367  		if k.Name() == name {
   368  			if cycle != 9999 {
   369  				if k.cycle == cycle {
   370  					return k.Object()
   371  				}
   372  				continue
   373  			}
   374  			keys = append(keys, k)
   375  		}
   376  	}
   377  	var key *Key
   378  	switch len(keys) {
   379  	case 0:
   380  		return nil, noKeyError{key: namecycle, obj: dir}
   381  	case 1:
   382  		key = keys[0]
   383  	default:
   384  		sort.Slice(keys, func(i, j int) bool {
   385  			return keys[i].Cycle() < keys[j].Cycle()
   386  		})
   387  		key = keys[len(keys)-1]
   388  	}
   389  
   390  	obj, err := key.Object()
   391  	if err != nil {
   392  		return nil, err
   393  	}
   394  
   395  	if obj != nil {
   396  		switch obj := obj.(type) {
   397  		case *tdirectoryFile:
   398  			obj.dir.parent = dir
   399  			if obj.dir.Name() == "" {
   400  				obj.dir.named.SetName(name)
   401  			}
   402  			if obj.Title() == "" {
   403  				obj.dir.named.SetTitle(name)
   404  			}
   405  		}
   406  	}
   407  	return obj, nil
   408  }
   409  
   410  func (dir *tdirectoryFile) Put(name string, obj root.Object) error {
   411  	if dir.file.w == nil {
   412  		return fmt.Errorf("could not put %q into directory %q: %w", name, dir.dir.Name(), ErrReadOnly)
   413  	}
   414  
   415  	if strings.Contains(name, "/") {
   416  		return fmt.Errorf("riofs: invalid path name %q (contains a '/')", name)
   417  	}
   418  
   419  	var (
   420  		cycle int16
   421  		title = ""
   422  	)
   423  	if v, ok := obj.(root.Named); ok {
   424  		if name == "" {
   425  			name = v.Name()
   426  		}
   427  		title = v.Title()
   428  	}
   429  	if name == "" {
   430  		return fmt.Errorf("riofs: empty key name")
   431  	}
   432  
   433  	// FIXME(sbinet): implement a fast look-up ?
   434  	for i := range dir.keys {
   435  		key := &dir.keys[i]
   436  		if key.name != name {
   437  			continue
   438  		}
   439  		if key.ClassName() != obj.Class() {
   440  			return keyTypeError{key: name, class: key.ClassName()}
   441  		}
   442  		if key.cycle > cycle {
   443  			cycle = key.cycle
   444  		}
   445  	}
   446  	cycle++
   447  
   448  	typename := obj.Class()
   449  
   450  	// make sure we have a streamer for this type.
   451  	if !isCoreType(typename) {
   452  		cxx := rdict.GoName2Cxx(typename)
   453  		si, err := dir.StreamerInfo(cxx, -1)
   454  		if err != nil {
   455  			_, err = streamerInfoFrom(obj, dir)
   456  			if err != nil {
   457  				return fmt.Errorf("riofs: could not generate streamer for key %q and type %T: %w", name, obj, err)
   458  			}
   459  			si, err = dir.StreamerInfo(cxx, -1)
   460  		}
   461  		if err != nil {
   462  			return fmt.Errorf("riofs: could not find streamer for %T: %w", obj, err)
   463  		}
   464  		dir.addStreamer(si)
   465  	}
   466  
   467  	key, err := newKeyFrom(dir, name, title, rdict.GoName2Cxx(typename), obj, dir.file, nil) // FIXME(sbinet): wire in key-opt ?
   468  	if err != nil {
   469  		return fmt.Errorf("riofs: could not create key %q for object %T: %w", name, obj, err)
   470  	}
   471  	key.cycle = cycle
   472  	_, err = key.writeFile(dir.file)
   473  	if err != nil {
   474  		return fmt.Errorf("riofs: could not write key %q to file: %w", name, err)
   475  	}
   476  
   477  	dir.keys = append(dir.keys, key)
   478  
   479  	return nil
   480  }
   481  
   482  // Keys returns the list of keys being held by this directory.
   483  func (dir *tdirectoryFile) Keys() []Key {
   484  	return dir.keys
   485  }
   486  
   487  // Mkdir creates a new subdirectory
   488  func (dir *tdirectoryFile) Mkdir(name string) (Directory, error) {
   489  	if _, err := dir.Get(name); err == nil {
   490  		return nil, fmt.Errorf("riofs: %q already exists", name)
   491  	}
   492  
   493  	if strings.Contains(name, "/") {
   494  		return nil, fmt.Errorf("riofs: invalid directory name %q (contains a '/')", name)
   495  	}
   496  
   497  	sub := newDirectoryFile(name, "", dir.file, dir)
   498  	dir.dirs = append(dir.dirs, sub)
   499  
   500  	return sub, nil
   501  }
   502  
   503  // Parent returns the directory holding this directory.
   504  // Parent returns nil if this is the top-level directory.
   505  func (dir *tdirectoryFile) Parent() Directory {
   506  	if dir.dir.parent == nil {
   507  		return dir.file
   508  	}
   509  	return dir.dir.parent
   510  }
   511  
   512  func (dir *tdirectoryFile) RVersion() int16 { return dir.dir.rvers }
   513  
   514  func (dir *tdirectoryFile) Class() string {
   515  	return "TDirectoryFile"
   516  }
   517  
   518  func (dir *tdirectoryFile) Name() string {
   519  	return dir.dir.named.Name()
   520  }
   521  
   522  func (dir *tdirectoryFile) Title() string {
   523  	return dir.dir.named.Title()
   524  }
   525  
   526  func (dir *tdirectoryFile) MarshalROOT(w *rbytes.WBuffer) (int, error) {
   527  	if w.Err() != nil {
   528  		return 0, w.Err()
   529  	}
   530  
   531  	beg := w.Pos()
   532  
   533  	version := dir.RVersion()
   534  	w.WriteI16(version)
   535  	w.WriteU32(time2datime(dir.ctime))
   536  	w.WriteU32(time2datime(dir.mtime))
   537  	w.WriteI32(dir.nbyteskeys)
   538  	w.WriteI32(dir.nbytesname)
   539  
   540  	switch {
   541  	case dir.isBigFile():
   542  		w.WriteI64(dir.seekdir)
   543  		w.WriteI64(dir.seekparent)
   544  		w.WriteI64(dir.seekkeys)
   545  	default:
   546  		w.WriteI32(int32(dir.seekdir))
   547  		w.WriteI32(int32(dir.seekparent))
   548  		w.WriteI32(int32(dir.seekkeys))
   549  	}
   550  
   551  	_, _ = dir.dir.uuid.MarshalROOT(w)
   552  
   553  	end := w.Pos()
   554  
   555  	return int(end - beg), w.Err()
   556  }
   557  
   558  func (dir *tdirectoryFile) UnmarshalROOT(r *rbytes.RBuffer) error {
   559  	var (
   560  		version = r.ReadI16()
   561  		ctime   = r.ReadU32()
   562  		mtime   = r.ReadU32()
   563  	)
   564  
   565  	dir.dir.rvers = version
   566  	dir.ctime = datime2time(ctime)
   567  	dir.mtime = datime2time(mtime)
   568  
   569  	dir.nbyteskeys = r.ReadI32()
   570  	dir.nbytesname = r.ReadI32()
   571  
   572  	switch {
   573  	case dir.isBigFile():
   574  		dir.seekdir = r.ReadI64()
   575  		dir.seekparent = r.ReadI64()
   576  		dir.seekkeys = r.ReadI64()
   577  	default:
   578  		dir.seekdir = int64(r.ReadI32())
   579  		dir.seekparent = int64(r.ReadI32())
   580  		dir.seekkeys = int64(r.ReadI32())
   581  	}
   582  
   583  	switch version := version % 1000; {
   584  	case version == 2:
   585  		_ = dir.dir.uuid.UnmarshalROOTv1(r)
   586  	case version > 2:
   587  		_ = dir.dir.uuid.UnmarshalROOT(r)
   588  	}
   589  
   590  	return r.Err()
   591  }
   592  
   593  // StreamerInfo returns the StreamerInfo with name of this directory, or nil otherwise.
   594  // If version is negative, the latest version should be returned.
   595  func (dir *tdirectoryFile) StreamerInfo(name string, version int) (rbytes.StreamerInfo, error) {
   596  	if dir.file == nil {
   597  		return nil, fmt.Errorf("riofs: no streamers")
   598  	}
   599  	return dir.file.StreamerInfo(name, version)
   600  }
   601  
   602  func (dir *tdirectoryFile) addStreamer(streamer rbytes.StreamerInfo) {
   603  	dir.file.addStreamer(streamer)
   604  }
   605  
   606  // writeKeys writes the list of keys to the file.
   607  // The list of keys is written out as a single data record.
   608  func (dir *tdirectoryFile) writeKeys() error {
   609  	var (
   610  		err    error
   611  		nbytes = int32(4) // space for n-keys
   612  	)
   613  
   614  	if dir.file.IsBigFile() {
   615  		nbytes += 8
   616  	}
   617  	for i := range dir.Keys() {
   618  		key := &dir.keys[i]
   619  		nbytes += key.keylen
   620  	}
   621  
   622  	hdr := newKey(dir, dir.Name(), dir.Title(), "TDirectory", nbytes, dir.file)
   623  
   624  	buf := rbytes.NewWBuffer(make([]byte, nbytes), nil, 0, nil)
   625  	buf.WriteI32(int32(len(dir.Keys())))
   626  	for _, k := range dir.Keys() {
   627  		_, err = k.MarshalROOT(buf)
   628  		if err != nil {
   629  			return fmt.Errorf("riofs: could not write key: %w", err)
   630  		}
   631  	}
   632  	hdr.buf = buf.Bytes()
   633  
   634  	dir.seekkeys = hdr.seekkey
   635  	dir.nbyteskeys = hdr.nbytes
   636  
   637  	_, err = hdr.writeFile(dir.file)
   638  	if err != nil {
   639  		return fmt.Errorf("riofs: could not write header key: %w", err)
   640  	}
   641  	return nil
   642  }
   643  
   644  // // writeDirHeader overwrites the Directory header record.
   645  // func (dir *tdirectoryFile) writeDirHeader() error {
   646  // 	var (
   647  // 		err error
   648  // 	)
   649  // 	dir.mtime = nowUTC()
   650  //
   651  // 	nbytes := int32(dir.recordSize(dir.file.version)) + int32(dir.file.nbytesname)
   652  // 	key := newKey(dir, dir.Name(), dir.Title(), "TFile", nbytes, dir.file)
   653  // 	key.seekkey = dir.seekdir
   654  // 	key.seekpdir = dir.file.begin
   655  //
   656  // 	buf := rbytes.NewWBuffer(make([]byte, nbytes), nil, 0, nil)
   657  // 	buf.WriteString(dir.Name())
   658  // 	buf.WriteString(dir.Title())
   659  // 	_, err = dir.MarshalROOT(buf)
   660  // 	if err != nil {
   661  // 		return fmt.Errorf("riofs: could not marshal dir-info: %w", err)
   662  // 	}
   663  //
   664  // 	key.buf = buf.Bytes()
   665  // 	_, err = key.writeFile(dir.file)
   666  // 	if err != nil {
   667  // 		return fmt.Errorf("riofs: could not write dir-info to file: %w", err)
   668  // 	}
   669  //
   670  // 	return nil
   671  // }
   672  //
   673  // func (dir *tdirectoryFile) sizeof() int32 {
   674  // 	nbytes := int32(22)
   675  //
   676  // 	nbytes += datimeSizeof() // ctime
   677  // 	nbytes += datimeSizeof() // mtime
   678  // 	nbytes += dir.dir.uuid.Sizeof()
   679  // 	if dir.file.version >= 40000 {
   680  // 		nbytes += 12 // files with >= 2Gb
   681  // 	}
   682  // 	return nbytes
   683  // }
   684  
   685  func (dir *tdirectoryFile) records(w io.Writer, indent int) error {
   686  	hdr := strings.Repeat("  ", indent)
   687  	fmt.Fprintf(w, "%s=== dir %q @%d ===\n", hdr, dir.Name(), dir.seekdir)
   688  	parent := "<nil>"
   689  	if dir.dir.parent != nil {
   690  		parent = fmt.Sprintf("@%d", dir.dir.parent.(*tdirectoryFile).seekdir)
   691  	}
   692  	fmt.Fprintf(w, "%sparent:      %s\n", hdr, parent)
   693  	fmt.Fprintf(w, "%snbytes-keys: %d\n", hdr, dir.nbyteskeys)
   694  	fmt.Fprintf(w, "%snbytes-name: %d\n", hdr, dir.nbytesname)
   695  	fmt.Fprintf(w, "%sseek-dir:    %d\n", hdr, dir.seekdir)
   696  	fmt.Fprintf(w, "%sseek-parent: %d\n", hdr, dir.seekparent)
   697  	fmt.Fprintf(w, "%sseek-keys:   %d\n", hdr, dir.seekkeys)
   698  	fmt.Fprintf(w, "%sclass:       %q\n", hdr, dir.classname)
   699  	fmt.Fprintf(w, "%skeys:        %d\n", hdr, len(dir.keys))
   700  	for i := range dir.keys {
   701  		k := &dir.keys[i]
   702  		fmt.Fprintf(w, "%skey[%d]: %q\n", hdr+" ", i, k.Name())
   703  		err := k.records(w, indent+1)
   704  		if err != nil {
   705  			return fmt.Errorf("could not inspect key %q: %w", k.Name(), err)
   706  		}
   707  	}
   708  
   709  	return nil
   710  }
   711  
   712  func init() {
   713  	{
   714  		f := func() reflect.Value {
   715  			o := &tdirectory{}
   716  			return reflect.ValueOf(o)
   717  		}
   718  		rtypes.Factory.Add("TDirectory", f)
   719  	}
   720  	{
   721  		f := func() reflect.Value {
   722  			o := newDirectoryFile("", "", nil, nil)
   723  			return reflect.ValueOf(o)
   724  		}
   725  		rtypes.Factory.Add("TDirectoryFile", f)
   726  	}
   727  }
   728  
   729  // coreTypes is the set of types that do not need a streamer info.
   730  var coreTypes = map[string]struct{}{
   731  	"TObject":        {},
   732  	"TFile":          {},
   733  	"TDirectoryFile": {},
   734  	"TKey":           {},
   735  	"TString":        {},
   736  
   737  	"TDatime":       {},
   738  	"TVirtualIndex": {},
   739  	"TBasket":       {},
   740  }
   741  
   742  func isCoreType(typename string) bool {
   743  	_, ok := coreTypes[typename]
   744  	return ok
   745  }
   746  
   747  func isCxxBuiltin(typename string) bool {
   748  	_, ok := rmeta.CxxBuiltins[typename]
   749  	return ok
   750  }
   751  
   752  var (
   753  	_ root.Object        = (*tdirectory)(nil)
   754  	_ root.Named         = (*tdirectory)(nil)
   755  	_ rbytes.Marshaler   = (*tdirectory)(nil)
   756  	_ rbytes.Unmarshaler = (*tdirectory)(nil)
   757  
   758  	_ root.Object                = (*tdirectoryFile)(nil)
   759  	_ root.Named                 = (*tdirectoryFile)(nil)
   760  	_ Directory                  = (*tdirectoryFile)(nil)
   761  	_ rbytes.StreamerInfoContext = (*tdirectoryFile)(nil)
   762  	_ streamerInfoStore          = (*tdirectoryFile)(nil)
   763  	_ rbytes.Marshaler           = (*tdirectoryFile)(nil)
   764  	_ rbytes.Unmarshaler         = (*tdirectoryFile)(nil)
   765  )