mirror of
https://github.com/bloeys/nterm.git
synced 2025-12-29 14:38:19 +00:00
Finish ring buffer
This commit is contained in:
51
ring/ring.go
51
ring/ring.go
@ -1,6 +1,8 @@
|
|||||||
package ring
|
package ring
|
||||||
|
|
||||||
import "golang.org/x/exp/constraints"
|
import (
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
type Buffer[T any] struct {
|
type Buffer[T any] struct {
|
||||||
Data []T
|
Data []T
|
||||||
@ -9,13 +11,13 @@ type Buffer[T any] struct {
|
|||||||
Cap int64
|
Cap int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Buffer[T]) Append(x ...T) {
|
func (b *Buffer[T]) Write(x ...T) {
|
||||||
|
|
||||||
inLen := int64(len(x))
|
inLen := int64(len(x))
|
||||||
|
|
||||||
for len(x) > 0 {
|
for len(x) > 0 {
|
||||||
|
|
||||||
copied := copy(b.Data[b.Head():], x)
|
copied := copy(b.Data[b.WriteHead():], x)
|
||||||
x = x[copied:]
|
x = x[copied:]
|
||||||
|
|
||||||
if b.Len == b.Cap {
|
if b.Len == b.Cap {
|
||||||
@ -26,33 +28,54 @@ func (b *Buffer[T]) Append(x ...T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Buffer[T]) Head() int64 {
|
//WriteHead is the absolute position within the buffer where new writes will happen
|
||||||
|
func (b *Buffer[T]) WriteHead() int64 {
|
||||||
return (b.Start + b.Len) % b.Cap
|
return (b.Start + b.Len) % b.Cap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Clear resets Len and Start to zero but elements within Data aren't touched.
|
||||||
|
//This gives you empty Views and new writes/inserts will overwrite old data
|
||||||
|
func (b *Buffer[T]) Clear() {
|
||||||
|
b.Len = 0
|
||||||
|
b.Start = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer[T]) IsFull() bool {
|
||||||
|
return b.Len == b.Cap
|
||||||
|
}
|
||||||
|
|
||||||
|
//Insert inserts the given elements starting at the provided index.
|
||||||
|
//
|
||||||
|
//Note: Insert is a no-op if the buffer is full or doesn't have enough place for the elements
|
||||||
func (b *Buffer[T]) Insert(index uint64, x ...T) {
|
func (b *Buffer[T]) Insert(index uint64, x ...T) {
|
||||||
|
|
||||||
delta := int64(len(x))
|
delta := int64(len(x))
|
||||||
newLen := b.Len + delta
|
newLen := b.Len + delta
|
||||||
if newLen <= b.Cap {
|
if newLen > b.Cap {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
copy(b.Data[b.Start+int64(index)+delta:], b.Data[index:])
|
copy(b.Data[b.Start+int64(index)+delta:], b.Data[index:])
|
||||||
copy(b.Data[index:], x)
|
copy(b.Data[index:], x)
|
||||||
|
|
||||||
b.Len = newLen
|
b.Len = newLen
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Buffer[T]) Delete(delStartIndex, elementsToDel uint64, x ...T) {
|
//DeleteN removes 'n' elements starting at the provided index.
|
||||||
|
//
|
||||||
|
//Note DeleteN is a no-op if Len==0 or if buffer is full with start>0
|
||||||
|
func (b *Buffer[T]) DeleteN(delStartIndex, n uint64) {
|
||||||
|
|
||||||
if b.Len == 0 {
|
if b.Len == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(b.Data[uint64(b.Start)+delStartIndex:], b.Data[uint64(b.Start)+delStartIndex+elementsToDel:])
|
if b.Len == b.Cap && b.Start > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// p.cmdBufLen--
|
relStartIndex := b.Start + int64(delStartIndex)
|
||||||
|
copy(b.Data[relStartIndex:], b.Data[relStartIndex+int64(n):])
|
||||||
|
b.Len = clamp(b.Len-int64(n), 0, b.Cap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func clamp[T constraints.Ordered](x, min, max T) T {
|
func clamp[T constraints.Ordered](x, min, max T) T {
|
||||||
@ -69,17 +92,17 @@ func clamp[T constraints.Ordered](x, min, max T) T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Views returns two slices that have 'Len' elements in total between them.
|
// Views returns two slices that have 'Len' elements in total between them.
|
||||||
// The first slice is from Start till min(Start+Len, Cap). If Start+Len<=Cap then the first slice contains all the data and the second is nil.
|
// The first slice is from Start till min(Start+Len, Cap). If Start+Len<=Cap then the first slice contains all the data and the second is empty.
|
||||||
// If Start+Len>Cap then the first slice contains the data from Start till Cap, and the second slice contains data from Zero till Start+Len-Cap (basically the remaining elements to reach Len in total)
|
// If Start+Len>Cap then the first slice contains the data from Start till Cap, and the second slice contains data from Zero till Start+Len-Cap (basically the remaining elements to reach Len in total)
|
||||||
//
|
//
|
||||||
// This function does NOT copy. Any changes on the returned slices will reflect on the buffer Data
|
// This function does NOT copy. Any changes on the returned slices will reflect on the buffer Data
|
||||||
func (b *Buffer[T]) Views() (v1, v2 []T) {
|
func (b *Buffer[T]) Views() (v1, v2 []T) {
|
||||||
|
|
||||||
if b.Start+b.Len <= b.Cap {
|
if b.Start+b.Len <= b.Cap {
|
||||||
return b.Data[b.Start : b.Start+b.Len], nil
|
return b.Data[b.Start : b.Start+b.Len], []T{}
|
||||||
}
|
}
|
||||||
|
|
||||||
v1 = b.Data[b.Start:b.Cap]
|
v1 = b.Data[b.Start:]
|
||||||
v2 = b.Data[:b.Start+b.Len-b.Cap]
|
v2 = b.Data[:b.Start+b.Len-b.Cap]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ func TestRing(t *testing.T) {
|
|||||||
|
|
||||||
// Basics
|
// Basics
|
||||||
b := ring.NewBuffer[rune](4)
|
b := ring.NewBuffer[rune](4)
|
||||||
b.Append('a', 'b', 'c', 'd')
|
b.Write('a', 'b', 'c', 'd')
|
||||||
CheckArr(t, []rune{'a', 'b', 'c', 'd'}, b.Data)
|
CheckArr(t, []rune{'a', 'b', 'c', 'd'}, b.Data)
|
||||||
|
|
||||||
v1, v2 := b.Views()
|
v1, v2 := b.Views()
|
||||||
@ -19,7 +19,7 @@ func TestRing(t *testing.T) {
|
|||||||
Check(t, 0, b.Start)
|
Check(t, 0, b.Start)
|
||||||
Check(t, 4, b.Len)
|
Check(t, 4, b.Len)
|
||||||
|
|
||||||
b.Append('e', 'f')
|
b.Write('e', 'f')
|
||||||
Check(t, 2, b.Start)
|
Check(t, 2, b.Start)
|
||||||
CheckArr(t, []rune{'e', 'f', 'c', 'd'}, b.Data)
|
CheckArr(t, []rune{'e', 'f', 'c', 'd'}, b.Data)
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ func TestRing(t *testing.T) {
|
|||||||
CheckArr(t, []rune{'c', 'd'}, v1)
|
CheckArr(t, []rune{'c', 'd'}, v1)
|
||||||
CheckArr(t, []rune{'e', 'f'}, v2)
|
CheckArr(t, []rune{'e', 'f'}, v2)
|
||||||
|
|
||||||
b.Append('g')
|
b.Write('g')
|
||||||
Check(t, 3, b.Start)
|
Check(t, 3, b.Start)
|
||||||
|
|
||||||
v1, v2 = b.Views()
|
v1, v2 = b.Views()
|
||||||
@ -35,26 +35,58 @@ func TestRing(t *testing.T) {
|
|||||||
CheckArr(t, []rune{'d'}, v1)
|
CheckArr(t, []rune{'d'}, v1)
|
||||||
CheckArr(t, []rune{'e', 'f', 'g'}, v2)
|
CheckArr(t, []rune{'e', 'f', 'g'}, v2)
|
||||||
|
|
||||||
|
b = ring.NewBuffer[rune](4)
|
||||||
|
b.Write('a', 'b', 'c', 'd', 'e')
|
||||||
|
Check(t, 1, b.Start)
|
||||||
|
|
||||||
// Input over 2x bigger than buffer
|
// Input over 2x bigger than buffer
|
||||||
b = ring.NewBuffer[rune](4)
|
b = ring.NewBuffer[rune](4)
|
||||||
b.Append('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i')
|
b.Write('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i')
|
||||||
CheckArr(t, []rune{'i', 'f', 'g', 'h'}, b.Data)
|
CheckArr(t, []rune{'i', 'f', 'g', 'h'}, b.Data)
|
||||||
|
|
||||||
// Input starting in the middle and having to loop back
|
// Input starting in the middle and having to loop back
|
||||||
b2 := ring.NewBuffer[int](4)
|
b2 := ring.NewBuffer[int](4)
|
||||||
b2.Append(1, 2, 3)
|
b2.Write(1, 2, 3)
|
||||||
|
|
||||||
b2.Append(4, 5)
|
b2.Write(4, 5)
|
||||||
CheckArr(t, []int{5, 2, 3, 4}, b2.Data)
|
CheckArr(t, []int{5, 2, 3, 4}, b2.Data)
|
||||||
|
|
||||||
b2.Append(6)
|
b2.Write(6)
|
||||||
CheckArr(t, []int{5, 6, 3, 4}, b2.Data)
|
CheckArr(t, []int{5, 6, 3, 4}, b2.Data)
|
||||||
|
|
||||||
b2.Append(7)
|
b2.Write(7)
|
||||||
CheckArr(t, []int{5, 6, 7, 4}, b2.Data)
|
CheckArr(t, []int{5, 6, 7, 4}, b2.Data)
|
||||||
|
|
||||||
b2.Append(8)
|
b2.Write(8)
|
||||||
CheckArr(t, []int{5, 6, 7, 8}, b2.Data)
|
CheckArr(t, []int{5, 6, 7, 8}, b2.Data)
|
||||||
|
|
||||||
|
// Insert
|
||||||
|
b2 = ring.NewBuffer[int](4)
|
||||||
|
b2.Write(1, 2)
|
||||||
|
|
||||||
|
b2.Insert(0, 3)
|
||||||
|
CheckArr(t, []int{3, 1, 2, 0}, b2.Data)
|
||||||
|
|
||||||
|
b2.Insert(3, 4)
|
||||||
|
CheckArr(t, []int{3, 1, 2, 4}, b2.Data)
|
||||||
|
|
||||||
|
b2.Insert(2, 5, 6)
|
||||||
|
CheckArr(t, []int{3, 1, 2, 4}, b2.Data)
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
b2 = ring.NewBuffer[int](4)
|
||||||
|
b2.Write(1, 2, 3, 4)
|
||||||
|
|
||||||
|
b2.DeleteN(0, 4)
|
||||||
|
Check(t, 0, b2.Start)
|
||||||
|
Check(t, 0, b2.Len)
|
||||||
|
CheckArr(t, []int{1, 2, 3, 4}, b2.Data)
|
||||||
|
|
||||||
|
b2.Write(5, 6, 7, 8)
|
||||||
|
Check(t, 4, b2.Len)
|
||||||
|
b2.DeleteN(2, 1)
|
||||||
|
Check(t, 3, b2.Len)
|
||||||
|
CheckArr(t, []int{5, 6, 8, 8}, b2.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Check[T comparable](t *testing.T, expected, got T) {
|
func Check[T comparable](t *testing.T, expected, got T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user