130 lines
3.1 KiB
Go
130 lines
3.1 KiB
Go
|
package mishmash
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"hash"
|
||
|
"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))
|
||
|
}
|
||
|
}
|
||
|
}
|