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  }