github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/merkledag/traverse/traverse.go (about) 1 // Package traverse provides merkledag traversal functions 2 package traverse 3 4 import ( 5 "errors" 6 7 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" 8 9 mdag "github.com/ipfs/go-ipfs/merkledag" 10 ) 11 12 // Order is an identifier for traversal algorithm orders 13 type Order int 14 15 const ( 16 DFSPre Order = iota // depth-first pre-order 17 DFSPost // depth-first post-order 18 BFS // breadth-first 19 ) 20 21 // Options specifies a series of traversal options 22 type Options struct { 23 DAG mdag.DAGService // the dagservice to fetch nodes 24 Order Order // what order to traverse in 25 Func Func // the function to perform at each step 26 ErrFunc ErrFunc // see ErrFunc. Optional 27 28 SkipDuplicates bool // whether to skip duplicate nodes 29 } 30 31 // State is a current traversal state 32 type State struct { 33 Node *mdag.Node 34 Depth int 35 } 36 37 type traversal struct { 38 opts Options 39 seen map[string]struct{} 40 } 41 42 func (t *traversal) shouldSkip(n *mdag.Node) (bool, error) { 43 if t.opts.SkipDuplicates { 44 k, err := n.Key() 45 if err != nil { 46 return true, err 47 } 48 49 if _, found := t.seen[string(k)]; found { 50 return true, nil 51 } 52 t.seen[string(k)] = struct{}{} 53 } 54 55 return false, nil 56 } 57 58 func (t *traversal) callFunc(next State) error { 59 return t.opts.Func(next) 60 } 61 62 // getNode returns the node for link. If it return an error, 63 // stop processing. if it returns a nil node, just skip it. 64 // 65 // the error handling is a little complicated. 66 func (t *traversal) getNode(link *mdag.Link) (*mdag.Node, error) { 67 68 getNode := func(l *mdag.Link) (*mdag.Node, error) { 69 next, err := l.GetNode(context.TODO(), t.opts.DAG) 70 if err != nil { 71 return nil, err 72 } 73 74 skip, err := t.shouldSkip(next) 75 if skip { 76 next = nil 77 } 78 return next, err 79 } 80 81 next, err := getNode(link) 82 if err != nil && t.opts.ErrFunc != nil { // attempt recovery. 83 err = t.opts.ErrFunc(err) 84 next = nil // skip regardless 85 } 86 return next, err 87 } 88 89 // Func is the type of the function called for each dag.Node visited by Traverse. 90 // The traversal argument contains the current traversal state. 91 // If an error is returned, processing stops. 92 type Func func(current State) error 93 94 // If there is a problem walking to the Node, and ErrFunc is provided, Traverse 95 // will call ErrFunc with the error encountered. ErrFunc can decide how to handle 96 // that error, and return an error back to Traversal with how to proceed: 97 // * nil - skip the Node and its children, but continue processing 98 // * all other errors halt processing immediately. 99 // 100 // If ErrFunc is nil, Traversal will stop, as if: 101 // 102 // opts.ErrFunc = func(err error) { return err } 103 // 104 type ErrFunc func(err error) error 105 106 func Traverse(root *mdag.Node, o Options) error { 107 t := traversal{ 108 opts: o, 109 seen: map[string]struct{}{}, 110 } 111 112 state := State{ 113 Node: root, 114 Depth: 0, 115 } 116 117 switch o.Order { 118 default: 119 return dfsPreTraverse(state, &t) 120 case DFSPre: 121 return dfsPreTraverse(state, &t) 122 case DFSPost: 123 return dfsPostTraverse(state, &t) 124 case BFS: 125 return bfsTraverse(state, &t) 126 } 127 } 128 129 type dfsFunc func(state State, t *traversal) error 130 131 func dfsPreTraverse(state State, t *traversal) error { 132 if err := t.callFunc(state); err != nil { 133 return err 134 } 135 if err := dfsDescend(dfsPreTraverse, state, t); err != nil { 136 return err 137 } 138 return nil 139 } 140 141 func dfsPostTraverse(state State, t *traversal) error { 142 if err := dfsDescend(dfsPostTraverse, state, t); err != nil { 143 return err 144 } 145 if err := t.callFunc(state); err != nil { 146 return err 147 } 148 return nil 149 } 150 151 func dfsDescend(df dfsFunc, curr State, t *traversal) error { 152 for _, l := range curr.Node.Links { 153 node, err := t.getNode(l) 154 if err != nil { 155 return err 156 } 157 if node == nil { // skip 158 continue 159 } 160 161 next := State{ 162 Node: node, 163 Depth: curr.Depth + 1, 164 } 165 if err := df(next, t); err != nil { 166 return err 167 } 168 } 169 return nil 170 } 171 172 func bfsTraverse(root State, t *traversal) error { 173 174 if skip, err := t.shouldSkip(root.Node); skip || err != nil { 175 return err 176 } 177 178 var q queue 179 q.enq(root) 180 for q.len() > 0 { 181 curr := q.deq() 182 if curr.Node == nil { 183 return errors.New("failed to dequeue though queue not empty") 184 } 185 186 // call user's func 187 if err := t.callFunc(curr); err != nil { 188 return err 189 } 190 191 for _, l := range curr.Node.Links { 192 node, err := t.getNode(l) 193 if err != nil { 194 return err 195 } 196 if node == nil { // skip 197 continue 198 } 199 200 q.enq(State{ 201 Node: node, 202 Depth: curr.Depth + 1, 203 }) 204 } 205 } 206 return nil 207 } 208 209 type queue struct { 210 s []State 211 } 212 213 func (q *queue) enq(n State) { 214 q.s = append(q.s, n) 215 } 216 217 func (q *queue) deq() State { 218 if len(q.s) < 1 { 219 return State{} 220 } 221 n := q.s[0] 222 q.s = q.s[1:] 223 return n 224 } 225 226 func (q *queue) len() int { 227 return len(q.s) 228 }