236 lines
4.6 KiB
Go
236 lines
4.6 KiB
Go
/*
|
|
Package ring provides a simple implementation of a ring buffer.
|
|
*/
|
|
package ring
|
|
|
|
import "sync"
|
|
|
|
/*
|
|
The DefaultCapacity of an uninitialized Ring buffer.
|
|
|
|
Changing this value only affects ring buffers created after it is changed.
|
|
*/
|
|
var DefaultCapacity int = 10
|
|
|
|
/*
|
|
Type Ring implements a Circular Buffer.
|
|
The default value of the Ring struct is a valid (empty) Ring buffer with capacity DefaultCapacify.
|
|
*/
|
|
type Ring struct {
|
|
sync.Mutex
|
|
head int // the most recent value written
|
|
tail int // the least recent value written
|
|
buff []interface{}
|
|
}
|
|
|
|
/*
|
|
Set the maximum size of the ring buffer.
|
|
*/
|
|
func (r *Ring) SetCapacity(size int) {
|
|
r.Lock()
|
|
defer r.Unlock()
|
|
|
|
r.checkInit()
|
|
r.extend(size)
|
|
}
|
|
|
|
/*
|
|
Capacity returns the current capacity of the ring buffer.
|
|
*/
|
|
func (r *Ring) Capacity() int {
|
|
r.Lock()
|
|
defer r.Unlock()
|
|
|
|
return r.capacity()
|
|
}
|
|
|
|
/*
|
|
ContentSize returns the current number of elements inside the ring buffer.
|
|
*/
|
|
func (r *Ring) ContentSize() int {
|
|
r.Lock()
|
|
defer r.Unlock()
|
|
|
|
if r.head == -1 {
|
|
return 0
|
|
} else {
|
|
difference := (r.head - r.tail)
|
|
if difference < 0 {
|
|
difference += r.capacity()
|
|
}
|
|
return difference + 1
|
|
}
|
|
}
|
|
|
|
/*
|
|
Enqueue a value into the Ring buffer.
|
|
*/
|
|
func (r *Ring) Enqueue(i interface{}) {
|
|
r.Lock()
|
|
defer r.Unlock()
|
|
|
|
r.checkInit()
|
|
r.set(r.head+1, i)
|
|
old := r.head
|
|
r.head = r.mod(r.head + 1)
|
|
if old != -1 && r.head == r.tail {
|
|
r.tail = r.mod(r.tail + 1)
|
|
}
|
|
}
|
|
|
|
/*
|
|
Dequeue a value from the Ring buffer.
|
|
|
|
Returns nil if the ring buffer is empty.
|
|
*/
|
|
func (r *Ring) Dequeue() interface{} {
|
|
r.Lock()
|
|
defer r.Unlock()
|
|
|
|
r.checkInit()
|
|
if r.head == -1 {
|
|
return nil
|
|
}
|
|
v := r.get(r.tail)
|
|
r.set(r.tail, nil)
|
|
if r.tail == r.head {
|
|
r.head = -1
|
|
r.tail = 0
|
|
} else {
|
|
r.tail = r.mod(r.tail + 1)
|
|
}
|
|
return v
|
|
}
|
|
|
|
/*
|
|
Read the value that Dequeue would have dequeued without actually dequeuing it.
|
|
|
|
Returns nil if the ring buffer is empty.
|
|
*/
|
|
func (r *Ring) Peek() interface{} {
|
|
r.Lock()
|
|
defer r.Unlock()
|
|
|
|
r.checkInit()
|
|
if r.head == -1 {
|
|
return nil
|
|
}
|
|
return r.get(r.tail)
|
|
}
|
|
|
|
/*
|
|
Values returns a slice of all the values in the circular buffer without modifying them at all.
|
|
The returned slice can be modified independently of the circular buffer. However, the values inside the slice
|
|
are shared between the slice and circular buffer.
|
|
*/
|
|
func (r *Ring) Values() []interface{} {
|
|
r.Lock()
|
|
defer r.Unlock()
|
|
|
|
if r.head == -1 {
|
|
return []interface{}{}
|
|
}
|
|
arr := make([]interface{}, 0, r.capacity())
|
|
for i := 0; i < r.capacity(); i++ {
|
|
idx := r.mod(i + r.tail)
|
|
arr = append(arr, r.get(idx))
|
|
if idx == r.head {
|
|
break
|
|
}
|
|
}
|
|
return arr
|
|
}
|
|
|
|
/**
|
|
*** Unexported methods beyond this point.
|
|
**/
|
|
|
|
func (r *Ring) capacity() int {
|
|
return len(r.buff)
|
|
}
|
|
|
|
// sets a value at the given unmodified index and returns the modified index of the value
|
|
func (r *Ring) set(p int, v interface{}) {
|
|
r.buff[r.mod(p)] = v
|
|
}
|
|
|
|
// gets a value based at a given unmodified index
|
|
func (r *Ring) get(p int) interface{} {
|
|
return r.buff[r.mod(p)]
|
|
}
|
|
|
|
// returns the modified index of an unmodified index
|
|
func (r *Ring) mod(p int) int {
|
|
v := p % len(r.buff)
|
|
for v < 0 { // this bit fixes negative indices
|
|
v += len(r.buff)
|
|
}
|
|
return v
|
|
}
|
|
|
|
func (r *Ring) checkInit() {
|
|
if r.buff != nil {
|
|
return
|
|
}
|
|
|
|
r.buff = make([]interface{}, DefaultCapacity)
|
|
for i := range r.buff {
|
|
r.buff[i] = nil
|
|
}
|
|
r.head, r.tail = -1, 0
|
|
}
|
|
|
|
func (r *Ring) extend(size int) {
|
|
if size == len(r.buff) {
|
|
return
|
|
}
|
|
|
|
if size < len(r.buff) {
|
|
// shrink the buffer
|
|
if r.head == -1 {
|
|
// nothing in the buffer, so just shrink it directly
|
|
r.buff = r.buff[0:size]
|
|
} else {
|
|
newb := make([]interface{}, 0, size)
|
|
// buffer has stuff in it, so save the most recent stuff...
|
|
// start at HEAD-SIZE-1 and walk forwards
|
|
for i := size - 1; i >= 0; i-- {
|
|
idx := r.mod(r.head - i)
|
|
newb = append(newb, r.buff[idx])
|
|
}
|
|
// reset head and tail to proper values
|
|
r.head = len(newb) - 1
|
|
r.tail = 0
|
|
r.buff = newb
|
|
}
|
|
return
|
|
}
|
|
|
|
// grow the buffer
|
|
newb := make([]interface{}, size-len(r.buff))
|
|
for i := range newb {
|
|
newb[i] = nil
|
|
}
|
|
if r.head == -1 {
|
|
// nothing in the buffer
|
|
r.buff = append(r.buff, newb...)
|
|
} else if r.head >= r.tail {
|
|
// growing at the end is safe
|
|
r.buff = append(r.buff, newb...)
|
|
} else {
|
|
// buffer has stuff that wraps around the end
|
|
// have to rearrange the buffer so the contents are still in order
|
|
part1 := make([]interface{}, len(r.buff[:r.head+1]))
|
|
copy(part1, r.buff[:r.head+1])
|
|
part2 := make([]interface{}, len(r.buff[r.tail:]))
|
|
copy(part2, r.buff[r.tail:])
|
|
r.buff = append(r.buff, newb...)
|
|
newTail := r.mod(r.tail + len(newb))
|
|
r.tail = newTail
|
|
copy(r.buff[:r.head+1], part1)
|
|
copy(r.buff[r.head+1:r.tail], newb)
|
|
copy(r.buff[r.tail:], part2)
|
|
|
|
}
|
|
}
|