diff --git a/ring.go b/ring.go index fe44724..97c854f 100644 --- a/ring.go +++ b/ring.go @@ -149,3 +149,21 @@ func (r *Ring[T]) Reset() { r.right = r.elements[:0] clear(r.elements) } + +// Scan calls the given function for each element in the ring, in order. +// If the function returns true, then the value and index of the element are returned. +// If no match is found, then returns the zero value of T and -1. +func (r *Ring[T]) Scan(fn func(T) bool) (T, int) { + for i, e := range r.right { + if fn(e) { + return e, i + } + } + for i, e := range r.left { + if fn(e) { + return e, i + len(r.right) + } + } + var zero T + return zero, -1 +} diff --git a/ring_test.go b/ring_test.go index 230a16d..d72db9f 100644 --- a/ring_test.go +++ b/ring_test.go @@ -123,6 +123,25 @@ func TestRingIndex_Wrap(t *testing.T) { require.Equal(t, 6, el) } +func TestRingScan(t *testing.T) { + r := collections.NewRing[int](5) + for i := 0; i < 4; i++ { + r.PushBack(i) + } + for i := 5; i < 100; i++ { + check := i % 4 + value, ok := r.PeekIndex(check) + require.True(t, ok) + found, idx := r.Scan(func(v int) bool { + return v == value + }) + require.Equal(t, value, found) + require.Equal(t, check, idx) + r.PopFront() + r.PushBack(i) + } +} + func BenchmarkRing(b *testing.B) { r := collections.NewRing[int](1024) // fill the ring @@ -201,6 +220,7 @@ const ( popFront popIndex peekIndex + scan lastOpForCounting // keep last ) @@ -273,6 +293,23 @@ func FuzzRing(f *testing.F) { if f1 != r1 || ok1 != ok2 { t.Fatalf("peekIndex differs: %v vs %v in %v vs %v", f1, r1, fake, real) } + case scan: + var idx int + if i+1 < len(ops) { + idx = int(ops[i+1]) + i++ + } + t.Logf("scan %d", idx) + scanNum := 0 + v, loc := real.Scan(func(v int) bool { + o := scanNum + scanNum++ + return o == idx + }) + v2, ok2 := real.PeekIndex(idx) + if ok2 && (loc != idx || v != v2) { + t.Fatalf("scan differs: %v vs %v in %v", v, v2, real) + } } if fake.Copy(buf1[:]) != real.Copy(buf2[:]) { t.Fatalf("copy differs")