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

import (
	"fmt"

6
	"context"
Jeromy's avatar
Jeromy committed
7

8
	cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid"
9 10
	mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash"
	key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key"
11 12
)

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

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

	// cache encoded/marshaled value
	encoded []byte

Jeromy's avatar
Jeromy committed
24
	cached *cid.Cid
25 26 27 28
}

// 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
}

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
51
	Cid *cid.Cid
52 53 54 55 56 57 58 59 60
}

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
61
func MakeLink(n Node) (*Link, error) {
62 63 64 65 66 67 68
	s, err := n.Size()
	if err != nil {
		return nil, err
	}

	return &Link{
		Size: s,
69
		Cid:  n.Cid(),
70 71 72 73
	}, nil
}

// GetNode returns the MDAG Node that this link points to
74 75
func (l *Link) GetNode(ctx context.Context, serv DAGService) (Node, error) {
	return serv.Get(ctx, l.Cid)
76 77
}

78 79
func NodeWithData(d []byte) *ProtoNode {
	return &ProtoNode{data: d}
80 81
}

82
// AddNodeLink adds a link to another node.
83
func (n *ProtoNode) AddNodeLink(name string, that *ProtoNode) error {
84
	n.encoded = nil
85

86
	lnk, err := MakeLink(that)
87 88

	lnk.Name = name
89 90 91 92
	if err != nil {
		return err
	}

93 94
	n.AddRawLink(name, lnk)

95 96 97
	return nil
}

98
// AddNodeLinkClean adds a link to another node. without keeping a reference to
99
// the child node
100
func (n *ProtoNode) AddNodeLinkClean(name string, that Node) error {
101
	n.encoded = nil
102 103 104 105
	lnk, err := MakeLink(that)
	if err != nil {
		return err
	}
106 107 108 109 110 111
	n.AddRawLink(name, lnk)

	return nil
}

// AddRawLink adds a copy of a link to this node
112
func (n *ProtoNode) AddRawLink(name string, l *Link) error {
113
	n.encoded = nil
114
	n.links = append(n.links, &Link{
115 116
		Name: name,
		Size: l.Size,
117
		Cid:  l.Cid,
118
	})
119 120 121 122 123

	return nil
}

// Remove a link on this node by the given name
124
func (n *ProtoNode) RemoveNodeLink(name string) error {
125
	n.encoded = nil
126
	good := make([]*Link, 0, len(n.links))
Jeromy's avatar
Jeromy committed
127 128
	var found bool

129
	for _, l := range n.links {
Jeromy's avatar
Jeromy committed
130 131 132 133
		if l.Name != name {
			good = append(good, l)
		} else {
			found = true
134 135
		}
	}
136
	n.links = good
Jeromy's avatar
Jeromy committed
137 138 139 140 141 142

	if !found {
		return ErrNotFound
	}

	return nil
143 144
}

145
// Return a copy of the link with given name
146 147
func (n *ProtoNode) GetNodeLink(name string) (*Link, error) {
	for _, l := range n.links {
148 149 150 151
		if l.Name == name {
			return &Link{
				Name: l.Name,
				Size: l.Size,
152
				Cid:  l.Cid,
153 154 155
			}, nil
		}
	}
156
	return nil, ErrLinkNotFound
157 158
}

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node")

func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds DAGService, name string) (*ProtoNode, error) {
	nd, err := n.GetLinkedNode(ctx, ds, name)
	if err != nil {
		return nil, err
	}

	pbnd, ok := nd.(*ProtoNode)
	if !ok {
		return nil, ErrNotProtobuf
	}

	return pbnd, nil
}

func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds DAGService, name string) (Node, error) {
176 177 178 179 180 181 182 183
	lnk, err := n.GetNodeLink(name)
	if err != nil {
		return nil, err
	}

	return lnk.GetNode(ctx, ds)
}

184
// Copy returns a copy of the node.
185
// NOTE: Does not make copies of Node objects in the links.
186 187
func (n *ProtoNode) Copy() *ProtoNode {
	nnode := new(ProtoNode)
188 189 190
	if len(n.data) > 0 {
		nnode.data = make([]byte, len(n.data))
		copy(nnode.data, n.data)
Jeromy's avatar
Jeromy committed
191
	}
192

193 194 195
	if len(n.links) > 0 {
		nnode.links = make([]*Link, len(n.links))
		copy(nnode.links, n.links)
Jeromy's avatar
Jeromy committed
196
	}
197 198 199
	return nnode
}

200
func (n *ProtoNode) RawData() []byte {
Jeromy's avatar
Jeromy committed
201 202 203 204
	out, _ := n.EncodeProtobuf(false)
	return out
}

205
func (n *ProtoNode) Data() []byte {
206 207 208
	return n.data
}

209
func (n *ProtoNode) SetData(d []byte) {
210 211 212 213 214
	n.encoded = nil
	n.cached = nil
	n.data = d
}

215 216
// 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.
217
func (n *ProtoNode) UpdateNodeLink(name string, that *ProtoNode) (*ProtoNode, error) {
218 219 220 221 222 223 224
	newnode := n.Copy()
	err := newnode.RemoveNodeLink(name)
	err = nil // ignore error
	err = newnode.AddNodeLink(name, that)
	return newnode, err
}

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

	s := uint64(len(b))
234
	for _, l := range n.links {
235 236 237 238 239 240
		s += l.Size
	}
	return s, nil
}

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

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

252
	return &NodeStat{
Jeromy's avatar
Jeromy committed
253
		Hash:           n.Key().B58String(),
254
		NumLinks:       len(n.links),
255
		BlockSize:      len(enc),
256 257
		LinksSize:      len(enc) - len(n.data), // includes framing.
		DataSize:       len(n.data),
258 259 260 261
		CumulativeSize: int(cumSize),
	}, nil
}

262
func (n *ProtoNode) Key() key.Key {
Jeromy's avatar
Jeromy committed
263 264 265
	return key.Key(n.Multihash())
}

266
func (n *ProtoNode) Loggable() map[string]interface{} {
Jeromy's avatar
Jeromy committed
267 268 269 270 271
	return map[string]interface{}{
		"node": n.String(),
	}
}

272
func (n *ProtoNode) Cid() *cid.Cid {
Jeromy's avatar
Jeromy committed
273 274 275 276 277
	h := n.Multihash()

	return cid.NewCidV0(h)
}

278
func (n *ProtoNode) String() string {
Jeromy's avatar
Jeromy committed
279 280 281
	return n.Cid().String()
}

282
// Multihash hashes the encoded data of this node.
283
func (n *ProtoNode) Multihash() mh.Multihash {
284
	// NOTE: EncodeProtobuf generates the hash and puts it in n.cached.
285
	_, err := n.EncodeProtobuf(false)
286
	if err != nil {
Jeromy's avatar
Jeromy committed
287 288
		// Note: no possibility exists for an error to be returned through here
		panic(err)
289 290
	}

Jeromy's avatar
Jeromy committed
291
	return n.cached.Hash()
292
}
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321

func (n *ProtoNode) Links() []*Link {
	return n.links
}

func (n *ProtoNode) SetLinks(links []*Link) {
	n.links = links
}

func (n *ProtoNode) Resolve(path []string) (*Link, []string, error) {
	if len(path) == 0 {
		return nil, nil, fmt.Errorf("end of path, no more links to resolve")
	}

	lnk, err := n.GetNodeLink(path[0])
	if err != nil {
		return nil, nil, err
	}

	return lnk, path[1:], nil
}

func (n *ProtoNode) Tree() []string {
	out := make([]string, 0, len(n.links))
	for _, lnk := range n.links {
		out = append(out, lnk.Name)
	}
	return out
}