Created
April 2, 2023 16:15
-
-
Save lucasepe/9c35d1395839e503c7943533af5ae63e to your computer and use it in GitHub Desktop.
Compute the hash of a stream of data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package hashes | |
| import ( | |
| "hash" | |
| "io" | |
| ) | |
| func Compute(r io.Reader, h hash.Hash) (res []byte, err error) { | |
| _, err = io.Copy(h, r) | |
| if err == nil { | |
| res = h.Sum(nil) | |
| } | |
| return | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package hashes_test | |
| import ( | |
| "bytes" | |
| "crypto/md5" | |
| "crypto/sha1" | |
| "hash" | |
| "hash/adler32" | |
| "hash/crc32" | |
| "hash/fnv" | |
| "io" | |
| "strings" | |
| "testing" | |
| "github.com/lucasepe/hashes" | |
| ) | |
| // hashFunc is a custom function that returns | |
| // a concrete hash function implementation | |
| type hashFunc func() hash.Hash | |
| // md5sum returns an hash function computing the MD5 checksum. | |
| func md5sum() hashFunc { | |
| return func() hash.Hash { | |
| return md5.New() | |
| } | |
| } | |
| // sha1sum returns an hash function computing the SHA1 checksum. | |
| func sha1sum() hashFunc { | |
| return func() hash.Hash { | |
| return sha1.New() | |
| } | |
| } | |
| // adler32 returns an hash function computing the Adler-32 checksum. | |
| func adler32sum() hashFunc { | |
| return func() hash.Hash { | |
| return adler32.New() | |
| } | |
| } | |
| // crc32sum returns an hash function computing the CRC-32 checksum | |
| // using the polynomial represented by the IEEE 802.3 table. | |
| func crc32sum() hashFunc { | |
| table := crc32.MakeTable(crc32.IEEE) | |
| return func() hash.Hash { | |
| return crc32.New(table) | |
| } | |
| } | |
| // fnv32sum returns an hash function computing 32-bit FNV-1a digest. | |
| func fnv32sum() hashFunc { | |
| return func() hash.Hash { | |
| return fnv.New32a() | |
| } | |
| } | |
| func TestSHA1(t *testing.T) { | |
| testHashFunction(t, sha1sum()) | |
| } | |
| func TestMD5(t *testing.T) { | |
| testHashFunction(t, md5sum()) | |
| } | |
| func TestAdler32(t *testing.T) { | |
| testHashFunction(t, adler32sum()) | |
| } | |
| func TestCRC32(t *testing.T) { | |
| testHashFunction(t, crc32sum()) | |
| } | |
| func TestFNV1a(t *testing.T) { | |
| testHashFunction(t, fnv32sum()) | |
| } | |
| func testHashFunction(t *testing.T, sum hashFunc) { | |
| var testcases = []struct { | |
| title string | |
| origin io.Reader | |
| target io.Reader | |
| want bool | |
| }{ | |
| { | |
| title: "hello-world", | |
| origin: strings.NewReader("Hello World!"), | |
| target: strings.NewReader("Hello World!"), | |
| want: true, | |
| }, | |
| { | |
| title: "lorem-ipsum", | |
| origin: strings.NewReader("Lorem ipsum dolor sit amet, consectetur adipiscing elit"), | |
| target: strings.NewReader("lorem ipsum dolor sit amet..."), | |
| want: false, | |
| }, | |
| } | |
| for _, tc := range testcases { | |
| t.Run(tc.title, func(t *testing.T) { | |
| d1, err := hashes.Compute(tc.origin, sum()) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| d2, err := hashes.Compute(tc.target, sum()) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| // t.Logf("origin hash: %X, target hash: %X", d1, d2) | |
| if got := bytes.Equal(d1, d2); got != tc.want { | |
| t.Fatalf("got: [%v], want: [%v]", got, tc.want) | |
| } | |
| }) | |
| } | |
| } | |
| func BenchmarkSHA1(b *testing.B) { | |
| benchmarkHashFunction(b, sha1sum()) | |
| } | |
| func BenchmarkAdler32(b *testing.B) { | |
| benchmarkHashFunction(b, adler32sum()) | |
| } | |
| func BenchmarkCRC32(b *testing.B) { | |
| benchmarkHashFunction(b, crc32sum()) | |
| } | |
| func benchmarkHashFunction(b *testing.B, fn func() hash.Hash) { | |
| var testcases = []struct { | |
| title string | |
| origin io.Reader | |
| target io.Reader | |
| want bool | |
| }{ | |
| { | |
| title: "hello-world", | |
| origin: strings.NewReader("Hello World!"), | |
| }, | |
| { | |
| title: "lorem-ipsum", | |
| origin: strings.NewReader("Lorem ipsum dolor sit amet, consectetur adipiscing elit"), | |
| }, | |
| } | |
| for _, tc := range testcases { | |
| b.Run(tc.title, func(b *testing.B) { | |
| for n := 0; n < b.N; n++ { | |
| _, err := hashes.Compute(tc.origin, fn()) | |
| if err != nil { | |
| b.Fatal(err) | |
| } | |
| } | |
| }) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment