github.com/decred/dcrlnd@v0.7.6/tlv/record.go (about) 1 package tlv 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "sort" 8 9 "github.com/decred/dcrd/dcrec/secp256k1/v4" 10 ) 11 12 // Type is an 64-bit identifier for a TLV Record. 13 type Type uint64 14 15 // TypeMap is a map of parsed Types. The map values are byte slices. If the byte 16 // slice is nil, the type was successfully parsed. Otherwise the value is byte 17 // slice containing the encoded data. 18 type TypeMap map[Type][]byte 19 20 // Encoder is a signature for methods that can encode TLV values. An error 21 // should be returned if the Encoder cannot support the underlying type of val. 22 // The provided scratch buffer must be non-nil. 23 type Encoder func(w io.Writer, val interface{}, buf *[8]byte) error 24 25 // Decoder is a signature for methods that can decode TLV values. An error 26 // should be returned if the Decoder cannot support the underlying type of val. 27 // The provided scratch buffer must be non-nil. 28 type Decoder func(r io.Reader, val interface{}, buf *[8]byte, l uint64) error 29 30 // ENOP is an encoder that doesn't modify the io.Writer and never fails. 31 func ENOP(io.Writer, interface{}, *[8]byte) error { return nil } 32 33 // DNOP is an encoder that doesn't modify the io.Reader and never fails. 34 func DNOP(io.Reader, interface{}, *[8]byte, uint64) error { return nil } 35 36 // SizeFunc is a function that can compute the length of a given field. Since 37 // the size of the underlying field can change, this allows the size of the 38 // field to be evaluated at the time of encoding. 39 type SizeFunc func() uint64 40 41 // SizeVarBytes returns a SizeFunc that can compute the length of a byte slice. 42 func SizeVarBytes(e *[]byte) SizeFunc { 43 return func() uint64 { 44 return uint64(len(*e)) 45 } 46 } 47 48 // RecorderProducer is an interface for objects that can produce a Record object 49 // capable of encoding and/or decoding the RecordProducer as a Record. 50 type RecordProducer interface { 51 // Record returns a Record that can be used to encode or decode the 52 // backing object. 53 Record() Record 54 } 55 56 // Record holds the required information to encode or decode a TLV record. 57 type Record struct { 58 value interface{} 59 typ Type 60 staticSize uint64 61 sizeFunc SizeFunc 62 encoder Encoder 63 decoder Decoder 64 } 65 66 // Size returns the size of the Record's value. If no static size is known, the 67 // dynamic size will be evaluated. 68 func (f *Record) Size() uint64 { 69 if f.sizeFunc == nil { 70 return f.staticSize 71 } 72 73 return f.sizeFunc() 74 } 75 76 // Type returns the type of the underlying TLV record. 77 func (f *Record) Type() Type { 78 return f.typ 79 } 80 81 // Encode writes out the TLV record to the passed writer. This is useful when a 82 // caller wants to obtain the raw encoding of a *single* TLV record, outside 83 // the context of the Stream struct. 84 func (f *Record) Encode(w io.Writer) error { 85 var b [8]byte 86 87 return f.encoder(w, f.value, &b) 88 } 89 90 // Decode read in the TLV record from the passed reader. This is useful when a 91 // caller wants decode a *single* TLV record, outside the context of the Stream 92 // struct. 93 func (f *Record) Decode(r io.Reader, l uint64) error { 94 var b [8]byte 95 return f.decoder(r, f.value, &b, l) 96 } 97 98 // MakePrimitiveRecord creates a record for common types. 99 func MakePrimitiveRecord(typ Type, val interface{}) Record { 100 var ( 101 staticSize uint64 102 sizeFunc SizeFunc 103 encoder Encoder 104 decoder Decoder 105 ) 106 switch e := val.(type) { 107 case *uint8: 108 staticSize = 1 109 encoder = EUint8 110 decoder = DUint8 111 112 case *uint16: 113 staticSize = 2 114 encoder = EUint16 115 decoder = DUint16 116 117 case *uint32: 118 staticSize = 4 119 encoder = EUint32 120 decoder = DUint32 121 122 case *uint64: 123 staticSize = 8 124 encoder = EUint64 125 decoder = DUint64 126 127 case *[32]byte: 128 staticSize = 32 129 encoder = EBytes32 130 decoder = DBytes32 131 132 case *[33]byte: 133 staticSize = 33 134 encoder = EBytes33 135 decoder = DBytes33 136 137 case **secp256k1.PublicKey: 138 staticSize = 33 139 encoder = EPubKey 140 decoder = DPubKey 141 142 case *[64]byte: 143 staticSize = 64 144 encoder = EBytes64 145 decoder = DBytes64 146 147 case *[]byte: 148 sizeFunc = SizeVarBytes(e) 149 encoder = EVarBytes 150 decoder = DVarBytes 151 152 default: 153 panic(fmt.Sprintf("unknown primitive type: %T", val)) 154 } 155 156 return Record{ 157 value: val, 158 typ: typ, 159 staticSize: staticSize, 160 sizeFunc: sizeFunc, 161 encoder: encoder, 162 decoder: decoder, 163 } 164 } 165 166 // MakeStaticRecord creates a record for a field of fixed-size 167 func MakeStaticRecord(typ Type, val interface{}, size uint64, encoder Encoder, 168 decoder Decoder) Record { 169 170 return Record{ 171 value: val, 172 typ: typ, 173 staticSize: size, 174 encoder: encoder, 175 decoder: decoder, 176 } 177 } 178 179 // MakeDynamicRecord creates a record whose size may vary, and will be 180 // determined at the time of encoding via sizeFunc. 181 func MakeDynamicRecord(typ Type, val interface{}, sizeFunc SizeFunc, 182 encoder Encoder, decoder Decoder) Record { 183 184 return Record{ 185 value: val, 186 typ: typ, 187 sizeFunc: sizeFunc, 188 encoder: encoder, 189 decoder: decoder, 190 } 191 } 192 193 // RecordsToMap encodes a series of TLV records as raw key-value pairs in the 194 // form of a map. 195 func RecordsToMap(records []Record) (map[uint64][]byte, error) { 196 tlvMap := make(map[uint64][]byte, len(records)) 197 198 for _, record := range records { 199 var b bytes.Buffer 200 if err := record.Encode(&b); err != nil { 201 return nil, err 202 } 203 204 tlvMap[uint64(record.Type())] = b.Bytes() 205 } 206 207 return tlvMap, nil 208 } 209 210 // StubEncoder is a factory function that makes a stub tlv.Encoder out of a raw 211 // value. We can use this to make a record that can be encoded when we don't 212 // actually know it's true underlying value, and only it serialization. 213 func StubEncoder(v []byte) Encoder { 214 return func(w io.Writer, val interface{}, buf *[8]byte) error { 215 _, err := w.Write(v) 216 return err 217 } 218 } 219 220 // MapToRecords encodes the passed TLV map as a series of regular tlv.Record 221 // instances. The resulting set of records will be returned in sorted order by 222 // their type. 223 func MapToRecords(tlvMap map[uint64][]byte) []Record { 224 records := make([]Record, 0, len(tlvMap)) 225 for k, v := range tlvMap { 226 // We don't pass in a decoder here since we don't actually know 227 // the type, and only expect this Record to be used for display 228 // and encoding purposes. 229 record := MakeStaticRecord( 230 Type(k), nil, uint64(len(v)), StubEncoder(v), nil, 231 ) 232 233 records = append(records, record) 234 } 235 236 SortRecords(records) 237 238 return records 239 } 240 241 // SortRecords is a helper function that will sort a slice of records in place 242 // according to their type. 243 func SortRecords(records []Record) { 244 if len(records) == 0 { 245 return 246 } 247 248 sort.Slice(records, func(i, j int) bool { 249 return records[i].Type() < records[j].Type() 250 }) 251 }