mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-09 05:23:40 +00:00
Add integer key support to hashmap
This commit is contained in:
+47
-15
@@ -30,14 +30,15 @@ type hashmapBucket struct {
|
||||
// allocated but as they're of variable size they can't be shown here.
|
||||
}
|
||||
|
||||
// Get FNV-1a hash of this string.
|
||||
// Get FNV-1a hash of this key.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
|
||||
func stringhash(s *string) uint32 {
|
||||
func hashmapHash(ptr unsafe.Pointer, n uintptr) uint32 {
|
||||
var result uint32 = 2166136261 // FNV offset basis
|
||||
for i := 0; i < len(*s); i++ {
|
||||
result ^= uint32((*s)[i])
|
||||
result *= 16777619 // FNV prime
|
||||
for i := uintptr(0); i < n; i++ {
|
||||
c := *(*uint8)(unsafe.Pointer(uintptr(ptr) + i))
|
||||
result ^= uint32(c) // XOR with byte
|
||||
result *= 16777619 // FNV prime
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -65,8 +66,7 @@ func hashmapMake(keySize, valueSize uint8) *hashmap {
|
||||
}
|
||||
|
||||
// Set a specified key to a given value. Grow the map if necessary.
|
||||
func hashmapSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||
hash := stringhash(&key)
|
||||
func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
|
||||
numBuckets := uintptr(1) << m.bucketBits
|
||||
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||
@@ -76,13 +76,13 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||
tophash := hashmapTopHash(hash)
|
||||
|
||||
// See whether the key already exists somewhere.
|
||||
var emptySlotKey *string
|
||||
var emptySlotKey unsafe.Pointer
|
||||
var emptySlotValue unsafe.Pointer
|
||||
var emptySlotTophash *byte
|
||||
for bucket != nil {
|
||||
for i := uintptr(0); i < 8; i++ {
|
||||
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(i)
|
||||
slotKey := (*string)(unsafe.Pointer(bucketAddr + slotKeyOffset))
|
||||
slotKey := unsafe.Pointer(bucketAddr + slotKeyOffset)
|
||||
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i)
|
||||
slotValue := unsafe.Pointer(bucketAddr + slotValueOffset)
|
||||
if bucket.tophash[i] == 0 && emptySlotKey == nil {
|
||||
@@ -94,7 +94,7 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||
}
|
||||
if bucket.tophash[i] == tophash {
|
||||
// Could be an existing value that's the same.
|
||||
if key == *slotKey {
|
||||
if keyEqual(key, slotKey, uintptr(m.keySize)) {
|
||||
// found same key, replace it
|
||||
memcpy(slotValue, value, uintptr(m.valueSize))
|
||||
return
|
||||
@@ -105,7 +105,7 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||
}
|
||||
if emptySlotKey != nil {
|
||||
m.count++
|
||||
*emptySlotKey = key
|
||||
memcpy(emptySlotKey, key, uintptr(m.keySize))
|
||||
memcpy(emptySlotValue, value, uintptr(m.valueSize))
|
||||
*emptySlotTophash = tophash
|
||||
return
|
||||
@@ -114,8 +114,7 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||
}
|
||||
|
||||
// Get the value of a specified key, or zero the value if not found.
|
||||
func hashmapGet(m *hashmap, key string, value unsafe.Pointer) {
|
||||
hash := stringhash(&key)
|
||||
func hashmapGet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
|
||||
numBuckets := uintptr(1) << m.bucketBits
|
||||
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||
@@ -132,12 +131,12 @@ func hashmapGet(m *hashmap, key string, value unsafe.Pointer) {
|
||||
for bucket != nil {
|
||||
for i := uintptr(0); i < 8; i++ {
|
||||
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(i)
|
||||
slotKey := (*string)(unsafe.Pointer(bucketAddr + slotKeyOffset))
|
||||
slotKey := unsafe.Pointer(bucketAddr + slotKeyOffset)
|
||||
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i)
|
||||
slotValue := unsafe.Pointer(bucketAddr + slotValueOffset)
|
||||
if bucket.tophash[i] == tophash {
|
||||
// This could be the key we're looking for.
|
||||
if key == *slotKey {
|
||||
if keyEqual(key, slotKey, uintptr(m.keySize)) {
|
||||
// Found the key, copy it.
|
||||
memcpy(value, slotValue, uintptr(m.valueSize))
|
||||
return
|
||||
@@ -150,3 +149,36 @@ func hashmapGet(m *hashmap, key string, value unsafe.Pointer) {
|
||||
// Did not find the key.
|
||||
memzero(value, uintptr(m.valueSize))
|
||||
}
|
||||
|
||||
// Hashmap with plain binary data keys (not containing strings etc.).
|
||||
|
||||
func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
|
||||
hash := hashmapHash(key, uintptr(m.keySize))
|
||||
hashmapSet(m, key, value, hash, memequal)
|
||||
}
|
||||
|
||||
func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer) {
|
||||
hash := hashmapHash(key, uintptr(m.keySize))
|
||||
hashmapGet(m, key, value, hash, memequal)
|
||||
}
|
||||
|
||||
// Hashmap with string keys (a common case).
|
||||
|
||||
func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
|
||||
return *(*string)(x) == *(*string)(y)
|
||||
}
|
||||
|
||||
func hashmapStringHash(s string) uint32 {
|
||||
_s := (*_string)(unsafe.Pointer(&s))
|
||||
return hashmapHash(unsafe.Pointer(_s.ptr), uintptr(_s.length))
|
||||
}
|
||||
|
||||
func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||
hash := hashmapStringHash(key)
|
||||
hashmapSet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual)
|
||||
}
|
||||
|
||||
func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer) {
|
||||
hash := hashmapStringHash(key)
|
||||
hashmapGet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual)
|
||||
}
|
||||
|
||||
@@ -35,6 +35,18 @@ func memzero(ptr unsafe.Pointer, size uintptr) {
|
||||
}
|
||||
}
|
||||
|
||||
// Compare two same-size buffers for equality.
|
||||
func memequal(x, y unsafe.Pointer, n uintptr) bool {
|
||||
for i := uintptr(0); i < n; i++ {
|
||||
cx := *(*uint8)(unsafe.Pointer(uintptr(x) + i))
|
||||
cy := *(*uint8)(unsafe.Pointer(uintptr(y) + i))
|
||||
if cx != cy {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func _panic(message interface{}) {
|
||||
printstring("panic: ")
|
||||
printitf(message)
|
||||
|
||||
Reference in New Issue
Block a user