go-hep.org/x/hep@v0.38.1/sio/record.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 sio 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "reflect" 12 ) 13 14 // recordHeader describes the on-disk record (header part) 15 type recordHeader struct { 16 Len uint32 17 Typ uint32 18 } 19 20 // recordData describes the on-disk record (payload part) 21 type recordData struct { 22 Options uint32 23 DataLen uint32 // length of compressed record data 24 UCmpLen uint32 // length of uncompressed record data 25 NameLen uint32 // length of record name 26 } 27 28 // Record manages blocks of data 29 type Record struct { 30 name string // record name 31 unpack bool // whether to unpack incoming records 32 options uint32 // options (flag word) 33 bindex map[string]int // index of connected blocks 34 bnames []string // connected blocks names 35 blocks []Block // connected blocks 36 } 37 38 // Name returns the name of this record 39 func (rec *Record) Name() string { 40 return rec.name 41 } 42 43 // Unpack returns whether to unpack incoming records 44 func (rec *Record) Unpack() bool { 45 return rec.unpack 46 } 47 48 // SetUnpack sets whether to unpack incoming records 49 func (rec *Record) SetUnpack(unpack bool) { 50 rec.unpack = unpack 51 } 52 53 // Compress returns the compression flag 54 func (rec *Record) Compress() bool { 55 return rec.options&optCompress != 0 56 } 57 58 // SetCompress sets or resets the compression flag 59 func (rec *Record) SetCompress(compress bool) { 60 rec.options &= optNotCompress 61 if compress { 62 rec.options |= optCompress 63 } 64 } 65 66 // Options returns the options of this record. 67 func (rec *Record) Options() uint32 { 68 return rec.options 69 } 70 71 // Disconnect disconnects all blocks previously connected to this 72 // Record (for reading or writing.) 73 func (rec *Record) Disconnect() { 74 rec.bnames = rec.bnames[:0] 75 rec.bindex = make(map[string]int) 76 rec.blocks = rec.blocks[:0] 77 } 78 79 // Connect connects a Block to this Record (for reading or writing) 80 func (rec *Record) Connect(name string, ptr any) error { 81 var err error 82 iblk, ok := rec.bindex[name] 83 if !ok { 84 iblk = len(rec.blocks) 85 rec.bnames = append(rec.bnames, name) 86 rec.blocks = append(rec.blocks, nil) 87 rec.bindex[name] = iblk 88 //return fmt.Errorf("sio.Record: Block name [%s] already connected", name) 89 //return ErrBlockConnected 90 } 91 var block Block 92 switch ptr := ptr.(type) { 93 case Block: 94 block = ptr 95 case Codec: 96 rt := reflect.TypeOf(ptr) 97 var vers uint32 98 if ptr, ok := ptr.(Versioner); ok { 99 vers = ptr.VersionSio() 100 } 101 block = &userBlock{ 102 blk: ptr, 103 version: vers, 104 name: rt.Name(), 105 } 106 107 default: 108 rt := reflect.TypeOf(ptr) 109 if rt.Kind() != reflect.Ptr { 110 return fmt.Errorf("sio: Connect needs a pointer to a block of data") 111 } 112 var vers uint32 113 if ptr, ok := ptr.(Versioner); ok { 114 vers = ptr.VersionSio() 115 } 116 block = &genericBlock{ 117 rt: rt, 118 rv: reflect.ValueOf(ptr), 119 version: vers, 120 name: rt.Name(), 121 } 122 } 123 rec.blocks[iblk] = block 124 return err 125 } 126 127 // read reads a record 128 func (rec *Record) read(r *reader) error { 129 var err error 130 // fmt.Printf("::: reading record [%s]... [%d]\n", rec.name, r.Len()) 131 type fixlink struct { 132 link Linker 133 vers uint32 134 } 135 var linkers []fixlink 136 // loop until data has been depleted 137 for r.Len() > 0 { 138 beg := r.Len() 139 // read block header 140 var hdr blockHeader 141 err = bread(r, &hdr) 142 if err != nil { 143 return err 144 } 145 if hdr.Typ != blkMarker { 146 // fmt.Printf("*** err record[%s]: noblockmarker\n", rec.name) 147 return ErrRecordNoBlockMarker 148 } 149 150 var data blockData 151 err = bread(r, &data) 152 if err != nil { 153 return err 154 } 155 r.ver = data.Version 156 157 var cbuf bytes.Buffer 158 nlen := align4U32(data.NameLen) 159 n, err := io.CopyN(&cbuf, r, int64(nlen)) 160 if err != nil { 161 // fmt.Printf(">>> err:%v\n", err) 162 return err 163 } 164 if n != int64(nlen) { 165 return ErrBlockShortRead 166 } 167 iblk, ok := rec.bindex[string(cbuf.Bytes()[:data.NameLen])] 168 if ok { 169 blk := rec.blocks[iblk] 170 // fmt.Printf("### %q\n", buf.String()) 171 err = blk.UnmarshalSio(r) 172 end := r.Len() 173 if err != nil { 174 // fmt.Printf("*** error unmarshaling record=%q block=%q: %v\n", rec.name, name, err) 175 return err 176 } 177 if beg-end != int(hdr.Len) { 178 /* 179 if true { 180 var typ any 181 switch blk := blk.(type) { 182 case *userBlock: 183 typ = blk.blk 184 case *genericBlock: 185 typ = blk.rv.Interface() 186 } 187 log.Printf("record %q block %q (%T) (beg-end=%d-%d=%d != %d)", rec.Name(), name, typ, beg, end, beg-end, int(hdr.Len)) 188 } else { 189 */ 190 return ErrBlockShortRead 191 } 192 // fmt.Printf(">>> read record=%q block=%q (buf=%d)\n", rec.name, name, buf.Len()) 193 if ublk, ok := blk.(*userBlock); ok { 194 if link, ok := ublk.blk.(Linker); ok { 195 linkers = append(linkers, fixlink{link, data.Version}) 196 } 197 } 198 } 199 200 // check whether there is still something to be read. 201 // if there is, check whether there is a block-marker 202 if r.Len() > 0 { 203 next := bytes.Index(r.Bytes(), blkMarkerBeg) 204 if next > 0 { 205 pos := next - 4 // sizeof mark-block 206 r.Next(pos) // drain the buffer until next block 207 } else { 208 // drain the whole buffer 209 r.Next(r.Len()) 210 } 211 } 212 } 213 r.relocate() 214 for _, fix := range linkers { 215 err = fix.link.LinkSio(fix.vers) 216 if err != nil { 217 return err 218 } 219 } 220 221 // fmt.Printf("::: reading record [%s]... [done]\n", rec.name) 222 return err 223 } 224 225 func (rec *Record) write(w *writer) error { 226 var ( 227 err error 228 work = make([]byte, 16*1024*1024) 229 ) 230 for i, k := range rec.bnames { 231 blk := rec.blocks[i] 232 bhdr := blockHeader{ 233 Typ: blkMarker, 234 } 235 236 bdata := blockData{ 237 Version: blk.VersionSio(), 238 NameLen: uint32(len(k)), 239 } 240 241 wblk := newWriterFrom(w) 242 wblk.ver = bdata.Version 243 244 err = blk.MarshalSio(wblk) 245 if err != nil { 246 return err 247 } 248 249 bhdr.Len = uint32(blockHeaderSize) + uint32(blockDataSize) + 250 align4U32(bdata.NameLen) + uint32(wblk.Len()) 251 252 // fmt.Printf("blockHeader: %v\n", bhdr) 253 // fmt.Printf("blockData: %v (%s)\n", bdata, k) 254 255 err = bwrite(w, &bhdr) 256 if err != nil { 257 return err 258 } 259 260 err = bwrite(w, &bdata) 261 if err != nil { 262 return err 263 } 264 265 _, err = w.Write([]byte(k)) 266 if err != nil { 267 return err 268 } 269 padlen := align4U32(bdata.NameLen) - bdata.NameLen 270 if padlen > 0 { 271 _, err = w.Write(make([]byte, int(padlen))) 272 if err != nil { 273 return err 274 } 275 } 276 277 _, err := io.CopyBuffer(w, wblk.buf, work) 278 if err != nil { 279 return err 280 } 281 w.ids = wblk.ids 282 w.tag = wblk.tag 283 w.ptr = wblk.ptr 284 } 285 return err 286 }