go-hep.org/x/hep@v0.38.1/rio/file.go (about)

     1  // Copyright ©2015 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 rio
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"sort"
    11  )
    12  
    13  // File random-read-access to a rio stream
    14  type File struct {
    15  	r    io.ReadSeeker
    16  	meta Metadata
    17  }
    18  
    19  // Open creates a new read-only File.
    20  func Open(r io.ReadSeeker) (*File, error) {
    21  
    22  	f := &File{
    23  		r: r,
    24  	}
    25  
    26  	// a rio stream starts with rio magic
    27  	hdr := [4]byte{}
    28  	_, err := f.r.Read(hdr[:])
    29  	if err != nil {
    30  		return nil, fmt.Errorf("rio: error reading magic-header: %w", err)
    31  	}
    32  	if hdr != rioMagic {
    33  		return nil, fmt.Errorf("rio: not a rio-stream. magic-header=%q. want=%q",
    34  			string(hdr[:]),
    35  			string(rioMagic[:]),
    36  		)
    37  	}
    38  
    39  	// a seek-able rio streams sports a rioFooter at the end.
    40  	_, err = f.r.Seek(-int64(ftrSize), io.SeekEnd)
    41  	if err != nil {
    42  		return nil, fmt.Errorf("rio: error seeking footer: %w", err)
    43  	}
    44  
    45  	// {
    46  	// 	fmt.Printf("==== tail ==== (%d)\n", ftrSize)
    47  	// 	buf := new(bytes.Buffer)
    48  	// 	io.Copy(buf, f.r)
    49  	// 	fmt.Printf("buf: %v\n", buf.Bytes())
    50  	// 	pos, err = f.r.Seek(-int64(ftrSize), 2)
    51  	// 	fmt.Printf("=== [tail] ===\n")
    52  	// }
    53  
    54  	var ftr rioFooter
    55  	err = ftr.RioUnmarshal(f.r)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	_, err = f.r.Seek(ftr.Meta, io.SeekStart)
    61  	if err != nil {
    62  		return nil, fmt.Errorf("rio: error seeking metadata: %w", err)
    63  	}
    64  
    65  	rec := newRecord(MetaRecord, 0)
    66  	rec.unpack = true
    67  
    68  	err = rec.readRecord(f.r)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	err = rec.Block(MetaRecord).Read(&f.meta)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	return f, err
    79  }
    80  
    81  // Keys returns the list of record names.
    82  func (f *File) Keys() []RecordDesc {
    83  	keys := make([]RecordDesc, len(f.meta.Records))
    84  	copy(keys, f.meta.Records)
    85  	sort.Sort(recordsByName(keys))
    86  	return keys
    87  }
    88  
    89  // Get reads the value `name` into `ptr`
    90  func (f *File) Get(name string, ptr any) error {
    91  	offsets, ok := f.meta.Offsets[name]
    92  	if !ok {
    93  		return fmt.Errorf("rio: no record [%s]", name)
    94  	}
    95  
    96  	if len(offsets) > 1 {
    97  		return fmt.Errorf("rio: multi-record streams unsupported")
    98  	}
    99  
   100  	offset := offsets[0]
   101  	_, err := f.r.Seek(offset.Pos, 0)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	rec := newRecord(name, 0)
   107  	rec.unpack = true
   108  
   109  	err = rec.readRecord(f.r)
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	err = rec.Block(name).Read(ptr)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	return err
   120  }
   121  
   122  // Has returns whether a record `name` exists in this file.
   123  func (f *File) Has(name string) bool {
   124  	_, ok := f.meta.Offsets[name]
   125  	return ok
   126  }
   127  
   128  // Close closes access to the rio-file.
   129  // It does not (and can not) close the underlying reader.
   130  func (f *File) Close() error {
   131  	return nil
   132  }