implement non-dag cbor codec

parent f02df08b
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,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")
......@@ -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
}
}
......@@ -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
}
}
......@@ -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
......
......@@ -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 {
......@@ -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)
}
......@@ -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 {
......@@ -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:
......@@ -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
}
......@@ -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
}
......@@ -166,6 +166,9 @@ 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
}
......@@ -173,7 +176,7 @@ func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token,
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)
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment