node.go 6.49 KB
Newer Older
1 2 3
package merkledag

import (
4
	"context"
5
	"encoding/json"
Jeromy's avatar
Jeromy committed
6
	"fmt"
Jeromy's avatar
Jeromy committed
7

Jeromy's avatar
Jeromy committed
8
	node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node"
9
	mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash"
Jeromy's avatar
Jeromy committed
10
	cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid"
11 12
)

Jeromy's avatar
Jeromy committed
13
var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node")
14 15
var ErrLinkNotFound = fmt.Errorf("no link by that name")

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

	// cache encoded/marshaled value
	encoded []byte

Jeromy's avatar
Jeromy committed
25
	cached *cid.Cid
26

27 28 29 30 31 32 33 34 35
	// Prefix specifies cid version and hashing function
	Prefix cid.Prefix
}

var defaultCidPrefix = cid.Prefix{
	Codec:    cid.DagProtobuf,
	MhLength: -1,
	MhType:   mh.SHA2_256,
	Version:  0,
36 37
}

Jeromy's avatar
Jeromy committed
38
type LinkSlice []*node.Link
39 40 41 42 43

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 }

44 45
func NodeWithData(d []byte) *ProtoNode {
	return &ProtoNode{data: d}
46 47
}

48
// AddNodeLink adds a link to another node.
49
func (n *ProtoNode) AddNodeLink(name string, that node.Node) error {
50
	n.encoded = nil
51

Jeromy's avatar
Jeromy committed
52
	lnk, err := node.MakeLink(that)
53 54 55 56
	if err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
57 58
	lnk.Name = name

59 60
	n.AddRawLink(name, lnk)

61 62 63
	return nil
}

64
// AddNodeLinkClean adds a link to another node. without keeping a reference to
65
// the child node
Jeromy's avatar
Jeromy committed
66
func (n *ProtoNode) AddNodeLinkClean(name string, that node.Node) error {
67
	n.encoded = nil
Jeromy's avatar
Jeromy committed
68
	lnk, err := node.MakeLink(that)
69 70 71
	if err != nil {
		return err
	}
72 73 74 75 76 77
	n.AddRawLink(name, lnk)

	return nil
}

// AddRawLink adds a copy of a link to this node
Jeromy's avatar
Jeromy committed
78
func (n *ProtoNode) AddRawLink(name string, l *node.Link) error {
79
	n.encoded = nil
Jeromy's avatar
Jeromy committed
80
	n.links = append(n.links, &node.Link{
81 82
		Name: name,
		Size: l.Size,
83
		Cid:  l.Cid,
84
	})
85 86 87 88 89

	return nil
}

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

95
	for _, l := range n.links {
Jeromy's avatar
Jeromy committed
96 97 98 99
		if l.Name != name {
			good = append(good, l)
		} else {
			found = true
100 101
		}
	}
102
	n.links = good
Jeromy's avatar
Jeromy committed
103 104 105 106 107 108

	if !found {
		return ErrNotFound
	}

	return nil
109 110
}

111
// Return a copy of the link with given name
Jeromy's avatar
Jeromy committed
112
func (n *ProtoNode) GetNodeLink(name string) (*node.Link, error) {
113
	for _, l := range n.links {
114
		if l.Name == name {
Jeromy's avatar
Jeromy committed
115
			return &node.Link{
116 117
				Name: l.Name,
				Size: l.Size,
118
				Cid:  l.Cid,
119 120 121
			}, nil
		}
	}
122
	return nil, ErrLinkNotFound
123 124
}

125 126 127 128 129 130 131 132 133 134 135 136 137 138
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
}

Jeromy's avatar
Jeromy committed
139
func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds DAGService, name string) (node.Node, error) {
140 141 142 143 144 145 146 147
	lnk, err := n.GetNodeLink(name)
	if err != nil {
		return nil, err
	}

	return lnk.GetNode(ctx, ds)
}

148
// Copy returns a copy of the node.
149
// NOTE: Does not make copies of Node objects in the links.
150
func (n *ProtoNode) Copy() node.Node {
151
	nnode := new(ProtoNode)
152 153 154
	if len(n.data) > 0 {
		nnode.data = make([]byte, len(n.data))
		copy(nnode.data, n.data)
Jeromy's avatar
Jeromy committed
155
	}
156

157
	if len(n.links) > 0 {
Jeromy's avatar
Jeromy committed
158
		nnode.links = make([]*node.Link, len(n.links))
159
		copy(nnode.links, n.links)
Jeromy's avatar
Jeromy committed
160
	}
161 162 163
	return nnode
}

164
func (n *ProtoNode) RawData() []byte {
Jeromy's avatar
Jeromy committed
165 166 167 168
	out, _ := n.EncodeProtobuf(false)
	return out
}

169
func (n *ProtoNode) Data() []byte {
170 171 172
	return n.data
}

173
func (n *ProtoNode) SetData(d []byte) {
174 175 176 177 178
	n.encoded = nil
	n.cached = nil
	n.data = d
}

179 180
// 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.
181
func (n *ProtoNode) UpdateNodeLink(name string, that *ProtoNode) (*ProtoNode, error) {
182
	newnode := n.Copy().(*ProtoNode)
183 184 185 186 187 188
	err := newnode.RemoveNodeLink(name)
	err = nil // ignore error
	err = newnode.AddNodeLink(name, that)
	return newnode, err
}

189 190
// Size returns the total size of the data addressed by node,
// including the total sizes of references.
191
func (n *ProtoNode) Size() (uint64, error) {
192
	b, err := n.EncodeProtobuf(false)
193 194 195 196 197
	if err != nil {
		return 0, err
	}

	s := uint64(len(b))
198
	for _, l := range n.links {
199 200 201 202 203 204
		s += l.Size
	}
	return s, nil
}

// Stat returns statistics on the node.
Jeromy's avatar
Jeromy committed
205
func (n *ProtoNode) Stat() (*node.NodeStat, error) {
206
	enc, err := n.EncodeProtobuf(false)
207
	if err != nil {
208
		return nil, err
209 210 211 212
	}

	cumSize, err := n.Size()
	if err != nil {
213
		return nil, err
214 215
	}

Jeromy's avatar
Jeromy committed
216
	return &node.NodeStat{
Jeromy's avatar
Jeromy committed
217
		Hash:           n.Cid().String(),
218
		NumLinks:       len(n.links),
219
		BlockSize:      len(enc),
220 221
		LinksSize:      len(enc) - len(n.data), // includes framing.
		DataSize:       len(n.data),
222 223 224 225
		CumulativeSize: int(cumSize),
	}, nil
}

226
func (n *ProtoNode) Loggable() map[string]interface{} {
Jeromy's avatar
Jeromy committed
227 228 229 230 231
	return map[string]interface{}{
		"node": n.String(),
	}
}

232 233 234 235 236 237 238 239 240
func (n *ProtoNode) MarshalJSON() ([]byte, error) {
	out := map[string]interface{}{
		"data":  n.data,
		"links": n.links,
	}

	return json.Marshal(out)
}

241
func (n *ProtoNode) Cid() *cid.Cid {
242 243 244 245 246 247 248 249 250 251 252 253 254
	if n.encoded != nil && n.cached != nil {
		return n.cached
	}

	if n.Prefix.Codec == 0 {
		n.Prefix = defaultCidPrefix
	}

	c, err := n.Prefix.Sum(n.RawData())
	if err != nil {
		// programmer error
		panic(err)
	}
Jeromy's avatar
Jeromy committed
255

256 257
	n.cached = c
	return c
Jeromy's avatar
Jeromy committed
258 259
}

260
func (n *ProtoNode) String() string {
Jeromy's avatar
Jeromy committed
261 262 263
	return n.Cid().String()
}

264
// Multihash hashes the encoded data of this node.
265
func (n *ProtoNode) Multihash() mh.Multihash {
266
	// NOTE: EncodeProtobuf generates the hash and puts it in n.cached.
267
	_, err := n.EncodeProtobuf(false)
268
	if err != nil {
Jeromy's avatar
Jeromy committed
269 270
		// Note: no possibility exists for an error to be returned through here
		panic(err)
271 272
	}

Jeromy's avatar
Jeromy committed
273
	return n.cached.Hash()
274
}
275

Jeromy's avatar
Jeromy committed
276
func (n *ProtoNode) Links() []*node.Link {
277 278 279
	return n.links
}

Jeromy's avatar
Jeromy committed
280
func (n *ProtoNode) SetLinks(links []*node.Link) {
281 282 283
	n.links = links
}

284 285 286 287 288
func (n *ProtoNode) Resolve(path []string) (interface{}, []string, error) {
	return n.ResolveLink(path)
}

func (n *ProtoNode) ResolveLink(path []string) (*node.Link, []string, error) {
289 290 291 292 293 294 295 296 297 298 299 300
	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
}

301 302 303 304 305 306 307
func (n *ProtoNode) Tree(p string, depth int) []string {
	// ProtoNodes are only ever one path deep, anything below that results in
	// nothing
	if p != "" {
		return nil
	}

308 309 310 311 312 313
	out := make([]string, 0, len(n.links))
	for _, lnk := range n.links {
		out = append(out, lnk.Name)
	}
	return out
}