github.com/haraldrudell/parl@v0.4.176/iters/integer-iterator.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package iters 7 8 import ( 9 "sync" 10 "sync/atomic" 11 12 "github.com/haraldrudell/parl/internal/cyclebreaker" 13 "golang.org/x/exp/constraints" 14 ) 15 16 type IntegerIterator[T constraints.Integer] struct { 17 lastValue, delta T 18 // isEnd is fast outside-lock check for no values available 19 isEnd atomic.Bool 20 21 lock sync.Mutex 22 // - behind lock 23 didReturnAValue bool 24 // - behind lock 25 value T 26 } 27 28 func NewIntegerIterator[T constraints.Integer](firstValue, lastValue T) (iterator Iterator[T]) { 29 i := &IntegerIterator[T]{value: firstValue, lastValue: lastValue} 30 if firstValue > lastValue { 31 var i64 = int64(-1) 32 i.delta = T(i64) 33 } else { 34 i.delta = T(1) 35 } 36 return i 37 } 38 39 // Init implements the right-hand side of a short variable declaration in 40 // the init statement of a Go “for” clause 41 // 42 // for i, iterator := iters.NewSlicePointerIterator(someSlice).Init(); iterator.Cond(&i); { 43 // // i is pointer to slice element 44 func (i *IntegerIterator[T]) Init() (iterationVariable T, iterator Iterator[T]) { iterator = i; return } 45 46 // Cond implements the condition statement of a Go “for” clause 47 // - condition is true if iterationVariable was assigned a value and the iteration should continue 48 // - the iterationVariable is updated by being provided as a pointer. 49 // iterationVariable cannot be nil 50 // - errp is an optional error pointer receiving any errors during iterator execution 51 // 52 // Usage: 53 // 54 // for i, iterator := iters.NewSlicePointerIterator(someSlice).Init(); iterator.Cond(&i); { 55 // // i is pointer to slice element 56 func (i *IntegerIterator[T]) Cond(iterationVariablep *T, errp ...*error) (condition bool) { 57 if iterationVariablep == nil { 58 cyclebreaker.NilError("iterationVariablep") 59 } 60 61 // check for next value 62 var value T 63 if value, condition = i.nextSame(IsNext); condition { 64 *iterationVariablep = value 65 } 66 67 return // condition and iterationVariablep updated, errp unchanged 68 } 69 70 // Next advances to next item and returns it 71 // - if hasValue true, value contains the next value 72 // - otherwise, no more items exist and value is the data type zero-value 73 func (i *IntegerIterator[T]) Next() (value T, hasValue bool) { return i.nextSame(IsNext) } 74 75 // Same returns the same value again 76 // - if hasValue true, value is valid 77 // - otherwise, no more items exist and value is the data type zero-value 78 // - If Next or Cond has not been invoked, Same first advances to the first item 79 func (i *IntegerIterator[T]) Same() (value T, hasValue bool) { return i.nextSame(IsSame) } 80 81 // Cancel stops an iteration 82 // - after Cancel invocation, Cond, Next and Same indicate no value available 83 // - Cancel returns the first error that occurred during iteration, if any 84 // - an iterator implementation may require Cancel invocation 85 // to release resources 86 // - Cancel is deferrable 87 func (i *IntegerIterator[T]) Cancel(errp ...*error) (err error) { 88 if i.isEnd.Load() { 89 return // already canceled 90 } 91 i.isEnd.CompareAndSwap(false, true) 92 93 return 94 } 95 96 // nextSame finds the next or the same value. Thread-safe 97 // - isSame == IsSame means first or same value should be returned 98 // - value is the sought value or the T type’s zero-value if no value exists 99 // - hasValue true means value was assigned a valid T value 100 func (i *IntegerIterator[T]) nextSame(isSame NextAction) (value T, hasValue bool) { 101 102 // outside lock check 103 if i.isEnd.Load() { 104 return // no more values return 105 } 106 i.lock.Lock() 107 defer i.lock.Unlock() 108 109 // inside lock check 110 if i.isEnd.Load() { 111 return // no more values return 112 } 113 114 // note that first value has been sought 115 if !i.didReturnAValue { 116 i.didReturnAValue = true 117 } else if isSame == IsNext { 118 if i.value == i.lastValue { 119 if !i.isEnd.Load() { 120 i.isEnd.CompareAndSwap(false, true) 121 } 122 return 123 } 124 i.value += i.delta 125 } 126 127 value = i.value 128 hasValue = true 129 130 return // value and hasValue valid 131 }