go-hep.org/x/hep@v0.38.1/rio/writer.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 "bufio" 9 "compress/flate" 10 "io" 11 12 riobin "codeberg.org/gonuts/binary" 13 ) 14 15 type cwriter struct { 16 w *bufio.Writer 17 n int64 18 } 19 20 func (w *cwriter) Write(data []byte) (int, error) { 21 n, err := w.w.Write(data) 22 w.n += int64(n) 23 return n, err 24 } 25 26 func (w *cwriter) Flush() error { 27 return w.w.Flush() 28 } 29 30 // Writer is a rio write-only stream 31 type Writer struct { 32 w *cwriter 33 34 options Options 35 version Version 36 37 recs map[string]*Record 38 offsets map[string][]Span 39 closed bool 40 } 41 42 // NewWriter returns a new write-only rio stream 43 func NewWriter(w io.Writer) (*Writer, error) { 44 ww := &cwriter{bufio.NewWriter(w), 0} 45 // a rio stream starts with rio magic. 46 _, err := ww.Write(rioMagic[:]) 47 if err != nil { 48 return nil, err 49 } 50 51 return &Writer{ 52 w: ww, 53 options: NewOptions(CompressDefault, flate.DefaultCompression, 0), 54 version: 1, 55 recs: make(map[string]*Record), 56 offsets: make(map[string][]Span), 57 }, nil 58 } 59 60 // SetCompressor enables compression and sets the compression method. 61 func (w *Writer) SetCompressor(compr CompressorKind, lvl int) error { 62 var err error 63 64 // FIXME(sbinet) handle codec (gob|cbor|xdr|riobin|...) 65 codec := 0 66 w.options = NewOptions(compr, lvl, codec) 67 68 return err 69 } 70 71 // Record adds a Record to the list of records to write or 72 // returns the Record with that name. 73 func (w *Writer) Record(name string) *Record { 74 rec, ok := w.recs[name] 75 if !ok { 76 rec = newRecord(name, w.options) 77 rec.w = w 78 w.recs[name] = rec 79 } 80 return rec 81 } 82 83 // Close finishes writing the rio write-only stream. 84 // It does not (and can not) close the underlying writer. 85 func (w *Writer) Close() error { 86 if w.closed { 87 return nil 88 } 89 w.closed = true 90 pos := w.w.n 91 var meta Metadata 92 for _, rec := range w.recs { 93 var blocks []BlockDesc 94 for _, blk := range rec.blocks { 95 blocks = append(blocks, BlockDesc{blk.Name(), nameFromType(blk.typ)}) 96 } 97 meta.Records = append( 98 meta.Records, 99 RecordDesc{ 100 Name: rec.Name(), 101 Blocks: blocks, 102 }, 103 ) 104 } 105 meta.Offsets = w.offsets 106 107 err := w.WriteValue(MetaRecord, &meta) 108 if err != nil { 109 return err 110 } 111 112 ftr := rioFooter{ 113 Header: rioHeader{ 114 Len: uint32(ftrSize), 115 Frame: ftrFrame, 116 }, 117 Meta: pos, 118 } 119 err = ftr.RioMarshal(w.w) 120 if err != nil { 121 return err 122 } 123 return w.w.Flush() 124 } 125 126 // writeRecord writes all the record data 127 func (w *Writer) writeRecord(rec *Record, hdr, data []byte) error { 128 var err error 129 beg := w.w.n 130 131 _, err = w.w.Write(hdr) 132 if err != nil { 133 return err 134 } 135 136 _, err = w.w.Write(data) 137 if err != nil { 138 return err 139 } 140 141 n := rioAlignU32(rec.raw.Header.Len) 142 if n != rec.raw.Header.Len { 143 _, err = w.w.Write(make([]byte, int(n-rec.raw.Header.Len))) 144 } 145 146 end := w.w.n 147 w.offsets[rec.Name()] = append(w.offsets[rec.Name()], Span{beg, end - beg}) 148 return err 149 } 150 151 // WriteValue writes a value to the stream. 152 // The value is written to a record named `name` with one block `name`. 153 func (w *Writer) WriteValue(name string, value any) error { 154 var err error 155 156 rec := w.Record(name) 157 err = rec.Connect(name, value) 158 if err != nil { 159 return err 160 } 161 162 blk := rec.Block(name) 163 err = blk.Write(value) 164 if err != nil { 165 return err 166 } 167 168 err = rec.Write() 169 if err != nil { 170 return err 171 } 172 173 return err 174 } 175 176 // encoder manages the encoding of data values into rioRecords 177 type encoder struct { 178 w io.Writer 179 } 180 181 func (enc *encoder) Encode(v any) error { 182 switch v := v.(type) { 183 case Marshaler: 184 return v.RioMarshal(enc.w) 185 } 186 187 e := riobin.NewEncoder(enc.w) 188 e.Order = Endian 189 return e.Encode(v) 190 }