diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 9803360f6..b473d6b72 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -131,8 +131,8 @@ }, { "ImportPath": "github.com/jbenet/go-multihash", - "Comment": "0.1.0-5-g1976046", - "Rev": "1976046c2b0db0b668791b3e541d76a38b7c1af7" + "Comment": "0.1.0-19-g8ce5cb1", + "Rev": "8ce5cb1b82e1b4c1bea1fdf3cd467ef49301734e" }, { "ImportPath": "github.com/jbenet/go-peerstream", diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/io.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/io.go new file mode 100644 index 000000000..cdd234c4b --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/io.go @@ -0,0 +1,79 @@ +package multihash + +import ( + "fmt" + "io" +) + +// Reader is an io.Reader wrapper that exposes a function +// to read a whole multihash, parse it, and return it. +type Reader interface { + io.Reader + + ReadMultihash() (Multihash, error) +} + +// Writer is an io.Writer wrapper that exposes a function +// to write a whole multihash. +type Writer interface { + io.Writer + + WriteMultihash(Multihash) error +} + +// NewReader wraps an io.Reader with a multihash.Reader +func NewReader(r io.Reader) Reader { + return &mhReader{r} +} + +// NewWriter wraps an io.Writer with a multihash.Writer +func NewWriter(w io.Writer) Writer { + return &mhWriter{w} +} + +type mhReader struct { + r io.Reader +} + +func (r *mhReader) Read(buf []byte) (n int, err error) { + return r.r.Read(buf) +} + +func (r *mhReader) ReadMultihash() (Multihash, error) { + mhhdr := make([]byte, 2) + if _, err := io.ReadFull(r.r, mhhdr); err != nil { + return nil, err + } + + // first byte is the algo, the second is the length. + + // (varints someday...) + length := uint(mhhdr[1]) + + if length > 127 { + return nil, fmt.Errorf("varints not yet supported (length is %d)", length) + } + + buf := make([]byte, length+2) + buf[0] = mhhdr[0] + buf[1] = mhhdr[1] + + if _, err := io.ReadFull(r.r, buf[2:]); err != nil { + return nil, err + } + + return Cast(buf) +} + +type mhWriter struct { + w io.Writer +} + +func (w *mhWriter) Write(buf []byte) (n int, err error) { + return w.w.Write(buf) +} + +func (w *mhWriter) WriteMultihash(m Multihash) error { + _, err := w.w.Write([]byte(m)) + return err +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/io_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/io_test.go new file mode 100644 index 000000000..44da78e1f --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/io_test.go @@ -0,0 +1,69 @@ +package multihash + +import ( + "bytes" + "io" + "testing" +) + +func TestReader(t *testing.T) { + + var buf bytes.Buffer + + for _, tc := range testCases { + m, err := tc.Multihash() + if err != nil { + t.Fatal(err) + } + + buf.Write([]byte(m)) + } + + r := NewReader(&buf) + + for _, tc := range testCases { + h, err := tc.Multihash() + if err != nil { + t.Fatal(err) + } + + h2, err := r.ReadMultihash() + if err != nil { + t.Error(err) + continue + } + + if !bytes.Equal(h, h2) { + t.Error("h and h2 should be equal") + } + } +} + +func TestWriter(t *testing.T) { + + var buf bytes.Buffer + w := NewWriter(&buf) + + for _, tc := range testCases { + m, err := tc.Multihash() + if err != nil { + t.Error(err) + continue + } + + if err := w.WriteMultihash(m); err != nil { + t.Error(err) + continue + } + + buf2 := make([]byte, len(m)) + if _, err := io.ReadFull(&buf, buf2); err != nil { + t.Error(err) + continue + } + + if !bytes.Equal(m, buf2) { + t.Error("m and buf2 should be equal") + } + } +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash.go index a56bc162b..9dbe7fd7f 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash.go @@ -2,43 +2,67 @@ package multihash import ( "encoding/hex" + "errors" "fmt" + b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" ) +// errors +var ( + ErrUnknownCode = errors.New("unknown multihash code") + ErrTooShort = errors.New("multihash too short. must be > 3 bytes") + ErrTooLong = errors.New("multihash too long. must be < 129 bytes") + ErrLenNotSupported = errors.New("multihash does not yet support digests longer than 127 bytes") +) + +// ErrInconsistentLen is returned when a decoded multihash has an inconsistent length +type ErrInconsistentLen struct { + dm *DecodedMultihash +} + +func (e ErrInconsistentLen) Error() string { + return fmt.Sprintf("multihash length inconsistent: %v", e.dm) +} + // constants -const SHA1 = 0x11 -const SHA2_256 = 0x12 -const SHA2_512 = 0x13 -const SHA3 = 0x14 -const BLAKE2B = 0x40 -const BLAKE2S = 0x41 +const ( + SHA1 = 0x11 + SHA2_256 = 0x12 + SHA2_512 = 0x13 + SHA3 = 0x14 + BLAKE2B = 0x40 + BLAKE2S = 0x41 +) +// Names maps the name of a hash to the code var Names = map[string]int{ - "sha1": 0x11, - "sha2-256": 0x12, - "sha2-512": 0x13, - "sha3": 0x14, - "blake2b": 0x40, - "blake2s": 0x41, + "sha1": SHA1, + "sha2-256": SHA2_256, + "sha2-512": SHA2_512, + "sha3": SHA3, + "blake2b": BLAKE2B, + "blake2s": BLAKE2S, } +// Codes maps a hash code to it's name var Codes = map[int]string{ - 0x11: "sha1", - 0x12: "sha2-256", - 0x13: "sha2-512", - 0x14: "sha3", - 0x40: "blake2b", - 0x41: "blake2s", + SHA1: "sha1", + SHA2_256: "sha2-256", + SHA2_512: "sha2-512", + SHA3: "sha3", + BLAKE2B: "blake2b", + BLAKE2S: "blake2s", } +// DefaultLengths maps a hash code to it's default length var DefaultLengths = map[int]int{ - 0x11: 20, - 0x12: 32, - 0x13: 64, - 0x14: 64, - 0x40: 64, - 0x41: 32, + SHA1: 20, + SHA2_256: 32, + SHA2_512: 64, + SHA3: 64, + BLAKE2B: 64, + BLAKE2S: 32, } type DecodedMultihash struct { @@ -50,8 +74,12 @@ type DecodedMultihash struct { type Multihash []byte -func (m Multihash) HexString() string { - return hex.EncodeToString([]byte(m)) +func (m *Multihash) HexString() string { + return hex.EncodeToString([]byte(*m)) +} + +func (m *Multihash) String() string { + return m.HexString() } func FromHexString(s string) (Multihash, error) { @@ -88,21 +116,21 @@ func Cast(buf []byte) (Multihash, error) { } if !ValidCode(dm.Code) { - return Multihash{}, fmt.Errorf("unknown multihash code") + return Multihash{}, ErrUnknownCode } return Multihash(buf), nil } -// Decodes a hash from the given Multihash. +// Decode a hash from the given Multihash. func Decode(buf []byte) (*DecodedMultihash, error) { if len(buf) < 3 { - return nil, fmt.Errorf("multihash too short. must be > 3 bytes.") + return nil, ErrTooShort } if len(buf) > 129 { - return nil, fmt.Errorf("multihash too long. must be < 129 bytes.") + return nil, ErrTooLong } dm := &DecodedMultihash{ @@ -113,23 +141,22 @@ func Decode(buf []byte) (*DecodedMultihash, error) { } if len(dm.Digest) != dm.Length { - return nil, fmt.Errorf("multihash length inconsistent: %v", dm) + return nil, ErrInconsistentLen{dm} } return dm, nil } -// Encodes a hash digest along with the specified function code. +// Encode a hash digest along with the specified function code. // Note: the length is derived from the length of the digest itself. func Encode(buf []byte, code int) ([]byte, error) { if !ValidCode(code) { - return nil, fmt.Errorf("unknown multihash code") + return nil, ErrUnknownCode } if len(buf) > 127 { - m := "multihash does not yet support digests longer than 127 bytes." - return nil, fmt.Errorf(m) + return nil, ErrLenNotSupported } pre := make([]byte, 2) @@ -142,7 +169,7 @@ func EncodeName(buf []byte, name string) ([]byte, error) { return Encode(buf, Names[name]) } -// Checks whether a multihash code is valid. +// ValidCode checks whether a multihash code is valid. func ValidCode(code int) bool { if AppCode(code) { return true @@ -155,7 +182,7 @@ func ValidCode(code int) bool { return false } -// Checks whether a multihash code is part of the App range. +// AppCode checks whether a multihash code is part of the App range. func AppCode(code int) bool { return code >= 0 && code < 0x10 } diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash_test.go index fac9f721b..dfa858e4f 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash_test.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash_test.go @@ -3,6 +3,7 @@ package multihash import ( "bytes" "encoding/hex" + "fmt" "testing" ) @@ -31,6 +32,19 @@ var testCases = []TestCase{ TestCase{"0beec7b5ea3f0fdbc9", 0x40, "blake2b"}, } +func (tc TestCase) Multihash() (Multihash, error) { + ob, err := hex.DecodeString(tc.hex) + if err != nil { + return nil, err + } + + b := make([]byte, 2+len(ob)) + b[0] = byte(uint8(tc.code)) + b[1] = byte(uint8(len(ob))) + copy(b[2:], ob) + return Cast(b) +} + func TestEncode(t *testing.T) { for _, tc := range testCases { ob, err := hex.DecodeString(tc.hex) @@ -63,9 +77,28 @@ func TestEncode(t *testing.T) { if !bytes.Equal(encN, nb) { t.Error("encoded byte mismatch: ", encN, nb) } + + h, err := tc.Multihash() + if err != nil { + t.Error(err) + } + if !bytes.Equal(h, nb) { + t.Error("Multihash func mismatch.") + } } } +func ExampleEncodeName() { + // ignores errors for simplicity - don't do that at home. + buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33") + mhbuf, _ := EncodeName(buf, "sha1") + mhhex := hex.EncodeToString(mhbuf) + fmt.Printf("hex: %v\n", mhhex) + + // Output: + // hex: 11140beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 +} + func TestDecode(t *testing.T) { for _, tc := range testCases { ob, err := hex.DecodeString(tc.hex) @@ -114,6 +147,18 @@ func TestTable(t *testing.T) { } } +func ExampleDecode() { + // ignores errors for simplicity - don't do that at home. + buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33") + mhbuf, _ := EncodeName(buf, "sha1") + o, _ := Decode(mhbuf) + mhhex := hex.EncodeToString(o.Digest) + fmt.Printf("obj: %v 0x%x %d %s\n", o.Name, o.Code, o.Length, mhhex) + + // Output: + // obj: sha1 0x11 20 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 +} + func TestValidCode(t *testing.T) { for i := 0; i < 0xff; i++ { _, ok := tCodes[i] @@ -127,7 +172,7 @@ func TestValidCode(t *testing.T) { func TestAppCode(t *testing.T) { for i := 0; i < 0xff; i++ { - b := i > 0 && i < 0x10 + b := i >= 0 && i < 0x10 if AppCode(i) != b { t.Error("AppCode incorrect for: ", i) } @@ -190,3 +235,36 @@ func TestHex(t *testing.T) { } } } + +func BenchmarkEncode(b *testing.B) { + tc := testCases[0] + ob, err := hex.DecodeString(tc.hex) + if err != nil { + b.Error(err) + return + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + Encode(ob, tc.code) + } +} + +func BenchmarkDecode(b *testing.B) { + tc := testCases[0] + ob, err := hex.DecodeString(tc.hex) + if err != nil { + b.Error(err) + return + } + + pre := make([]byte, 2) + pre[0] = byte(uint8(tc.code)) + pre[1] = byte(uint8(len(ob))) + nb := append(pre, ob...) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + Decode(nb) + } +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum.go index 767be814e..4dc29ee3c 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum.go @@ -4,10 +4,14 @@ import ( "crypto/sha1" "crypto/sha256" "crypto/sha512" + "errors" "fmt" + sha3 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.crypto/sha3" ) +var ErrSumNotSupported = errors.New("Function not implemented. Complain to lib maintainer.") + func Sum(data []byte, code int, length int) (Multihash, error) { m := Multihash{} err := error(nil) @@ -26,7 +30,7 @@ func Sum(data []byte, code int, length int) (Multihash, error) { case SHA3: d, err = sumSHA3(data) default: - return m, fmt.Errorf("Function not implemented. Complain to lib maintainer.") + return m, ErrSumNotSupported } if err != nil { @@ -60,7 +64,7 @@ func sumSHA512(data []byte) []byte { } func sumSHA3(data []byte) ([]byte, error) { - h := sha3.NewKeccak512() + h := sha3.New512() if _, err := h.Write(data); err != nil { return nil, err } diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum_test.go index 3431d6f67..1e5891bbb 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum_test.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum_test.go @@ -57,3 +57,10 @@ func TestSum(t *testing.T) { } } } + +func BenchmarkSum(b *testing.B) { + tc := sumTestCases[0] + for i := 0; i < b.N; i++ { + Sum([]byte(tc.input), tc.code, tc.length) + } +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/foo.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/foo.go deleted file mode 100644 index 21d33d658..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/foo.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "encoding/hex" - "fmt" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" -) - -func main() { - // ignores errors for simplicity. - // don't do that at home. - - buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33") - mhbuf, _ := multihash.EncodeName(buf, "sha1") - mhhex := hex.EncodeToString(mhbuf) - fmt.Printf("hex: %v\n", mhhex) - - o, _ := multihash.Decode(mhbuf) - mhhex = hex.EncodeToString(o.Digest) - fmt.Printf("obj: %v 0x%x %d %s\n", o.Name, o.Code, o.Length, mhhex) -} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/test b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/test new file mode 100644 index 000000000..9e5199531 Binary files /dev/null and b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/test differ