From 7c510662ae2ea9beef3702e1149ab05cb7c0cf1d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Jul 2015 12:45:33 -0700 Subject: [PATCH] break merkledag utils into its own package License: MIT Signed-off-by: Jeromy --- core/commands/object.go | 117 +-------------------------------- merkledag/node.go | 9 +++ merkledag/utils/utils.go | 113 ++++++++++++++++++++++++++++++++ merkledag/utils/utils_test.go | 118 ++++++++++++++++++++++++++++++++++ 4 files changed, 243 insertions(+), 114 deletions(-) create mode 100644 merkledag/utils/utils.go create mode 100644 merkledag/utils/utils_test.go diff --git a/core/commands/object.go b/core/commands/object.go index 277c9616e..d30c5d1f2 100644 --- a/core/commands/object.go +++ b/core/commands/object.go @@ -18,6 +18,7 @@ import ( cmds "github.com/ipfs/go-ipfs/commands" core "github.com/ipfs/go-ipfs/core" dag "github.com/ipfs/go-ipfs/merkledag" + dagutils "github.com/ipfs/go-ipfs/merkledag/utils" path "github.com/ipfs/go-ipfs/path" ft "github.com/ipfs/go-ipfs/unixfs" u "github.com/ipfs/go-ipfs/util" @@ -588,7 +589,7 @@ func rmLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) { path := strings.Split(req.Arguments()[2], "/") - nnode, err := rmLink(req.Context(), nd.DAG, root, path) + nnode, err := dagutils.RmLink(req.Context(), nd.DAG, root, path) if err != nil { return "", err } @@ -596,51 +597,6 @@ func rmLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) { return nnode.Key() } -func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) { - if len(path) == 1 { - // base case, remove node in question - err := root.RemoveNodeLink(path[0]) - if err != nil { - return nil, err - } - - _, err = ds.Add(root) - if err != nil { - return nil, err - } - - return root, nil - } - - nchild, err := root.GetNodeLink(path[0]) - if err != nil { - return nil, err - } - - nd, err := nchild.GetNode(ctx, ds) - if err != nil { - return nil, err - } - - nnode, err := rmLink(ctx, ds, nd, path[1:]) - if err != nil { - return nil, err - } - - _ = root.RemoveNodeLink(path[0]) - err = root.AddNodeLinkClean(path[0], nnode) - if err != nil { - return nil, err - } - - _, err = ds.Add(root) - if err != nil { - return nil, err - } - - return root, nil -} - func addLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) { if len(req.Arguments()) < 4 { return "", fmt.Errorf("not enough arguments for add-link") @@ -661,80 +617,13 @@ func addLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) { return "", err } - nnode, err := insertNodeAtPath(req.Context(), nd.DAG, root, parts, childk, create) + nnode, err := dagutils.InsertNodeAtPath(req.Context(), nd.DAG, root, parts, childk, create) if err != nil { return "", err } return nnode.Key() } -func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) { - if childname == "" { - return nil, errors.New("cannot create link with no name!") - } - - ctx, cancel := context.WithTimeout(ctx, time.Second*30) - defer cancel() - childnd, err := ds.Get(ctx, childk) - if err != nil { - return nil, err - } - - // ensure no link with that name already exists - _ = root.RemoveNodeLink(childname) // ignore error, only option is ErrNotFound - - err = root.AddNodeLinkClean(childname, childnd) - if err != nil { - return nil, err - } - - _, err = ds.Add(root) - if err != nil { - return nil, err - } - return root, nil -} - -func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create bool) (*dag.Node, error) { - if len(path) == 1 { - return addLink(ctx, ds, root, path[0], toinsert) - } - - var nd *dag.Node - child, err := root.GetNodeLink(path[0]) - if err != nil { - // if 'create' is true, we create directories on the way down as needed - if err == dag.ErrNotFound && create { - nd = &dag.Node{Data: ft.FolderPBData()} - } else { - return nil, err - } - } else { - nd, err = child.GetNode(ctx, ds) - if err != nil { - return nil, err - } - } - - ndprime, err := insertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create) - if err != nil { - return nil, err - } - - _ = root.RemoveNodeLink(path[0]) - err = root.AddNodeLinkClean(path[0], ndprime) - if err != nil { - return nil, err - } - - _, err = ds.Add(root) - if err != nil { - return nil, err - } - - return root, nil -} - func nodeFromTemplate(template string) (*dag.Node, error) { switch template { case "unixfs-dir": diff --git a/merkledag/node.go b/merkledag/node.go index 71a5b5b32..e90ac95e8 100644 --- a/merkledag/node.go +++ b/merkledag/node.go @@ -153,6 +153,15 @@ func (n *Node) GetNodeLink(name string) (*Link, error) { return nil, ErrNotFound } +func (n *Node) GetLinkedNode(ctx context.Context, ds DAGService, name string) (*Node, error) { + lnk, err := n.GetNodeLink(name) + if err != nil { + return nil, err + } + + return lnk.GetNode(ctx, ds) +} + // Copy returns a copy of the node. // NOTE: does not make copies of Node objects in the links. func (n *Node) Copy() *Node { diff --git a/merkledag/utils/utils.go b/merkledag/utils/utils.go new file mode 100644 index 000000000..e82d00229 --- /dev/null +++ b/merkledag/utils/utils.go @@ -0,0 +1,113 @@ +package dagutils + +import ( + "errors" + "time" + + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + key "github.com/ipfs/go-ipfs/blocks/key" + dag "github.com/ipfs/go-ipfs/merkledag" + ft "github.com/ipfs/go-ipfs/unixfs" +) + +func AddLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) { + if childname == "" { + return nil, errors.New("cannot create link with no name!") + } + + ctx, cancel := context.WithTimeout(ctx, time.Second*30) + defer cancel() + childnd, err := ds.Get(ctx, childk) + if err != nil { + return nil, err + } + + // ensure no link with that name already exists + _ = root.RemoveNodeLink(childname) // ignore error, only option is ErrNotFound + + err = root.AddNodeLinkClean(childname, childnd) + if err != nil { + return nil, err + } + + _, err = ds.Add(root) + if err != nil { + return nil, err + } + return root, nil +} + +func InsertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create bool) (*dag.Node, error) { + if len(path) == 1 { + return AddLink(ctx, ds, root, path[0], toinsert) + } + + nd, err := root.GetLinkedNode(ctx, ds, path[0]) + if err != nil { + // if 'create' is true, we create directories on the way down as needed + if err == dag.ErrNotFound && create { + nd = &dag.Node{Data: ft.FolderPBData()} + } else { + return nil, err + } + } + + ndprime, err := InsertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create) + if err != nil { + return nil, err + } + + _ = root.RemoveNodeLink(path[0]) + err = root.AddNodeLinkClean(path[0], ndprime) + if err != nil { + return nil, err + } + + _, err = ds.Add(root) + if err != nil { + return nil, err + } + + return root, nil +} + +func RmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) { + if len(path) == 1 { + // base case, remove node in question + err := root.RemoveNodeLink(path[0]) + if err != nil { + return nil, err + } + + _, err = ds.Add(root) + if err != nil { + return nil, err + } + + return root, nil + } + + nd, err := root.GetLinkedNode(ctx, ds, path[0]) + if err != nil { + return nil, err + } + + nnode, err := RmLink(ctx, ds, nd, path[1:]) + if err != nil { + return nil, err + } + + _ = root.RemoveNodeLink(path[0]) + err = root.AddNodeLinkClean(path[0], nnode) + if err != nil { + return nil, err + } + + _, err = ds.Add(root) + if err != nil { + return nil, err + } + + return root, nil +} diff --git a/merkledag/utils/utils_test.go b/merkledag/utils/utils_test.go new file mode 100644 index 000000000..36da81687 --- /dev/null +++ b/merkledag/utils/utils_test.go @@ -0,0 +1,118 @@ +package dagutils + +import ( + "testing" + + key "github.com/ipfs/go-ipfs/blocks/key" + dag "github.com/ipfs/go-ipfs/merkledag" + mdtest "github.com/ipfs/go-ipfs/merkledag/test" + + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" +) + +func TestAddLink(t *testing.T) { + ds := mdtest.Mock(t) + fishnode := &dag.Node{ + Data: []byte("fishcakes!"), + } + + fk, err := ds.Add(fishnode) + if err != nil { + t.Fatal(err) + } + + nd := new(dag.Node) + nnode, err := AddLink(context.Background(), ds, nd, "fish", fk) + if err != nil { + t.Fatal(err) + } + + fnprime, err := nnode.GetLinkedNode(context.Background(), ds, "fish") + if err != nil { + t.Fatal(err) + } + + fnpkey, err := fnprime.Key() + if err != nil { + t.Fatal(err) + } + + if fnpkey != fk { + t.Fatal("wrong child node found!") + } +} + +func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path []string, exp key.Key) { + cur := root + for _, e := range path { + nxt, err := cur.GetLinkedNode(context.Background(), ds, e) + if err != nil { + t.Fatal(err) + } + + cur = nxt + } + + curk, err := cur.Key() + if err != nil { + t.Fatal(err) + } + + if curk != exp { + t.Fatal("node not as expected at end of path") + } +} + +func TestInsertNode(t *testing.T) { + ds := mdtest.Mock(t) + root := new(dag.Node) + + childa := &dag.Node{ + Data: []byte("This is child A"), + } + ak, err := ds.Add(childa) + if err != nil { + t.Fatal(err) + } + + path := []string{"a", "b", "c", "d"} + root_a, err := InsertNodeAtPath(context.Background(), ds, root, path, ak, true) + if err != nil { + t.Fatal(err) + } + assertNodeAtPath(t, ds, root_a, path, ak) + + childb := &dag.Node{Data: []byte("this is the second child")} + bk, err := ds.Add(childb) + if err != nil { + t.Fatal(err) + } + + // this one should fail, we are specifying a non-existant path + // with create == false + path2 := []string{"a", "b", "e", "f"} + _, err = InsertNodeAtPath(context.Background(), ds, root_a, path2, bk, false) + if err == nil { + t.Fatal("that shouldnt have worked") + } + if err != dag.ErrNotFound { + t.Fatal("expected this to fail with 'not found'") + } + + // inserting a path of length one should work with create == false + path3 := []string{"x"} + root_b, err := InsertNodeAtPath(context.Background(), ds, root_a, path3, bk, false) + if err != nil { + t.Fatal(err) + } + + assertNodeAtPath(t, ds, root_b, path3, bk) + + // now try overwriting a path + root_c, err := InsertNodeAtPath(context.Background(), ds, root_b, path, bk, false) + if err != nil { + t.Fatal(err) + } + + assertNodeAtPath(t, ds, root_c, path, bk) +}