github.com/m4gshm/gollections@v0.0.13-0.20240331203319-a34a86e58a24/slice/iter.go (about) 1 package slice 2 3 import ( 4 "unsafe" 5 6 "github.com/m4gshm/gollections/c" 7 "github.com/m4gshm/gollections/loop" 8 "github.com/m4gshm/gollections/notsafe" 9 ) 10 11 // IterNoStarted is the head Iterator position 12 const IterNoStarted = -1 13 14 // NewHead instantiates Iter based on elements slice 15 func NewHead[TS ~[]T, T any](elements TS) Iter[T] { 16 var ( 17 elementSize = notsafe.GetTypeSize[T]() 18 header = notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elements)) 19 array = unsafe.Pointer(header.Data) 20 size = header.Len 21 ) 22 return Iter[T]{ 23 array: array, 24 elementSize: elementSize, 25 size: size, 26 maxHasNext: size - 2, 27 current: IterNoStarted, 28 } 29 } 30 31 // NewTail instantiates Iter based on elements slice for reverse iterating 32 func NewTail[T any](elements []T) Iter[T] { 33 var ( 34 elementSize = notsafe.GetTypeSize[T]() 35 header = notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elements)) 36 array = unsafe.Pointer(header.Data) 37 size = header.Len 38 ) 39 return Iter[T]{ 40 array: array, 41 elementSize: elementSize, 42 size: size, 43 maxHasNext: size - 2, 44 current: size, 45 } 46 } 47 48 // Iter is the Iterator implementation. 49 type Iter[T any] struct { 50 array unsafe.Pointer 51 elementSize uintptr 52 size, maxHasNext, current int 53 } 54 55 var ( 56 _ c.Iterator[any] = (*Iter[any])(nil) 57 _ c.PrevIterator[any] = (*Iter[any])(nil) 58 ) 59 60 // All is used to iterate through the iterator using `for ... range`. Supported since go 1.22 with GOEXPERIMENT=rangefunc enabled. 61 func (i *Iter[T]) All(consumer func(element T) bool) { 62 loop.All(i.Next, consumer) 63 } 64 65 // For takes elements retrieved by the iterator. Can be interrupt by returning Break 66 func (i *Iter[T]) For(consumer func(element T) error) error { 67 return loop.For(i.Next, consumer) 68 } 69 70 // ForEach takes all elements retrieved by the iterator. 71 func (i *Iter[T]) ForEach(consumer func(element T)) { 72 loop.ForEach(i.Next, consumer) 73 } 74 75 // HasNext checks the next element existing 76 func (i *Iter[T]) HasNext() bool { 77 if i == nil { 78 return false 79 } 80 return CanIterateByRange(IterNoStarted, i.maxHasNext, i.current) 81 } 82 83 // HasPrev checks the previous element existing 84 func (i *Iter[T]) HasPrev() bool { 85 if i == nil { 86 return false 87 } 88 return CanIterateByRange(1, i.size, i.current) 89 } 90 91 // GetNext returns the next element 92 func (i *Iter[T]) GetNext() T { 93 t, _ := i.Next() 94 return t 95 } 96 97 // GetPrev returns the previous element 98 func (i *Iter[T]) GetPrev() T { 99 t, _ := i.Prev() 100 return t 101 } 102 103 // Next returns the next element. 104 // The ok result indicates whether the element was returned by the iterator. 105 // If ok == false, then the iteration must be completed. 106 func (i *Iter[T]) Next() (v T, ok bool) { 107 if !(i == nil || i.array == nil) { 108 if current := i.current; CanIterateByRange(IterNoStarted, i.maxHasNext, current) { 109 current++ 110 i.current = current 111 return *(*T)(notsafe.GetArrayElemRef(i.array, current, i.elementSize)), true 112 } 113 } 114 return v, ok 115 } 116 117 // Prev returns the previos element. 118 // The ok result indicates whether the element was returned by the iterator. 119 // If ok == false, then the iteration must be completed. 120 func (i *Iter[T]) Prev() (v T, ok bool) { 121 if !(i == nil || i.array == nil) { 122 current := i.current 123 if CanIterateByRange(1, i.size, current) { 124 current-- 125 i.current = current 126 return *(*T)(notsafe.GetArrayElemRef(i.array, current, i.elementSize)), true 127 } 128 } 129 return v, ok 130 } 131 132 // Get returns the current element. 133 // The ok result indicates whether the element was returned by the iterator. 134 // If ok == false, then the iteration must be completed. 135 func (i *Iter[T]) Get() (v T, ok bool) { 136 if !(i == nil || i.array == nil) { 137 current := i.current 138 if IsValidIndex(i.size, current) { 139 return *(*T)(notsafe.GetArrayElemRef(i.array, current, i.elementSize)), true 140 } 141 } 142 return v, ok 143 } 144 145 // Size returns the iterator capacity 146 func (i *Iter[T]) Size() int { 147 if i == nil { 148 return 0 149 } 150 return i.size 151 } 152 153 // Crank rertieves a next element, returns the iterator, element and successfully flag. 154 func (i *Iter[T]) Crank() (it *Iter[T], t T, ok bool) { 155 if i != nil { 156 t, ok = i.Next() 157 } 158 return i, t, ok 159 } 160 161 // CrankPrev rertieves a prev element, returns the iterator, element and successfully flag. 162 func (i *Iter[T]) CrankPrev() (it *Iter[T], t T, ok bool) { 163 if i != nil { 164 t, ok = i.Prev() 165 } 166 return i, t, ok 167 } 168 169 // HasNext checks if an iterator can go forward 170 func HasNext[T any](elements []T, current int) bool { 171 return HasNextBySize(notsafe.GetLen(elements), current) 172 } 173 174 // HasPrev checks if an iterator can go backwards 175 func HasPrev[T any](elements []T, current int) bool { 176 return HasPrevBySize(notsafe.GetLen(elements), current) 177 } 178 179 // HasNextBySize checks if an iterator can go forward 180 func HasNextBySize(size int, current int) bool { 181 return CanIterateByRange(IterNoStarted, size-2, current) 182 } 183 184 // HasPrevBySize checks if an iterator can go backwards 185 func HasPrevBySize(size, current int) bool { 186 return CanIterateByRange(1, size, current) 187 } 188 189 // CanIterateByRange checks if an iterator can go further or stop 190 func CanIterateByRange(first, last, current int) bool { 191 return current >= first && current <= last 192 } 193 194 // IsValidIndex checks if index is out of range 195 func IsValidIndex(size, index int) bool { 196 return index > -1 && index < size 197 } 198 199 // Get safely returns an element of the 'elements' slice by the 'current' index or return zero value of T if the index is more than size-1 or less 0 200 func Get[TS ~[]T, T any](elements TS, current int) T { 201 v, _ := Gett(elements, current) 202 return v 203 } 204 205 // Gett safely returns an element of the 'elements' slice by the 'current' index or return zero value of T if the index is more than size-1 or less 0 206 // ok == true if success 207 func Gett[TS ~[]T, T any](elements TS, current int) (element T, ok bool) { 208 if !(current < 0 || current >= len(elements)) { 209 element, ok = (elements)[current], true 210 } 211 return element, ok 212 }