174 lines
4.2 KiB
Go
174 lines
4.2 KiB
Go
package mishmash
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"hash"
|
|
"math/big"
|
|
"math/rand"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
type HashedString struct {
|
|
s string
|
|
h32 uint32
|
|
h64 uint64
|
|
}
|
|
|
|
var (
|
|
TheCppOutputFile = "word_hashes.txt"
|
|
TheCppOutput = func() []HashedString {
|
|
f, err := os.Open(TheCppOutputFile)
|
|
if err != nil {
|
|
panic("error opening " + TheCppOutputFile + ": " + err.Error())
|
|
}
|
|
defer f.Close()
|
|
results := make([]HashedString, 0)
|
|
scanner := bufio.NewScanner(f)
|
|
for scanner.Scan() {
|
|
if line := strings.TrimSpace(scanner.Text()); len(line) > 0 {
|
|
fields := strings.Fields(line)
|
|
if len(fields) == 3 {
|
|
n1, err := strconv.ParseUint(fields[1], 16, 32)
|
|
if err != nil {
|
|
panic("error parsing " + fields[1] + ": " + err.Error())
|
|
}
|
|
n2, err := strconv.ParseUint(fields[2], 16, 64)
|
|
if err != nil {
|
|
panic("error parsing " + fields[2] + ": " + err.Error())
|
|
}
|
|
results = append(results, HashedString{fields[0], uint32(n1), n2})
|
|
}
|
|
}
|
|
}
|
|
return results
|
|
}()
|
|
RandomWordAndHash = func() HashedString {
|
|
return TheCppOutput[rand.Intn(len(TheCppOutput))]
|
|
}
|
|
)
|
|
|
|
func TestPrimes(t *testing.T) {
|
|
for _, kvp := range TheCppOutput {
|
|
if word, want, got := kvp.s, kvp.h32, MishmashString(kvp.s); want != got {
|
|
t.Fatalf("error: %s; wanted %08x; got %08x\n", word, want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHash32Interface(t *testing.T) {
|
|
for _, kvp := range TheCppOutput {
|
|
var h hash.Hash32 = &Mishmash32{}
|
|
word := kvp.s
|
|
h.Write([]byte(word))
|
|
if want, got := kvp.h32, h.Sum32(); want != got {
|
|
t.Fatalf("error: %s; wanted %08x; got %08x\n", word, want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHash64Interface(t *testing.T) {
|
|
for _, kvp := range TheCppOutput {
|
|
var h hash.Hash64 = &Mishmash64{}
|
|
word := kvp.s
|
|
h.Write([]byte(word))
|
|
if want, got := kvp.h64, h.Sum64(); want != got {
|
|
t.Fatalf("error: %s; wanted %016x; got %016x\n", word, want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHash32Collision(t *testing.T) {
|
|
m := make(map[uint32][]string, len(TheCppOutput))
|
|
for _, kvp := range TheCppOutput {
|
|
m[kvp.h32] = append(m[kvp.h32], kvp.s)
|
|
}
|
|
errs := make([]error, 0)
|
|
for hash, values := range m {
|
|
if len(values) > 1 {
|
|
errs = append(errs, fmt.Errorf("%08x: %s", hash, strings.Join(values, " ")))
|
|
}
|
|
}
|
|
if err := errors.Join(errs...); err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
}
|
|
|
|
func BenchmarkLoadEmbededPrimes(b *testing.B) {
|
|
const filename = "mishmash_primes.txt"
|
|
for _ = range b.N {
|
|
if _, err := LoadPrimesSet(filename); err != nil {
|
|
panic("error: " + err.Error())
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkMishmash32(b *testing.B) {
|
|
for _ = range b.N {
|
|
kvp := RandomWordAndHash()
|
|
var hash hash.Hash32 = &Mishmash32{}
|
|
hash.Write([]byte(kvp.s))
|
|
if want, got := kvp.h32, hash.Sum32(); want != got {
|
|
panic(fmt.Sprintf("error: %s; wanted %08x; got %08x\n", kvp.s, want, got))
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkMishmash64(b *testing.B) {
|
|
for _ = range b.N {
|
|
kvp := RandomWordAndHash()
|
|
var hash hash.Hash64 = &Mishmash64{}
|
|
hash.Write([]byte(kvp.s))
|
|
if want, got := kvp.h64, hash.Sum64(); want != got {
|
|
panic(fmt.Sprintf("error: %s; wanted %016x; got %016x\n", kvp.s, want, got))
|
|
}
|
|
}
|
|
}
|
|
|
|
func mask32(bits int) uint32 {
|
|
var t uint32
|
|
for range bits {
|
|
t <<= 1
|
|
t |= 1
|
|
}
|
|
for range 32 - bits {
|
|
t <<= 1
|
|
}
|
|
return t
|
|
}
|
|
|
|
func TestSum32(t *testing.T) {
|
|
hash, ok := New32().(*Mishmash32)
|
|
if !ok {
|
|
t.Fatalf("error reflecting hash.Hash to *Mishmash32\n")
|
|
}
|
|
s := []byte("hello world!")
|
|
hash.Write(s)
|
|
accumulator := hash.accumulator
|
|
if want, got := big.NewInt(int64(accumulator&UINT32_RESULTS_MASK)).Bytes(), hash.Bytes(); !bytes.Equal(want, got) {
|
|
t.Fatalf("error: wanted %v; got %v\n", want, got)
|
|
} else if want, got := append(s, hash.Bytes()...), hash.Sum(s); !bytes.Equal(want, got) {
|
|
t.Fatalf("error: wanted %v; got %v\n", want, got)
|
|
}
|
|
}
|
|
|
|
func TestSum64(t *testing.T) {
|
|
hash, ok := New64().(*Mishmash64)
|
|
if !ok {
|
|
t.Fatalf("error reflecting hash.Hash to *Mishmash64")
|
|
}
|
|
s := []byte("hello world!")
|
|
hash.Write(s)
|
|
accumulator := hash.accumulator
|
|
if want, got := big.NewInt(int64(accumulator)).Bytes(), hash.Bytes(); !bytes.Equal(want, got) {
|
|
t.Fatalf("error: wanted %v; got %v\n", want, got)
|
|
} else if want, got := append(s, hash.Bytes()...), hash.Sum(s); !bytes.Equal(want, got) {
|
|
t.Fatalf("error: wanted %v; got %v\n", want, got)
|
|
}
|
|
}
|