@cryptotaxi247 / kubo / commits / f46120b19

refs edges + stream fix

Juan Batiz-Benet committed Jan 7, 2015 at 12:56 UTC f46120b194f8031451e829b0c9adecc7d53ed0ff
1 file changed +159 -50
core/commands/refs.go
+159 -50
@@ -2,10 +2,11 @@ package commands
2
3 import (
4 "bytes"
5 - "fmt"
5 "io"
6 + "sync"
7 +
8 + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
9
8 - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
10 cmds "github.com/jbenet/go-ipfs/commands"
11 "github.com/jbenet/go-ipfs/core"
12 dag "github.com/jbenet/go-ipfs/merkledag"
@@ -44,6 +45,7 @@ Note: list all refs recursively with -r.
45 cmds.StringArg("ipfs-path", true, true, "Path to the object(s) to list refs from"),
46 },
47 Options: []cmds.Option{
48 + cmds.BoolOption("edges", "e", "Emit edge format: <from> -> <to>"),
49 cmds.BoolOption("unique", "u", "Omit duplicate refs from output"),
50 cmds.BoolOption("recursive", "r", "Recursively list links of child nodes"),
51 },
@@ -53,84 +55,191 @@ Note: list all refs recursively with -r.
55 return nil, err
56 }
57
56 - unique, found, err := req.Option("unique").Bool()
58 + unique, _, err := req.Option("unique").Bool()
59 if err != nil {
60 return nil, err
61 }
60 - if !found {
61 - unique = false
62 +
63 + recursive, _, err := req.Option("recursive").Bool()
64 + if err != nil {
65 + return nil, err
66 }
67
64 - recursive, found, err := req.Option("recursive").Bool()
68 + edges, _, err := req.Option("edges").Bool()
69 if err != nil {
70 return nil, err
71 }
68 - if !found {
69 - recursive = false
72 +
73 + objs, err := objectsForPaths(n, req.Arguments())
74 + if err != nil {
75 + return nil, err
76 }
77
72 - return getRefs(n, req.Arguments(), unique, recursive)
73 - },
74 - Type: KeyList{},
75 - Marshalers: cmds.MarshalerMap{
76 - cmds.Text: KeyListTextMarshaler,
78 + piper, pipew := io.Pipe()
79 + eptr := &ErrPassThroughReader{R: piper}
80 +
81 + go func() {
82 + defer pipew.Close()
83 +
84 + rw := RefWriter{
85 + W: pipew,
86 + DAG: n.DAG,
87 + Ctx: n.Context(),
88 + Unique: unique,
89 + PrintEdge: edges,
90 + Recursive: recursive,
91 + }
92 +
93 + for _, o := range objs {
94 + if _, err := rw.WriteRefs(o); err != nil {
95 + eptr.SetError(err)
96 + }
97 + }
98 + }()
99 +
100 + return eptr, nil
101 },
102 }
103
80 -func getRefs(n *core.IpfsNode, paths []string, unique, recursive bool) (*KeyList, error) {
81 - var refsSeen map[u.Key]bool
82 - if unique {
83 - refsSeen = make(map[u.Key]bool)
104 +func objectsForPaths(n *core.IpfsNode, paths []string) ([]*dag.Node, error) {
105 + objects := make([]*dag.Node, len(paths))
106 + for i, p := range paths {
107 + o, err := n.Resolver.ResolvePath(p)
108 + if err != nil {
109 + return nil, err
110 + }
111 + objects[i] = o
112 + }
113 + return objects, nil
114 +}
115 +
116 +// ErrPassThroughReader is a reader that may return an externally set error.
117 +type ErrPassThroughReader struct {
118 + R io.ReadCloser
119 + err error
120 +
121 + sync.RWMutex
122 +}
123 +
124 +func (r *ErrPassThroughReader) Error() error {
125 + r.RLock()
126 + defer r.RUnlock()
127 + return r.err
128 +}
129 +
130 +func (r *ErrPassThroughReader) SetError(err error) {
131 + r.Lock()
132 + r.err = err
133 + r.Unlock()
134 +}
135 +
136 +func (r *ErrPassThroughReader) Read(buf []byte) (int, error) {
137 + err := r.Error()
138 + if err != nil {
139 + return 0, err
140 + }
141 +
142 + return r.R.Read(buf)
143 +}
144 +
145 +func (r *ErrPassThroughReader) Close() error {
146 + err1 := r.R.Close()
147 + err2 := r.Error()
148 + if err2 != nil {
149 + return err2
150 }
151 + return err1
152 +}
153 +
154 +type RefWriter struct {
155 + W io.Writer
156 + DAG dag.DAGService
157 + Ctx context.Context
158
86 - refs := make([]u.Key, 0)
159 + Unique bool
160 + Recursive bool
161 + PrintEdge bool
162 +
163 + seen map[u.Key]struct{}
164 +}
165 +
166 +// WriteRefs writes refs of the given object to the underlying writer.
167 +func (rw *RefWriter) WriteRefs(n *dag.Node) (int, error) {
168 + nkey, err := n.Key()
169 + if err != nil {
170 + return 0, err
171 + }
172 +
173 + if rw.skip(nkey) {
174 + return 0, nil
175 + }
176 +
177 + count := 0
178 + for _, l := range n.Links {
179 + lk := u.Key(l.Hash)
180 +
181 + if rw.skip(lk) {
182 + continue
183 + }
184 +
185 + if err := rw.WriteEdge(nkey, lk); err != nil {
186 + return count, err
187 + }
188 + count++
189
88 - for _, path := range paths {
89 - object, err := n.Resolver.ResolvePath(path)
190 + if !rw.Recursive {
191 + continue
192 + }
193 +
194 + child, err := l.GetNode(rw.DAG)
195 if err != nil {
91 - return nil, err
196 + return count, err
197 }
198
94 - refs, err = addRefs(n, object, refs, refsSeen, recursive)
199 + c, err := rw.WriteRefs(child)
200 + count += c
201 if err != nil {
96 - return nil, err
202 + return count, err
203 }
204 }
99 -
100 - return &KeyList{refs}, nil
205 + return count, nil
206 }
207
103 -func addRefs(n *core.IpfsNode, object *dag.Node, refs []u.Key, refsSeen map[u.Key]bool, recursive bool) ([]u.Key, error) {
104 - for _, link := range object.Links {
105 - var found bool
106 - found, refs = addRef(link.Hash, refs, refsSeen)
107 -
108 - if recursive && !found {
109 - child, err := n.DAG.Get(u.Key(link.Hash))
110 - if err != nil {
111 - return nil, fmt.Errorf("cannot retrieve %s (%s)", link.Hash.B58String(), err)
112 - }
208 +// skip returns whether to skip a key
209 +func (rw *RefWriter) skip(k u.Key) bool {
210 + if !rw.Unique {
211 + return false
212 + }
213
114 - refs, err = addRefs(n, child, refs, refsSeen, recursive)
115 - if err != nil {
116 - return nil, err
117 - }
118 - }
214 + if rw.seen == nil {
215 + rw.seen = make(map[u.Key]struct{})
216 }
217
121 - return refs, nil
218 + _, found := rw.seen[k]
219 + if !found {
220 + rw.seen[k] = struct{}{}
221 + }
222 + return found
223 }
224
124 -func addRef(h mh.Multihash, refs []u.Key, refsSeen map[u.Key]bool) (bool, []u.Key) {
125 - key := u.Key(h)
126 - if refsSeen != nil {
127 - _, found := refsSeen[key]
128 - if found {
129 - return true, refs
225 +// Write one edge
226 +func (rw *RefWriter) WriteEdge(from, to u.Key) error {
227 + if rw.Ctx != nil {
228 + select {
229 + case <-rw.Ctx.Done(): // just in case.
230 + return rw.Ctx.Err()
231 + default:
232 }
131 - refsSeen[key] = true
233 }
234
134 - refs = append(refs, key)
135 - return false, refs
235 + var s string
236 + if rw.PrintEdge {
237 + s = from.Pretty() + " -> "
238 + }
239 + s += to.Pretty() + "\n"
240 +
241 + if _, err := rw.W.Write([]byte(s)); err != nil {
242 + return err
243 + }
244 + return nil
245 }