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

import (
4
	"context"
Jeromy's avatar
Jeromy committed
5
	"fmt"
Jeromy's avatar
Jeromy committed
6

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

Jeromy's avatar
Jeromy committed
12
var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node")
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
type ProtoNode struct {
Jeromy's avatar
Jeromy committed
18
	links []*node.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 29 30 31 32 33 34
	// 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,
35 36
}

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

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 }

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

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

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

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

58 59
	n.AddRawLink(name, lnk)

60 61 62
	return nil
}

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

	return nil
}

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

	return nil
}

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

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

	if !found {
		return ErrNotFound
	}

	return nil
108 109
}

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

124 125 126 127 128 129 130 131 132 133 134 135 136 137
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
138
func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds DAGService, name string) (node.Node, error) {
139 140 141 142 143 144 145 146
	lnk, err := n.GetNodeLink(name)
	if err != nil {
		return nil, err
	}

	return lnk.GetNode(ctx, ds)
}

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

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

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

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

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

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

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

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

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

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

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

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

231
func (n *ProtoNode) Cid() *cid.Cid {
232 233 234 235 236 237 238 239 240 241 242 243 244
	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
245

246 247
	n.cached = c
	return c
Jeromy's avatar
Jeromy committed
248 249
}

250
func (n *ProtoNode) String() string {
Jeromy's avatar
Jeromy committed
251 252 253
	return n.Cid().String()
}

254
// Multihash hashes the encoded data of this node.
255
func (n *ProtoNode) Multihash() mh.Multihash {
256
	// NOTE: EncodeProtobuf generates the hash and puts it in n.cached.
257
	_, err := n.EncodeProtobuf(false)
258
	if err != nil {
Jeromy's avatar
Jeromy committed
259 260
		// Note: no possibility exists for an error to be returned through here
		panic(err)
261 262
	}

Jeromy's avatar
Jeromy committed
263
	return n.cached.Hash()
264
}
265

Jeromy's avatar
Jeromy committed
266
func (n *ProtoNode) Links() []*node.Link {
267 268 269
	return n.links
}

Jeromy's avatar
Jeromy committed
270
func (n *ProtoNode) SetLinks(links []*node.Link) {
271 272 273
	n.links = links
}

274 275 276 277 278
func (n *ProtoNode) Resolve(path []string) (interface{}, []string, error) {
	return n.ResolveLink(path)
}

func (n *ProtoNode) ResolveLink(path []string) (*node.Link, []string, error) {
279 280 281 282 283 284 285 286 287 288 289 290
	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
}

291 292 293 294 295 296 297
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
	}

298 299 300 301 302 303
	out := make([]string, 0, len(n.links))
	for _, lnk := range n.links {
		out = append(out, lnk.Name)
	}
	return out
}