diff --git a/cid.go b/cid.go index e23080c..b3c489b 100644 --- a/cid.go +++ b/cid.go @@ -290,36 +290,16 @@ func uvError(read int) error { // Please use decode when parsing a regular Cid string, as Cast does not // expect multibase-encoded data. Cast accepts the output of Cid.Bytes(). func Cast(data []byte) (Cid, error) { - if len(data) == 34 && data[0] == 18 && data[1] == 32 { - h, err := mh.Cast(data) - if err != nil { - return Undef, err - } - - return NewCidV0(h), nil - } - - vers, n := binary.Uvarint(data) - if err := uvError(n); err != nil { - return Undef, err - } - - if vers != 1 { - return Undef, fmt.Errorf("expected 1 as the cid version number, got: %d", vers) - } - - _, cn := binary.Uvarint(data[n:]) - if err := uvError(cn); err != nil { - return Undef, err - } - - rest := data[n+cn:] - h, err := mh.Cast(rest) + nr, c, err := CidFromBytes(data) if err != nil { return Undef, err } - return Cid{string(data[0 : n+cn+len(h)])}, nil + if nr != len(data) { + return Undef, fmt.Errorf("trailing bytes in data buffer passed to cid Cast") + } + + return c, nil } // UnmarshalBinary is equivalent to Cast(). It implements the @@ -607,3 +587,41 @@ func PrefixFromBytes(buf []byte) (Prefix, error) { MhLength: int(mhlen), }, nil } + +func CidFromBytes(data []byte) (int, Cid, error) { + if len(data) > 2 && data[0] == 18 && data[1] == 32 { + if len(data) < 34 { + return 0, Undef, fmt.Errorf("not enough bytes for cid v0") + } + + h, err := mh.Cast(data[:34]) + if err != nil { + return 0, Undef, err + } + + return 34, NewCidV0(h), nil + } + + vers, n := binary.Uvarint(data) + if err := uvError(n); err != nil { + return 0, Undef, err + } + + if vers != 1 { + return 0, Undef, fmt.Errorf("expected 1 as the cid version number, got: %d", vers) + } + + _, cn := binary.Uvarint(data[n:]) + if err := uvError(cn); err != nil { + return 0, Undef, err + } + + mhnr, _, err := mh.MHFromBytes(data[n+cn:]) + if err != nil { + return 0, Undef, err + } + + l := n + cn + mhnr + + return l, Cid{string(data[0:l])}, nil +} diff --git a/cid_test.go b/cid_test.go index 7fb8441..5d07497 100644 --- a/cid_test.go +++ b/cid_test.go @@ -550,3 +550,37 @@ func BenchmarkStringV1(b *testing.B) { b.FailNow() } } + +func TestReadCidsFromBuffer(t *testing.T) { + cidstr := []string{ + "bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm", + "Qmf5Qzp6nGBku7CEn2UQx4mgN8TW69YUok36DrGa6NN893", + "zb2rhZi1JR4eNc2jBGaRYJKYM8JEB4ovenym8L1CmFsRAytkz", + } + + var cids []Cid + var buf []byte + for _, cs := range cidstr { + c, err := Decode(cs) + if err != nil { + t.Fatal(err) + } + cids = append(cids, c) + buf = append(buf, c.Bytes()...) + } + + var cur int + for _, expc := range cids { + n, c, err := CidFromBytes(buf[cur:]) + if err != nil { + t.Fatal(err) + } + if c != expc { + t.Fatal("cids mismatched") + } + cur += n + } + if cur != len(buf) { + t.Fatal("had trailing bytes") + } +} diff --git a/go.mod b/go.mod index 77f0800..4644bc8 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/ipfs/go-cid require ( github.com/multiformats/go-multibase v0.0.1 - github.com/multiformats/go-multihash v0.0.8 + github.com/multiformats/go-multihash v0.0.9 ) go 1.13 diff --git a/go.sum b/go.sum index 9d488e5..8153cf6 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmr github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= github.com/multiformats/go-multihash v0.0.8 h1:wrYcW5yxSi3dU07n5jnuS5PrNwyHy0zRHGVoUugWvXg= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.0.9 h1:aoijQXYYl7Xtb2pUUP68R+ys1TlnlR3eX6wmozr0Hp4= +github.com/multiformats/go-multihash v0.0.9/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=