Ring buffer iterator

This commit is contained in:
bloeys
2022-07-19 21:21:54 +04:00
parent 0fdebbeafe
commit b4f06bda54
3 changed files with 211 additions and 0 deletions

View File

@ -284,6 +284,8 @@ func (p *program) MainUpdate() {
var newPosNewLines int64 var newPosNewLines int64
w, _ := p.GridSize() w, _ := p.GridSize()
if mouseWheelYNorm < 0 { if mouseWheelYNorm < 0 {
// @TODO This is wrong because it deals with data as a normal array while its a ring buffer
newPosNewLines, _ = find_n_lines_index(p.textBuf.Data, p.scrollPos, p.scrollSpd*mouseWheelYNorm-1, int64(w)) newPosNewLines, _ = find_n_lines_index(p.textBuf.Data, p.scrollPos, p.scrollSpd*mouseWheelYNorm-1, int64(w))
} else { } else {
newPosNewLines, _ = find_n_lines_index(p.textBuf.Data, p.scrollPos, p.scrollSpd*mouseWheelYNorm, int64(w)) newPosNewLines, _ = find_n_lines_index(p.textBuf.Data, p.scrollPos, p.scrollSpd*mouseWheelYNorm, int64(w))

View File

@ -96,6 +96,8 @@ func clamp[T constraints.Ordered](x, min, max T) T {
// 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
//
// Note: Views become invalid when a write/insert is done on the buffer
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 {
@ -107,6 +109,17 @@ func (b *Buffer[T]) Views() (v1, v2 []T) {
return return
} }
func (b *Buffer[T]) Iterator() Iterator[T] {
v1, v2 := b.Views()
return Iterator[T]{
V1: v1,
V2: v2,
Curr: 0,
InV1: true,
}
}
func NewBuffer[T any](capacity uint64) *Buffer[T] { func NewBuffer[T any](capacity uint64) *Buffer[T] {
return &Buffer[T]{ return &Buffer[T]{
@ -116,3 +129,110 @@ func NewBuffer[T any](capacity uint64) *Buffer[T] {
Cap: int64(capacity), Cap: int64(capacity),
} }
} }
// Iterator provides a way of iterating and indexing values of a ring buffer as if it was a flat array
// without having to deal with wrapping and so on.
//
// Indices used are all relative to 'Buffer.Start'
type Iterator[T any] struct {
V1 []T
V2 []T
// Curr is the index of the element that will be returned on Next()
Curr int64
InV1 bool
}
// Next returns the value at Iterator.Curr and done=false
//
// If there are no more values to return the default value is returned for v and done=true
func (it *Iterator[T]) Next() (v T, done bool) {
if it.InV1 {
v = it.V1[it.Curr]
it.Curr++
if it.Curr >= int64(len(it.V1)) {
it.Curr = 0
it.InV1 = false
}
return v, false
}
if it.Curr >= int64(len(it.V2)) {
return v, true
}
v = it.V2[it.Curr]
it.Curr++
return v, false
}
// Next returns the value at Iterator.Curr-1 and done=false
//
// If there are no more values to return the default value is returned for v and done=true
func (it *Iterator[T]) Prev() (v T, done bool) {
if it.InV1 {
if it.Curr <= 0 {
return v, true
}
it.Curr--
v = it.V1[it.Curr]
return v, false
}
it.Curr--
if it.Curr < 0 {
it.InV1 = true
it.Curr = int64(len(it.V1))
return it.Prev()
}
v = it.V2[it.Curr]
return v, false
}
// GotoStart adjusts the iterator such that the following Next() call returns the value at index=0
// and the next Prev() call returns done=true
func (it *Iterator[T]) GotoStart() {
it.Curr = 0
it.InV1 = true
}
// GotoIndex goes to the index n relative to Buffer.Start
func (it *Iterator[T]) GotoIndex(n int64) {
if n <= 0 {
it.GotoStart()
return
}
v1Len := int64(len(it.V1))
if n < v1Len {
it.Curr = n
it.InV1 = true
return
}
n -= v1Len
if n < int64(len(it.V2)) {
it.Curr = n
it.InV1 = false
return
}
it.GotoEnd()
}
// GotoEnd adjusts the iterator such that the following Prev() call returns the value at index=Len-1
// and the following Next() call returns done=true
func (it *Iterator[T]) GotoEnd() {
it.Curr = int64(len(it.V2))
it.InV1 = false
}

View File

@ -89,6 +89,95 @@ func TestRing(t *testing.T) {
CheckArr(t, []int{5, 6, 8, 8}, b2.Data) CheckArr(t, []int{5, 6, 8, 8}, b2.Data)
} }
func TestIterator(t *testing.T) {
// Only v1 set
b := ring.NewBuffer[int](4)
b.Write(1, 2)
got := []int{}
ans := []int{1, 2}
it := b.Iterator()
for v, done := it.Next(); !done; v, done = it.Next() {
got = append(got, v)
}
CheckArr(t, ans, got)
got = []int{}
ans = []int{2, 1}
for v, done := it.Prev(); !done; v, done = it.Prev() {
got = append(got, v)
}
CheckArr(t, ans, got)
got = []int{}
ans = []int{1, 2}
it.GotoStart()
for v, done := it.Next(); !done; v, done = it.Next() {
got = append(got, v)
}
CheckArr(t, ans, got)
// V1 and v2 set
b.Write(3, 4, 5, 6)
got = []int{}
ans = []int{3, 4, 5, 6}
it = b.Iterator()
for v, done := it.Next(); !done; v, done = it.Next() {
got = append(got, v)
}
CheckArr(t, ans, got)
it.GotoEnd()
got = []int{}
ans = []int{6, 5, 4, 3}
for v, done := it.Prev(); !done; v, done = it.Prev() {
got = append(got, v)
}
CheckArr(t, ans, got)
// GotoIndex
got = []int{}
ans = []int{5, 6}
it.GotoIndex(2)
for v, done := it.Next(); !done; v, done = it.Next() {
got = append(got, v)
}
CheckArr(t, ans, got)
got = []int{}
ans = []int{4, 3}
it.GotoIndex(2)
for v, done := it.Prev(); !done; v, done = it.Prev() {
got = append(got, v)
}
CheckArr(t, ans, got)
got = []int{}
ans = []int{}
it.GotoIndex(-100)
for v, done := it.Prev(); !done; v, done = it.Prev() {
got = append(got, v)
}
CheckArr(t, ans, got)
got = []int{}
ans = []int{6, 5, 4, 3}
it.GotoIndex(100)
for v, done := it.Prev(); !done; v, done = it.Prev() {
got = append(got, v)
}
CheckArr(t, ans, got)
got = []int{}
ans = []int{}
it.GotoIndex(100)
for v, done := it.Next(); !done; v, done = it.Next() {
got = append(got, v)
}
CheckArr(t, ans, got)
}
func Check[T comparable](t *testing.T, expected, got T) { func Check[T comparable](t *testing.T, expected, got T) {
if got != expected { if got != expected {
t.Fatalf("Expected %v but got %v\n", expected, got) t.Fatalf("Expected %v but got %v\n", expected, got)