github.com/grailbio/base@v0.0.11/file/fsnode/iterators.go (about) 1 package fsnode 2 3 import ( 4 "context" 5 "io" 6 "os" 7 ) 8 9 type sliceIterator struct { 10 remaining []T 11 closed bool 12 } 13 14 var _ Iterator = (*sliceIterator)(nil) 15 16 // NewIterator returns an iterator that yields the given nodes. 17 func NewIterator(nodes ...T) Iterator { 18 // Copy the slice because we'll mutate to nil later. 19 nodes = append([]T{}, nodes...) 20 return &sliceIterator{remaining: nodes} 21 } 22 23 func (it *sliceIterator) Next(ctx context.Context) (T, error) { 24 if it.closed { 25 return nil, os.ErrClosed 26 } 27 if len(it.remaining) == 0 { 28 return nil, io.EOF 29 } 30 next := it.remaining[0] 31 it.remaining[0] = nil // TODO: Is this necessary to allow GC? 32 it.remaining = it.remaining[1:] 33 return next, nil 34 } 35 36 func (it *sliceIterator) Close(context.Context) error { 37 it.closed = true 38 it.remaining = nil 39 return nil 40 } 41 42 type lazyIterator struct { 43 make func(context.Context) ([]T, error) 44 fetched bool 45 delegate Iterator 46 } 47 48 var _ Iterator = (*lazyIterator)(nil) 49 50 // NewLazyIterator uses the given make function upon the first call to Next to 51 // make the nodes that it yields. 52 func NewLazyIterator(make func(context.Context) ([]T, error)) Iterator { 53 return &lazyIterator{make: make} 54 } 55 56 func (it *lazyIterator) Next(ctx context.Context) (T, error) { 57 if err := it.ensureFetched(ctx); err != nil { 58 return nil, err 59 } 60 return it.delegate.Next(ctx) 61 } 62 63 func (it *lazyIterator) Close(ctx context.Context) error { 64 if it.delegate == nil { 65 return nil 66 } 67 err := it.delegate.Close(ctx) 68 it.delegate = nil 69 return err 70 } 71 72 func (it *lazyIterator) ensureFetched(ctx context.Context) error { 73 if it.fetched { 74 if it.delegate == nil { 75 return os.ErrClosed 76 } 77 return nil 78 } 79 nodes, err := it.make(ctx) 80 if err != nil { 81 return err 82 } 83 it.delegate = NewIterator(nodes...) 84 it.fetched = true 85 return nil 86 } 87 88 type concatIterator struct { 89 iters []Iterator 90 closed bool 91 } 92 93 var _ Iterator = (*concatIterator)(nil) 94 95 // NewConcatIterator returns the elements of the given iterators in order, reading each until EOF. 96 // Manages calling Close on constituents (as it goes along and upon its own Close). 97 func NewConcatIterator(iterators ...Iterator) Iterator { 98 return &concatIterator{iters: append([]Iterator{}, iterators...)} 99 } 100 101 func (it *concatIterator) Next(ctx context.Context) (T, error) { 102 if it.closed { 103 return nil, os.ErrClosed 104 } 105 for { 106 if len(it.iters) == 0 { 107 return nil, io.EOF 108 } 109 next, err := it.iters[0].Next(ctx) 110 if err == io.EOF { 111 err = nil 112 if next != nil { 113 err = iteratorEOFError(it.iters[0]) 114 } 115 if closeErr := it.iters[0].Close(ctx); closeErr != nil && err == nil { 116 err = closeErr 117 } 118 it.iters[0] = nil // TODO: Is this actually necessary to allow GC? 119 it.iters = it.iters[1:] 120 if err != nil { 121 return nil, err 122 } 123 continue 124 } 125 return next, err 126 } 127 } 128 129 // Close attempts to Close remaining constituent iterators. Returns the first constituent Close 130 // error (but attempts to close the rest anyway). 131 func (it *concatIterator) Close(ctx context.Context) error { 132 it.closed = true 133 var err error 134 for _, iter := range it.iters { 135 if err2 := iter.Close(ctx); err2 != nil && err == nil { 136 err = err2 137 } 138 } 139 it.iters = nil 140 return err 141 } 142 143 type mapIterator struct { 144 iter Iterator 145 fn func(context.Context, T) (T, error) 146 } 147 148 // MapIterator returns an Iterator that applies fn to each T yielded by iter. 149 func MapIterator(iter Iterator, fn func(context.Context, T) (T, error)) Iterator { 150 return mapIterator{iter, fn} 151 } 152 func (it mapIterator) Next(ctx context.Context) (T, error) { 153 if it.fn == nil { 154 return nil, os.ErrClosed 155 } 156 node, err := it.iter.Next(ctx) 157 if err == nil && it.fn != nil { 158 node, err = it.fn(ctx, node) 159 } 160 return node, err 161 } 162 func (it mapIterator) Close(ctx context.Context) error { 163 it.fn = nil 164 return it.iter.Close(ctx) 165 }