1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-06-27 16:07:42 +08:00

updated multihash (io)

This commit is contained in:
Juan Batiz-Benet
2014-12-24 10:20:25 -08:00
parent 7bbc0084be
commit 4d08eb0140
9 changed files with 306 additions and 63 deletions

4
Godeps/Godeps.json generated
View File

@ -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",

View File

@ -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
}

View File

@ -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")
}
}
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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)
}

Binary file not shown.