- Added guards at the bucket level, to ensure that getting an item in an empty bucket doesn't cause an error. - Added grow() and shrink() functions to Table, to prevent a capacity of 0 from not being able to grow. - Updated the fuzz test to use `go_fuzz-utils`.
79 lines
1.5 KiB
Go
79 lines
1.5 KiB
Go
package cuckoo_test
|
|
|
|
import (
|
|
"maps"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
go_fuzz_utils "github.com/trailofbits/go-fuzz-utils"
|
|
|
|
"git.maximhutz.com/tools/go-cuckoo"
|
|
)
|
|
|
|
func offsetHash(seed uint32) cuckoo.Hash[uint32] {
|
|
return func(x uint32) uint64 {
|
|
v := uint64(x) ^ uint64(seed)
|
|
v = (v ^ (v >> 30)) * 0xbf58476d1ce4e5b9
|
|
v = (v ^ (v >> 27)) * 0x94d049bb133111eb
|
|
return v ^ (v >> 31)
|
|
}
|
|
}
|
|
|
|
type fuzzStep struct {
|
|
drop bool
|
|
key, value uint32
|
|
}
|
|
|
|
type fuzzScenario struct {
|
|
seedA, seedB uint32
|
|
steps []fuzzStep
|
|
}
|
|
|
|
func FuzzInsertLookup(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
var scenario fuzzScenario
|
|
assert := assert.New(t)
|
|
|
|
if tp, err := go_fuzz_utils.NewTypeProvider(data); err != nil {
|
|
return
|
|
} else if err := tp.Fill(&scenario); err != nil {
|
|
return
|
|
}
|
|
|
|
if scenario.seedA == scenario.seedB {
|
|
return
|
|
}
|
|
|
|
actual := cuckoo.NewCustomTable[uint32, uint32](
|
|
offsetHash(scenario.seedA),
|
|
offsetHash(scenario.seedB),
|
|
func(a, b uint32) bool { return a == b },
|
|
)
|
|
|
|
expected := map[uint32]uint32{}
|
|
|
|
for _, step := range scenario.steps {
|
|
if step.drop {
|
|
err := actual.Drop(step.key)
|
|
assert.NoError(err)
|
|
|
|
delete(expected, step.key)
|
|
|
|
_, err = actual.Get(step.key)
|
|
assert.Error(err)
|
|
} else {
|
|
err := actual.Put(step.key, step.value)
|
|
assert.NoError(err)
|
|
|
|
expected[step.key] = step.value
|
|
|
|
found, err := actual.Get(step.key)
|
|
assert.NoError(err)
|
|
assert.Equal(step.value, found)
|
|
}
|
|
|
|
assert.Equal(expected, maps.Collect(actual.Entries()))
|
|
}
|
|
})
|
|
}
|