add CHATHISTORY and HISTORY implementations

This commit is contained in:
Shivaram Lingamneni 2019-02-04 05:18:17 -05:00
parent ea07d99074
commit f6b3008f8f
12 changed files with 404 additions and 48 deletions

View file

@ -36,6 +36,13 @@ type Item struct {
Msgid string
}
// HasMsgid tests whether a message has the message id `msgid`.
func (item *Item) HasMsgid(msgid string) bool {
// XXX we stuff other data in the Msgid field sometimes,
// don't match it by accident
return (item.Type == Privmsg || item.Type == Notice) && item.Msgid == msgid
}
type Predicate func(item Item) (matches bool)
// Buffer is a ring buffer holding message/event history for a channel or user
@ -115,7 +122,8 @@ func (list *Buffer) Add(item Item) {
list.buffer[pos] = item
}
func reverse(results []Item) {
// Reverse reverses an []Item, in-place.
func Reverse(results []Item) {
for i, j := 0, len(results)-1; i < j; i, j = i+1, j-1 {
results[i], results[j] = results[j], results[i]
}
@ -125,7 +133,7 @@ func reverse(results []Item) {
// with an indication of whether the results are complete or are missing items
// because some of that period was discarded. A zero value of `before` is considered
// higher than all other times.
func (list *Buffer) Between(after, before time.Time) (results []Item, complete bool) {
func (list *Buffer) Between(after, before time.Time, ascending bool, limit int) (results []Item, complete bool) {
if !list.Enabled() {
return
}
@ -139,14 +147,17 @@ func (list *Buffer) Between(after, before time.Time) (results []Item, complete b
return (after.IsZero() || item.Time.After(after)) && (before.IsZero() || item.Time.Before(before))
}
return list.matchInternal(satisfies, 0), complete
return list.matchInternal(satisfies, ascending, limit), complete
}
// Match returns all history items such that `predicate` returns true for them.
// Items are considered in reverse insertion order, up to a total of `limit` matches.
// Items are considered in reverse insertion order if `ascending` is false, or
// in insertion order if `ascending` is true, up to a total of `limit` matches
// if `limit` > 0 (unlimited otherwise).
// `predicate` MAY be a closure that maintains its own state across invocations;
// it MUST NOT acquire any locks or otherwise do anything weird.
func (list *Buffer) Match(predicate Predicate, limit int) (results []Item) {
// Results are always returned in insertion order.
func (list *Buffer) Match(predicate Predicate, ascending bool, limit int) (results []Item) {
if !list.Enabled() {
return
}
@ -154,28 +165,42 @@ func (list *Buffer) Match(predicate Predicate, limit int) (results []Item) {
list.RLock()
defer list.RUnlock()
return list.matchInternal(predicate, limit)
return list.matchInternal(predicate, ascending, limit)
}
// you must be holding the read lock to call this
func (list *Buffer) matchInternal(predicate Predicate, limit int) (results []Item) {
func (list *Buffer) matchInternal(predicate Predicate, ascending bool, limit int) (results []Item) {
if list.start == -1 {
return
}
pos := list.prev(list.end)
var pos, stop int
if ascending {
pos = list.start
stop = list.prev(list.end)
} else {
pos = list.prev(list.end)
stop = list.start
}
for {
if predicate(list.buffer[pos]) {
results = append(results, list.buffer[pos])
}
if pos == list.start || (limit != 0 && len(results) == limit) {
if pos == stop || (limit != 0 && len(results) == limit) {
break
}
pos = list.prev(pos)
if ascending {
pos = list.next(pos)
} else {
pos = list.prev(pos)
}
}
// TODO sort by time instead?
reverse(results)
if !ascending {
Reverse(results)
}
return
}
@ -183,7 +208,7 @@ func (list *Buffer) matchInternal(predicate Predicate, limit int) (results []Ite
// it returns all items.
func (list *Buffer) Latest(limit int) (results []Item) {
matchAll := func(item Item) bool { return true }
return list.Match(matchAll, limit)
return list.Match(matchAll, false, limit)
}
// LastDiscarded returns the latest time of any entry that was evicted
@ -204,6 +229,15 @@ func (list *Buffer) prev(index int) int {
}
}
func (list *Buffer) next(index int) int {
switch index {
case len(list.buffer) - 1:
return 0
default:
return index + 1
}
}
// Resize shrinks or expands the buffer
func (list *Buffer) Resize(size int) {
newbuffer := make([]Item, size)