From b4f06bda54ad89558dee4fffbf5c9ea45c0b47e8 Mon Sep 17 00:00:00 2001 From: bloeys Date: Tue, 19 Jul 2022 21:21:54 +0400 Subject: [PATCH] Ring buffer iterator --- main.go | 2 + ring/ring.go | 120 ++++++++++++++++++++++++++++++++++++++++++++++ ring/ring_test.go | 89 ++++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+) diff --git a/main.go b/main.go index 07ac9bb..1ee16e4 100755 --- a/main.go +++ b/main.go @@ -284,6 +284,8 @@ func (p *program) MainUpdate() { var newPosNewLines int64 w, _ := p.GridSize() 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)) } else { newPosNewLines, _ = find_n_lines_index(p.textBuf.Data, p.scrollPos, p.scrollSpd*mouseWheelYNorm, int64(w)) diff --git a/ring/ring.go b/ring/ring.go index 793aa46..4daf254 100755 --- a/ring/ring.go +++ b/ring/ring.go @@ -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) // // 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) { if b.Start+b.Len <= b.Cap { @@ -107,6 +109,17 @@ func (b *Buffer[T]) Views() (v1, v2 []T) { 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] { return &Buffer[T]{ @@ -116,3 +129,110 @@ func NewBuffer[T any](capacity uint64) *Buffer[T] { 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 +} diff --git a/ring/ring_test.go b/ring/ring_test.go index aec305e..5351091 100755 --- a/ring/ring_test.go +++ b/ring/ring_test.go @@ -89,6 +89,95 @@ func TestRing(t *testing.T) { 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) { if got != expected { t.Fatalf("Expected %v but got %v\n", expected, got)