Skip to content

Commit

Permalink
wip checkpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
warpfork committed Feb 5, 2021
1 parent 0b3adb9 commit 06be6f1
Show file tree
Hide file tree
Showing 35 changed files with 969 additions and 628 deletions.
16 changes: 5 additions & 11 deletions codec/api.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package codec

import (
"io"

"github.com/ipld/go-ipld-prime"
)

// Encoder is the essential definition of a function that takes IPLD Data Model data in memory and serializes it.
// IPLD Codecs are written by implementing this function interface (as well as (typically) a matched Decoder).
// Encoder is defined in the root ipld package; this alias is just for documentation and discoverability.
//
// Encoder functions can be composed into an ipld.LinkSystem to provide
// a "one stop shop" API for handling content addressable storage.
Expand All @@ -33,15 +30,12 @@ import (
// in all scenarios that use codecs indirectly.
// There is also no standard interface for such configurations: by nature,
// if they exist at all, they vary per codec.
type Encoder func(data ipld.Node, output io.Writer) error
type Encoder = ipld.Encoder

// Decoder is the essential definiton of a function that consumes serial data and unfurls it into IPLD Data Model-compatible in-memory representations.
// IPLD Codecs are written by implementing this function interface (as well as (typically) a matched Encoder).
// Decoder is defined in the root ipld package; this alias is just for documentation and discoverability.
//
// Decoder is the dual of Encoder.
// Most of the documentation for the Encoder function interface
// also applies wholesale to the Decoder interface.
type Decoder func(into ipld.NodeAssembler, input io.Reader) error
// Most of the documentation for Encoder also applies wholesale to the Decoder interface.
type Decoder = ipld.Decoder

type ErrBudgetExhausted struct{}

Expand Down
18 changes: 8 additions & 10 deletions codec/dagcbor/multicodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,22 @@ import (

"github.com/polydawn/refmt/cbor"

ipld "github.com/ipld/go-ipld-prime"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/codec"
)

var (
_ cidlink.MulticodecDecoder = Decoder
_ cidlink.MulticodecEncoder = Encoder
_ ipld.Decoder = Decode
_ ipld.Encoder = Encode
)

func init() {
cidlink.RegisterMulticodecDecoder(0x71, Decoder)
cidlink.RegisterMulticodecEncoder(0x71, Encoder)
codec.MulticodecEncoderRegistry[0x71] = Encode
codec.MulticodecDecoderRegistry[0x71] = Decode
}

func Decoder(na ipld.NodeAssembler, r io.Reader) error {
func Decode(na ipld.NodeAssembler, r io.Reader) error {
// Probe for a builtin fast path. Shortcut to that if possible.
// (ipldcbor.NodeBuilder supports this, for example.)
type detectFastPath interface {
DecodeDagCbor(io.Reader) error
}
Expand All @@ -32,9 +31,8 @@ func Decoder(na ipld.NodeAssembler, r io.Reader) error {
return Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r))
}

func Encoder(n ipld.Node, w io.Writer) error {
func Encode(n ipld.Node, w io.Writer) error {
// Probe for a builtin fast path. Shortcut to that if possible.
// (ipldcbor.Node supports this, for example.)
type detectFastPath interface {
EncodeDagCbor(io.Writer) error
}
Expand Down
26 changes: 12 additions & 14 deletions codec/dagcbor/roundtripCidlink_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package dagcbor

import (
"bytes"
"context"
"io"
"testing"

Expand All @@ -15,27 +14,26 @@ import (
)

func TestRoundtripCidlink(t *testing.T) {
lb := cidlink.LinkBuilder{cid.Prefix{
lp := cidlink.LinkPrototype{cid.Prefix{
Version: 1,
Codec: 0x71,
MhType: 0x17,
MhLength: 4,
}}
lsys := cidlink.DefaultLinkSystem()

buf := bytes.Buffer{}
lnk, err := lb.Build(context.Background(), ipld.LinkContext{}, n,
func(ipld.LinkContext) (io.Writer, ipld.StoreCommitter, error) {
return &buf, func(lnk ipld.Link) error { return nil }, nil
},
)
lsys.StorageWriteOpener = func(lnkCtx ipld.LinkContext) (io.Writer, ipld.BlockWriteCommitter, error) {
return &buf, func(lnk ipld.Link) error { return nil }, nil
}
lsys.StorageReadOpener = func(lnkCtx ipld.LinkContext, lnk ipld.Link) (io.Reader, error) {
return bytes.NewReader(buf.Bytes()), nil
}

lnk, err := lsys.Store(ipld.LinkContext{}, lp, n)
Require(t, err, ShouldEqual, nil)

nb := basicnode.Prototype__Any{}.NewBuilder()
err = lnk.Load(context.Background(), ipld.LinkContext{}, nb,
func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) {
return bytes.NewReader(buf.Bytes()), nil
},
)
n2, err := lsys.Load(ipld.LinkContext{}, lnk, basicnode.Prototype.Any)
Require(t, err, ShouldEqual, nil)
Wish(t, nb.Build(), ShouldEqual, n)
Wish(t, n2, ShouldEqual, n)
}
28 changes: 9 additions & 19 deletions codec/dagcbor/roundtrip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@ package dagcbor

import (
"bytes"
"context"
"crypto/rand"
"io"
"strings"
"testing"

cid "github.com/ipfs/go-cid"
. "github.com/warpfork/go-wish"

ipld "github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/fluent"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
basicnode "github.com/ipld/go-ipld-prime/node/basic"
Expand All @@ -38,14 +35,14 @@ var serial = "\xa4eplainkolde stringcmap\xa2cone\x01ctwo\x02dlist\x82ethreedfour
func TestRoundtrip(t *testing.T) {
t.Run("encoding", func(t *testing.T) {
var buf bytes.Buffer
err := Encoder(n, &buf)
err := Encode(n, &buf)
Require(t, err, ShouldEqual, nil)
Wish(t, buf.String(), ShouldEqual, serial)
})
t.Run("decoding", func(t *testing.T) {
buf := strings.NewReader(serial)
nb := basicnode.Prototype__Map{}.NewBuilder()
err := Decoder(nb, buf)
err := Decode(nb, buf)
Require(t, err, ShouldEqual, nil)
Wish(t, nb.Build(), ShouldEqual, n)
})
Expand All @@ -57,33 +54,26 @@ func TestRoundtripScalar(t *testing.T) {
simple := nb.Build()
t.Run("encoding", func(t *testing.T) {
var buf bytes.Buffer
err := Encoder(simple, &buf)
err := Encode(simple, &buf)
Require(t, err, ShouldEqual, nil)
Wish(t, buf.String(), ShouldEqual, `japplesauce`)
})
t.Run("decoding", func(t *testing.T) {
buf := strings.NewReader(`japplesauce`)
nb := basicnode.Prototype__String{}.NewBuilder()
err := Decoder(nb, buf)
err := Decode(nb, buf)
Require(t, err, ShouldEqual, nil)
Wish(t, nb.Build(), ShouldEqual, simple)
})
}

func TestRoundtripLinksAndBytes(t *testing.T) {
lb := cidlink.LinkBuilder{cid.Prefix{
lnk := cidlink.LinkPrototype{cid.Prefix{
Version: 1,
Codec: 0x71,
MhType: 0x17,
MhLength: 4,
}}
buf := bytes.Buffer{}
lnk, err := lb.Build(context.Background(), ipld.LinkContext{}, n,
func(ipld.LinkContext) (io.Writer, ipld.StoreCommitter, error) {
return &buf, func(lnk ipld.Link) error { return nil }, nil
},
)
Require(t, err, ShouldEqual, nil)
}}.BuildLink([]byte{1, 2, 3, 4}) // dummy value, content does not matter to this test.

var linkByteNode = fluent.MustBuildMap(basicnode.Prototype__Map{}, 4, func(na fluent.MapAssembler) {
nva := na.AssembleEntry("Link")
Expand All @@ -94,11 +84,11 @@ func TestRoundtripLinksAndBytes(t *testing.T) {
nva.AssignBytes(bytes)
})

buf.Reset()
err = Encoder(linkByteNode, &buf)
buf := bytes.Buffer{}
err := Encode(linkByteNode, &buf)
Require(t, err, ShouldEqual, nil)
nb := basicnode.Prototype__Map{}.NewBuilder()
err = Decoder(nb, &buf)
err = Decode(nb, &buf)
Require(t, err, ShouldEqual, nil)
reconstructed := nb.Build()
Wish(t, reconstructed, ShouldEqual, linkByteNode)
Expand Down
8 changes: 4 additions & 4 deletions codec/dagcbor/unmarshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,28 @@ func TestFunBlocks(t *testing.T) {
// This fixture has a zero length link -- not even the multibase byte (which dag-cbor insists must be zero) is there.
buf := strings.NewReader("\x8d\x8d\x97\xd8*@")
nb := basicnode.Prototype.Any.NewBuilder()
err := Decoder(nb, buf)
err := Decode(nb, buf)
Require(t, err, ShouldEqual, ErrInvalidMultibase)
})
t.Run("fuzz001", func(t *testing.T) {
// This fixture might cause an overly large allocation if you aren't careful to have resource budgets.
buf := strings.NewReader("\x9a\xff000")
nb := basicnode.Prototype.Any.NewBuilder()
err := Decoder(nb, buf)
err := Decode(nb, buf)
Require(t, err, ShouldEqual, ErrAllocationBudgetExceeded)
})
t.Run("fuzz002", func(t *testing.T) {
// This fixture might cause an overly large allocation if you aren't careful to have resource budgets.
buf := strings.NewReader("\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9a\xff000")
nb := basicnode.Prototype.Any.NewBuilder()
err := Decoder(nb, buf)
err := Decode(nb, buf)
Require(t, err, ShouldEqual, ErrAllocationBudgetExceeded)
})
t.Run("fuzz003", func(t *testing.T) {
// This fixture might cause an overly large allocation if you aren't careful to have resource budgets.
buf := strings.NewReader("\x9f\x9f\x9f\x9f\x9f\x9f\x9f\xbb00000000")
nb := basicnode.Prototype.Any.NewBuilder()
err := Decoder(nb, buf)
err := Decode(nb, buf)
Require(t, err, ShouldEqual, ErrAllocationBudgetExceeded)
})
}
16 changes: 8 additions & 8 deletions codec/dagjson/multicodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ import (

"github.com/polydawn/refmt/json"

ipld "github.com/ipld/go-ipld-prime"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/codec"
)

var (
_ cidlink.MulticodecDecoder = Decoder
_ cidlink.MulticodecEncoder = Encoder
_ ipld.Decoder = Decode
_ ipld.Encoder = Encode
)

func init() {
cidlink.RegisterMulticodecDecoder(0x0129, Decoder)
cidlink.RegisterMulticodecEncoder(0x0129, Encoder)
codec.MulticodecEncoderRegistry[0x0129] = Encode
codec.MulticodecDecoderRegistry[0x0129] = Decode
}

func Decoder(na ipld.NodeAssembler, r io.Reader) error {
func Decode(na ipld.NodeAssembler, r io.Reader) error {
// Shell out directly to generic builder path.
// (There's not really any fastpaths of note for json.)
err := Unmarshal(na, json.NewDecoder(r))
Expand Down Expand Up @@ -52,7 +52,7 @@ func Decoder(na ipld.NodeAssembler, r io.Reader) error {
return err
}

func Encoder(n ipld.Node, w io.Writer) error {
func Encode(n ipld.Node, w io.Writer) error {
// Shell out directly to generic inspection path.
// (There's not really any fastpaths of note for json.)
// Write another function if you need to tune encoding options about whitespace.
Expand Down
40 changes: 16 additions & 24 deletions codec/dagjson/roundtripCidlink_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package dagjson

import (
"bytes"
"context"
"io"
"io/ioutil"
"strings"
"testing"

Expand All @@ -17,29 +15,28 @@ import (
)

func TestRoundtripCidlink(t *testing.T) {
lb := cidlink.LinkBuilder{cid.Prefix{
lp := cidlink.LinkPrototype{cid.Prefix{
Version: 1,
Codec: 0x0129,
MhType: 0x17,
MhLength: 4,
}}
lsys := cidlink.DefaultLinkSystem()

buf := bytes.Buffer{}
lnk, err := lb.Build(context.Background(), ipld.LinkContext{}, n,
func(ipld.LinkContext) (io.Writer, ipld.StoreCommitter, error) {
return &buf, func(lnk ipld.Link) error { return nil }, nil
},
)
lsys.StorageWriteOpener = func(lnkCtx ipld.LinkContext) (io.Writer, ipld.BlockWriteCommitter, error) {
return &buf, func(lnk ipld.Link) error { return nil }, nil
}
lsys.StorageReadOpener = func(lnkCtx ipld.LinkContext, lnk ipld.Link) (io.Reader, error) {
return bytes.NewReader(buf.Bytes()), nil
}

lnk, err := lsys.Store(ipld.LinkContext{}, lp, n)
Require(t, err, ShouldEqual, nil)

nb := basicnode.Prototype__Any{}.NewBuilder()
err = lnk.Load(context.Background(), ipld.LinkContext{}, nb,
func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) {
return bytes.NewReader(buf.Bytes()), nil
},
)
n2, err := lsys.Load(ipld.LinkContext{}, lnk, basicnode.Prototype.Any)
Require(t, err, ShouldEqual, nil)
Wish(t, nb.Build(), ShouldEqual, n)
Wish(t, n2, ShouldEqual, n)
}

// Make sure that a map that *almost* looks like a link is handled safely.
Expand All @@ -48,24 +45,19 @@ func TestRoundtripCidlink(t *testing.T) {
// tokens have to be reprocessed before a recursion that find a real link appears.
func TestUnmarshalTrickyMapContainingLink(t *testing.T) {
// Create a link; don't particularly care about its contents.
lnk, err := cidlink.LinkBuilder{cid.Prefix{
lnk := cidlink.LinkPrototype{cid.Prefix{
Version: 1,
Codec: 0x0129,
Codec: 0x71,
MhType: 0x17,
MhLength: 4,
}}.Build(context.Background(), ipld.LinkContext{}, n,
func(ipld.LinkContext) (io.Writer, ipld.StoreCommitter, error) {
return ioutil.Discard, func(lnk ipld.Link) error { return nil }, nil
},
)
Require(t, err, ShouldEqual, nil)
}}.BuildLink([]byte{1, 2, 3, 4}) // dummy value, content does not matter to this test.

// Compose the tricky corpus. (lnk.String "happens" to work here, although this isn't recommended or correct in general.)
tricky := `{"/":{"/":"` + lnk.String() + `"}}`

// Unmarshal. Hopefully we get a map with a link in it.
nb := basicnode.Prototype__Any{}.NewBuilder()
err = Decoder(nb, strings.NewReader(tricky))
err := Decode(nb, strings.NewReader(tricky))
Require(t, err, ShouldEqual, nil)
n := nb.Build()
Wish(t, n.Kind(), ShouldEqual, ipld.Kind_Map)
Expand Down
8 changes: 4 additions & 4 deletions codec/dagjson/roundtrip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ var serial = `{
func TestRoundtrip(t *testing.T) {
t.Run("encoding", func(t *testing.T) {
var buf bytes.Buffer
err := Encoder(n, &buf)
err := Encode(n, &buf)
Require(t, err, ShouldEqual, nil)
Wish(t, buf.String(), ShouldEqual, serial)
})
t.Run("decoding", func(t *testing.T) {
buf := strings.NewReader(serial)
nb := basicnode.Prototype__Map{}.NewBuilder()
err := Decoder(nb, buf)
err := Decode(nb, buf)
Require(t, err, ShouldEqual, nil)
Wish(t, nb.Build(), ShouldEqual, n)
})
Expand All @@ -67,14 +67,14 @@ func TestRoundtripScalar(t *testing.T) {
simple := nb.Build()
t.Run("encoding", func(t *testing.T) {
var buf bytes.Buffer
err := Encoder(simple, &buf)
err := Encode(simple, &buf)
Require(t, err, ShouldEqual, nil)
Wish(t, buf.String(), ShouldEqual, `"applesauce"`)
})
t.Run("decoding", func(t *testing.T) {
buf := strings.NewReader(`"applesauce"`)
nb := basicnode.Prototype__String{}.NewBuilder()
err := Decoder(nb, buf)
err := Decode(nb, buf)
Require(t, err, ShouldEqual, nil)
Wish(t, nb.Build(), ShouldEqual, simple)
})
Expand Down
Loading

0 comments on commit 06be6f1

Please sign in to comment.