node.go 5.94 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
}

Jeromy's avatar
Jeromy committed
27
type LinkSlice []*node.Link
28 29 30 31 32

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 }

33 34
func NodeWithData(d []byte) *ProtoNode {
	return &ProtoNode{data: d}
35 36
}

37
// AddNodeLink adds a link to another node.
38
func (n *ProtoNode) AddNodeLink(name string, that node.Node) error {
39
	n.encoded = nil
40

Jeromy's avatar
Jeromy committed
41
	lnk, err := node.MakeLink(that)
42 43 44 45
	if err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
46 47
	lnk.Name = name

48 49
	n.AddRawLink(name, lnk)

50 51 52
	return nil
}

53
// AddNodeLinkClean adds a link to another node. without keeping a reference to
54
// the child node
Jeromy's avatar
Jeromy committed
55
func (n *ProtoNode) AddNodeLinkClean(name string, that node.Node) error {
56
	n.encoded = nil
Jeromy's avatar
Jeromy committed
57
	lnk, err := node.MakeLink(that)
58 59 60
	if err != nil {
		return err
	}
61 62 63 64 65 66
	n.AddRawLink(name, lnk)

	return nil
}

// AddRawLink adds a copy of a link to this node
Jeromy's avatar
Jeromy committed
67
func (n *ProtoNode) AddRawLink(name string, l *node.Link) error {
68
	n.encoded = nil
Jeromy's avatar
Jeromy committed
69
	n.links = append(n.links, &node.Link{
70 71
		Name: name,
		Size: l.Size,
72
		Cid:  l.Cid,
73
	})
74 75 76 77 78

	return nil
}

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

84
	for _, l := range n.links {
Jeromy's avatar
Jeromy committed
85 86 87 88
		if l.Name != name {
			good = append(good, l)
		} else {
			found = true
89 90
		}
	}
91
	n.links = good
Jeromy's avatar
Jeromy committed
92 93 94 95 96 97

	if !found {
		return ErrNotFound
	}

	return nil
98 99
}

100
// Return a copy of the link with given name
Jeromy's avatar
Jeromy committed
101
func (n *ProtoNode) GetNodeLink(name string) (*node.Link, error) {
102
	for _, l := range n.links {
103
		if l.Name == name {
Jeromy's avatar
Jeromy committed
104
			return &node.Link{
105 106
				Name: l.Name,
				Size: l.Size,
107
				Cid:  l.Cid,
108 109 110
			}, nil
		}
	}
111
	return nil, ErrLinkNotFound
112 113
}

114 115 116 117 118 119 120 121 122 123 124 125 126 127
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
128
func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds DAGService, name string) (node.Node, error) {
129 130 131 132 133 134 135 136
	lnk, err := n.GetNodeLink(name)
	if err != nil {
		return nil, err
	}

	return lnk.GetNode(ctx, ds)
}

137
// Copy returns a copy of the node.
138
// NOTE: Does not make copies of Node objects in the links.
139
func (n *ProtoNode) Copy() node.Node {
140
	nnode := new(ProtoNode)
141 142 143
	if len(n.data) > 0 {
		nnode.data = make([]byte, len(n.data))
		copy(nnode.data, n.data)
Jeromy's avatar
Jeromy committed
144
	}
145

146
	if len(n.links) > 0 {
Jeromy's avatar
Jeromy committed
147
		nnode.links = make([]*node.Link, len(n.links))
148
		copy(nnode.links, n.links)
Jeromy's avatar
Jeromy committed
149
	}
150 151 152
	return nnode
}

153
func (n *ProtoNode) RawData() []byte {
Jeromy's avatar
Jeromy committed
154 155 156 157
	out, _ := n.EncodeProtobuf(false)
	return out
}

158
func (n *ProtoNode) Data() []byte {
159 160 161
	return n.data
}

162
func (n *ProtoNode) SetData(d []byte) {
163 164 165 166 167
	n.encoded = nil
	n.cached = nil
	n.data = d
}

168 169
// 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.
170
func (n *ProtoNode) UpdateNodeLink(name string, that *ProtoNode) (*ProtoNode, error) {
171
	newnode := n.Copy().(*ProtoNode)
172 173 174 175 176 177
	err := newnode.RemoveNodeLink(name)
	err = nil // ignore error
	err = newnode.AddNodeLink(name, that)
	return newnode, err
}

178 179
// Size returns the total size of the data addressed by node,
// including the total sizes of references.
180
func (n *ProtoNode) Size() (uint64, error) {
181
	b, err := n.EncodeProtobuf(false)
182 183 184 185 186
	if err != nil {
		return 0, err
	}

	s := uint64(len(b))
187
	for _, l := range n.links {
188 189 190 191 192 193
		s += l.Size
	}
	return s, nil
}

// Stat returns statistics on the node.
Jeromy's avatar
Jeromy committed
194
func (n *ProtoNode) Stat() (*node.NodeStat, error) {
195
	enc, err := n.EncodeProtobuf(false)
196
	if err != nil {
197
		return nil, err
198 199 200 201
	}

	cumSize, err := n.Size()
	if err != nil {
202
		return nil, err
203 204
	}

Jeromy's avatar
Jeromy committed
205
	return &node.NodeStat{
Jeromy's avatar
Jeromy committed
206
		Hash:           n.Cid().String(),
207
		NumLinks:       len(n.links),
208
		BlockSize:      len(enc),
209 210
		LinksSize:      len(enc) - len(n.data), // includes framing.
		DataSize:       len(n.data),
211 212 213 214
		CumulativeSize: int(cumSize),
	}, nil
}

215
func (n *ProtoNode) Loggable() map[string]interface{} {
Jeromy's avatar
Jeromy committed
216 217 218 219 220
	return map[string]interface{}{
		"node": n.String(),
	}
}

221
func (n *ProtoNode) Cid() *cid.Cid {
Jeromy's avatar
Jeromy committed
222 223 224 225 226
	h := n.Multihash()

	return cid.NewCidV0(h)
}

227
func (n *ProtoNode) String() string {
Jeromy's avatar
Jeromy committed
228 229 230
	return n.Cid().String()
}

231
// Multihash hashes the encoded data of this node.
232
func (n *ProtoNode) Multihash() mh.Multihash {
233
	// NOTE: EncodeProtobuf generates the hash and puts it in n.cached.
234
	_, err := n.EncodeProtobuf(false)
235
	if err != nil {
Jeromy's avatar
Jeromy committed
236 237
		// Note: no possibility exists for an error to be returned through here
		panic(err)
238 239
	}

Jeromy's avatar
Jeromy committed
240
	return n.cached.Hash()
241
}
242

Jeromy's avatar
Jeromy committed
243
func (n *ProtoNode) Links() []*node.Link {
244 245 246
	return n.links
}

Jeromy's avatar
Jeromy committed
247
func (n *ProtoNode) SetLinks(links []*node.Link) {
248 249 250
	n.links = links
}

251 252 253 254 255
func (n *ProtoNode) Resolve(path []string) (interface{}, []string, error) {
	return n.ResolveLink(path)
}

func (n *ProtoNode) ResolveLink(path []string) (*node.Link, []string, error) {
256 257 258 259 260 261 262 263 264 265 266 267
	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
}

268 269 270 271 272 273 274
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
	}

275 276 277 278 279 280
	out := make([]string, 0, len(n.links))
	for _, lnk := range n.links {
		out = append(out, lnk.Name)
	}
	return out
}