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)) } } }