Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

history.go 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // Copyright (c) 2018 Shivaram Lingamneni <slingamn@cs.stanford.edu>
  2. // released under the MIT license
  3. package history
  4. import (
  5. "github.com/oragono/oragono/irc/utils"
  6. "sync"
  7. "sync/atomic"
  8. "time"
  9. )
  10. type ItemType uint
  11. const (
  12. uninitializedItem ItemType = iota
  13. Privmsg
  14. Notice
  15. Join
  16. Part
  17. Kick
  18. Quit
  19. Mode
  20. )
  21. // Item represents an event (e.g., a PRIVMSG or a JOIN) and its associated data
  22. type Item struct {
  23. Type ItemType
  24. Time time.Time
  25. Nick string
  26. // this is the uncasefolded account name, if there's no account it should be set to "*"
  27. AccountName string
  28. Message utils.SplitMessage
  29. Msgid string
  30. }
  31. // Buffer is a ring buffer holding message/event history for a channel or user
  32. type Buffer struct {
  33. sync.RWMutex
  34. // ring buffer, see irc/whowas.go for conventions
  35. buffer []Item
  36. start int
  37. end int
  38. lastDiscarded time.Time
  39. enabled uint32
  40. }
  41. func NewHistoryBuffer(size int) (result *Buffer) {
  42. result = new(Buffer)
  43. result.Initialize(size)
  44. return
  45. }
  46. func (hist *Buffer) Initialize(size int) {
  47. hist.buffer = make([]Item, size)
  48. hist.start = -1
  49. hist.end = -1
  50. hist.setEnabled(size)
  51. }
  52. func (hist *Buffer) setEnabled(size int) {
  53. var enabled uint32
  54. if size != 0 {
  55. enabled = 1
  56. }
  57. atomic.StoreUint32(&hist.enabled, enabled)
  58. }
  59. // Enabled returns whether the buffer is currently storing messages
  60. // (a disabled buffer blackholes everything it sees)
  61. func (list *Buffer) Enabled() bool {
  62. return atomic.LoadUint32(&list.enabled) != 0
  63. }
  64. // Add adds a history item to the buffer
  65. func (list *Buffer) Add(item Item) {
  66. // fast path without a lock acquisition for when we are not storing history
  67. if !list.Enabled() {
  68. return
  69. }
  70. if item.Time.IsZero() {
  71. item.Time = time.Now()
  72. }
  73. list.Lock()
  74. defer list.Unlock()
  75. var pos int
  76. if list.start == -1 { // empty
  77. pos = 0
  78. list.start = 0
  79. list.end = 1 % len(list.buffer)
  80. } else if list.start != list.end { // partially full
  81. pos = list.end
  82. list.end = (list.end + 1) % len(list.buffer)
  83. } else if list.start == list.end { // full
  84. pos = list.end
  85. list.end = (list.end + 1) % len(list.buffer)
  86. list.start = list.end // advance start as well, overwriting first entry
  87. // record the timestamp of the overwritten item
  88. if list.lastDiscarded.Before(list.buffer[pos].Time) {
  89. list.lastDiscarded = list.buffer[pos].Time
  90. }
  91. }
  92. list.buffer[pos] = item
  93. }
  94. // Between returns all history items with a time `after` <= time <= `before`,
  95. // with an indication of whether the results are complete or are missing items
  96. // because some of that period was discarded. A zero value of `before` is considered
  97. // higher than all other times.
  98. func (list *Buffer) Between(after, before time.Time) (results []Item, complete bool) {
  99. if !list.Enabled() {
  100. return
  101. }
  102. list.RLock()
  103. defer list.RUnlock()
  104. complete = after.Equal(list.lastDiscarded) || after.After(list.lastDiscarded)
  105. if list.start == -1 {
  106. return
  107. }
  108. satisfies := func(itime time.Time) bool {
  109. return (after.IsZero() || itime.After(after)) && (before.IsZero() || itime.Before(before))
  110. }
  111. // TODO: if we can guarantee that the insertion order is also the monotonic clock order,
  112. // then this can do a single allocation and use binary search and 1-2 copy calls
  113. pos := list.prev(list.end)
  114. for {
  115. if satisfies(list.buffer[pos].Time) {
  116. results = append(results, list.buffer[pos])
  117. }
  118. if pos == list.start {
  119. break
  120. }
  121. pos = list.prev(pos)
  122. }
  123. // reverse the results
  124. for i, j := 0, len(results)-1; i < j; i, j = i+1, j-1 {
  125. results[i], results[j] = results[j], results[i]
  126. }
  127. return
  128. }
  129. // All returns all available history items as a slice
  130. func (list *Buffer) All() (results []Item) {
  131. list.RLock()
  132. defer list.RUnlock()
  133. if list.start == -1 {
  134. return
  135. }
  136. results = make([]Item, list.length())
  137. if list.start < list.end {
  138. copy(results, list.buffer[list.start:list.end])
  139. } else {
  140. initialSegment := copy(results, list.buffer[list.start:])
  141. copy(results[initialSegment:], list.buffer[:list.end])
  142. }
  143. return
  144. }
  145. // LastDiscarded returns the latest time of any entry that was evicted
  146. // from the ring buffer.
  147. func (list *Buffer) LastDiscarded() time.Time {
  148. list.RLock()
  149. defer list.RUnlock()
  150. return list.lastDiscarded
  151. }
  152. func (list *Buffer) prev(index int) int {
  153. switch index {
  154. case 0:
  155. return len(list.buffer) - 1
  156. default:
  157. return index - 1
  158. }
  159. }
  160. // Resize shrinks or expands the buffer
  161. func (list *Buffer) Resize(size int) {
  162. newbuffer := make([]Item, size)
  163. list.Lock()
  164. defer list.Unlock()
  165. list.setEnabled(size)
  166. if list.start == -1 {
  167. // indices are already correct and nothing needs to be copied
  168. } else if size == 0 {
  169. // this is now the empty list
  170. list.start = -1
  171. list.end = -1
  172. } else {
  173. currentLength := list.length()
  174. start := list.start
  175. end := list.end
  176. // if we're truncating, keep the latest entries, not the earliest
  177. if size < currentLength {
  178. start = list.end - size
  179. if start < 0 {
  180. start += len(list.buffer)
  181. }
  182. // update lastDiscarded for discarded entries
  183. for i := list.start; i != start; i = (i + 1) % len(list.buffer) {
  184. if list.lastDiscarded.Before(list.buffer[i].Time) {
  185. list.lastDiscarded = list.buffer[i].Time
  186. }
  187. }
  188. }
  189. if start < end {
  190. copied := copy(newbuffer, list.buffer[start:end])
  191. list.start = 0
  192. list.end = copied % size
  193. } else {
  194. lenInitial := len(list.buffer) - start
  195. copied := copy(newbuffer, list.buffer[start:])
  196. copied += copy(newbuffer[lenInitial:], list.buffer[:end])
  197. list.start = 0
  198. list.end = copied % size
  199. }
  200. }
  201. list.buffer = newbuffer
  202. }
  203. func (hist *Buffer) length() int {
  204. if hist.start == -1 {
  205. return 0
  206. } else if hist.start < hist.end {
  207. return hist.end - hist.start
  208. } else {
  209. return len(hist.buffer) - (hist.start - hist.end)
  210. }
  211. }