Official cwtch.im peer and server implementations. https://cwtch.im
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

message.go 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package model
  2. import (
  3. "encoding/json"
  4. "sort"
  5. "sync"
  6. "time"
  7. )
  8. // Timeline encapsulates a collection of ordered Messages, and a mechanism to access them
  9. // in a threadsafe manner.
  10. type Timeline struct {
  11. Messages []Message
  12. SignedGroupID []byte
  13. lock sync.Mutex
  14. }
  15. // Message is a local representation of a given message sent over a group chat channel.
  16. type Message struct {
  17. Timestamp time.Time
  18. Received time.Time
  19. PeerID string
  20. Message string
  21. Signature []byte
  22. PreviousMessageSig []byte
  23. ReceivedByServer bool // messages sent to a server
  24. Acknowledged bool // peer to peer
  25. Error string `json:",omitempty"`
  26. }
  27. // MessageBaseSize is a rough estimate of the base number of bytes the struct uses before strings are populated
  28. const MessageBaseSize = 104
  29. func compareSignatures(a []byte, b []byte) bool {
  30. if len(a) != len(b) {
  31. return false
  32. }
  33. for i := range a {
  34. if a[i] != b[i] {
  35. return false
  36. }
  37. }
  38. return true
  39. }
  40. // GetMessages returns a copy of the entire timeline
  41. func (t *Timeline) GetMessages() []Message {
  42. t.lock.Lock()
  43. messages := make([]Message, len(t.Messages))
  44. copy(messages[:], t.Messages[:])
  45. t.lock.Unlock()
  46. return messages
  47. }
  48. // GetCopy returns a duplicate of the Timeline
  49. func (t *Timeline) GetCopy() *Timeline {
  50. t.lock.Lock()
  51. defer t.lock.Unlock()
  52. bytes, _ := json.Marshal(t)
  53. newt := &Timeline{}
  54. json.Unmarshal(bytes, newt)
  55. return newt
  56. }
  57. // SetMessages sets the Messages of this timeline. Only to be used in loading/initialization
  58. func (t *Timeline) SetMessages(messages []Message) {
  59. t.lock.Lock()
  60. defer t.lock.Unlock()
  61. t.Messages = messages
  62. }
  63. // Len gets the length of the timeline
  64. func (t *Timeline) Len() int {
  65. return len(t.Messages)
  66. }
  67. // Swap swaps 2 Messages on the timeline.
  68. func (t *Timeline) Swap(i, j int) {
  69. t.Messages[i], t.Messages[j] = t.Messages[j], t.Messages[i]
  70. }
  71. // Less checks 2 Messages (i and j) in the timeline and returns true if i occurred before j, else false
  72. func (t *Timeline) Less(i, j int) bool {
  73. if t.Messages[i].Timestamp.Before(t.Messages[j].Timestamp) {
  74. return true
  75. }
  76. // Short circuit false if j is before i, signature checks will give a wrong order in this case.
  77. if t.Messages[j].Timestamp.Before(t.Messages[i].Timestamp) {
  78. return false
  79. }
  80. if compareSignatures(t.Messages[i].PreviousMessageSig, t.SignedGroupID) {
  81. return true
  82. }
  83. if compareSignatures(t.Messages[i].Signature, t.Messages[j].PreviousMessageSig) {
  84. return true
  85. }
  86. return false
  87. }
  88. // Sort sorts the timeline in a canonical order.
  89. // TODO: There is almost definitely a more efficient way of doing things that involve not calling this method on every timeline load.
  90. func (t *Timeline) Sort() {
  91. t.lock.Lock()
  92. defer t.lock.Unlock()
  93. sort.Sort(t)
  94. }
  95. // Insert inserts a message into the timeline in a thread safe way.
  96. func (t *Timeline) Insert(mi *Message) bool {
  97. t.lock.Lock()
  98. defer t.lock.Unlock()
  99. for _, m := range t.Messages {
  100. // If the message already exists, then we don't add it
  101. if compareSignatures(m.Signature, mi.Signature) {
  102. return true
  103. }
  104. }
  105. t.Messages = append(t.Messages, *mi)
  106. sort.Sort(t)
  107. return false
  108. }