topic.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. // Copyright 2018 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package feed
  17. import (
  18. "bytes"
  19. "encoding/json"
  20. "fmt"
  21. "github.com/ethereum/go-ethereum/common/bitutil"
  22. "github.com/ethereum/go-ethereum/common/hexutil"
  23. "github.com/ethereum/go-ethereum/swarm/storage"
  24. )
  25. // TopicLength establishes the max length of a topic string
  26. const TopicLength = storage.AddressLength
  27. // Topic represents what a feed is about
  28. type Topic [TopicLength]byte
  29. // ErrTopicTooLong is returned when creating a topic with a name/related content too long
  30. var ErrTopicTooLong = fmt.Errorf("Topic is too long. Max length is %d", TopicLength)
  31. // NewTopic creates a new topic from a provided name and "related content" byte array,
  32. // merging the two together.
  33. // If relatedContent or name are longer than TopicLength, they will be truncated and an error returned
  34. // name can be an empty string
  35. // relatedContent can be nil
  36. func NewTopic(name string, relatedContent []byte) (topic Topic, err error) {
  37. if relatedContent != nil {
  38. contentLength := len(relatedContent)
  39. if contentLength > TopicLength {
  40. contentLength = TopicLength
  41. err = ErrTopicTooLong
  42. }
  43. copy(topic[:], relatedContent[:contentLength])
  44. }
  45. nameBytes := []byte(name)
  46. nameLength := len(nameBytes)
  47. if nameLength > TopicLength {
  48. nameLength = TopicLength
  49. err = ErrTopicTooLong
  50. }
  51. bitutil.XORBytes(topic[:], topic[:], nameBytes[:nameLength])
  52. return topic, err
  53. }
  54. // Hex will return the topic encoded as an hex string
  55. func (t *Topic) Hex() string {
  56. return hexutil.Encode(t[:])
  57. }
  58. // FromHex will parse a hex string into this Topic instance
  59. func (t *Topic) FromHex(hex string) error {
  60. bytes, err := hexutil.Decode(hex)
  61. if err != nil || len(bytes) != len(t) {
  62. return NewErrorf(ErrInvalidValue, "Cannot decode topic")
  63. }
  64. copy(t[:], bytes)
  65. return nil
  66. }
  67. // Name will try to extract the topic name out of the Topic
  68. func (t *Topic) Name(relatedContent []byte) string {
  69. nameBytes := *t
  70. if relatedContent != nil {
  71. contentLength := len(relatedContent)
  72. if contentLength > TopicLength {
  73. contentLength = TopicLength
  74. }
  75. bitutil.XORBytes(nameBytes[:], t[:], relatedContent[:contentLength])
  76. }
  77. z := bytes.IndexByte(nameBytes[:], 0)
  78. if z < 0 {
  79. z = TopicLength
  80. }
  81. return string(nameBytes[:z])
  82. }
  83. // UnmarshalJSON implements the json.Unmarshaller interface
  84. func (t *Topic) UnmarshalJSON(data []byte) error {
  85. var hex string
  86. json.Unmarshal(data, &hex)
  87. return t.FromHex(hex)
  88. }
  89. // MarshalJSON implements the json.Marshaller interface
  90. func (t *Topic) MarshalJSON() ([]byte, error) {
  91. return json.Marshal(t.Hex())
  92. }