родитель
8568d4f625
коммит
6812a4dc04
2 изменённых файлов: 156 добавлений и 28 удалений
|
@ -10,6 +10,13 @@ import (
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// constants for hashmap algorithms; must match src/runtime/hashmap.go
|
||||||
|
const (
|
||||||
|
hashmapAlgorithmBinary = iota
|
||||||
|
hashmapAlgorithmString
|
||||||
|
hashmapAlgorithmInterface
|
||||||
|
)
|
||||||
|
|
||||||
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
|
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
|
||||||
// initializing an appropriately sized object.
|
// initializing an appropriately sized object.
|
||||||
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||||
|
@ -17,22 +24,27 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||||
keyType := mapType.Key().Underlying()
|
keyType := mapType.Key().Underlying()
|
||||||
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
|
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
|
||||||
var llvmKeyType llvm.Type
|
var llvmKeyType llvm.Type
|
||||||
|
var alg uint64 // must match values in src/runtime/hashmap.go
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// String keys.
|
// String keys.
|
||||||
llvmKeyType = b.getLLVMType(keyType)
|
llvmKeyType = b.getLLVMType(keyType)
|
||||||
|
alg = hashmapAlgorithmString
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
// Trivially comparable keys.
|
// Trivially comparable keys.
|
||||||
llvmKeyType = b.getLLVMType(keyType)
|
llvmKeyType = b.getLLVMType(keyType)
|
||||||
|
alg = hashmapAlgorithmBinary
|
||||||
} else {
|
} else {
|
||||||
// All other keys. Implemented as map[interface{}]valueType for ease of
|
// All other keys. Implemented as map[interface{}]valueType for ease of
|
||||||
// implementation.
|
// implementation.
|
||||||
llvmKeyType = b.getLLVMRuntimeType("_interface")
|
llvmKeyType = b.getLLVMRuntimeType("_interface")
|
||||||
|
alg = hashmapAlgorithmInterface
|
||||||
}
|
}
|
||||||
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
||||||
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
||||||
llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false)
|
llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false)
|
||||||
llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false)
|
llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false)
|
||||||
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
|
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
|
||||||
|
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
|
||||||
if expr.Reserve != nil {
|
if expr.Reserve != nil {
|
||||||
sizeHint = b.getValue(expr.Reserve)
|
sizeHint = b.getValue(expr.Reserve)
|
||||||
var err error
|
var err error
|
||||||
|
@ -41,7 +53,7 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
|
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint, algEnum}, "")
|
||||||
return hashmap, nil
|
return hashmap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,23 @@ import (
|
||||||
|
|
||||||
// The underlying hashmap structure for Go.
|
// The underlying hashmap structure for Go.
|
||||||
type hashmap struct {
|
type hashmap struct {
|
||||||
next *hashmap // hashmap after evacuate (for iterators)
|
|
||||||
buckets unsafe.Pointer // pointer to array of buckets
|
buckets unsafe.Pointer // pointer to array of buckets
|
||||||
count uintptr
|
count uintptr
|
||||||
keySize uint8 // maybe this can store the key type as well? E.g. keysize == 5 means string?
|
keySize uint8 // maybe this can store the key type as well? E.g. keysize == 5 means string?
|
||||||
valueSize uint8
|
valueSize uint8
|
||||||
bucketBits uint8
|
bucketBits uint8
|
||||||
|
keyEqual func(x, y unsafe.Pointer, n uintptr) bool
|
||||||
|
keyHash func(key unsafe.Pointer, size uintptr) uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type hashmapAlgorithm uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
hashmapAlgorithmBinary hashmapAlgorithm = iota
|
||||||
|
hashmapAlgorithmString
|
||||||
|
hashmapAlgorithmInterface
|
||||||
|
)
|
||||||
|
|
||||||
// A hashmap bucket. A bucket is a container of 8 key/value pairs: first the
|
// A hashmap bucket. A bucket is a container of 8 key/value pairs: first the
|
||||||
// following two entries, then the 8 keys, then the 8 values. This somewhat odd
|
// following two entries, then the 8 keys, then the 8 values. This somewhat odd
|
||||||
// ordering is to make sure the keys and values are well aligned when one of
|
// ordering is to make sure the keys and values are well aligned when one of
|
||||||
|
@ -32,6 +41,8 @@ type hashmapBucket struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type hashmapIterator struct {
|
type hashmapIterator struct {
|
||||||
|
buckets unsafe.Pointer
|
||||||
|
numBuckets uintptr
|
||||||
bucketNumber uintptr
|
bucketNumber uintptr
|
||||||
bucket *hashmapBucket
|
bucket *hashmapBucket
|
||||||
bucketIndex uint8
|
bucketIndex uint8
|
||||||
|
@ -48,7 +59,7 @@ func hashmapTopHash(hash uint32) uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new hashmap with the given keySize and valueSize.
|
// Create a new hashmap with the given keySize and valueSize.
|
||||||
func hashmapMake(keySize, valueSize uint8, sizeHint uintptr) *hashmap {
|
func hashmapMake(keySize, valueSize uint8, sizeHint uintptr, alg uint8) *hashmap {
|
||||||
numBuckets := sizeHint / 8
|
numBuckets := sizeHint / 8
|
||||||
bucketBits := uint8(0)
|
bucketBits := uint8(0)
|
||||||
for numBuckets != 0 {
|
for numBuckets != 0 {
|
||||||
|
@ -57,14 +68,58 @@ func hashmapMake(keySize, valueSize uint8, sizeHint uintptr) *hashmap {
|
||||||
}
|
}
|
||||||
bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(keySize)*8 + uintptr(valueSize)*8
|
bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(keySize)*8 + uintptr(valueSize)*8
|
||||||
buckets := alloc(bucketBufSize*(1<<bucketBits), nil)
|
buckets := alloc(bucketBufSize*(1<<bucketBits), nil)
|
||||||
|
|
||||||
|
keyHash := hashmapKeyHashAlg(hashmapAlgorithm(alg))
|
||||||
|
keyEqual := hashmapKeyEqualAlg(hashmapAlgorithm(alg))
|
||||||
|
|
||||||
return &hashmap{
|
return &hashmap{
|
||||||
buckets: buckets,
|
buckets: buckets,
|
||||||
keySize: keySize,
|
keySize: keySize,
|
||||||
valueSize: valueSize,
|
valueSize: valueSize,
|
||||||
bucketBits: bucketBits,
|
bucketBits: bucketBits,
|
||||||
|
keyEqual: keyEqual,
|
||||||
|
keyHash: keyHash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hashmapKeyEqualAlg(alg hashmapAlgorithm) func(x, y unsafe.Pointer, n uintptr) bool {
|
||||||
|
switch alg {
|
||||||
|
case hashmapAlgorithmBinary:
|
||||||
|
return memequal
|
||||||
|
case hashmapAlgorithmString:
|
||||||
|
return hashmapStringEqual
|
||||||
|
case hashmapAlgorithmInterface:
|
||||||
|
return hashmapInterfaceEqual
|
||||||
|
default:
|
||||||
|
panic("unknown hashmap equal algorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashmapKeyHashAlg(alg hashmapAlgorithm) func(key unsafe.Pointer, n uintptr) uint32 {
|
||||||
|
switch alg {
|
||||||
|
case hashmapAlgorithmBinary:
|
||||||
|
return hash32
|
||||||
|
case hashmapAlgorithmString:
|
||||||
|
return hashmapStringPtrHash
|
||||||
|
case hashmapAlgorithmInterface:
|
||||||
|
return hashmapInterfacePtrHash
|
||||||
|
default:
|
||||||
|
panic("unknown hashmap hash algorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashmapShouldGrow(m *hashmap) bool {
|
||||||
|
if m.bucketBits == 32 {
|
||||||
|
// can't grow any more
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// "maximum" number of elements is 0.75 * buckets * elements per bucket
|
||||||
|
max := uintptr((3 * ((1 << m.bucketBits) * 8)) / 4)
|
||||||
|
|
||||||
|
return m.count > max
|
||||||
|
}
|
||||||
|
|
||||||
// Return the number of entries in this hashmap, called from the len builtin.
|
// Return the number of entries in this hashmap, called from the len builtin.
|
||||||
// A nil hashmap is defined as having length 0.
|
// A nil hashmap is defined as having length 0.
|
||||||
//go:inline
|
//go:inline
|
||||||
|
@ -83,7 +138,7 @@ func hashmapLenUnsafePointer(p unsafe.Pointer) int {
|
||||||
|
|
||||||
// Set a specified key to a given value. Grow the map if necessary.
|
// Set a specified key to a given value. Grow the map if necessary.
|
||||||
//go:nobounds
|
//go:nobounds
|
||||||
func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
|
func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32) {
|
||||||
tophash := hashmapTopHash(hash)
|
tophash := hashmapTopHash(hash)
|
||||||
|
|
||||||
if m.buckets == nil {
|
if m.buckets == nil {
|
||||||
|
@ -92,6 +147,10 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if hashmapShouldGrow(m) {
|
||||||
|
hashmapGrow(m)
|
||||||
|
}
|
||||||
|
|
||||||
numBuckets := uintptr(1) << m.bucketBits
|
numBuckets := uintptr(1) << m.bucketBits
|
||||||
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
||||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||||
|
@ -118,7 +177,7 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
||||||
}
|
}
|
||||||
if bucket.tophash[i] == tophash {
|
if bucket.tophash[i] == tophash {
|
||||||
// Could be an existing key that's the same.
|
// Could be an existing key that's the same.
|
||||||
if keyEqual(key, slotKey, uintptr(m.keySize)) {
|
if m.keyEqual(key, slotKey, uintptr(m.keySize)) {
|
||||||
// found same key, replace it
|
// found same key, replace it
|
||||||
memcpy(slotValue, value, uintptr(m.valueSize))
|
memcpy(slotValue, value, uintptr(m.valueSize))
|
||||||
return
|
return
|
||||||
|
@ -158,9 +217,35 @@ func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash u
|
||||||
return bucket
|
return bucket
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hashmapGrow(m *hashmap) {
|
||||||
|
|
||||||
|
// clone map as empty
|
||||||
|
n := *m
|
||||||
|
n.count = 0
|
||||||
|
|
||||||
|
// allocate our new buckets twice as big
|
||||||
|
n.bucketBits = m.bucketBits + 1
|
||||||
|
numBuckets := uintptr(1) << n.bucketBits
|
||||||
|
bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||||
|
n.buckets = alloc(bucketBufSize*numBuckets, nil)
|
||||||
|
|
||||||
|
// use a hashmap iterator to go through the old map
|
||||||
|
var it hashmapIterator
|
||||||
|
|
||||||
|
var key = alloc(uintptr(m.keySize), nil)
|
||||||
|
var value = alloc(uintptr(m.valueSize), nil)
|
||||||
|
|
||||||
|
for hashmapNext(m, &it, key, value) {
|
||||||
|
h := m.keyHash(key, uintptr(m.keySize))
|
||||||
|
hashmapSet(&n, key, value, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
*m = n
|
||||||
|
}
|
||||||
|
|
||||||
// Get the value of a specified key, or zero the value if not found.
|
// Get the value of a specified key, or zero the value if not found.
|
||||||
//go:nobounds
|
//go:nobounds
|
||||||
func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) bool {
|
func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash uint32) bool {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
// Getting a value out of a nil map is valid. From the spec:
|
// Getting a value out of a nil map is valid. From the spec:
|
||||||
// > if the map is nil or does not contain such an entry, a[x] is the
|
// > if the map is nil or does not contain such an entry, a[x] is the
|
||||||
|
@ -189,7 +274,7 @@ func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash u
|
||||||
slotValue := unsafe.Pointer(uintptr(unsafe.Pointer(bucket)) + slotValueOffset)
|
slotValue := unsafe.Pointer(uintptr(unsafe.Pointer(bucket)) + slotValueOffset)
|
||||||
if bucket.tophash[i] == tophash {
|
if bucket.tophash[i] == tophash {
|
||||||
// This could be the key we're looking for.
|
// This could be the key we're looking for.
|
||||||
if keyEqual(key, slotKey, uintptr(m.keySize)) {
|
if m.keyEqual(key, slotKey, uintptr(m.keySize)) {
|
||||||
// Found the key, copy it.
|
// Found the key, copy it.
|
||||||
memcpy(value, slotValue, uintptr(m.valueSize))
|
memcpy(value, slotValue, uintptr(m.valueSize))
|
||||||
return true
|
return true
|
||||||
|
@ -207,7 +292,7 @@ func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash u
|
||||||
// Delete a given key from the map. No-op when the key does not exist in the
|
// Delete a given key from the map. No-op when the key does not exist in the
|
||||||
// map.
|
// map.
|
||||||
//go:nobounds
|
//go:nobounds
|
||||||
func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
|
func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
// The delete builtin is defined even when the map is nil. From the spec:
|
// The delete builtin is defined even when the map is nil. From the spec:
|
||||||
// > If the map m is nil or the element m[k] does not exist, delete is a
|
// > If the map m is nil or the element m[k] does not exist, delete is a
|
||||||
|
@ -233,7 +318,7 @@ func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32, keyEqual func(x,
|
||||||
slotKey := unsafe.Pointer(uintptr(unsafe.Pointer(bucket)) + slotKeyOffset)
|
slotKey := unsafe.Pointer(uintptr(unsafe.Pointer(bucket)) + slotKeyOffset)
|
||||||
if bucket.tophash[i] == tophash {
|
if bucket.tophash[i] == tophash {
|
||||||
// This could be the key we're looking for.
|
// This could be the key we're looking for.
|
||||||
if keyEqual(key, slotKey, uintptr(m.keySize)) {
|
if m.keyEqual(key, slotKey, uintptr(m.keySize)) {
|
||||||
// Found the key, delete it.
|
// Found the key, delete it.
|
||||||
bucket.tophash[i] = 0
|
bucket.tophash[i] = 0
|
||||||
m.count--
|
m.count--
|
||||||
|
@ -249,13 +334,16 @@ func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32, keyEqual func(x,
|
||||||
//go:nobounds
|
//go:nobounds
|
||||||
func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) bool {
|
func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) bool {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
// Iterating over a nil slice appears to be allowed by the Go spec:
|
// From the spec: If the map is nil, the number of iterations is 0.
|
||||||
// https://groups.google.com/g/golang-nuts/c/gVgVLQU1FFE?pli=1
|
|
||||||
// https://play.golang.org/p/S8jxAMytKDB
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
numBuckets := uintptr(1) << m.bucketBits
|
if it.buckets == nil {
|
||||||
|
// initialize iterator
|
||||||
|
it.buckets = m.buckets
|
||||||
|
it.numBuckets = uintptr(1) << m.bucketBits
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if it.bucketIndex >= 8 {
|
if it.bucketIndex >= 8 {
|
||||||
// end of bucket, move to the next in the chain
|
// end of bucket, move to the next in the chain
|
||||||
|
@ -263,12 +351,12 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo
|
||||||
it.bucket = it.bucket.next
|
it.bucket = it.bucket.next
|
||||||
}
|
}
|
||||||
if it.bucket == nil {
|
if it.bucket == nil {
|
||||||
if it.bucketNumber >= numBuckets {
|
if it.bucketNumber >= it.numBuckets {
|
||||||
// went through all buckets
|
// went through all buckets
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||||
bucketAddr := uintptr(m.buckets) + bucketSize*it.bucketNumber
|
bucketAddr := uintptr(it.buckets) + bucketSize*it.bucketNumber
|
||||||
it.bucket = (*hashmapBucket)(unsafe.Pointer(bucketAddr))
|
it.bucket = (*hashmapBucket)(unsafe.Pointer(bucketAddr))
|
||||||
it.bucketNumber++ // next bucket
|
it.bucketNumber++ // next bucket
|
||||||
}
|
}
|
||||||
|
@ -281,11 +369,29 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo
|
||||||
bucketAddr := uintptr(unsafe.Pointer(it.bucket))
|
bucketAddr := uintptr(unsafe.Pointer(it.bucket))
|
||||||
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(it.bucketIndex)
|
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(it.bucketIndex)
|
||||||
slotKey := unsafe.Pointer(bucketAddr + slotKeyOffset)
|
slotKey := unsafe.Pointer(bucketAddr + slotKeyOffset)
|
||||||
|
memcpy(key, slotKey, uintptr(m.keySize))
|
||||||
|
|
||||||
|
if it.buckets == m.buckets {
|
||||||
|
// Our view of the buckets is the same as the parent map.
|
||||||
|
// Just copy the value we have
|
||||||
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(it.bucketIndex)
|
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(it.bucketIndex)
|
||||||
slotValue := unsafe.Pointer(bucketAddr + slotValueOffset)
|
slotValue := unsafe.Pointer(bucketAddr + slotValueOffset)
|
||||||
memcpy(key, slotKey, uintptr(m.keySize))
|
|
||||||
memcpy(value, slotValue, uintptr(m.valueSize))
|
memcpy(value, slotValue, uintptr(m.valueSize))
|
||||||
it.bucketIndex++
|
it.bucketIndex++
|
||||||
|
} else {
|
||||||
|
it.bucketIndex++
|
||||||
|
|
||||||
|
// Our view of the buckets doesn't match the parent map.
|
||||||
|
// Look up the key in the new buckets and return that value if it exists
|
||||||
|
hash := m.keyHash(key, uintptr(m.keySize))
|
||||||
|
ok := hashmapGet(m, key, value, uintptr(m.valueSize), hash)
|
||||||
|
if !ok {
|
||||||
|
// doesn't exist in parent map; try next key
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// All good.
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -296,7 +402,7 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo
|
||||||
func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
|
func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
|
||||||
// TODO: detect nil map here and throw a better panic message?
|
// TODO: detect nil map here and throw a better panic message?
|
||||||
hash := hash32(key, uintptr(m.keySize))
|
hash := hash32(key, uintptr(m.keySize))
|
||||||
hashmapSet(m, key, value, hash, memequal)
|
hashmapSet(m, key, value, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool {
|
func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool {
|
||||||
|
@ -305,7 +411,7 @@ func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
hash := hash32(key, uintptr(m.keySize))
|
hash := hash32(key, uintptr(m.keySize))
|
||||||
return hashmapGet(m, key, value, valueSize, hash, memequal)
|
return hashmapGet(m, key, value, valueSize, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
|
func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
|
||||||
|
@ -313,7 +419,7 @@ func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hash := hash32(key, uintptr(m.keySize))
|
hash := hash32(key, uintptr(m.keySize))
|
||||||
hashmapDelete(m, key, hash, memequal)
|
hashmapDelete(m, key, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hashmap with string keys (a common case).
|
// Hashmap with string keys (a common case).
|
||||||
|
@ -327,19 +433,24 @@ func hashmapStringHash(s string) uint32 {
|
||||||
return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length))
|
return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hashmapStringPtrHash(sptr unsafe.Pointer, size uintptr) uint32 {
|
||||||
|
_s := *(*_string)(sptr)
|
||||||
|
return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length))
|
||||||
|
}
|
||||||
|
|
||||||
func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) {
|
func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||||
hash := hashmapStringHash(key)
|
hash := hashmapStringHash(key)
|
||||||
hashmapSet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual)
|
hashmapSet(m, unsafe.Pointer(&key), value, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool {
|
func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool {
|
||||||
hash := hashmapStringHash(key)
|
hash := hashmapStringHash(key)
|
||||||
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash, hashmapStringEqual)
|
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashmapStringDelete(m *hashmap, key string) {
|
func hashmapStringDelete(m *hashmap, key string) {
|
||||||
hash := hashmapStringHash(key)
|
hash := hashmapStringHash(key)
|
||||||
hashmapDelete(m, unsafe.Pointer(&key), hash, hashmapStringEqual)
|
hashmapDelete(m, unsafe.Pointer(&key), hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hashmap with interface keys (for everything else).
|
// Hashmap with interface keys (for everything else).
|
||||||
|
@ -427,21 +538,26 @@ func hashmapInterfaceHash(itf interface{}) uint32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr) uint32 {
|
||||||
|
_i := *(*_interface)(iptr)
|
||||||
|
return hashmapInterfaceHash(_i)
|
||||||
|
}
|
||||||
|
|
||||||
func hashmapInterfaceEqual(x, y unsafe.Pointer, n uintptr) bool {
|
func hashmapInterfaceEqual(x, y unsafe.Pointer, n uintptr) bool {
|
||||||
return *(*interface{})(x) == *(*interface{})(y)
|
return *(*interface{})(x) == *(*interface{})(y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashmapInterfaceSet(m *hashmap, key interface{}, value unsafe.Pointer) {
|
func hashmapInterfaceSet(m *hashmap, key interface{}, value unsafe.Pointer) {
|
||||||
hash := hashmapInterfaceHash(key)
|
hash := hashmapInterfaceHash(key)
|
||||||
hashmapSet(m, unsafe.Pointer(&key), value, hash, hashmapInterfaceEqual)
|
hashmapSet(m, unsafe.Pointer(&key), value, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashmapInterfaceGet(m *hashmap, key interface{}, value unsafe.Pointer, valueSize uintptr) bool {
|
func hashmapInterfaceGet(m *hashmap, key interface{}, value unsafe.Pointer, valueSize uintptr) bool {
|
||||||
hash := hashmapInterfaceHash(key)
|
hash := hashmapInterfaceHash(key)
|
||||||
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash, hashmapInterfaceEqual)
|
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashmapInterfaceDelete(m *hashmap, key interface{}) {
|
func hashmapInterfaceDelete(m *hashmap, key interface{}) {
|
||||||
hash := hashmapInterfaceHash(key)
|
hash := hashmapInterfaceHash(key)
|
||||||
hashmapDelete(m, unsafe.Pointer(&key), hash, hashmapInterfaceEqual)
|
hashmapDelete(m, unsafe.Pointer(&key), hash)
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче