container: add cbor serialisation

This commit is contained in:
Michael Muré
2024-09-19 21:26:42 +02:00
parent df9beadf9c
commit 346efbd31d
5 changed files with 66 additions and 8 deletions

View File

@@ -29,7 +29,9 @@ type carBlock struct {
data []byte
}
// writeCar writes a CARv1 file with no roots, containing the blocks from the iterator.
// writeCar writes a CARv1 file containing the blocks from the iterator.
// If no roots are provided, a single EmptyCid is used as root to make the file
// spec compliant.
func writeCar(w io.Writer, roots []cid.Cid, blocks iter.Seq[carBlock]) error {
if len(roots) == 0 {
roots = []cid.Cid{EmptyCid}

View File

@@ -5,6 +5,8 @@ import (
"io"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/multiformats/go-multihash"
)
// TODO: should the invocation being set as root in the car file?
@@ -37,6 +39,43 @@ func FromCarBase64(r io.Reader) (Container, error) {
return FromCar(base64.NewDecoder(base64.StdEncoding, r))
}
func FromCbor(r io.Reader) (Container, error) {
var raw [][]byte
err := cbor.DecodeReader(r, &raw)
if err != nil {
return nil, err
}
// TODO: the CID computation will likely be handled in the envelope
// TODO: the envelope should likely be able to deserialize arbitrary types based on the tag value
// TODO: the container should likely expose the decoded token, and have search methods (simple, but also DAG reconstruction, graph path search)
var cidBuilder = cid.V1Builder{Codec: cid.DagCBOR, MhType: multihash.SHA2_256}
ctn := make(Container, len(raw))
for _, bytes := range raw {
c, err := cidBuilder.Sum(bytes)
if err != nil {
return nil, err
}
ctn[c] = bytes
}
return ctn, nil
}
func FromCborBase64(r io.Reader) (Container, error) {
return FromCbor(base64.NewDecoder(base64.StdEncoding, r))
}
func (ctn Container) AddBytes(cid cid.Cid, data []byte) {
ctn[cid] = data
}
func (ctn Container) GetBytes(cid cid.Cid) ([]byte, bool) {
b, ok := ctn[cid]
return b, ok
}
func (ctn Container) ToCar(w io.Writer) error {
return writeCar(w, nil, func(yield func(carBlock) bool) {
for c, bytes := range ctn {
@@ -53,11 +92,16 @@ func (ctn Container) ToCarBase64(w io.Writer) error {
return ctn.ToCar(w2)
}
func (ctn Container) AddBytes(cid cid.Cid, data []byte) {
ctn[cid] = data
func (ctn Container) ToCbor(w io.Writer) error {
raw := make([][]byte, 0, len(ctn))
for _, bytes := range ctn {
raw = append(raw, bytes)
}
return cbor.EncodeWriter(raw, w)
}
func (ctn Container) GetBytes(cid cid.Cid) ([]byte, bool) {
b, ok := ctn[cid]
return b, ok
func (ctn Container) ToCborBase64(w io.Writer) error {
w2 := base64.NewEncoder(base64.StdEncoding, w)
defer w2.Close()
return ctn.ToCbor(w2)
}

View File

@@ -19,17 +19,22 @@ func TestContainerRoundTrip(t *testing.T) {
}{
{"carBytes", Container.ToCar, FromCar},
{"carBase64", Container.ToCarBase64, FromCarBase64},
{"cbor", Container.ToCbor, FromCbor},
{"cborBase64", Container.ToCborBase64, FromCborBase64},
} {
t.Run(tc.name, func(t *testing.T) {
ctn := New()
builder := cid.V1Builder{Codec: cid.Raw, MhType: mh.SHA2_256}
builder := cid.V1Builder{Codec: cid.DagCBOR, MhType: mh.SHA2_256}
var dataSize int
for i := 0; i < 10; i++ {
data := randBytes(32)
c, err := builder.Sum(data)
require.NoError(t, err)
ctn.AddBytes(c, data)
dataSize += len(data)
}
buf := bytes.NewBuffer(nil)
@@ -37,6 +42,9 @@ func TestContainerRoundTrip(t *testing.T) {
err := tc.writer(ctn, buf)
require.NoError(t, err)
t.Logf("data size %d", dataSize)
t.Logf("container overhead: %d%%, %d bytes", int(float32(buf.Len()-dataSize)/float32(dataSize)*100.0), buf.Len()-dataSize)
ctn2, err := tc.reader(bytes.NewReader(buf.Bytes()))
require.NoError(t, err)