Skip to content

Commit

Permalink
Merge pull request #153 from ipld/codec/cbor
Browse files Browse the repository at this point in the history
implement non-dag cbor codec
  • Loading branch information
warpfork authored Mar 23, 2021
2 parents f02df08 + 6e55e9b commit 678a428
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 15 deletions.
29 changes: 29 additions & 0 deletions codec/cbor/multicodec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cbor

import (
"io"

"github.com/polydawn/refmt/cbor"

"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/codec/dagcbor"
"github.com/ipld/go-ipld-prime/multicodec"
)

var (
_ ipld.Decoder = Decode
_ ipld.Encoder = Encode
)

func init() {
multicodec.RegisterEncoder(0x51, Encode)
multicodec.RegisterDecoder(0x51, Decode)
}

func Decode(na ipld.NodeAssembler, r io.Reader) error {
return dagcbor.Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r), false)
}

func Encode(n ipld.Node, w io.Writer) error {
return dagcbor.Marshal(n, cbor.NewEncoder(w), false)
}
13 changes: 8 additions & 5 deletions codec/dagcbor/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import (
// This should be identical to the general feature in the parent package,
// except for the `case ipld.Kind_Link` block,
// which is dag-cbor's special sauce for schemafree links.
func Marshal(n ipld.Node, sink shared.TokenSink) error {
func Marshal(n ipld.Node, sink shared.TokenSink, allowLinks bool) error {
var tk tok.Token
return marshal(n, &tk, sink)
return marshal(n, &tk, sink, allowLinks)
}

func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error {
func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink, allowLinks bool) error {
switch n.Kind() {
case ipld.Kind_Invalid:
return fmt.Errorf("cannot traverse a node that is absent")
Expand Down Expand Up @@ -47,7 +47,7 @@ func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error {
if _, err := sink.Step(tk); err != nil {
return err
}
if err := marshal(v, tk, sink); err != nil {
if err := marshal(v, tk, sink, allowLinks); err != nil {
return err
}
}
Expand All @@ -69,7 +69,7 @@ func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error {
if err != nil {
return err
}
if err := marshal(v, tk, sink); err != nil {
if err := marshal(v, tk, sink, allowLinks); err != nil {
return err
}
}
Expand Down Expand Up @@ -123,6 +123,9 @@ func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error {
_, err = sink.Step(tk)
return err
case ipld.Kind_Link:
if !allowLinks {
return fmt.Errorf("cannot Marshal ipld links to CBOR")
}
v, err := n.AsLink()
if err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions codec/dagcbor/multicodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func Decode(na ipld.NodeAssembler, r io.Reader) error {
return na2.DecodeDagCbor(r)
}
// Okay, generic builder path.
return Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r))
return Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r), true)
}

func Encode(n ipld.Node, w io.Writer) error {
Expand All @@ -40,5 +40,5 @@ func Encode(n ipld.Node, w io.Writer) error {
return n2.EncodeDagCbor(w)
}
// Okay, generic inspection path.
return Marshal(n, cbor.NewEncoder(w))
return Marshal(n, cbor.NewEncoder(w), true)
}
19 changes: 11 additions & 8 deletions codec/dagcbor/unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ const (
// except for the `case tok.TBytes` block,
// which has dag-cbor's special sauce for detecting schemafree links.

func Unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource) error {
func Unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, allowLinks bool) error {
// Have a gas budget, which will be decremented as we allocate memory, and an error returned when execeeded (or about to be exceeded).
// This is a DoS defense mechanism.
// It's *roughly* in units of bytes (but only very, VERY roughly) -- it also treats words as 1 in many cases.
// FUTURE: this ought be configurable somehow. (How, and at what granularity though?)
var gas int = 1048576 * 10
return unmarshal1(na, tokSrc, &gas)
return unmarshal1(na, tokSrc, &gas, allowLinks)
}

func unmarshal1(na ipld.NodeAssembler, tokSrc shared.TokenSource, gas *int) error {
func unmarshal1(na ipld.NodeAssembler, tokSrc shared.TokenSource, gas *int, allowLinks bool) error {
var tk tok.Token
done, err := tokSrc.Step(&tk)
if err != nil {
Expand All @@ -45,12 +45,12 @@ func unmarshal1(na ipld.NodeAssembler, tokSrc shared.TokenSource, gas *int) erro
if done && !tk.Type.IsValue() {
return fmt.Errorf("unexpected eof")
}
return unmarshal2(na, tokSrc, &tk, gas)
return unmarshal2(na, tokSrc, &tk, gas, allowLinks)
}

// starts with the first token already primed. Necessary to get recursion
// to flow right without a peek+unpeek system.
func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token, gas *int) error {
func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token, gas *int, allowLinks bool) error {
// FUTURE: check for schema.TypedNodeBuilder that's going to parse a Link (they can slurp any token kind they want).
switch tk.Type {
case tok.TMapOpen:
Expand Down Expand Up @@ -97,7 +97,7 @@ func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token,
if err != nil { // return in error if the key was rejected
return err
}
err = unmarshal1(mva, tokSrc, gas)
err = unmarshal1(mva, tokSrc, gas, allowLinks)
if err != nil { // return in error if some part of the recursion errored
return err
}
Expand Down Expand Up @@ -140,7 +140,7 @@ func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token,
if observedLen > expectLen {
return fmt.Errorf("unexpected continuation of array elements beyond declared length")
}
err := unmarshal2(la.AssembleValue(), tokSrc, tk, gas)
err := unmarshal2(la.AssembleValue(), tokSrc, tk, gas, allowLinks)
if err != nil { // return in error if some part of the recursion errored
return err
}
Expand All @@ -166,14 +166,17 @@ func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token,
}
switch tk.Tag {
case linkTag:
if !allowLinks {
return fmt.Errorf("unhandled cbor tag %d", tk.Tag)
}
if len(tk.Bytes) < 1 || tk.Bytes[0] != 0 {
return ErrInvalidMultibase
}
elCid, err := cid.Cast(tk.Bytes[1:])
if err != nil {
return err
}
return na.AssignLink(cidlink.Link{elCid})
return na.AssignLink(cidlink.Link{Cid: elCid})
default:
return fmt.Errorf("unhandled cbor tag %d", tk.Tag)
}
Expand Down

0 comments on commit 678a428

Please sign in to comment.