tlv.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. package wire
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. )
  7. // TLV represents dynamically typed data in the OSCAR protocol. Each message
  8. // consists of a tag (or key) and a blob value. TLVs are typically grouped
  9. // together in arrays.
  10. type TLV struct {
  11. Tag uint16
  12. Value []byte `len_prefix:"uint16"`
  13. }
  14. // NewTLV creates a new instance of TLV.
  15. func NewTLV(tag uint16, val any) TLV {
  16. t := TLV{
  17. Tag: tag,
  18. }
  19. if _, ok := val.([]byte); ok {
  20. t.Value = val.([]byte)
  21. } else {
  22. buf := &bytes.Buffer{}
  23. if err := Marshal(val, buf); err != nil {
  24. panic(fmt.Sprintf("unable to create TLV: %s", err.Error()))
  25. }
  26. t.Value = buf.Bytes()
  27. }
  28. return t
  29. }
  30. // TLVRestBlock is a type of TLV array that does not have any length
  31. // information encoded in the blob. This typically means that a given offset in
  32. // the SNAC payload, the TLV occupies the "rest" of the payload.
  33. type TLVRestBlock struct {
  34. TLVList
  35. }
  36. // TLVBlock is a type of TLV array that has the TLV element count encoded as a
  37. // 2-byte value at the beginning of the encoded blob.
  38. type TLVBlock struct {
  39. TLVList `count_prefix:"uint16"`
  40. }
  41. // TLVLBlock is a type of TLV array that has the TLV blob byte-length encoded
  42. // as a 2-byte value at the beginning of the encoded blob.
  43. type TLVLBlock struct {
  44. TLVList `len_prefix:"uint16"`
  45. }
  46. // TLVList is a list of TLV elements. It provides methods to append and access
  47. // TLVs in the array. It provides methods that decode the data blob into the
  48. // appropriate type at runtime. The caller assumes the TLV data type at runtime
  49. // based on the protocol specification. These methods are not safe for
  50. // read-write access by multiple goroutines.
  51. type TLVList []TLV
  52. // Append adds a TLV to the end of the TLV list.
  53. func (s *TLVList) Append(tlv TLV) {
  54. *s = append(*s, tlv)
  55. }
  56. // AppendList adds a TLV list to the end of the TLV list.
  57. func (s *TLVList) AppendList(tlvs []TLV) {
  58. *s = append(*s, tlvs...)
  59. }
  60. // String retrieves the string value of a TLV with a tag value from the TLV
  61. // list. It returns false if the tag does not exist in the list.
  62. func (s *TLVList) String(tag uint16) (string, bool) {
  63. for _, tlv := range *s {
  64. if tag == tlv.Tag {
  65. return string(tlv.Value), true
  66. }
  67. }
  68. return "", false
  69. }
  70. // Slice retrieves the slice value of a TLV with a tag value from the TLV
  71. // list. It returns false if the tag does not exist in the list.
  72. func (s *TLVList) Slice(tag uint16) ([]byte, bool) {
  73. for _, tlv := range *s {
  74. if tag == tlv.Tag {
  75. return tlv.Value, true
  76. }
  77. }
  78. return nil, false
  79. }
  80. // Uint16 retrieves the uint16 value of a TLV with a tag value from the TLV
  81. // list. It returns false if the tag does not exist in the list. It may panic
  82. // if the TLV value is not uint16.
  83. func (s *TLVList) Uint16(tag uint16) (uint16, bool) {
  84. for _, tlv := range *s {
  85. if tag == tlv.Tag {
  86. return binary.BigEndian.Uint16(tlv.Value), true
  87. }
  88. }
  89. return 0, false
  90. }
  91. // Uint32 retrieves the uint32 value of a TLV with a tag value from the TLV
  92. // list. It returns false if the tag does not exist in the list. It may panic
  93. // if the TLV value is not uint32.
  94. func (s *TLVList) Uint32(tag uint16) (uint32, bool) {
  95. for _, tlv := range *s {
  96. if tag == tlv.Tag {
  97. return binary.BigEndian.Uint32(tlv.Value), true
  98. }
  99. }
  100. return 0, false
  101. }