2019-02-13 19:09:26 -05:00
|
|
|
package chart
|
2017-05-12 20:12:23 -04:00
|
|
|
|
|
|
|
import (
|
2022-01-10 18:52:32 -05:00
|
|
|
"constraints"
|
2017-05-12 20:12:23 -04:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
bufferMinimumGrow = 4
|
|
|
|
bufferShrinkThreshold = 32
|
|
|
|
bufferGrowFactor = 200
|
|
|
|
bufferDefaultCapacity = 4
|
|
|
|
)
|
|
|
|
|
2019-02-13 19:09:26 -05:00
|
|
|
// NewValueBuffer creates a new value buffer with an optional set of values.
|
2022-01-10 18:52:32 -05:00
|
|
|
func NewValueBuffer[A constraints.Ordered](values ...A) *ValueBuffer[A] {
|
2017-05-12 20:12:23 -04:00
|
|
|
var tail int
|
2022-01-10 18:52:32 -05:00
|
|
|
array := make([]A, Max(len(values), bufferDefaultCapacity))
|
2017-05-12 20:12:23 -04:00
|
|
|
if len(values) > 0 {
|
|
|
|
copy(array, values)
|
|
|
|
tail = len(values)
|
|
|
|
}
|
2022-01-10 18:52:32 -05:00
|
|
|
return &ValueBuffer[A]{
|
2017-05-12 20:12:23 -04:00
|
|
|
array: array,
|
|
|
|
head: 0,
|
|
|
|
tail: tail,
|
|
|
|
size: len(values),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-13 19:09:26 -05:00
|
|
|
// NewValueBufferWithCapacity creates a new ValueBuffer pre-allocated with the given capacity.
|
2022-01-10 18:52:32 -05:00
|
|
|
func NewValueBufferWithCapacity[A any](capacity int) *ValueBuffer[A] {
|
|
|
|
return &ValueBuffer[A]{
|
|
|
|
array: make([]A, capacity),
|
2017-05-12 20:12:23 -04:00
|
|
|
head: 0,
|
|
|
|
tail: 0,
|
|
|
|
size: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-10 18:52:32 -05:00
|
|
|
// ValueBuffer is a fifo data structure that is backed by a pre-allocated array.
|
2017-05-12 20:12:23 -04:00
|
|
|
// Instead of allocating a whole new node object for each element, array elements are re-used (which saves GC churn).
|
|
|
|
// Enqueue can be O(n), Dequeue is generally O(1).
|
|
|
|
// Buffer implements `seq.Provider`
|
2022-01-10 18:52:32 -05:00
|
|
|
type ValueBuffer[A any] struct {
|
|
|
|
array []A
|
2017-05-12 20:12:23 -04:00
|
|
|
head int
|
|
|
|
tail int
|
|
|
|
size int
|
|
|
|
}
|
|
|
|
|
|
|
|
// Len returns the length of the Buffer (as it is currently populated).
|
|
|
|
// Actual memory footprint may be different.
|
2022-01-10 18:52:32 -05:00
|
|
|
func (b *ValueBuffer[A]) Len() int {
|
2017-05-12 20:12:23 -04:00
|
|
|
return b.size
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetValue implements seq provider.
|
2022-01-10 18:52:32 -05:00
|
|
|
func (b *ValueBuffer[A]) GetValue(index int) A {
|
2017-05-12 20:12:23 -04:00
|
|
|
effectiveIndex := (b.head + index) % len(b.array)
|
|
|
|
return b.array[effectiveIndex]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Capacity returns the total size of the Buffer, including empty elements.
|
2022-01-10 18:52:32 -05:00
|
|
|
func (b *ValueBuffer[A]) Capacity() int {
|
2017-05-12 20:12:23 -04:00
|
|
|
return len(b.array)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetCapacity sets the capacity of the Buffer.
|
2022-01-10 18:52:32 -05:00
|
|
|
func (b *ValueBuffer[A]) SetCapacity(capacity int) {
|
|
|
|
newArray := make([]A, capacity)
|
2017-05-12 20:12:23 -04:00
|
|
|
if b.size > 0 {
|
|
|
|
if b.head < b.tail {
|
|
|
|
arrayCopy(b.array, b.head, newArray, 0, b.size)
|
|
|
|
} else {
|
|
|
|
arrayCopy(b.array, b.head, newArray, 0, len(b.array)-b.head)
|
|
|
|
arrayCopy(b.array, 0, newArray, len(b.array)-b.head, b.tail)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b.array = newArray
|
|
|
|
b.head = 0
|
|
|
|
if b.size == capacity {
|
|
|
|
b.tail = 0
|
|
|
|
} else {
|
|
|
|
b.tail = b.size
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear removes all objects from the Buffer.
|
2022-01-10 18:52:32 -05:00
|
|
|
func (b *ValueBuffer[A]) Clear() {
|
|
|
|
b.array = make([]A, bufferDefaultCapacity)
|
2017-05-12 20:12:23 -04:00
|
|
|
b.head = 0
|
|
|
|
b.tail = 0
|
|
|
|
b.size = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enqueue adds an element to the "back" of the Buffer.
|
2022-01-10 18:52:32 -05:00
|
|
|
func (b *ValueBuffer[A]) Enqueue(value A) {
|
2017-05-12 20:12:23 -04:00
|
|
|
if b.size == len(b.array) {
|
|
|
|
newCapacity := int(len(b.array) * int(bufferGrowFactor/100))
|
|
|
|
if newCapacity < (len(b.array) + bufferMinimumGrow) {
|
|
|
|
newCapacity = len(b.array) + bufferMinimumGrow
|
|
|
|
}
|
|
|
|
b.SetCapacity(newCapacity)
|
|
|
|
}
|
|
|
|
|
|
|
|
b.array[b.tail] = value
|
|
|
|
b.tail = (b.tail + 1) % len(b.array)
|
|
|
|
b.size++
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dequeue removes the first element from the RingBuffer.
|
2022-01-10 18:52:32 -05:00
|
|
|
func (b *ValueBuffer[A]) Dequeue() (output A) {
|
2017-05-12 20:12:23 -04:00
|
|
|
if b.size == 0 {
|
2022-01-10 18:52:32 -05:00
|
|
|
return
|
2017-05-12 20:12:23 -04:00
|
|
|
}
|
|
|
|
|
2022-01-10 18:52:32 -05:00
|
|
|
output = b.array[b.head]
|
2017-05-12 20:12:23 -04:00
|
|
|
b.head = (b.head + 1) % len(b.array)
|
|
|
|
b.size--
|
2022-01-10 18:52:32 -05:00
|
|
|
return
|
2017-05-12 20:12:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Peek returns but does not remove the first element.
|
2022-01-10 18:52:32 -05:00
|
|
|
func (b *ValueBuffer[A]) Peek() (output A) {
|
2017-05-12 20:12:23 -04:00
|
|
|
if b.size == 0 {
|
2022-01-10 18:52:32 -05:00
|
|
|
return
|
2017-05-12 20:12:23 -04:00
|
|
|
}
|
2022-01-10 18:52:32 -05:00
|
|
|
output = b.array[b.head]
|
|
|
|
return
|
2017-05-12 20:12:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// PeekBack returns but does not remove the last element.
|
2022-01-10 18:52:32 -05:00
|
|
|
func (b *ValueBuffer[A]) PeekBack() (output A) {
|
2017-05-12 20:12:23 -04:00
|
|
|
if b.size == 0 {
|
2022-01-10 18:52:32 -05:00
|
|
|
return
|
2017-05-12 20:12:23 -04:00
|
|
|
}
|
|
|
|
if b.tail == 0 {
|
2022-01-10 18:52:32 -05:00
|
|
|
output = b.array[len(b.array)-1]
|
|
|
|
return
|
2017-05-12 20:12:23 -04:00
|
|
|
}
|
2022-01-10 18:52:32 -05:00
|
|
|
output = b.array[b.tail-1]
|
|
|
|
return
|
2017-05-12 20:12:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// TrimExcess resizes the capacity of the buffer to better fit the contents.
|
2022-01-10 18:52:32 -05:00
|
|
|
func (b *ValueBuffer[A]) TrimExcess() {
|
2017-05-12 20:12:23 -04:00
|
|
|
threshold := float64(len(b.array)) * 0.9
|
|
|
|
if b.size < int(threshold) {
|
|
|
|
b.SetCapacity(b.size)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Array returns the ring buffer, in order, as an array.
|
2022-01-10 18:52:32 -05:00
|
|
|
func (b *ValueBuffer[A]) Array() []A {
|
|
|
|
newArray := make([]A, b.size)
|
2017-05-12 20:12:23 -04:00
|
|
|
|
|
|
|
if b.size == 0 {
|
|
|
|
return newArray
|
|
|
|
}
|
|
|
|
|
|
|
|
if b.head < b.tail {
|
|
|
|
arrayCopy(b.array, b.head, newArray, 0, b.size)
|
|
|
|
} else {
|
|
|
|
arrayCopy(b.array, b.head, newArray, 0, len(b.array)-b.head)
|
|
|
|
arrayCopy(b.array, 0, newArray, len(b.array)-b.head, b.tail)
|
|
|
|
}
|
|
|
|
|
2022-01-10 18:52:32 -05:00
|
|
|
return newArray
|
2017-05-12 20:12:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Each calls the consumer for each element in the buffer.
|
2022-01-10 18:52:32 -05:00
|
|
|
func (b *ValueBuffer[A]) Each(mapfn func(int, A)) {
|
2017-05-12 20:12:23 -04:00
|
|
|
if b.size == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var index int
|
|
|
|
if b.head < b.tail {
|
|
|
|
for cursor := b.head; cursor < b.tail; cursor++ {
|
|
|
|
mapfn(index, b.array[cursor])
|
|
|
|
index++
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for cursor := b.head; cursor < len(b.array); cursor++ {
|
|
|
|
mapfn(index, b.array[cursor])
|
|
|
|
index++
|
|
|
|
}
|
|
|
|
for cursor := 0; cursor < b.tail; cursor++ {
|
|
|
|
mapfn(index, b.array[cursor])
|
|
|
|
index++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// String returns a string representation for value buffers.
|
2022-01-10 18:52:32 -05:00
|
|
|
func (b *ValueBuffer[A]) String() string {
|
2017-05-12 20:12:23 -04:00
|
|
|
var values []string
|
|
|
|
for _, elem := range b.Array() {
|
2022-01-10 18:52:32 -05:00
|
|
|
values = append(values, fmt.Sprint(elem))
|
2017-05-12 20:12:23 -04:00
|
|
|
}
|
|
|
|
return strings.Join(values, " <= ")
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
// Util methods
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
|
2022-01-10 18:52:32 -05:00
|
|
|
func arrayClear[A any](source []A, index, length int) {
|
|
|
|
var zero A
|
2017-05-12 20:12:23 -04:00
|
|
|
for x := 0; x < length; x++ {
|
|
|
|
absoluteIndex := x + index
|
2022-01-10 18:52:32 -05:00
|
|
|
source[absoluteIndex] = zero
|
2017-05-12 20:12:23 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-10 18:52:32 -05:00
|
|
|
func arrayCopy[A any](source []A, sourceIndex int, destination []A, destinationIndex, length int) {
|
2017-05-12 20:12:23 -04:00
|
|
|
for x := 0; x < length; x++ {
|
|
|
|
from := sourceIndex + x
|
|
|
|
to := destinationIndex + x
|
|
|
|
|
|
|
|
destination[to] = source[from]
|
|
|
|
}
|
|
|
|
}
|