updated multihash (io)
Juan Batiz-Benet committed
Dec 24, 2014 at 10:20 UTC
4d08eb0140c5d6536742adfffb0b8724aee6ad51
9 files changed
+306
-63
Godeps/Godeps.json
+2
-2
@@ -131,8 +131,8 @@
131
},
132
{
133
"ImportPath": "github.com/jbenet/go-multihash",
134
- "Comment": "0.1.0-5-g1976046",
135
- "Rev": "1976046c2b0db0b668791b3e541d76a38b7c1af7"
134
+ "Comment": "0.1.0-19-g8ce5cb1",
135
+ "Rev": "8ce5cb1b82e1b4c1bea1fdf3cd467ef49301734e"
136
},
137
{
138
"ImportPath": "github.com/jbenet/go-peerstream",
Godeps/_workspace/src/github.com/jbenet/go-multihash/io.go
new
+79
@@ -0,0 +1,79 @@
1
+package multihash
2
+
3
+import (
4
+ "fmt"
5
+ "io"
6
+)
7
+
8
+// Reader is an io.Reader wrapper that exposes a function
9
+// to read a whole multihash, parse it, and return it.
10
+type Reader interface {
11
+ io.Reader
12
+
13
+ ReadMultihash() (Multihash, error)
14
+}
15
+
16
+// Writer is an io.Writer wrapper that exposes a function
17
+// to write a whole multihash.
18
+type Writer interface {
19
+ io.Writer
20
+
21
+ WriteMultihash(Multihash) error
22
+}
23
+
24
+// NewReader wraps an io.Reader with a multihash.Reader
25
+func NewReader(r io.Reader) Reader {
26
+ return &mhReader{r}
27
+}
28
+
29
+// NewWriter wraps an io.Writer with a multihash.Writer
30
+func NewWriter(w io.Writer) Writer {
31
+ return &mhWriter{w}
32
+}
33
+
34
+type mhReader struct {
35
+ r io.Reader
36
+}
37
+
38
+func (r *mhReader) Read(buf []byte) (n int, err error) {
39
+ return r.r.Read(buf)
40
+}
41
+
42
+func (r *mhReader) ReadMultihash() (Multihash, error) {
43
+ mhhdr := make([]byte, 2)
44
+ if _, err := io.ReadFull(r.r, mhhdr); err != nil {
45
+ return nil, err
46
+ }
47
+
48
+ // first byte is the algo, the second is the length.
49
+
50
+ // (varints someday...)
51
+ length := uint(mhhdr[1])
52
+
53
+ if length > 127 {
54
+ return nil, fmt.Errorf("varints not yet supported (length is %d)", length)
55
+ }
56
+
57
+ buf := make([]byte, length+2)
58
+ buf[0] = mhhdr[0]
59
+ buf[1] = mhhdr[1]
60
+
61
+ if _, err := io.ReadFull(r.r, buf[2:]); err != nil {
62
+ return nil, err
63
+ }
64
+
65
+ return Cast(buf)
66
+}
67
+
68
+type mhWriter struct {
69
+ w io.Writer
70
+}
71
+
72
+func (w *mhWriter) Write(buf []byte) (n int, err error) {
73
+ return w.w.Write(buf)
74
+}
75
+
76
+func (w *mhWriter) WriteMultihash(m Multihash) error {
77
+ _, err := w.w.Write([]byte(m))
78
+ return err
79
+}
Godeps/_workspace/src/github.com/jbenet/go-multihash/io_test.go
new
+69
@@ -0,0 +1,69 @@
1
+package multihash
2
+
3
+import (
4
+ "bytes"
5
+ "io"
6
+ "testing"
7
+)
8
+
9
+func TestReader(t *testing.T) {
10
+
11
+ var buf bytes.Buffer
12
+
13
+ for _, tc := range testCases {
14
+ m, err := tc.Multihash()
15
+ if err != nil {
16
+ t.Fatal(err)
17
+ }
18
+
19
+ buf.Write([]byte(m))
20
+ }
21
+
22
+ r := NewReader(&buf)
23
+
24
+ for _, tc := range testCases {
25
+ h, err := tc.Multihash()
26
+ if err != nil {
27
+ t.Fatal(err)
28
+ }
29
+
30
+ h2, err := r.ReadMultihash()
31
+ if err != nil {
32
+ t.Error(err)
33
+ continue
34
+ }
35
+
36
+ if !bytes.Equal(h, h2) {
37
+ t.Error("h and h2 should be equal")
38
+ }
39
+ }
40
+}
41
+
42
+func TestWriter(t *testing.T) {
43
+
44
+ var buf bytes.Buffer
45
+ w := NewWriter(&buf)
46
+
47
+ for _, tc := range testCases {
48
+ m, err := tc.Multihash()
49
+ if err != nil {
50
+ t.Error(err)
51
+ continue
52
+ }
53
+
54
+ if err := w.WriteMultihash(m); err != nil {
55
+ t.Error(err)
56
+ continue
57
+ }
58
+
59
+ buf2 := make([]byte, len(m))
60
+ if _, err := io.ReadFull(&buf, buf2); err != nil {
61
+ t.Error(err)
62
+ continue
63
+ }
64
+
65
+ if !bytes.Equal(m, buf2) {
66
+ t.Error("m and buf2 should be equal")
67
+ }
68
+ }
69
+}
Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash.go
+64
-37
@@ -2,43 +2,67 @@ package multihash
2
3
import (
4
"encoding/hex"
5
+ "errors"
6
"fmt"
7
+
8
b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
9
)
10
11
+// errors
12
+var (
13
+ ErrUnknownCode = errors.New("unknown multihash code")
14
+ ErrTooShort = errors.New("multihash too short. must be > 3 bytes")
15
+ ErrTooLong = errors.New("multihash too long. must be < 129 bytes")
16
+ ErrLenNotSupported = errors.New("multihash does not yet support digests longer than 127 bytes")
17
+)
18
+
19
+// ErrInconsistentLen is returned when a decoded multihash has an inconsistent length
20
+type ErrInconsistentLen struct {
21
+ dm *DecodedMultihash
22
+}
23
+
24
+func (e ErrInconsistentLen) Error() string {
25
+ return fmt.Sprintf("multihash length inconsistent: %v", e.dm)
26
+}
27
+
28
// constants
10
-const SHA1 = 0x11
11
-const SHA2_256 = 0x12
12
-const SHA2_512 = 0x13
13
-const SHA3 = 0x14
14
-const BLAKE2B = 0x40
15
-const BLAKE2S = 0x41
29
+const (
30
+ SHA1 = 0x11
31
+ SHA2_256 = 0x12
32
+ SHA2_512 = 0x13
33
+ SHA3 = 0x14
34
+ BLAKE2B = 0x40
35
+ BLAKE2S = 0x41
36
+)
37
38
+// Names maps the name of a hash to the code
39
var Names = map[string]int{
18
- "sha1": 0x11,
19
- "sha2-256": 0x12,
20
- "sha2-512": 0x13,
21
- "sha3": 0x14,
22
- "blake2b": 0x40,
23
- "blake2s": 0x41,
40
+ "sha1": SHA1,
41
+ "sha2-256": SHA2_256,
42
+ "sha2-512": SHA2_512,
43
+ "sha3": SHA3,
44
+ "blake2b": BLAKE2B,
45
+ "blake2s": BLAKE2S,
46
}
47
48
+// Codes maps a hash code to it's name
49
var Codes = map[int]string{
27
- 0x11: "sha1",
28
- 0x12: "sha2-256",
29
- 0x13: "sha2-512",
30
- 0x14: "sha3",
31
- 0x40: "blake2b",
32
- 0x41: "blake2s",
50
+ SHA1: "sha1",
51
+ SHA2_256: "sha2-256",
52
+ SHA2_512: "sha2-512",
53
+ SHA3: "sha3",
54
+ BLAKE2B: "blake2b",
55
+ BLAKE2S: "blake2s",
56
}
57
58
+// DefaultLengths maps a hash code to it's default length
59
var DefaultLengths = map[int]int{
36
- 0x11: 20,
37
- 0x12: 32,
38
- 0x13: 64,
39
- 0x14: 64,
40
- 0x40: 64,
41
- 0x41: 32,
60
+ SHA1: 20,
61
+ SHA2_256: 32,
62
+ SHA2_512: 64,
63
+ SHA3: 64,
64
+ BLAKE2B: 64,
65
+ BLAKE2S: 32,
66
}
67
68
type DecodedMultihash struct {
@@ -50,8 +74,12 @@ type DecodedMultihash struct {
74
75
type Multihash []byte
76
53
-func (m Multihash) HexString() string {
54
- return hex.EncodeToString([]byte(m))
77
+func (m *Multihash) HexString() string {
78
+ return hex.EncodeToString([]byte(*m))
79
+}
80
+
81
+func (m *Multihash) String() string {
82
+ return m.HexString()
83
}
84
85
func FromHexString(s string) (Multihash, error) {
@@ -88,21 +116,21 @@ func Cast(buf []byte) (Multihash, error) {
116
}
117
118
if !ValidCode(dm.Code) {
91
- return Multihash{}, fmt.Errorf("unknown multihash code")
119
+ return Multihash{}, ErrUnknownCode
120
}
121
122
return Multihash(buf), nil
123
}
124
97
-// Decodes a hash from the given Multihash.
125
+// Decode a hash from the given Multihash.
126
func Decode(buf []byte) (*DecodedMultihash, error) {
127
128
if len(buf) < 3 {
101
- return nil, fmt.Errorf("multihash too short. must be > 3 bytes.")
129
+ return nil, ErrTooShort
130
}
131
132
if len(buf) > 129 {
105
- return nil, fmt.Errorf("multihash too long. must be < 129 bytes.")
133
+ return nil, ErrTooLong
134
}
135
136
dm := &DecodedMultihash{
@@ -113,23 +141,22 @@ func Decode(buf []byte) (*DecodedMultihash, error) {
141
}
142
143
if len(dm.Digest) != dm.Length {
116
- return nil, fmt.Errorf("multihash length inconsistent: %v", dm)
144
+ return nil, ErrInconsistentLen{dm}
145
}
146
147
return dm, nil
148
}
149
122
-// Encodes a hash digest along with the specified function code.
150
+// Encode a hash digest along with the specified function code.
151
// Note: the length is derived from the length of the digest itself.
152
func Encode(buf []byte, code int) ([]byte, error) {
153
154
if !ValidCode(code) {
127
- return nil, fmt.Errorf("unknown multihash code")
155
+ return nil, ErrUnknownCode
156
}
157
158
if len(buf) > 127 {
131
- m := "multihash does not yet support digests longer than 127 bytes."
132
- return nil, fmt.Errorf(m)
159
+ return nil, ErrLenNotSupported
160
}
161
162
pre := make([]byte, 2)
@@ -142,7 +169,7 @@ func EncodeName(buf []byte, name string) ([]byte, error) {
169
return Encode(buf, Names[name])
170
}
171
145
-// Checks whether a multihash code is valid.
172
+// ValidCode checks whether a multihash code is valid.
173
func ValidCode(code int) bool {
174
if AppCode(code) {
175
return true
@@ -155,7 +182,7 @@ func ValidCode(code int) bool {
182
return false
183
}
184
158
-// Checks whether a multihash code is part of the App range.
185
+// AppCode checks whether a multihash code is part of the App range.
186
func AppCode(code int) bool {
187
return code >= 0 && code < 0x10
188
}
Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash_test.go
+79
-1
@@ -3,6 +3,7 @@ package multihash
3
import (
4
"bytes"
5
"encoding/hex"
6
+ "fmt"
7
"testing"
8
)
9
@@ -31,6 +32,19 @@ var testCases = []TestCase{
32
TestCase{"0beec7b5ea3f0fdbc9", 0x40, "blake2b"},
33
}
34
35
+func (tc TestCase) Multihash() (Multihash, error) {
36
+ ob, err := hex.DecodeString(tc.hex)
37
+ if err != nil {
38
+ return nil, err
39
+ }
40
+
41
+ b := make([]byte, 2+len(ob))
42
+ b[0] = byte(uint8(tc.code))
43
+ b[1] = byte(uint8(len(ob)))
44
+ copy(b[2:], ob)
45
+ return Cast(b)
46
+}
47
+
48
func TestEncode(t *testing.T) {
49
for _, tc := range testCases {
50
ob, err := hex.DecodeString(tc.hex)
@@ -63,9 +77,28 @@ func TestEncode(t *testing.T) {
77
if !bytes.Equal(encN, nb) {
78
t.Error("encoded byte mismatch: ", encN, nb)
79
}
80
+
81
+ h, err := tc.Multihash()
82
+ if err != nil {
83
+ t.Error(err)
84
+ }
85
+ if !bytes.Equal(h, nb) {
86
+ t.Error("Multihash func mismatch.")
87
+ }
88
}
89
}
90
91
+func ExampleEncodeName() {
92
+ // ignores errors for simplicity - don't do that at home.
93
+ buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")
94
+ mhbuf, _ := EncodeName(buf, "sha1")
95
+ mhhex := hex.EncodeToString(mhbuf)
96
+ fmt.Printf("hex: %v\n", mhhex)
97
+
98
+ // Output:
99
+ // hex: 11140beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
100
+}
101
+
102
func TestDecode(t *testing.T) {
103
for _, tc := range testCases {
104
ob, err := hex.DecodeString(tc.hex)
@@ -114,6 +147,18 @@ func TestTable(t *testing.T) {
147
}
148
}
149
150
+func ExampleDecode() {
151
+ // ignores errors for simplicity - don't do that at home.
152
+ buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")
153
+ mhbuf, _ := EncodeName(buf, "sha1")
154
+ o, _ := Decode(mhbuf)
155
+ mhhex := hex.EncodeToString(o.Digest)
156
+ fmt.Printf("obj: %v 0x%x %d %s\n", o.Name, o.Code, o.Length, mhhex)
157
+
158
+ // Output:
159
+ // obj: sha1 0x11 20 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
160
+}
161
+
162
func TestValidCode(t *testing.T) {
163
for i := 0; i < 0xff; i++ {
164
_, ok := tCodes[i]
@@ -127,7 +172,7 @@ func TestValidCode(t *testing.T) {
172
173
func TestAppCode(t *testing.T) {
174
for i := 0; i < 0xff; i++ {
130
- b := i > 0 && i < 0x10
175
+ b := i >= 0 && i < 0x10
176
if AppCode(i) != b {
177
t.Error("AppCode incorrect for: ", i)
178
}
@@ -190,3 +235,36 @@ func TestHex(t *testing.T) {
235
}
236
}
237
}
238
+
239
+func BenchmarkEncode(b *testing.B) {
240
+ tc := testCases[0]
241
+ ob, err := hex.DecodeString(tc.hex)
242
+ if err != nil {
243
+ b.Error(err)
244
+ return
245
+ }
246
+
247
+ b.ResetTimer()
248
+ for i := 0; i < b.N; i++ {
249
+ Encode(ob, tc.code)
250
+ }
251
+}
252
+
253
+func BenchmarkDecode(b *testing.B) {
254
+ tc := testCases[0]
255
+ ob, err := hex.DecodeString(tc.hex)
256
+ if err != nil {
257
+ b.Error(err)
258
+ return
259
+ }
260
+
261
+ pre := make([]byte, 2)
262
+ pre[0] = byte(uint8(tc.code))
263
+ pre[1] = byte(uint8(len(ob)))
264
+ nb := append(pre, ob...)
265
+
266
+ b.ResetTimer()
267
+ for i := 0; i < b.N; i++ {
268
+ Decode(nb)
269
+ }
270
+}
Godeps/_workspace/src/github.com/jbenet/go-multihash/sum.go
+6
-2
@@ -4,10 +4,14 @@ import (
4
"crypto/sha1"
5
"crypto/sha256"
6
"crypto/sha512"
7
+ "errors"
8
"fmt"
9
+
10
sha3 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.crypto/sha3"
11
)
12
13
+var ErrSumNotSupported = errors.New("Function not implemented. Complain to lib maintainer.")
14
+
15
func Sum(data []byte, code int, length int) (Multihash, error) {
16
m := Multihash{}
17
err := error(nil)
@@ -26,7 +30,7 @@ func Sum(data []byte, code int, length int) (Multihash, error) {
30
case SHA3:
31
d, err = sumSHA3(data)
32
default:
29
- return m, fmt.Errorf("Function not implemented. Complain to lib maintainer.")
33
+ return m, ErrSumNotSupported
34
}
35
36
if err != nil {
@@ -60,7 +64,7 @@ func sumSHA512(data []byte) []byte {
64
}
65
66
func sumSHA3(data []byte) ([]byte, error) {
63
- h := sha3.NewKeccak512()
67
+ h := sha3.New512()
68
if _, err := h.Write(data); err != nil {
69
return nil, err
70
}
Godeps/_workspace/src/github.com/jbenet/go-multihash/sum_test.go
+7
@@ -57,3 +57,10 @@ func TestSum(t *testing.T) {
57
}
58
}
59
}
60
+
61
+func BenchmarkSum(b *testing.B) {
62
+ tc := sumTestCases[0]
63
+ for i := 0; i < b.N; i++ {
64
+ Sum([]byte(tc.input), tc.code, tc.length)
65
+ }
66
+}
Godeps/_workspace/src/github.com/jbenet/go-multihash/test/foo.go
deleted
-21
@@ -1,21 +0,0 @@
1
-package main
2
-
3
-import (
4
- "encoding/hex"
5
- "fmt"
6
- "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
7
-)
8
-
9
-func main() {
10
- // ignores errors for simplicity.
11
- // don't do that at home.
12
-
13
- buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")
14
- mhbuf, _ := multihash.EncodeName(buf, "sha1")
15
- mhhex := hex.EncodeToString(mhbuf)
16
- fmt.Printf("hex: %v\n", mhhex)
17
-
18
- o, _ := multihash.Decode(mhbuf)
19
- mhhex = hex.EncodeToString(o.Digest)
20
- fmt.Printf("obj: %v 0x%x %d %s\n", o.Name, o.Code, o.Length, mhhex)
21
-}
Godeps/_workspace/src/github.com/jbenet/go-multihash/test/test
Binary files /dev/null and b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/test differ