Currently, the `Table.Drop()` function is deprecated because it is not implemented yet. Let's add that functionality. - Adds true drop functionality to the table, through `Table.Drop()`. - Adds tests for functionality. - Rewrites fuzz test using `go_fuzz_utils`, to test arbitrary usage patterns. - Rewrite `bucket` to allow a capacity of zero. - Rename `Table.Capacity()` to `Table.TotalCapacity()`, to reflect to different between the capacity of the buckets vs. the whole table. - Enforce 100% mutation test coverage. Reviewed-on: #6 Co-authored-by: M.V. Hutz <git@maximhutz.me> Co-committed-by: M.V. Hutz <git@maximhutz.me>
104 lines
1.9 KiB
Go
104 lines
1.9 KiB
Go
package cuckoo
|
|
|
|
type entry[K, V any] struct {
|
|
key K
|
|
value V
|
|
}
|
|
|
|
type slot[K, V any] struct {
|
|
entry[K, V]
|
|
occupied bool
|
|
}
|
|
|
|
type bucket[K, V any] struct {
|
|
hash Hash[K]
|
|
slots []slot[K, V]
|
|
capacity, size uint64
|
|
compare EqualFunc[K]
|
|
}
|
|
|
|
// location determines where in the bucket a certain key would be placed. If the
|
|
// capacity is 0, this will panic.
|
|
func (b bucket[K, V]) location(key K) uint64 {
|
|
return b.hash(key) % b.capacity
|
|
}
|
|
|
|
func (b bucket[K, V]) get(key K) (value V, found bool) {
|
|
if b.capacity == 0 {
|
|
return
|
|
}
|
|
|
|
slot := b.slots[b.location(key)]
|
|
return slot.value, slot.occupied && b.compare(slot.key, key)
|
|
}
|
|
|
|
func (b *bucket[K, V]) drop(key K) (occupied bool) {
|
|
if b.capacity == 0 {
|
|
return
|
|
}
|
|
|
|
slot := &b.slots[b.location(key)]
|
|
|
|
if slot.occupied && b.compare(slot.key, key) {
|
|
slot.occupied = false
|
|
b.size--
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (b *bucket[K, V]) resize(capacity uint64) {
|
|
b.slots = make([]slot[K, V], capacity)
|
|
b.capacity = capacity
|
|
b.size = 0
|
|
}
|
|
|
|
func (b bucket[K, V]) update(key K, value V) (updated bool) {
|
|
if b.capacity == 0 {
|
|
return
|
|
}
|
|
|
|
slot := &b.slots[b.location(key)]
|
|
|
|
if slot.occupied && b.compare(slot.key, key) {
|
|
slot.value = value
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (b *bucket[K, V]) evict(insertion entry[K, V]) (evicted entry[K, V], eviction bool) {
|
|
if b.capacity == 0 {
|
|
return insertion, true
|
|
}
|
|
|
|
slot := &b.slots[b.location(insertion.key)]
|
|
|
|
if !slot.occupied {
|
|
slot.entry = insertion
|
|
slot.occupied = true
|
|
b.size++
|
|
return
|
|
}
|
|
|
|
if b.compare(slot.key, insertion.key) {
|
|
slot.value = insertion.value
|
|
return
|
|
}
|
|
|
|
insertion, slot.entry = slot.entry, insertion
|
|
return insertion, true
|
|
}
|
|
|
|
func newBucket[K, V any](capacity uint64, hash Hash[K], compare EqualFunc[K]) bucket[K, V] {
|
|
return bucket[K, V]{
|
|
hash: hash,
|
|
capacity: capacity,
|
|
compare: compare,
|
|
size: 0,
|
|
slots: make([]slot[K, V], capacity),
|
|
}
|
|
}
|