github.com/m4gshm/gollections@v0.0.10/break/loop/flatt.go (about)

     1  package loop
     2  
     3  import (
     4  	"unsafe"
     5  
     6  	"github.com/m4gshm/gollections/break/c"
     7  	"github.com/m4gshm/gollections/notsafe"
     8  )
     9  
    10  // FlattFiltIter is the Iterator wrapper that converts an element to a slice with addition filtering of the element by a Predicate and iterates over the slice.
    11  type FlattFiltIter[From, To any] struct {
    12  	arrayTo       unsafe.Pointer
    13  	elemSizeTo    uintptr
    14  	indTo, sizeTo int
    15  	next          func() (From, bool, error)
    16  	flattener     func(From) ([]To, error)
    17  	filterFrom    func(From) (bool, error)
    18  	filterTo      func(To) (bool, error)
    19  }
    20  
    21  var _ c.Iterator[any] = (*FlattFiltIter[any, any])(nil)
    22  var _ c.IterFor[any, *FlattFiltIter[any, any]] = (*FlattFiltIter[any, any])(nil)
    23  
    24  // For takes elements retrieved by the iterator. Can be interrupt by returning ErrBreak
    25  func (i *FlattFiltIter[From, To]) For(walker func(element To) error) error {
    26  	return For(i.Next, walker)
    27  }
    28  
    29  // Next returns the next element.
    30  // The ok result indicates whether the element was returned by the iterator.
    31  // If ok == false, then the iteration must be completed.
    32  func (i *FlattFiltIter[From, To]) Next() (t To, ok bool, err error) {
    33  	if i == nil {
    34  		return t, false, nil
    35  	}
    36  
    37  	next := i.next
    38  	if next == nil {
    39  		return t, false, nil
    40  	}
    41  
    42  	for {
    43  		if sizeTo := i.sizeTo; sizeTo > 0 {
    44  			if indTo := i.indTo; indTo < sizeTo {
    45  				i.indTo++
    46  				t = *(*To)(notsafe.GetArrayElemRef(i.arrayTo, indTo, i.elemSizeTo))
    47  				if ok, err := i.filterTo(t); err != nil {
    48  					return t, false, err
    49  				} else if ok {
    50  					return t, true, nil
    51  				}
    52  			}
    53  			i.indTo = 0
    54  			i.arrayTo = nil
    55  			i.sizeTo = 0
    56  		}
    57  
    58  		if v, ok, err := next(); err != nil || !ok {
    59  			return t, false, err
    60  		} else if ok, err := i.filterFrom(v); err != nil {
    61  			return t, false, err
    62  		} else if ok {
    63  			if elementsTo, err := i.flattener(v); err != nil {
    64  				return t, false, err
    65  			} else if len(elementsTo) > 0 {
    66  				i.indTo = 1
    67  				header := notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elementsTo))
    68  				i.arrayTo = unsafe.Pointer(header.Data)
    69  				i.sizeTo = header.Len
    70  				t = *(*To)(notsafe.GetArrayElemRef(i.arrayTo, 0, i.elemSizeTo))
    71  				if ok, err := i.filterTo(t); err != nil || ok {
    72  					return t, ok, err
    73  				}
    74  			}
    75  		}
    76  	}
    77  }
    78  
    79  // Start is used with for loop construct like 'for i, val, ok, err := i.Start(); ok || err != nil ; val, ok, err = i.Next() { if err != nil { return err }}'
    80  func (i *FlattFiltIter[From, To]) Start() (*FlattFiltIter[From, To], To, bool, error) {
    81  	return startIt[To](i)
    82  }
    83  
    84  // FlatIter is the Iterator wrapper that converts an element to a slice and iterates over the elements of that slice.
    85  // For example, FlatIter can be used to iterate over all the elements of a multi-dimensional array as if it were a one-dimensional array ([][]int -> []int).
    86  type FlatIter[From, To any] struct {
    87  	arrayTo       unsafe.Pointer
    88  	elemSizeTo    uintptr
    89  	indTo, sizeTo int
    90  	next          func() (From, bool, error)
    91  	flattener     func(From) ([]To, error)
    92  }
    93  
    94  var _ c.Iterator[any] = (*FlatIter[any, any])(nil)
    95  var _ c.IterFor[any, *FlatIter[any, any]] = (*FlatIter[any, any])(nil)
    96  
    97  // For takes elements retrieved by the iterator. Can be interrupt by returning ErrBreak
    98  func (i *FlatIter[From, To]) For(walker func(element To) error) error {
    99  	return For(i.Next, walker)
   100  }
   101  
   102  // Next returns the next element.
   103  // The ok result indicates whether the element was returned by the iterator.
   104  // If ok == false, then the iteration must be completed.
   105  func (i *FlatIter[From, To]) Next() (t To, ok bool, err error) {
   106  	sizeTo := i.sizeTo
   107  	if sizeTo > 0 {
   108  		if indTo := i.indTo; indTo < sizeTo {
   109  			i.indTo++
   110  			return *(*To)(notsafe.GetArrayElemRef(i.arrayTo, indTo, i.elemSizeTo)), true, nil
   111  		}
   112  		i.indTo = 0
   113  		i.arrayTo = nil
   114  		i.sizeTo = 0
   115  	}
   116  
   117  	for {
   118  		if v, ok, err := i.next(); err != nil {
   119  			return t, false, err
   120  		} else if !ok {
   121  			return t, false, nil
   122  		} else if elementsTo, err := i.flattener(v); err != nil {
   123  			return t, false, err
   124  		} else if len(elementsTo) > 0 {
   125  			i.indTo = 1
   126  			header := notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elementsTo))
   127  			i.arrayTo = unsafe.Pointer(header.Data)
   128  			i.sizeTo = header.Len
   129  			return *(*To)(notsafe.GetArrayElemRef(i.arrayTo, 0, i.elemSizeTo)), true, nil
   130  		}
   131  	}
   132  }
   133  
   134  // Start is used with for loop construct like 'for i, val, ok, err := i.Start(); ok || err != nil ; val, ok, err = i.Next() { if err != nil { return err }}'
   135  func (i *FlatIter[From, To]) Start() (*FlatIter[From, To], To, bool, error) {
   136  	return startIt[To](i)
   137  }