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.

Revisions

  1. lucasepe created this gist Apr 2, 2023.
    15 changes: 15 additions & 0 deletions hashes.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    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
    }
    159 changes: 159 additions & 0 deletions hashes_test.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,159 @@
    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)
    }
    }
    })
    }
    }