container: streamed and non-streamed IO, documentation
This commit is contained in:
@@ -85,7 +85,11 @@ func (p Persona) Name() string {
|
|||||||
|
|
||||||
// PrivKey returns the Ed25519 private key for the Persona.
|
// PrivKey returns the Ed25519 private key for the Persona.
|
||||||
func (p Persona) PrivKey() crypto.PrivKey {
|
func (p Persona) PrivKey() crypto.PrivKey {
|
||||||
return privKeys[p]
|
res, ok := privKeys[p]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("Unknown persona: %v", p))
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// PubKey returns the Ed25519 public key for the Persona.
|
// PubKey returns the Ed25519 public key for the Persona.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -71,33 +72,13 @@ func (ctn Reader) GetInvocation() (*invocation.Token, error) {
|
|||||||
return nil, ErrNotFound
|
return nil, ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromCar(r io.Reader) (Reader, error) {
|
// FromCbor decodes a DAG-CBOR encoded container.
|
||||||
_, it, err := readCar(r)
|
func FromCbor(data []byte) (Reader, error) {
|
||||||
if err != nil {
|
return FromCborReader(bytes.NewReader(data))
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctn := make(Reader)
|
|
||||||
|
|
||||||
for block, err := range it {
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ctn.addToken(block.data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromCarBase64(r io.Reader) (Reader, error) {
|
// FromCborReader is the same as FromCbor, but with an io.Reader.
|
||||||
return FromCar(base64.NewDecoder(base64.StdEncoding, r))
|
func FromCborReader(r io.Reader) (Reader, error) {
|
||||||
}
|
|
||||||
|
|
||||||
func FromCbor(r io.Reader) (Reader, error) {
|
|
||||||
n, err := ipld.DecodeStreaming(r, dagcbor.Decode)
|
n, err := ipld.DecodeStreaming(r, dagcbor.Decode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -147,8 +128,52 @@ func FromCbor(r io.Reader) (Reader, error) {
|
|||||||
return ctn, nil
|
return ctn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromCborBase64(r io.Reader) (Reader, error) {
|
// FromCborBase64 decodes a base64 DAG-CBOR encoded container.
|
||||||
return FromCbor(base64.NewDecoder(base64.StdEncoding, r))
|
func FromCborBase64(data []byte) (Reader, error) {
|
||||||
|
return FromCborBase64Reader(bytes.NewReader(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromCborBase64Reader is the same as FromCborBase64, but with an io.Reader.
|
||||||
|
func FromCborBase64Reader(r io.Reader) (Reader, error) {
|
||||||
|
return FromCborReader(base64.NewDecoder(base64.StdEncoding, r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromCar decodes a CAR file encoded container.
|
||||||
|
func FromCar(data []byte) (Reader, error) {
|
||||||
|
return FromCarReader(bytes.NewReader(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromCarReader is the same as FromCar, but with an io.Reader.
|
||||||
|
func FromCarReader(r io.Reader) (Reader, error) {
|
||||||
|
_, it, err := readCar(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctn := make(Reader)
|
||||||
|
|
||||||
|
for block, err := range it {
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ctn.addToken(block.data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromCarBase64 decodes a base64 CAR file encoded container.
|
||||||
|
func FromCarBase64(data []byte) (Reader, error) {
|
||||||
|
return FromCarReader(bytes.NewReader(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromCarBase64Reader is the same as FromCarBase64, but with an io.Reader.
|
||||||
|
func FromCarBase64Reader(r io.Reader) (Reader, error) {
|
||||||
|
return FromCarReader(base64.NewDecoder(base64.StdEncoding, r))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctn Reader) addToken(data []byte) error {
|
func (ctn Reader) addToken(data []byte) error {
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ func TestContainerRoundTrip(t *testing.T) {
|
|||||||
writer func(ctn Writer, w io.Writer) error
|
writer func(ctn Writer, w io.Writer) error
|
||||||
reader func(io.Reader) (Reader, error)
|
reader func(io.Reader) (Reader, error)
|
||||||
}{
|
}{
|
||||||
{"car", Writer.ToCar, FromCar},
|
{"car", Writer.ToCarWriter, FromCarReader},
|
||||||
{"carBase64", Writer.ToCarBase64, FromCarBase64},
|
{"carBase64", Writer.ToCarBase64Writer, FromCarBase64Reader},
|
||||||
{"cbor", Writer.ToCbor, FromCbor},
|
{"cbor", Writer.ToCborWriter, FromCborReader},
|
||||||
{"cborBase64", Writer.ToCborBase64, FromCborBase64},
|
{"cborBase64", Writer.ToCborBase64Writer, FromCborBase64Reader},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
tokens := make(map[cid.Cid]*delegation.Token)
|
tokens := make(map[cid.Cid]*delegation.Token)
|
||||||
@@ -98,10 +98,10 @@ func BenchmarkContainerSerialisation(b *testing.B) {
|
|||||||
writer func(ctn Writer, w io.Writer) error
|
writer func(ctn Writer, w io.Writer) error
|
||||||
reader func(io.Reader) (Reader, error)
|
reader func(io.Reader) (Reader, error)
|
||||||
}{
|
}{
|
||||||
{"car", Writer.ToCar, FromCar},
|
{"car", Writer.ToCarWriter, FromCarReader},
|
||||||
{"carBase64", Writer.ToCarBase64, FromCarBase64},
|
{"carBase64", Writer.ToCarBase64Writer, FromCarBase64Reader},
|
||||||
{"cbor", Writer.ToCbor, FromCbor},
|
{"cbor", Writer.ToCborWriter, FromCborReader},
|
||||||
{"cborBase64", Writer.ToCborBase64, FromCborBase64},
|
{"cborBase64", Writer.ToCborBase64Writer, FromCborBase64Reader},
|
||||||
} {
|
} {
|
||||||
writer := NewWriter()
|
writer := NewWriter()
|
||||||
|
|
||||||
@@ -185,18 +185,17 @@ func FuzzContainerRead(f *testing.F) {
|
|||||||
_, c, data := randToken()
|
_, c, data := randToken()
|
||||||
writer.AddSealed(c, data)
|
writer.AddSealed(c, data)
|
||||||
}
|
}
|
||||||
buf := bytes.NewBuffer(nil)
|
data, err := writer.ToCbor()
|
||||||
err := writer.ToCbor(buf)
|
|
||||||
require.NoError(f, err)
|
require.NoError(f, err)
|
||||||
|
|
||||||
f.Add(buf.Bytes())
|
f.Add(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Fuzz(func(t *testing.T, data []byte) {
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
// search for panics
|
// search for panics
|
||||||
_, _ = FromCbor(bytes.NewReader(data))
|
_, _ = FromCbor(data)
|
||||||
|
|
||||||
if time.Since(start) > 100*time.Millisecond {
|
if time.Since(start) > 100*time.Millisecond {
|
||||||
panic("too long")
|
panic("too long")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
@@ -12,10 +13,6 @@ import (
|
|||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: should we have a multibase to wrap the cbor? but there is no reader/write in go-multibase :-(
|
|
||||||
|
|
||||||
const currentContainerVersion = "ctn-v1"
|
|
||||||
|
|
||||||
// Writer is a token container writer. It provides a convenient way to aggregate and serialize tokens together.
|
// Writer is a token container writer. It provides a convenient way to aggregate and serialize tokens together.
|
||||||
type Writer map[cid.Cid][]byte
|
type Writer map[cid.Cid][]byte
|
||||||
|
|
||||||
@@ -28,27 +25,24 @@ func (ctn Writer) AddSealed(cid cid.Cid, data []byte) {
|
|||||||
ctn[cid] = data
|
ctn[cid] = data
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctn Writer) ToCar(w io.Writer) error {
|
const currentContainerVersion = "ctn-v1"
|
||||||
return writeCar(w, nil, func(yield func(carBlock, error) bool) {
|
|
||||||
for c, bytes := range ctn {
|
// ToCbor encode the container into a DAG-CBOR binary format.
|
||||||
if !yield(carBlock{c: c, data: bytes}, nil) {
|
func (ctn Writer) ToCbor() ([]byte, error) {
|
||||||
return
|
var buf bytes.Buffer
|
||||||
|
err := ctn.ToCborWriter(&buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
return buf.Bytes(), nil
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctn Writer) ToCarBase64(w io.Writer) error {
|
// ToCborWriter is the same as ToCbor, but with an io.Writer.
|
||||||
w2 := base64.NewEncoder(base64.StdEncoding, w)
|
func (ctn Writer) ToCborWriter(w io.Writer) error {
|
||||||
defer w2.Close()
|
|
||||||
return ctn.ToCar(w2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctn Writer) ToCbor(w io.Writer) error {
|
|
||||||
node, err := qp.BuildMap(basicnode.Prototype.Any, 1, func(ma datamodel.MapAssembler) {
|
node, err := qp.BuildMap(basicnode.Prototype.Any, 1, func(ma datamodel.MapAssembler) {
|
||||||
qp.MapEntry(ma, currentContainerVersion, qp.List(int64(len(ctn)), func(la datamodel.ListAssembler) {
|
qp.MapEntry(ma, currentContainerVersion, qp.List(int64(len(ctn)), func(la datamodel.ListAssembler) {
|
||||||
for _, bytes := range ctn {
|
for _, data := range ctn {
|
||||||
qp.ListEntry(la, qp.Bytes(bytes))
|
qp.ListEntry(la, qp.Bytes(data))
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
@@ -58,8 +52,57 @@ func (ctn Writer) ToCbor(w io.Writer) error {
|
|||||||
return ipld.EncodeStreaming(w, node, dagcbor.Encode)
|
return ipld.EncodeStreaming(w, node, dagcbor.Encode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctn Writer) ToCborBase64(w io.Writer) error {
|
// ToCborBase64 encode the container into a base64 encoded DAG-CBOR binary format.
|
||||||
|
func (ctn Writer) ToCborBase64() ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := ctn.ToCborBase64Writer(&buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToCborBase64Writer is the same as ToCborBase64, but with an io.Writer.
|
||||||
|
func (ctn Writer) ToCborBase64Writer(w io.Writer) error {
|
||||||
w2 := base64.NewEncoder(base64.StdEncoding, w)
|
w2 := base64.NewEncoder(base64.StdEncoding, w)
|
||||||
defer w2.Close()
|
defer w2.Close()
|
||||||
return ctn.ToCbor(w2)
|
return ctn.ToCborWriter(w2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToCar encode the container into a CAR file.
|
||||||
|
func (ctn Writer) ToCar() ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := ctn.ToCarWriter(&buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToCarWriter is the same as ToCar, but with an io.Writer.
|
||||||
|
func (ctn Writer) ToCarWriter(w io.Writer) error {
|
||||||
|
return writeCar(w, nil, func(yield func(carBlock, error) bool) {
|
||||||
|
for c, data := range ctn {
|
||||||
|
if !yield(carBlock{c: c, data: data}, nil) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToCarBase64 encode the container into a base64 encoded CAR file.
|
||||||
|
func (ctn Writer) ToCarBase64() ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := ctn.ToCarBase64Writer(&buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToCarBase64Writer is the same as ToCarBase64, but with an io.Writer.
|
||||||
|
func (ctn Writer) ToCarBase64Writer(w io.Writer) error {
|
||||||
|
w2 := base64.NewEncoder(base64.StdEncoding, w)
|
||||||
|
defer w2.Close()
|
||||||
|
return ctn.ToCarWriter(w2)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user