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 }