Skip to content

Instantly share code, notes, and snippets.

@lucasepe
Created April 2, 2023 16:15
Show Gist options
  • Select an option

  • Save lucasepe/9c35d1395839e503c7943533af5ae63e to your computer and use it in GitHub Desktop.

Select an option

Save lucasepe/9c35d1395839e503c7943533af5ae63e to your computer and use it in GitHub Desktop.
Compute the hash of a stream of data
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
}
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