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 }