github.com/haraldrudell/parl@v0.4.176/iters/slice.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 // Package iters provides thread-safe iterators usable with Go’s for statement 7 package iters 8 9 import ( 10 "sync/atomic" 11 12 "github.com/haraldrudell/parl/internal/cyclebreaker" 13 ) 14 15 // Slice traverses a slice container. thread-safe 16 type Slice[T any] struct { 17 slice []T // the slice providing values 18 // index is next slice-index to return 19 // - if index == len(slice) there are no more values 20 index atomic.Uint64 21 } 22 23 // NewSliceIterator returns an iterator iterating over slice T values 24 // - thread-safe 25 // - uses non-pointer atomics 26 func NewSliceIterator[T any](slice []T) (iterator Iterator[T]) { return &Slice[T]{slice: slice} } 27 28 // Init implements the right-hand side of a short variable declaration in 29 // the init statement for a Go “for” clause 30 // 31 // Usage: 32 // 33 // for i, iterator := NewSlicePointerIterator(someSlice).Init(); iterator.Cond(&i); { 34 // // i is pointer to slice element 35 func (i *Slice[T]) Init() (iterationVariable T, iterator Iterator[T]) { 36 iterator = i 37 return 38 } 39 40 // Cond implements the condition statement of a Go “for” clause 41 // - the iterationVariable is updated by being provided as a pointer. 42 // iterationVariable cannot be nil 43 // - errp is an optional error pointer receiving any errors during iterator execution 44 // - condition is true if iterationVariable was assigned a value and the iteration should continue 45 // 46 // Usage: 47 // 48 // for i, iterator := NewSlicePointerIterator(someSlice).Init(); iterator.Cond(&i); { 49 // // i is pointer to slice element 50 func (i *Slice[T]) Cond(iterationVariablep *T, errp ...*error) (condition bool) { 51 if iterationVariablep == nil { 52 cyclebreaker.NilError("iterationVariablep") 53 } 54 55 // check for next value 56 var value T 57 if value, condition = i.Next(); condition { 58 *iterationVariablep = value 59 } 60 61 return // condition and iterationVariablep updated, errp unchanged 62 } 63 64 // Next advances to next item and returns it 65 // - if hasValue true, value contains the next value 66 // - otherwise, no more items exist and value is the data type zero-value 67 func (i *Slice[T]) Next() (value T, hasValue bool) { 68 for { 69 var index = i.index.Load() 70 if int(index) == len(i.slice) { 71 return // no more values 72 } 73 if hasValue = i.index.CompareAndSwap(index, index+1); hasValue { 74 value = i.slice[index] 75 return 76 } 77 } 78 } 79 80 // Cancel release resources for this iterator. Thread-safe 81 // - not every iterator requires a Cancel invocation 82 func (i *Slice[T]) Cancel(errp ...*error) (err error) { 83 for { 84 var index = i.index.Load() 85 if int(index) == len(i.slice) { 86 return // no more values: already canceled 87 } 88 if i.index.CompareAndSwap(index, uint64(len(i.slice))) { 89 return // cancel complete 90 } 91 } 92 }