node.go 6.18 KB
Newer Older
1 2 3 4
package merkledag

import (
	"fmt"
5
	"time"
6

7
	"gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
Jeromy's avatar
Jeromy committed
8

9
	mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash"
10
	key "github.com/ipfs/go-ipfs/blocks/key"
11 12
)

13 14
var ErrLinkNotFound = fmt.Errorf("no link by that name")

15 16 17 18 19 20 21 22 23 24 25 26 27 28
// Node represents a node in the IPFS Merkle DAG.
// nodes have opaque data and a set of navigable links.
type Node struct {
	Links []*Link
	Data  []byte

	// cache encoded/marshaled value
	encoded []byte

	cached mh.Multihash
}

// NodeStat is a statistics object for a Node. Mostly sizes.
type NodeStat struct {
29
	Hash           string
30
	NumLinks       int // number of links in link table
31
	BlockSize      int // size of the raw, encoded data
32 33
	LinksSize      int // size of the links segment
	DataSize       int // size of the data segment
34
	CumulativeSize int // cumulative size of object and its references
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
}

func (ns NodeStat) String() string {
	f := "NodeStat{NumLinks: %d, BlockSize: %d, LinksSize: %d, DataSize: %d, CumulativeSize: %d}"
	return fmt.Sprintf(f, ns.NumLinks, ns.BlockSize, ns.LinksSize, ns.DataSize, ns.CumulativeSize)
}

// Link represents an IPFS Merkle DAG Link between Nodes.
type Link struct {
	// utf string name. should be unique per object
	Name string // utf8

	// cumulative size of target object
	Size uint64

	// multihash of the target object
	Hash mh.Multihash

	// a ptr to the actual node for graph manipulation
	Node *Node
}

type LinkSlice []*Link

func (ls LinkSlice) Len() int           { return len(ls) }
func (ls LinkSlice) Swap(a, b int)      { ls[a], ls[b] = ls[b], ls[a] }
func (ls LinkSlice) Less(a, b int) bool { return ls[a].Name < ls[b].Name }

// MakeLink creates a link to the given node
func MakeLink(n *Node) (*Link, error) {
	s, err := n.Size()
	if err != nil {
		return nil, err
	}

	h, err := n.Multihash()
	if err != nil {
		return nil, err
	}
	return &Link{
		Size: s,
		Hash: h,
	}, nil
}

// GetNode returns the MDAG Node that this link points to
Jeromy's avatar
Jeromy committed
81
func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) {
82 83 84 85
	if l.Node != nil {
		return l.Node, nil
	}

86
	return serv.Get(ctx, key.Key(l.Hash))
87 88
}

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
// GetNodeAndCache return the MDAG Node that the link points to and store a
// pointer to that node along with the link to speed up further retrivals. A
// timeout is to be specified to avoid taking too much time.
func (l *Link) GetNodeAndCache(ctx context.Context, serv DAGService, timeout time.Duration) (*Node, error) {
	if l.Node == nil {
		if timeout != 0 {
			var cancel context.CancelFunc
			ctx, cancel = context.WithTimeout(ctx, time.Minute)
			defer cancel()
		}
		nd, err := serv.Get(ctx, key.Key(l.Hash))
		if err != nil {
			return nil, err
		}
		l.Node = nd
	}

	return l.Node, nil
}

109 110
// AddNodeLink adds a link to another node.
func (n *Node) AddNodeLink(name string, that *Node) error {
111
	n.encoded = nil
112

113
	lnk, err := MakeLink(that)
114 115 116

	lnk.Name = name
	lnk.Node = that
117 118 119 120
	if err != nil {
		return err
	}

121 122
	n.AddRawLink(name, lnk)

123 124 125
	return nil
}

126
// AddNodeLinkClean adds a link to another node. without keeping a reference to
127 128
// the child node
func (n *Node) AddNodeLinkClean(name string, that *Node) error {
129
	n.encoded = nil
130 131 132 133
	lnk, err := MakeLink(that)
	if err != nil {
		return err
	}
134 135 136 137 138 139 140 141 142 143 144 145 146 147
	n.AddRawLink(name, lnk)

	return nil
}

// AddRawLink adds a copy of a link to this node
func (n *Node) AddRawLink(name string, l *Link) error {
	n.encoded = nil
	n.Links = append(n.Links, &Link{
		Name: name,
		Size: l.Size,
		Hash: l.Hash,
		Node: l.Node,
	})
148 149 150 151 152 153

	return nil
}

// Remove a link on this node by the given name
func (n *Node) RemoveNodeLink(name string) error {
154
	n.encoded = nil
Jeromy's avatar
Jeromy committed
155 156 157 158 159 160 161 162
	good := make([]*Link, 0, len(n.Links))
	var found bool

	for _, l := range n.Links {
		if l.Name != name {
			good = append(good, l)
		} else {
			found = true
163 164
		}
	}
Jeromy's avatar
Jeromy committed
165 166 167 168 169 170 171
	n.Links = good

	if !found {
		return ErrNotFound
	}

	return nil
172 173
}

174 175 176 177 178 179 180 181 182 183 184 185
// Return a copy of the link with given name
func (n *Node) GetNodeLink(name string) (*Link, error) {
	for _, l := range n.Links {
		if l.Name == name {
			return &Link{
				Name: l.Name,
				Size: l.Size,
				Hash: l.Hash,
				Node: l.Node,
			}, nil
		}
	}
186
	return nil, ErrLinkNotFound
187 188
}

189 190 191 192 193 194 195 196 197
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)
}

198 199 200 201
// Copy returns a copy of the node.
// NOTE: does not make copies of Node objects in the links.
func (n *Node) Copy() *Node {
	nnode := new(Node)
Jeromy's avatar
Jeromy committed
202 203 204 205
	if len(n.Data) > 0 {
		nnode.Data = make([]byte, len(n.Data))
		copy(nnode.Data, n.Data)
	}
206

Jeromy's avatar
Jeromy committed
207 208 209 210
	if len(n.Links) > 0 {
		nnode.Links = make([]*Link, len(n.Links))
		copy(nnode.Links, n.Links)
	}
211 212 213
	return nnode
}

214 215 216 217 218 219 220 221 222 223
// UpdateNodeLink return a copy of the node with the link name set to point to
// that. If a link of the same name existed, it is removed.
func (n *Node) UpdateNodeLink(name string, that *Node) (*Node, error) {
	newnode := n.Copy()
	err := newnode.RemoveNodeLink(name)
	err = nil // ignore error
	err = newnode.AddNodeLink(name, that)
	return newnode, err
}

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
// Size returns the total size of the data addressed by node,
// including the total sizes of references.
func (n *Node) Size() (uint64, error) {
	b, err := n.Encoded(false)
	if err != nil {
		return 0, err
	}

	s := uint64(len(b))
	for _, l := range n.Links {
		s += l.Size
	}
	return s, nil
}

// Stat returns statistics on the node.
240
func (n *Node) Stat() (*NodeStat, error) {
241 242
	enc, err := n.Encoded(false)
	if err != nil {
243
		return nil, err
244 245 246 247
	}

	cumSize, err := n.Size()
	if err != nil {
248
		return nil, err
249 250
	}

251 252 253 254 255
	key, err := n.Key()
	if err != nil {
		return nil, err
	}

256
	return &NodeStat{
257
		Hash:           key.B58String(),
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
		NumLinks:       len(n.Links),
		BlockSize:      len(enc),
		LinksSize:      len(enc) - len(n.Data), // includes framing.
		DataSize:       len(n.Data),
		CumulativeSize: int(cumSize),
	}, nil
}

// Multihash hashes the encoded data of this node.
func (n *Node) Multihash() (mh.Multihash, error) {
	// Note: Encoded generates the hash and puts it in n.cached.
	_, err := n.Encoded(false)
	if err != nil {
		return nil, err
	}

	return n.cached, nil
}

// Key returns the Multihash as a key, for maps.
278
func (n *Node) Key() (key.Key, error) {
279
	h, err := n.Multihash()
280
	return key.Key(h), err
281
}