mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-06-28 10:00:34 +00:00
bcb80d3ff4
* [chore] bump gruf/go-store to v2 * no more boobs
46 lines
1.7 KiB
Go
46 lines
1.7 KiB
Go
package hashmap
|
|
|
|
import (
|
|
"sync/atomic"
|
|
"unsafe"
|
|
)
|
|
|
|
type store[Key comparable, Value any] struct {
|
|
keyShifts uintptr // Pointer size - log2 of array size, to be used as index in the data array
|
|
count atomic.Uintptr // count of filled elements in the slice
|
|
array unsafe.Pointer // pointer to slice data array
|
|
index []*ListElement[Key, Value] // storage for the slice for the garbage collector to not clean it up
|
|
}
|
|
|
|
// item returns the item for the given hashed key.
|
|
func (s *store[Key, Value]) item(hashedKey uintptr) *ListElement[Key, Value] {
|
|
index := hashedKey >> s.keyShifts
|
|
ptr := (*unsafe.Pointer)(unsafe.Pointer(uintptr(s.array) + index*intSizeBytes))
|
|
item := (*ListElement[Key, Value])(atomic.LoadPointer(ptr))
|
|
return item
|
|
}
|
|
|
|
// adds an item to the index if needed and returns the new item counter if it changed, otherwise 0.
|
|
func (s *store[Key, Value]) addItem(item *ListElement[Key, Value]) uintptr {
|
|
index := item.keyHash >> s.keyShifts
|
|
ptr := (*unsafe.Pointer)(unsafe.Pointer(uintptr(s.array) + index*intSizeBytes))
|
|
|
|
for { // loop until the smallest key hash is in the index
|
|
element := (*ListElement[Key, Value])(atomic.LoadPointer(ptr)) // get the current item in the index
|
|
if element == nil { // no item yet at this index
|
|
if atomic.CompareAndSwapPointer(ptr, nil, unsafe.Pointer(item)) {
|
|
return s.count.Add(1)
|
|
}
|
|
continue // a new item was inserted concurrently, retry
|
|
}
|
|
|
|
if item.keyHash < element.keyHash {
|
|
// the new item is the smallest for this index?
|
|
if !atomic.CompareAndSwapPointer(ptr, unsafe.Pointer(element), unsafe.Pointer(item)) {
|
|
continue // a new item was inserted concurrently, retry
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
}
|