diff --git a/cid.go b/cid.go index b5d0bbc..8bf2d09 100644 --- a/cid.go +++ b/cid.go @@ -174,3 +174,76 @@ func (c *Cid) Loggable() map[string]interface{} { "cid": c, } } + +func (c *Cid) Prefix() Prefix { + dec, _ := mh.Decode(c.hash) // assuming we got a valid multiaddr, this will not error + return Prefix{ + MhType: dec.Code, + MhLength: dec.Length, + Version: c.version, + Codec: c.codec, + } +} + +// Prefix represents all the metadata of a cid, minus any actual content information +type Prefix struct { + Version uint64 + Codec uint64 + MhType int + MhLength int +} + +func (p Prefix) Sum(data []byte) (*Cid, error) { + hash, err := mh.Sum(data, p.MhType, p.MhLength) + if err != nil { + return nil, err + } + + switch p.Version { + case 0: + return NewCidV0(hash), nil + case 1: + return NewCidV1(p.Codec, hash), nil + default: + return nil, fmt.Errorf("invalid cid version") + } +} + +func (p Prefix) Bytes() []byte { + buf := make([]byte, 16) + n := binary.PutUvarint(buf, p.Version) + n += binary.PutUvarint(buf[n:], p.Codec) + n += binary.PutUvarint(buf[n:], uint64(p.MhType)) + n += binary.PutUvarint(buf[n:], uint64(p.MhLength)) + return buf[:n] +} + +func PrefixFromBytes(buf []byte) (Prefix, error) { + r := bytes.NewReader(buf) + vers, err := binary.ReadUvarint(r) + if err != nil { + return Prefix{}, err + } + + codec, err := binary.ReadUvarint(r) + if err != nil { + return Prefix{}, err + } + + mhtype, err := binary.ReadUvarint(r) + if err != nil { + return Prefix{}, err + } + + mhlen, err := binary.ReadUvarint(r) + if err != nil { + return Prefix{}, err + } + + return Prefix{ + Version: vers, + Codec: codec, + MhType: int(mhtype), + MhLength: int(mhlen), + }, nil +} diff --git a/cid_test.go b/cid_test.go index 9e64f90..209692d 100644 --- a/cid_test.go +++ b/cid_test.go @@ -79,3 +79,32 @@ func TestV0ErrorCases(t *testing.T) { t.Fatal("should have failed to decode that ref") } } + +func TestPrefixRoundtrip(t *testing.T) { + data := []byte("this is some test content") + hash, _ := mh.Sum(data, mh.SHA2_256, -1) + c := NewCidV1(CBOR, hash) + + pref := c.Prefix() + + c2, err := pref.Sum(data) + if err != nil { + t.Fatal(err) + } + + if !c.Equals(c2) { + t.Fatal("output didnt match original") + } + + pb := pref.Bytes() + + pref2, err := PrefixFromBytes(pb) + if err != nil { + t.Fatal(err) + } + + if pref.Version != pref2.Version || pref.Codec != pref2.Codec || + pref.MhType != pref2.MhType || pref.MhLength != pref2.MhLength { + t.Fatal("input prefix didnt match output") + } +} diff --git a/set.go b/set.go index a237ae3..0a1cef2 100644 --- a/set.go +++ b/set.go @@ -26,7 +26,7 @@ func (s *Set) Len() int { } func (s *Set) Keys() []*Cid { - var out []*Cid + out := make([]*Cid, 0, len(s.set)) for k, _ := range s.set { c, _ := Cast([]byte(k)) out = append(out, c) @@ -42,3 +42,14 @@ func (s *Set) Visit(c *Cid) bool { return false } + +func (s *Set) ForEach(f func(c *Cid) error) error { + for cs, _ := range s.set { + c, _ := Cast([]byte(cs)) + err := f(c) + if err != nil { + return err + } + } + return nil +}