-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
history.go
98 lines (79 loc) · 2.73 KB
/
history.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/*
Copyright 2019 The Vitess Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreedto in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package history implements a circular buffer with adjacent-item deduplication.
package history
import (
"sync"
)
// Deduplicable is an interface that records should implement if the
// history should perform their deduplication. An example would be
// deduplicating records whose only difference is their timestamp.
type Deduplicable interface {
// IsDuplicate returns true if other is considered to be a
// duplicate of the calling instance.
IsDuplicate(any) bool
}
// History is a data structure that allows you to keep some number of
// records.
type History struct {
mu sync.Mutex
records []any
lastAdded any
latest any
next int
length int
}
// New returns a History with the specified maximum length.
func New(length int) *History {
return &History{records: make([]any, length)}
}
// Add a new record in a threadsafe manner. If record implements
// Deduplicable, and IsDuplicate returns true when called on the last
// previously added record, it will not be added.
func (history *History) Add(record any) {
history.mu.Lock()
defer history.mu.Unlock()
history.latest = record
if equiv, ok := record.(Deduplicable); ok && history.length > 0 {
if equiv.IsDuplicate(history.lastAdded) {
return
}
}
history.records[history.next] = record
history.lastAdded = record
if history.length < len(history.records) {
history.length++
}
history.next = (history.next + 1) % len(history.records)
}
// Records returns the kept records in reverse chronological order in a
// threadsafe manner.
func (history *History) Records() []any {
history.mu.Lock()
defer history.mu.Unlock()
records := make([]any, 0, history.length)
records = append(records, history.records[history.next:history.length]...)
records = append(records, history.records[:history.next]...)
// In place reverse.
for i := 0; i < history.length/2; i++ {
records[i], records[history.length-i-1] = records[history.length-i-1], records[i]
}
return records
}
// Latest returns the record most recently passed to Add(),
// regardless of whether it was actually added or dropped as a duplicate.
func (history *History) Latest() any {
history.mu.Lock()
defer history.mu.Unlock()
return history.latest
}