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  }