Skip to content

Commit 275d083

Browse files
authored
Merge pull request ipfs/go-merkledag#67 from ipfs/feat/ipld-in-ipfs
Use IPLD-prime: target merge branch This commit was moved from ipfs/go-merkledag@8794146
2 parents ab33100 + 98297d4 commit 275d083

File tree

7 files changed

+513
-133
lines changed

7 files changed

+513
-133
lines changed

ipld/merkledag/coding.go

+78-30
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ import (
66
"strings"
77

88
blocks "github.com/ipfs/go-block-format"
9-
pb "github.com/ipfs/go-merkledag/pb"
10-
119
cid "github.com/ipfs/go-cid"
12-
ipld "github.com/ipfs/go-ipld-format"
10+
format "github.com/ipfs/go-ipld-format"
11+
pb "github.com/ipfs/go-merkledag/pb"
12+
dagpb "github.com/ipld/go-codec-dagpb"
13+
ipld "github.com/ipld/go-ipld-prime"
14+
"github.com/ipld/go-ipld-prime/fluent/qp"
15+
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
1316
)
1417

1518
// Make sure the user doesn't upgrade this file.
@@ -22,38 +25,84 @@ const _ = pb.DoNotUpgradeFileEverItWillChangeYourHashes
2225

2326
// unmarshal decodes raw data into a *Node instance.
2427
// The conversion uses an intermediate PBNode.
25-
func (n *ProtoNode) unmarshal(encoded []byte) error {
26-
var pbn pb.PBNode
27-
if err := pbn.Unmarshal(encoded); err != nil {
28-
return fmt.Errorf("unmarshal failed. %v", err)
28+
func unmarshal(encodedBytes []byte) (*ProtoNode, error) {
29+
nb := dagpb.Type.PBNode.NewBuilder()
30+
if err := dagpb.DecodeBytes(nb, encodedBytes); err != nil {
31+
return nil, err
2932
}
33+
nd := nb.Build()
34+
return fromImmutableNode(&immutableProtoNode{encodedBytes, nd.(dagpb.PBNode)}), nil
35+
}
3036

31-
pbnl := pbn.GetLinks()
32-
n.links = make([]*ipld.Link, len(pbnl))
33-
for i, l := range pbnl {
34-
n.links[i] = &ipld.Link{Name: l.GetName(), Size: l.GetTsize()}
35-
c, err := cid.Cast(l.GetHash())
36-
if err != nil {
37-
return fmt.Errorf("link hash #%d is not valid multihash. %v", i, err)
37+
func fromImmutableNode(encoded *immutableProtoNode) *ProtoNode {
38+
n := new(ProtoNode)
39+
n.encoded = encoded
40+
if n.encoded.PBNode.Data.Exists() {
41+
n.data = n.encoded.PBNode.Data.Must().Bytes()
42+
}
43+
numLinks := n.encoded.PBNode.Links.Length()
44+
n.links = make([]*format.Link, numLinks)
45+
linkAllocs := make([]format.Link, numLinks)
46+
for i := int64(0); i < numLinks; i++ {
47+
next := n.encoded.PBNode.Links.Lookup(i)
48+
name := ""
49+
if next.FieldName().Exists() {
50+
name = next.FieldName().Must().String()
3851
}
39-
n.links[i].Cid = c
52+
c := cid.Undef
53+
c = next.FieldHash().Link().(cidlink.Link).Cid
54+
size := uint64(0)
55+
if next.FieldTsize().Exists() {
56+
size = uint64(next.FieldTsize().Must().Int())
57+
}
58+
link := &linkAllocs[i]
59+
link.Name = name
60+
link.Size = size
61+
link.Cid = c
62+
n.links[i] = link
63+
}
64+
return n
65+
}
66+
func (n *ProtoNode) marshalImmutable() (*immutableProtoNode, error) {
67+
nd, err := qp.BuildMap(dagpb.Type.PBNode, 2, func(ma ipld.MapAssembler) {
68+
qp.MapEntry(ma, "Links", qp.List(int64(len(n.links)), func(la ipld.ListAssembler) {
69+
for _, link := range n.links {
70+
qp.ListEntry(la, qp.Map(3, func(ma ipld.MapAssembler) {
71+
if link.Cid.Defined() {
72+
qp.MapEntry(ma, "Hash", qp.Link(cidlink.Link{Cid: link.Cid}))
73+
}
74+
qp.MapEntry(ma, "Name", qp.String(link.Name))
75+
qp.MapEntry(ma, "Tsize", qp.Int(int64(link.Size)))
76+
}))
77+
}
78+
}))
79+
if n.data != nil {
80+
qp.MapEntry(ma, "Data", qp.Bytes(n.data))
81+
}
82+
})
83+
if err != nil {
84+
return nil, err
4085
}
41-
sort.Stable(LinkSlice(n.links)) // keep links sorted
4286

43-
n.data = pbn.GetData()
44-
n.encoded = encoded
45-
return nil
87+
// 1KiB can be allocated on the stack, and covers most small nodes
88+
// without having to grow the buffer and cause allocations.
89+
enc := make([]byte, 0, 1024)
90+
91+
enc, err = dagpb.AppendEncode(enc, nd)
92+
if err != nil {
93+
return nil, err
94+
}
95+
return &immutableProtoNode{enc, nd.(dagpb.PBNode)}, nil
4696
}
4797

4898
// Marshal encodes a *Node instance into a new byte slice.
4999
// The conversion uses an intermediate PBNode.
50100
func (n *ProtoNode) Marshal() ([]byte, error) {
51-
pbn := n.GetPBNode()
52-
data, err := pbn.Marshal()
101+
enc, err := n.marshalImmutable()
53102
if err != nil {
54-
return data, fmt.Errorf("marshal failed. %v", err)
103+
return nil, err
55104
}
56-
return data, nil
105+
return enc.encoded, nil
57106
}
58107

59108
// GetPBNode converts *ProtoNode into it's protocol buffer variant.
@@ -88,28 +137,27 @@ func (n *ProtoNode) EncodeProtobuf(force bool) ([]byte, error) {
88137
if n.encoded == nil || force {
89138
n.cached = cid.Undef
90139
var err error
91-
n.encoded, err = n.Marshal()
140+
n.encoded, err = n.marshalImmutable()
92141
if err != nil {
93142
return nil, err
94143
}
95144
}
96145

97146
if !n.cached.Defined() {
98-
c, err := n.CidBuilder().Sum(n.encoded)
147+
c, err := n.CidBuilder().Sum(n.encoded.encoded)
99148
if err != nil {
100149
return nil, err
101150
}
102151

103152
n.cached = c
104153
}
105154

106-
return n.encoded, nil
155+
return n.encoded.encoded, nil
107156
}
108157

109158
// DecodeProtobuf decodes raw data and returns a new Node instance.
110159
func DecodeProtobuf(encoded []byte) (*ProtoNode, error) {
111-
n := new(ProtoNode)
112-
err := n.unmarshal(encoded)
160+
n, err := unmarshal(encoded)
113161
if err != nil {
114162
return nil, fmt.Errorf("incorrectly formatted merkledag node: %s", err)
115163
}
@@ -118,7 +166,7 @@ func DecodeProtobuf(encoded []byte) (*ProtoNode, error) {
118166

119167
// DecodeProtobufBlock is a block decoder for protobuf IPLD nodes conforming to
120168
// node.DecodeBlockFunc
121-
func DecodeProtobufBlock(b blocks.Block) (ipld.Node, error) {
169+
func DecodeProtobufBlock(b blocks.Block) (format.Node, error) {
122170
c := b.Cid()
123171
if c.Type() != cid.DagProtobuf {
124172
return nil, fmt.Errorf("this function can only decode protobuf nodes")
@@ -138,4 +186,4 @@ func DecodeProtobufBlock(b blocks.Block) (ipld.Node, error) {
138186
}
139187

140188
// Type assertion
141-
var _ ipld.DecodeBlockFunc = DecodeProtobufBlock
189+
var _ format.DecodeBlockFunc = DecodeProtobufBlock

ipld/merkledag/coding_test.go

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package merkledag_test
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"testing"
7+
8+
cid "github.com/ipfs/go-cid"
9+
ipld "github.com/ipfs/go-ipld-format"
10+
"github.com/ipfs/go-merkledag"
11+
)
12+
13+
var benchInput []byte
14+
15+
func init() {
16+
someData := bytes.Repeat([]byte("some plaintext data\n"), 10)
17+
// make a test CID -- doesn't matter just to add as a link
18+
someCid, _ := cid.Cast([]byte{1, 85, 0, 5, 0, 1, 2, 3, 4})
19+
20+
node := &merkledag.ProtoNode{}
21+
node.SetData(someData)
22+
for i := 0; i < 10; i++ {
23+
node.AddRawLink(fmt.Sprintf("%d", i), &ipld.Link{
24+
Size: 10,
25+
Cid: someCid,
26+
})
27+
}
28+
29+
enc, err := node.EncodeProtobuf(true)
30+
if err != nil {
31+
panic(err)
32+
}
33+
benchInput = enc
34+
}
35+
36+
func BenchmarkRoundtrip(b *testing.B) {
37+
b.ReportAllocs()
38+
b.RunParallel(func(pb *testing.PB) {
39+
for pb.Next() {
40+
node, err := merkledag.DecodeProtobuf(benchInput)
41+
if err != nil {
42+
b.Fatal(err)
43+
}
44+
45+
enc, err := node.EncodeProtobuf(true)
46+
if err != nil {
47+
b.Fatal(err)
48+
}
49+
_ = enc
50+
}
51+
})
52+
}

0 commit comments

Comments
 (0)