github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/importer/helpers/dagbuilder.go (about)

     1  package helpers
     2  
     3  import (
     4  	dag "github.com/ipfs/go-ipfs/merkledag"
     5  	"github.com/ipfs/go-ipfs/pin"
     6  )
     7  
     8  // NodeCB is callback function for dag generation
     9  // the `last` flag signifies whether or not this is the last
    10  // (top-most root) node being added. useful for things like
    11  // only pinning the first node recursively.
    12  type NodeCB func(node *dag.Node, last bool) error
    13  
    14  var nilFunc NodeCB = func(_ *dag.Node, _ bool) error { return nil }
    15  
    16  // DagBuilderHelper wraps together a bunch of objects needed to
    17  // efficiently create unixfs dag trees
    18  type DagBuilderHelper struct {
    19  	dserv    dag.DAGService
    20  	mp       pin.ManualPinner
    21  	in       <-chan []byte
    22  	errs     <-chan error
    23  	recvdErr error
    24  	nextData []byte // the next item to return.
    25  	maxlinks int
    26  	ncb      NodeCB
    27  
    28  	batch *dag.Batch
    29  }
    30  
    31  type DagBuilderParams struct {
    32  	// Maximum number of links per intermediate node
    33  	Maxlinks int
    34  
    35  	// DAGService to write blocks to (required)
    36  	Dagserv dag.DAGService
    37  
    38  	// Callback for each block added
    39  	NodeCB NodeCB
    40  }
    41  
    42  // Generate a new DagBuilderHelper from the given params, using 'in' as a
    43  // data source
    44  func (dbp *DagBuilderParams) New(in <-chan []byte, errs <-chan error) *DagBuilderHelper {
    45  	ncb := dbp.NodeCB
    46  	if ncb == nil {
    47  		ncb = nilFunc
    48  	}
    49  
    50  	return &DagBuilderHelper{
    51  		dserv:    dbp.Dagserv,
    52  		in:       in,
    53  		errs:     errs,
    54  		maxlinks: dbp.Maxlinks,
    55  		ncb:      ncb,
    56  		batch:    dbp.Dagserv.Batch(),
    57  	}
    58  }
    59  
    60  // prepareNext consumes the next item from the channel and puts it
    61  // in the nextData field. it is idempotent-- if nextData is full
    62  // it will do nothing.
    63  //
    64  // i realized that building the dag becomes _a lot_ easier if we can
    65  // "peek" the "are done yet?" (i.e. not consume it from the channel)
    66  func (db *DagBuilderHelper) prepareNext() {
    67  	if db.in == nil {
    68  		// if our input is nil, there is "nothing to do". we're done.
    69  		// as if there was no data at all. (a sort of zero-value)
    70  		return
    71  	}
    72  
    73  	// if we already have data waiting to be consumed, we're ready.
    74  	if db.nextData != nil {
    75  		return
    76  	}
    77  
    78  	// if it's closed, nextData will be correctly set to nil, signaling
    79  	// that we're done consuming from the channel.
    80  	db.nextData = <-db.in
    81  }
    82  
    83  // Done returns whether or not we're done consuming the incoming data.
    84  func (db *DagBuilderHelper) Done() bool {
    85  	// ensure we have an accurate perspective on data
    86  	// as `done` this may be called before `next`.
    87  	db.prepareNext() // idempotent
    88  	return db.nextData == nil
    89  }
    90  
    91  // Next returns the next chunk of data to be inserted into the dag
    92  // if it returns nil, that signifies that the stream is at an end, and
    93  // that the current building operation should finish
    94  func (db *DagBuilderHelper) Next() []byte {
    95  	db.prepareNext() // idempotent
    96  	d := db.nextData
    97  	db.nextData = nil // signal we've consumed it
    98  	return d
    99  }
   100  
   101  // GetDagServ returns the dagservice object this Helper is using
   102  func (db *DagBuilderHelper) GetDagServ() dag.DAGService {
   103  	return db.dserv
   104  }
   105  
   106  // FillNodeLayer will add datanodes as children to the give node until
   107  // at most db.indirSize ndoes are added
   108  //
   109  // warning: **children** pinned indirectly, but input node IS NOT pinned.
   110  func (db *DagBuilderHelper) FillNodeLayer(node *UnixfsNode) error {
   111  
   112  	// while we have room AND we're not done
   113  	for node.NumChildren() < db.maxlinks && !db.Done() {
   114  		child := NewUnixfsBlock()
   115  
   116  		if err := db.FillNodeWithData(child); err != nil {
   117  			return err
   118  		}
   119  
   120  		if err := node.AddChild(child, db); err != nil {
   121  			return err
   122  		}
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  func (db *DagBuilderHelper) FillNodeWithData(node *UnixfsNode) error {
   129  	data := db.Next()
   130  	if data == nil { // we're done!
   131  		return nil
   132  	}
   133  
   134  	if len(data) > BlockSizeLimit {
   135  		return ErrSizeLimitExceeded
   136  	}
   137  
   138  	node.SetData(data)
   139  	return nil
   140  }
   141  
   142  func (db *DagBuilderHelper) Add(node *UnixfsNode) (*dag.Node, error) {
   143  	dn, err := node.GetDagNode()
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	_, err = db.dserv.Add(dn)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	// node callback
   154  	err = db.ncb(dn, true)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  
   159  	return dn, nil
   160  }
   161  
   162  func (db *DagBuilderHelper) Maxlinks() int {
   163  	return db.maxlinks
   164  }
   165  
   166  func (db *DagBuilderHelper) Close() error {
   167  	return db.batch.Commit()
   168  }