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

import (
	"fmt"

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

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

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

14 15 16 17 18 19 20 21 22 23 24 25 26 27
// 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 {
28
	Hash           string
29
	NumLinks       int // number of links in link table
30
	BlockSize      int // size of the raw, encoded data
31 32
	LinksSize      int // size of the links segment
	DataSize       int // size of the data segment
33
	CumulativeSize int // cumulative size of object and its references
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
}

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
53
	node *Node
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
}

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
}

79 80
// GetCachedNode returns the MDAG Node that was cached, or nil
func (l *Link) GetCachedNode() *Node {
81
	return l.node
82 83
}

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

90
	return serv.Get(ctx, key.Key(l.Hash))
91 92
}

93 94 95
// 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.
96
func (l *Link) GetNodeAndCache(ctx context.Context, serv DAGService) (*Node, error) {
97
	if l.node == nil {
98 99 100 101
		nd, err := serv.Get(ctx, key.Key(l.Hash))
		if err != nil {
			return nil, err
		}
102
		l.node = nd
103 104
	}

105
	return l.node, nil
106 107
}

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

112
	lnk, err := MakeLink(that)
113 114

	lnk.Name = name
115
	lnk.node = that
116 117 118 119
	if err != nil {
		return err
	}

120 121
	n.AddRawLink(name, lnk)

122 123 124
	return nil
}

125
// AddNodeLinkClean adds a link to another node. without keeping a reference to
126 127
// the child node
func (n *Node) AddNodeLinkClean(name string, that *Node) error {
128
	n.encoded = nil
129 130 131 132
	lnk, err := MakeLink(that)
	if err != nil {
		return err
	}
133 134 135 136 137 138 139 140 141 142 143 144
	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,
145
		node: l.node,
146
	})
147 148 149 150 151 152

	return nil
}

// Remove a link on this node by the given name
func (n *Node) RemoveNodeLink(name string) error {
153
	n.encoded = nil
Jeromy's avatar
Jeromy committed
154 155 156 157 158 159 160 161
	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
162 163
		}
	}
Jeromy's avatar
Jeromy committed
164 165 166 167 168 169 170
	n.Links = good

	if !found {
		return ErrNotFound
	}

	return nil
171 172
}

173 174 175 176 177 178 179 180
// 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,
181
				node: l.node,
182 183 184
			}, nil
		}
	}
185
	return nil, ErrLinkNotFound
186 187
}

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

197 198 199 200
// 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
201 202 203 204
	if len(n.Data) > 0 {
		nnode.Data = make([]byte, len(n.Data))
		copy(nnode.Data, n.Data)
	}
205

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

213 214 215 216 217 218 219 220 221 222
// 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
}

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
// 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.
239
func (n *Node) Stat() (*NodeStat, error) {
240 241
	enc, err := n.Encoded(false)
	if err != nil {
242
		return nil, err
243 244 245 246
	}

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

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

255
	return &NodeStat{
256
		Hash:           key.B58String(),
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
		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.
277
func (n *Node) Key() (key.Key, error) {
278
	h, err := n.Multihash()
279
	return key.Key(h), err
280
}