diffenum.go 2.2 KB
Newer Older
Jeromy's avatar
Jeromy committed
1 2 3 4 5 6 7 8
package dagutils

import (
	"context"
	"fmt"

	mdag "github.com/ipfs/go-ipfs/merkledag"

9 10
	node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format"
	cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid"
Jeromy's avatar
Jeromy committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
)

// DiffEnumerate fetches every object in the graph pointed to by 'to' that is
// not in 'from'. This can be used to more efficiently fetch a graph if you can
// guarantee you already have the entirety of 'from'
func DiffEnumerate(ctx context.Context, dserv node.NodeGetter, from, to *cid.Cid) error {
	fnd, err := dserv.Get(ctx, from)
	if err != nil {
		return fmt.Errorf("get %s: %s", from, err)
	}

	tnd, err := dserv.Get(ctx, to)
	if err != nil {
		return fmt.Errorf("get %s: %s", to, err)
	}

	diff := getLinkDiff(fnd, tnd)

	sset := cid.NewSet()
	for _, c := range diff {
31 32 33 34 35 36 37 38 39 40 41 42 43
		// Since we're already assuming we have everything in the 'from' graph,
		// add all those cids to our 'already seen' set to avoid potentially
		// enumerating them later
		if c.bef != nil {
			sset.Add(c.bef)
		}
	}
	for _, c := range diff {
		if c.bef == nil {
			if sset.Has(c.aft) {
				continue
			}
			err := mdag.EnumerateChildrenAsync(ctx, mdag.GetLinksDirect(dserv), c.aft, sset.Visit)
Jeromy's avatar
Jeromy committed
44 45 46 47
			if err != nil {
				return err
			}
		} else {
48
			err := DiffEnumerate(ctx, dserv, c.bef, c.aft)
Jeromy's avatar
Jeromy committed
49 50 51 52 53 54 55 56 57
			if err != nil {
				return err
			}
		}
	}

	return nil
}

Jeromy's avatar
Jeromy committed
58 59 60
// if both bef and aft are not nil, then that signifies bef was replaces with aft.
// if bef is nil and aft is not, that means aft was newly added
// if aft is nil and bef is not, that means bef was deleted
Jeromy's avatar
Jeromy committed
61
type diffpair struct {
62
	bef, aft *cid.Cid
Jeromy's avatar
Jeromy committed
63 64
}

Jeromy's avatar
Jeromy committed
65 66
// getLinkDiff returns a changeset between nodes 'a' and 'b'. Currently does
// not log deletions as our usecase doesnt call for this.
Jeromy's avatar
Jeromy committed
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
func getLinkDiff(a, b node.Node) []diffpair {
	have := make(map[string]*node.Link)
	names := make(map[string]*node.Link)
	for _, l := range a.Links() {
		have[l.Cid.KeyString()] = l
		names[l.Name] = l
	}

	var out []diffpair

	for _, l := range b.Links() {
		if have[l.Cid.KeyString()] != nil {
			continue
		}

		match, ok := names[l.Name]
		if !ok {
84
			out = append(out, diffpair{aft: l.Cid})
Jeromy's avatar
Jeromy committed
85 86 87
			continue
		}

88
		out = append(out, diffpair{bef: match.Cid, aft: l.Cid})
Jeromy's avatar
Jeromy committed
89 90 91
	}
	return out
}