-
Notifications
You must be signed in to change notification settings - Fork 6
Update ipld prime, use proper code-gen #5
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,8 +7,8 @@ import ( | |
|
||
"github.com/ipfs/go-cid" | ||
merkledag_pb "github.com/ipfs/go-merkledag/pb" | ||
"github.com/ipld/go-ipld-prime/fluent" | ||
cidlink "github.com/ipld/go-ipld-prime/linking/cid" | ||
"github.com/ipld/go-ipld-prime/schema" | ||
) | ||
|
||
// byteAccessor is a reader interface that can access underlying bytes | ||
|
@@ -17,8 +17,8 @@ type byteAccesor interface { | |
} | ||
|
||
// DecodeDagProto is a fast path decoding to protobuf | ||
// from PBNode__NodeBuilders | ||
func (nb _PBNode__NodeBuilder) DecodeDagProto(r io.Reader) error { | ||
// from PBNode__Builders | ||
func (nb *_PBNode__Builder) DecodeDagProto(r io.Reader) error { | ||
var pbn merkledag_pb.PBNode | ||
var encoded []byte | ||
var err error | ||
|
@@ -34,64 +34,63 @@ func (nb _PBNode__NodeBuilder) DecodeDagProto(r io.Reader) error { | |
if err := pbn.Unmarshal(encoded); err != nil { | ||
return fmt.Errorf("unmarshal failed. %v", err) | ||
} | ||
pbLinks := make([]PBLink, 0, len(pbn.Links)) | ||
for _, link := range pbn.Links { | ||
hash, err := cid.Cast(link.GetHash()) | ||
return fluent.Recover(func() { | ||
fb := fluent.WrapAssembler(nb) | ||
fb.CreateMap(0, func(fmb fluent.MapAssembler) { | ||
fmb.AssembleEntry("Links").CreateList(len(pbn.Links), func(flb fluent.ListAssembler) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I think @rvagg 's comment about the links list here suggests there should be a branch on I can't decide if I think that's weird or not, though, now that I think about it. I'm hedging on if I think we should update our IPLD Schema describing this to just say that the list is required... and then if it's zero-length, sure, we can encode it to the proto wire format as missing. That's a discussion that probably @rvagg and I should hash out on our specs documents about it though. If this is the behavior this code has had already, I think it's fine to keep it today. (I don't want to force you through testing a change like this in either direction today...) |
||
for _, link := range pbn.Links { | ||
hash, err := cid.Cast(link.GetHash()) | ||
|
||
if err != nil { | ||
return fmt.Errorf("unmarshal failed. %v", err) | ||
} | ||
pbLinks = append(pbLinks, PBLink{ | ||
d: PBLink__Content{ | ||
Hash: MaybeLink{ | ||
Maybe: schema.Maybe_Value, | ||
Value: Link{cidlink.Link{Cid: hash}}, | ||
}, | ||
Name: MaybeString{ | ||
Maybe: schema.Maybe_Value, | ||
Value: String{link.GetName()}, | ||
}, | ||
Tsize: MaybeInt{ | ||
Maybe: schema.Maybe_Value, | ||
Value: Int{int(link.GetTsize())}, | ||
}, | ||
}, | ||
if err != nil { | ||
panic(fluent.Error{Err: fmt.Errorf("unmarshal failed. %v", err)}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rvagg : I went deep on exactly what happens here if the protobuf has a nil value for hash, because of its relevance to our other discussions about documenting this behavior: tl;dr: a lack of hash here is not actually tolerated by this code: we get an error returned here. (In detail: So I think this is a pretty solid argument that we don't have to consider |
||
} | ||
flb.AssembleValue().CreateMap(0, func(fmb fluent.MapAssembler) { | ||
fmb.AssembleEntry("Hash").AssignLink(cidlink.Link{Cid: hash}) | ||
fmb.AssembleEntry("Name").AssignString(link.GetName()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm moderately confused what the protobuf library is doing here -- we had to hand the protobuf lib a pointer to the name string elsewhere, because it's option in protobuf's mind, right? But here it's returning a string (no pointer). Does this panic if the string is missing? Or silently coerce it to an empty string? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yea it silently coerces it tp empty string. Which is weird behavior except -- that's exactly what go-merkledag uses in in IPFS. My basic approach is follow as close as possible so as to not break things. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, cool. I appreciate that, thanks for talking through it with us so we have it also as input to the other standardization quests @rvagg mentioned :) |
||
fmb.AssembleEntry("Tsize").AssignInt(int(link.GetTsize())) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question as above about name strings. Does this panic if the field isn't in the protobuf? Or silently coerce it to a zero? |
||
}) | ||
} | ||
}) | ||
fmb.AssembleEntry("Data").AssignBytes(pbn.GetData()) | ||
}) | ||
} | ||
nb.nd.d.Links.x = pbLinks | ||
nb.nd.d.Data.x = pbn.GetData() | ||
return nil | ||
}) | ||
} | ||
|
||
// EncodeDagProto is a fast path encoding to protobuf | ||
// for PBNode types | ||
func (nd PBNode) EncodeDagProto(w io.Writer) error { | ||
pbn := new(merkledag_pb.PBNode) | ||
pbn.Links = make([]*merkledag_pb.PBLink, 0, len(nd.d.Links.x)) | ||
for _, link := range nd.d.Links.x { | ||
pbn.Links = make([]*merkledag_pb.PBLink, 0, nd.FieldLinks().Length()) | ||
linksIter := nd.FieldLinks().ListIterator() | ||
for !linksIter.Done() { | ||
_, nlink, err := linksIter.Next() | ||
if err != nil { | ||
return err | ||
} | ||
link := nlink.(PBLink) | ||
var hash []byte | ||
if link.d.Hash.Maybe == schema.Maybe_Value { | ||
cid := link.d.Hash.Value.x.(cidlink.Link).Cid | ||
if link.FieldHash().Exists() { | ||
cid := link.FieldHash().Must().Link().(cidlink.Link).Cid | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if cid.Defined() { | ||
hash = cid.Bytes() | ||
} | ||
} | ||
var name *string | ||
if link.d.Name.Maybe == schema.Maybe_Value { | ||
tmp := link.d.Name.Value.x | ||
if link.FieldName().Exists() { | ||
tmp := link.FieldName().Must().String() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd write a review about how all this causes unfortunate allocations, but... it's unavoidable in interacting with protobufs, as I understand it. Ah well. (Fun fact if we ever come back to optimize this though, without just tearing deep through and doing the parse protobuf ourselves completely: I think we could bump these |
||
name = &tmp | ||
} | ||
var tsize *uint64 | ||
if link.d.Tsize.Maybe == schema.Maybe_Value { | ||
tmp := uint64(link.d.Tsize.Value.x) | ||
if link.FieldTsize().Exists() { | ||
tmp := uint64(link.FieldTsize().Must().Int()) | ||
tsize = &tmp | ||
} | ||
pbn.Links = append(pbn.Links, &merkledag_pb.PBLink{ | ||
Hash: hash, | ||
Name: name, | ||
Tsize: tsize}) | ||
} | ||
pbn.Data = nd.d.Data.x | ||
pbn.Data = nd.FieldData().Bytes() | ||
data, err := pbn.Marshal() | ||
if err != nil { | ||
return fmt.Errorf("marshal failed. %v", err) | ||
|
@@ -104,25 +103,27 @@ func (nd PBNode) EncodeDagProto(w io.Writer) error { | |
} | ||
|
||
// DecodeDagRaw is a fast path decoding to protobuf | ||
// from RawNode__NodeBuilders | ||
func (nb _RawNode__NodeBuilder) DecodeDagRaw(r io.Reader) error { | ||
byteBuf, ok := r.(byteAccesor) | ||
if ok { | ||
nb.nd.x = byteBuf.Bytes() | ||
return nil | ||
} | ||
data, err := ioutil.ReadAll(r) | ||
if err != nil { | ||
return fmt.Errorf("io error during unmarshal. %v", err) | ||
} | ||
nb.nd.x = data | ||
return nil | ||
// from RawNode__Builders | ||
func (nb *_RawNode__Builder) DecodeDagRaw(r io.Reader) error { | ||
return fluent.Recover(func() { | ||
fnb := fluent.WrapAssembler(nb) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will definitely cause at least one extra alloc, and idk if it's paying for itself in simplicity added in this case. But I'm not complaining enough to demand a change, either. Probably not a big issue unless we profile it and find out that it is. |
||
byteBuf, ok := r.(byteAccesor) | ||
if ok { | ||
fnb.AssignBytes(byteBuf.Bytes()) | ||
return | ||
} | ||
data, err := ioutil.ReadAll(r) | ||
if err != nil { | ||
panic(fluent.Error{Err: fmt.Errorf("io error during unmarshal. %v", err)}) | ||
} | ||
fnb.AssignBytes(data) | ||
}) | ||
} | ||
|
||
// EncodeDagRaw is a fast path encoding to protobuf | ||
// for RawNode types | ||
func (nd RawNode) EncodeDagRaw(w io.Writer) error { | ||
_, err := w.Write(nd.x) | ||
_, err := w.Write(nd.Bytes()) | ||
if err != nil { | ||
return fmt.Errorf(" error during marshal. %v", err) | ||
} | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,83 +1,42 @@ | ||
package main | ||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
"regexp" | ||
"os/exec" | ||
|
||
"github.com/ipld/go-ipld-prime/schema" | ||
gengo "github.com/ipld/go-ipld-prime/schema/gen/go" | ||
) | ||
|
||
func main() { | ||
openOrPanic := func(filename string) *os.File { | ||
y, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return y | ||
} | ||
|
||
tString := schema.SpawnString("String") | ||
tInt := schema.SpawnInt("Int") | ||
tLink := schema.SpawnLink("Link") | ||
tBytes := schema.SpawnBytes("Bytes") | ||
|
||
tPBLink := schema.SpawnStruct("PBLink", | ||
[]schema.StructField{ | ||
schema.SpawnStructField("Hash", tLink, true, false), | ||
schema.SpawnStructField("Name", tString, true, false), | ||
schema.SpawnStructField("Tsize", tInt, true, false), | ||
}, | ||
schema.StructRepresentation_Map{}, | ||
) | ||
|
||
tPBLinks := schema.SpawnList("PBLinks", tPBLink, false) | ||
|
||
tPBNode := schema.SpawnStruct("PBNode", | ||
[]schema.StructField{ | ||
schema.SpawnStructField("Links", tPBLinks, false, false), | ||
schema.SpawnStructField("Data", tBytes, false, false), | ||
}, | ||
schema.StructRepresentation_Map{}, | ||
) | ||
|
||
tRaw := schema.SpawnBytes("RawNode") | ||
|
||
ts := schema.TypeSystem{} | ||
ts.Init() | ||
adjCfg := &gengo.AdjunctCfg{} | ||
|
||
pkgName := "dagpb" | ||
|
||
f := openOrPanic("common_gen.go") | ||
gengo.EmitInternalEnums(pkgName, f) | ||
ts.Accumulate(schema.SpawnString("String")) | ||
ts.Accumulate(schema.SpawnInt("Int")) | ||
ts.Accumulate(schema.SpawnLink("Link")) | ||
ts.Accumulate(schema.SpawnBytes("Bytes")) | ||
|
||
f = openOrPanic("pb_node_gen.go") | ||
gengo.EmitFileHeader(pkgName, f) | ||
gengo.EmitEntireType(gengo.NewStringReprStringGenerator(pkgName, tString, adjCfg), f) | ||
gengo.EmitEntireType(gengo.NewIntReprIntGenerator(pkgName, tInt, adjCfg), f) | ||
gengo.EmitEntireType(gengo.NewBytesReprBytesGenerator(pkgName, tBytes, adjCfg), f) | ||
gengo.EmitEntireType(gengo.NewLinkReprLinkGenerator(pkgName, tLink, adjCfg), f) | ||
gengo.EmitEntireType(gengo.NewStructReprMapGenerator(pkgName, tPBLink, adjCfg), f) | ||
gengo.EmitEntireType(gengo.NewListReprListGenerator(pkgName, tPBLinks, adjCfg), f) | ||
gengo.EmitEntireType(gengo.NewStructReprMapGenerator(pkgName, tPBNode, adjCfg), f) | ||
|
||
if err := f.Close(); err != nil { | ||
panic(err) | ||
} | ||
read, err := ioutil.ReadFile("pb_node_gen.go") | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
re := regexp.MustCompile("ipld\\.ErrInvalidKey\\{[^}]*\\}") | ||
newContents := re.ReplaceAll(read, []byte("err")) | ||
|
||
err = ioutil.WriteFile("pb_node_gen.go", []byte(newContents), 0) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
f = openOrPanic("raw_node_gen.go") | ||
gengo.EmitFileHeader("dagpb", f) | ||
gengo.EmitEntireType(gengo.NewBytesReprBytesGenerator(pkgName, tRaw, adjCfg), f) | ||
ts.Accumulate(schema.SpawnStruct("PBLink", | ||
[]schema.StructField{ | ||
schema.SpawnStructField("Hash", "Link", true, false), | ||
schema.SpawnStructField("Name", "String", true, false), | ||
schema.SpawnStructField("Tsize", "Int", true, false), | ||
}, | ||
schema.SpawnStructRepresentationMap(nil), | ||
)) | ||
ts.Accumulate(schema.SpawnList("PBLinks", "PBLink", false)) | ||
ts.Accumulate(schema.SpawnStruct("PBNode", | ||
[]schema.StructField{ | ||
schema.SpawnStructField("Links", "PBLinks", false, false), | ||
schema.SpawnStructField("Data", "Bytes", false, false), | ||
}, | ||
schema.SpawnStructRepresentationMap(nil), | ||
)) | ||
ts.Accumulate(schema.SpawnBytes("RawNode")) | ||
gengo.Generate(".", pkgName, ts, adjCfg) | ||
exec.Command("go", "fmt").Run() | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd mildly rather that these be -1 when they're saying "shrug idk deal with it". The contract about this isn't strict, but there are some logic branches that look at 0 as a "ah, expect 0" and look at -1 as "ah, take a guess". It's also helpful for reading.
... but none of those codepaths happen apply here in a codegen'd struct, so, this is a nit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to improve the godoc on this, I've realized. Mea culpa.