From 867a1d49df8d182689fadc9529b3be1314cb6def Mon Sep 17 00:00:00 2001 From: "M.V. Hutz" Date: Tue, 14 Apr 2026 00:38:11 +0000 Subject: [PATCH] feat: sentinel error `ErrBadHash` (#19) ## Description Currently, the errors are not sentinel, and so are hard to test for. This PR makes sure hash collision errors are accounted for. ## Changes - Add `ErrBadHash`. Happens when there are too many collisions for an item to be added. ### Design Decisions - Chose to name `ErrBadHash` over `ErrCycle` because the feedbach that the user should be given is to evaluate their hash functions. Cycle collision is a bit esoteric. ## Checklist - [x] Tests pass - [x] Docs updated Reviewed-on: https://git.maximhutz.com/tools/go-cuckoo/pulls/19 Co-authored-by: M.V. Hutz Co-committed-by: M.V. Hutz --- table.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/table.go b/table.go index 922aa81..52e1a69 100644 --- a/table.go +++ b/table.go @@ -1,12 +1,21 @@ package cuckoo import ( + "errors" "fmt" "iter" "math/bits" "strings" ) +// ErrBadHash occurs when the hashes given to a [Table] cause too many key +// collisions. Try rebuilding the table using: +// +// 1. Different hash seeds. Equal seeds produce equal hash functions, which +// always cycle. +// 2. A different [Hash] algorithm. +var ErrBadHash = errors.New("bad hash") + // A Table is hash table that uses cuckoo hashing to resolve collision. Create // one with [NewTable]. Or if you want more granularity, use [NewTableBy] or // [NewCustomTable]. @@ -128,7 +137,7 @@ func (t *Table[K, V]) Put(key K, value V) (err error) { } if t.load() < t.minLoadFactor { - return fmt.Errorf("bad hash: resize on load %d/%d = %f", t.Size(), t.TotalCapacity(), t.load()) + return fmt.Errorf("hash functions produced a cycle at load %d/%d: %w", t.Size(), t.TotalCapacity(), ErrBadHash) } if err := t.grow(); err != nil {