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 }