forked from Shopify/zk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ring_buffer.go
134 lines (120 loc) · 3.11 KB
/
ring_buffer.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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package zk
import "fmt"
// ringBuffer is a circular buffer that overwrites the oldest item when full.
// It is not thread-safe.
type ringBuffer[T any] struct {
// The buffer.
buf []T
// The index of the first item in the buffer.
head uint32
// The index of the last item in the buffer.
tail uint32
// The number of items in the buffer.
count uint32
// The capacity of the buffer.
capacity uint32
}
// newRingBuffer creates a new ringBuffer with the given capacity.
func newRingBuffer[T any](capacity uint32) *ringBuffer[T] {
return &ringBuffer[T]{
buf: make([]T, capacity),
capacity: capacity,
}
}
func (rb *ringBuffer[T]) isEmpty() bool {
return rb.count == 0
}
// len returns the number of items in the buffer.
func (rb *ringBuffer[T]) len() uint32 {
return rb.count
}
// cap returns the capacity of the buffer.
func (rb *ringBuffer[T]) cap() uint32 {
return rb.capacity
}
// offer adds an item to the buffer, if there is space.
// Returns true if the item was added, false otherwise (buffer full).
func (rb *ringBuffer[T]) offer(t T) bool {
if rb.count == rb.capacity {
return false
}
rb.buf[rb.tail] = t
rb.tail = (rb.tail + 1) % rb.capacity
rb.count++
return true
}
// push adds an item to the buffer.
// If the buffer is full, the oldest item is overwritten.
func (rb *ringBuffer[T]) push(t T) {
rb.buf[rb.tail] = t
rb.tail = (rb.tail + 1) % rb.capacity
if rb.count == rb.capacity {
rb.head = rb.tail
} else {
rb.count++
}
}
// peek returns the oldest item in the buffer, without removing it.
// If the buffer is empty, returns the zero value and false.
func (rb *ringBuffer[T]) peek() (T, bool) {
if rb.count == 0 {
var zero T
return zero, false
}
return rb.buf[rb.head], true
}
// pop returns the oldest item in the buffer, removing it.
// If the buffer is empty, returns the zero value and false.
func (rb *ringBuffer[T]) pop() (T, bool) {
if rb.count == 0 {
var zero T
return zero, false
}
t := rb.buf[rb.head]
rb.head = (rb.head + 1) % rb.capacity
rb.count--
return t, true
}
// clear removes all items from the buffer.
func (rb *ringBuffer[T]) clear() {
rb.head = 0
rb.tail = 0
rb.count = 0
}
// ensureCapacity increases the capacity of the buffer to at least the given capacity.
// If the buffer is already at least that large, this is a no-op.
func (rb *ringBuffer[T]) ensureCapacity(minCapacity uint32) {
if minCapacity <= rb.capacity {
return
}
newBuf := make([]T, minCapacity)
if rb.count > 0 {
if rb.head < rb.tail {
copy(newBuf, rb.buf[rb.head:rb.tail])
} else {
n := copy(newBuf, rb.buf[rb.head:])
copy(newBuf[n:], rb.buf[:rb.tail])
}
}
rb.buf = newBuf
rb.head = 0
rb.tail = rb.count
rb.capacity = minCapacity
}
// toSlice returns a slice of the items in the buffer (not aliased).
func (rb *ringBuffer[T]) toSlice() []T {
if rb.count == 0 {
return []T{}
}
out := make([]T, rb.count)
if rb.tail > rb.head {
copy(out, rb.buf[rb.head:rb.tail])
} else {
copy(out, rb.buf[rb.head:])
copy(out[rb.capacity-rb.head:], rb.buf[:rb.tail])
}
return out
}
func (rb *ringBuffer[T]) String() string {
return fmt.Sprintf("ringBuffer[%d/%d]", rb.count, rb.capacity)
}