go-hep.org/x/hep@v0.38.1/rio/record.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 "bytes" 9 "fmt" 10 "io" 11 "reflect" 12 ) 13 14 // Record manages and describes blocks of data 15 type Record struct { 16 unpack bool // whether to unpack incoming/outcoming records 17 blocks []Block // connected blocks 18 bmap map[string]int // connected blocks 19 20 w *Writer 21 r *Reader 22 23 cw Compressor 24 xr Decompressor 25 26 raw rioRecord 27 } 28 29 func newRecord(name string, options Options) *Record { 30 31 rec := Record{ 32 unpack: false, 33 blocks: make([]Block, 0, 2), 34 bmap: make(map[string]int, 2), 35 raw: rioRecord{ 36 Header: rioHeader{ 37 Len: 0, 38 Frame: recFrame, 39 }, 40 Options: options, 41 Name: name, 42 }, 43 } 44 45 return &rec 46 } 47 48 // Connect connects a Block to this Record (for reading or writing) 49 func (rec *Record) Connect(name string, ptr any) error { 50 _, dup := rec.bmap[name] 51 if dup { 52 return fmt.Errorf("rio: block [%s] already connected to record [%s]", name, rec.Name()) 53 } 54 55 version := Version(0) 56 switch t := ptr.(type) { 57 case Streamer: 58 version = t.RioVersion() 59 } 60 61 rec.bmap[name] = len(rec.blocks) 62 rec.blocks = append( 63 rec.blocks, 64 newBlock(name, version), 65 ) 66 rec.blocks[rec.bmap[name]].typ = reflect.TypeOf(ptr) 67 68 return nil 69 } 70 71 // Block returns the block named name for reading or writing 72 // Block returns nil if the block doesn't exist 73 func (rec *Record) Block(name string) *Block { 74 i, ok := rec.bmap[name] 75 if !ok { 76 return nil 77 } 78 block := &rec.blocks[i] 79 return block 80 } 81 82 // Write writes data to the Writer, in the rio format 83 func (rec *Record) Write() error { 84 var err error 85 xbuf := new(bytes.Buffer) // FIXME(sbinet): use a sync.Pool 86 87 for i := range rec.blocks { 88 block := &rec.blocks[i] 89 err = block.raw.RioMarshal(xbuf) 90 if err != nil { 91 return fmt.Errorf("rio: error writing block #%d (%s): %w", i, block.Name(), err) 92 } 93 } 94 95 xlen := xbuf.Len() 96 97 var cbuf *bytes.Buffer 98 switch { 99 case rec.Compress(): 100 cbuf = new(bytes.Buffer) 101 switch { 102 case rec.cw == nil: 103 compr := rec.raw.Options.CompressorKind() 104 cw, err := compr.NewCompressor(cbuf, rec.raw.Options) 105 if err != nil { 106 return err 107 } 108 rec.cw = cw 109 default: 110 err = rec.cw.Reset(cbuf) 111 if err != nil { 112 return err 113 } 114 } 115 _, err = io.CopyBuffer(rec.cw, xbuf, make([]byte, 16*1024*1024)) 116 if err != nil { 117 return fmt.Errorf("rio: error compressing blocks: %w", err) 118 } 119 err = rec.cw.Flush() 120 if err != nil { 121 return fmt.Errorf("rio: error compressing blocks: %w", err) 122 } 123 124 default: 125 cbuf = xbuf 126 } 127 128 clen := cbuf.Len() 129 130 rec.raw.Header.Len = uint32(clen) 131 rec.raw.CLen = uint32(clen) 132 rec.raw.XLen = uint32(xlen) 133 134 buf := new(bytes.Buffer) 135 err = rec.raw.RioMarshal(buf) 136 if err != nil { 137 return err 138 } 139 140 err = rec.w.writeRecord(rec, buf.Bytes(), cbuf.Bytes()) 141 142 return err 143 } 144 145 // Read reads data from the Reader, in the rio format 146 func (rec *Record) Read() error { 147 return rec.readRecord(rec.r.r) 148 } 149 150 // readRecord reads data from the Reader r, in the rio format 151 func (rec *Record) readRecord(r io.Reader) error { 152 err := rec.raw.RioUnmarshal(r) 153 if err != nil { 154 return err 155 } 156 157 clen := int64(rioAlignU32(rec.raw.CLen)) 158 if !rec.unpack { 159 switch r := r.(type) { 160 case io.Seeker: 161 _, err = r.Seek(clen, 0) 162 default: 163 _, err = io.CopyN(io.Discard, r, clen) 164 } 165 return err 166 } 167 168 return rec.readBlocks(r) 169 } 170 171 // readBlocks reads the blocks data from the Reader 172 func (rec *Record) readBlocks(r io.Reader) error { 173 var err error 174 clen := int64(rioAlignU32(rec.raw.CLen)) 175 176 lr := &io.LimitedReader{ 177 R: r, 178 N: clen, 179 } 180 181 // decompression 182 switch { 183 case rec.xr == nil: 184 compr := rec.raw.Options.CompressorKind() 185 xr, err := compr.NewDecompressor(lr) 186 if err != nil { 187 return err 188 } 189 rec.xr = xr 190 lr = &io.LimitedReader{ 191 R: xr, 192 N: int64(rec.raw.XLen), 193 } 194 195 default: 196 err = rec.xr.Reset(lr) 197 if err != nil { 198 return err 199 } 200 lr = &io.LimitedReader{ 201 R: rec.xr, 202 N: int64(rec.raw.XLen), 203 } 204 } 205 206 for lr.N > 0 { 207 blk := newBlock("", 0) 208 err = blk.raw.RioUnmarshal(lr) 209 if err == io.EOF { 210 err = nil 211 break 212 } 213 if err != nil { 214 return err 215 } 216 n := blk.Name() 217 if i, ok := rec.bmap[n]; ok { 218 rec.blocks[i] = blk 219 } else { 220 rec.bmap[n] = len(rec.blocks) 221 rec.blocks = append(rec.blocks, blk) 222 } 223 } 224 225 if lr.N > 0 { 226 return fmt.Errorf("rio: record read too few bytes (want=%d. got=%d)", clen, clen-lr.N) 227 } 228 return err 229 } 230 231 // Name returns the name of this record 232 func (rec *Record) Name() string { 233 return rec.raw.Name 234 } 235 236 // Unpack returns whether to unpack incoming records 237 func (rec *Record) Unpack() bool { 238 return rec.unpack 239 } 240 241 // SetUnpack sets whether to unpack incoming records 242 func (rec *Record) SetUnpack(unpack bool) { 243 rec.unpack = unpack 244 } 245 246 // Compress returns the compression flag 247 func (rec *Record) Compress() bool { 248 return CompressorKind((rec.raw.Options&gMaskCompr)>>16) != CompressNone 249 } 250 251 // Options returns the options of this record. 252 func (rec *Record) Options() Options { 253 return rec.raw.Options 254 } 255 256 // EOF