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

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

7
	cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid"
8 9
	mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash"
	key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key"
Jeromy's avatar
Jeromy committed
10
	node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node"
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
}

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

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 }

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

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

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

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

49 50
	n.AddRawLink(name, lnk)

51 52 53
	return nil
}

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

	return nil
}

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

	return nil
}

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

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

	if !found {
		return ErrNotFound
	}

	return nil
99 100
}

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

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

	return lnk.GetNode(ctx, ds)
}

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

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

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

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

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

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

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

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

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

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

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

216
func (n *ProtoNode) Key() key.Key {
Jeromy's avatar
Jeromy committed
217 218 219
	return key.Key(n.Multihash())
}

220
func (n *ProtoNode) Loggable() map[string]interface{} {
Jeromy's avatar
Jeromy committed
221 222 223 224 225
	return map[string]interface{}{
		"node": n.String(),
	}
}

226
func (n *ProtoNode) Cid() *cid.Cid {
Jeromy's avatar
Jeromy committed
227 228 229 230 231
	h := n.Multihash()

	return cid.NewCidV0(h)
}

232
func (n *ProtoNode) String() string {
Jeromy's avatar
Jeromy committed
233 234 235
	return n.Cid().String()
}

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

Jeromy's avatar
Jeromy committed
245
	return n.cached.Hash()
246
}
247

Jeromy's avatar
Jeromy committed
248
func (n *ProtoNode) Links() []*node.Link {
249 250 251
	return n.links
}

Jeromy's avatar
Jeromy committed
252
func (n *ProtoNode) SetLinks(links []*node.Link) {
253 254 255
	n.links = links
}

Jeromy's avatar
Jeromy committed
256
func (n *ProtoNode) Resolve(path []string) (*node.Link, []string, error) {
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
	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
}