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  }