github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/hashmap.go (about)

     1  package runtime
     2  
     3  // This is a hashmap implementation for the map[T]T type.
     4  // It is very roughly based on the implementation of the Go hashmap:
     5  //
     6  //     https://golang.org/src/runtime/map.go
     7  
     8  import (
     9  	"reflect"
    10  	"unsafe"
    11  )
    12  
    13  // The underlying hashmap structure for Go.
    14  type hashmap struct {
    15  	buckets    unsafe.Pointer // pointer to array of buckets
    16  	seed       uintptr
    17  	count      uintptr
    18  	keySize    uintptr // maybe this can store the key type as well? E.g. keysize == 5 means string?
    19  	valueSize  uintptr
    20  	bucketBits uint8
    21  	keyEqual   func(x, y unsafe.Pointer, n uintptr) bool
    22  	keyHash    func(key unsafe.Pointer, size, seed uintptr) uint32
    23  }
    24  
    25  type hashmapAlgorithm uint8
    26  
    27  const (
    28  	hashmapAlgorithmBinary hashmapAlgorithm = iota
    29  	hashmapAlgorithmString
    30  	hashmapAlgorithmInterface
    31  )
    32  
    33  // A hashmap bucket. A bucket is a container of 8 key/value pairs: first the
    34  // following two entries, then the 8 keys, then the 8 values. This somewhat odd
    35  // ordering is to make sure the keys and values are well aligned when one of
    36  // them is smaller than the system word size.
    37  type hashmapBucket struct {
    38  	tophash [8]uint8
    39  	next    *hashmapBucket // next bucket (if there are more than 8 in a chain)
    40  	// Followed by the actual keys, and then the actual values. These are
    41  	// allocated but as they're of variable size they can't be shown here.
    42  }
    43  
    44  type hashmapIterator struct {
    45  	buckets      unsafe.Pointer // pointer to array of hashapBuckets
    46  	numBuckets   uintptr        // length of buckets array
    47  	bucketNumber uintptr        // current index into buckets array
    48  	bucket       *hashmapBucket // current bucket in chain
    49  	bucketIndex  uint8          // current index into bucket
    50  }
    51  
    52  func hashmapNewIterator() unsafe.Pointer {
    53  	return unsafe.Pointer(new(hashmapIterator))
    54  }
    55  
    56  // Get the topmost 8 bits of the hash, without using a special value (like 0).
    57  func hashmapTopHash(hash uint32) uint8 {
    58  	tophash := uint8(hash >> 24)
    59  	if tophash < 1 {
    60  		// 0 means empty slot, so make it bigger.
    61  		tophash += 1
    62  	}
    63  	return tophash
    64  }
    65  
    66  // Create a new hashmap with the given keySize and valueSize.
    67  func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) *hashmap {
    68  	bucketBits := uint8(0)
    69  	for hashmapHasSpaceToGrow(bucketBits) && hashmapOverLoadFactor(sizeHint, bucketBits) {
    70  		bucketBits++
    71  	}
    72  
    73  	bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + keySize*8 + valueSize*8
    74  	buckets := alloc(bucketBufSize*(1<<bucketBits), nil)
    75  
    76  	keyHash := hashmapKeyHashAlg(hashmapAlgorithm(alg))
    77  	keyEqual := hashmapKeyEqualAlg(hashmapAlgorithm(alg))
    78  
    79  	return &hashmap{
    80  		buckets:    buckets,
    81  		seed:       uintptr(fastrand()),
    82  		keySize:    keySize,
    83  		valueSize:  valueSize,
    84  		bucketBits: bucketBits,
    85  		keyEqual:   keyEqual,
    86  		keyHash:    keyHash,
    87  	}
    88  }
    89  
    90  func hashmapMakeUnsafePointer(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer {
    91  	return (unsafe.Pointer)(hashmapMake(keySize, valueSize, sizeHint, alg))
    92  }
    93  
    94  // Remove all entries from the map, without actually deallocating the space for
    95  // it. This is used for the clear builtin, and can be used to reuse a map (to
    96  // avoid extra heap allocations).
    97  func hashmapClear(m *hashmap) {
    98  	if m == nil {
    99  		// Nothing to do. According to the spec:
   100  		// > If the map or slice is nil, clear is a no-op.
   101  		return
   102  	}
   103  
   104  	m.count = 0
   105  	numBuckets := uintptr(1) << m.bucketBits
   106  	bucketSize := hashmapBucketSize(m)
   107  	for i := uintptr(0); i < numBuckets; i++ {
   108  		bucket := hashmapBucketAddr(m, m.buckets, i)
   109  		for bucket != nil {
   110  			// Clear the tophash, to mark these keys/values as removed.
   111  			bucket.tophash = [8]uint8{}
   112  
   113  			// Clear the keys and values in the bucket so that the GC won't pin
   114  			// these allocations.
   115  			memzero(unsafe.Add(unsafe.Pointer(bucket), unsafe.Sizeof(hashmapBucket{})), bucketSize-unsafe.Sizeof(hashmapBucket{}))
   116  
   117  			// Move on to the next bucket in the chain.
   118  			bucket = bucket.next
   119  		}
   120  	}
   121  }
   122  
   123  func hashmapKeyEqualAlg(alg hashmapAlgorithm) func(x, y unsafe.Pointer, n uintptr) bool {
   124  	switch alg {
   125  	case hashmapAlgorithmBinary:
   126  		return memequal
   127  	case hashmapAlgorithmString:
   128  		return hashmapStringEqual
   129  	case hashmapAlgorithmInterface:
   130  		return hashmapInterfaceEqual
   131  	default:
   132  		// compiler bug :(
   133  		return nil
   134  	}
   135  }
   136  
   137  func hashmapKeyHashAlg(alg hashmapAlgorithm) func(key unsafe.Pointer, n, seed uintptr) uint32 {
   138  	switch alg {
   139  	case hashmapAlgorithmBinary:
   140  		return hash32
   141  	case hashmapAlgorithmString:
   142  		return hashmapStringPtrHash
   143  	case hashmapAlgorithmInterface:
   144  		return hashmapInterfacePtrHash
   145  	default:
   146  		// compiler bug :(
   147  		return nil
   148  	}
   149  }
   150  
   151  func hashmapHasSpaceToGrow(bucketBits uint8) bool {
   152  	// Over this limit, we're likely to overflow uintptrs during calculations
   153  	// or numbers of hash elements.   Don't allow any more growth.
   154  	// With 29 bits, this is 2^32 elements anyway.
   155  	return bucketBits <= uint8((unsafe.Sizeof(uintptr(0))*8)-3)
   156  }
   157  
   158  func hashmapOverLoadFactor(n uintptr, bucketBits uint8) bool {
   159  	// "maximum" number of elements is 0.75 * buckets * elements per bucket
   160  	// to avoid overflow, this is calculated as
   161  	// max = 3 * (1/4 * buckets * elements per bucket)
   162  	//     = 3 * (buckets * (elements per bucket)/4)
   163  	//     = 3 * (buckets * (8/4)
   164  	//     = 3 * (buckets * 2)
   165  	//     = 6 * buckets
   166  	max := (uintptr(6) << bucketBits)
   167  	return n > max
   168  }
   169  
   170  // Return the number of entries in this hashmap, called from the len builtin.
   171  // A nil hashmap is defined as having length 0.
   172  //
   173  //go:inline
   174  func hashmapLen(m *hashmap) int {
   175  	if m == nil {
   176  		return 0
   177  	}
   178  	return int(m.count)
   179  }
   180  
   181  func hashmapLenUnsafePointer(m unsafe.Pointer) int {
   182  	return hashmapLen((*hashmap)(m))
   183  }
   184  
   185  //go:inline
   186  func hashmapBucketSize(m *hashmap) uintptr {
   187  	return unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
   188  }
   189  
   190  //go:inline
   191  func hashmapBucketAddr(m *hashmap, buckets unsafe.Pointer, n uintptr) *hashmapBucket {
   192  	bucketSize := hashmapBucketSize(m)
   193  	bucket := (*hashmapBucket)(unsafe.Add(buckets, bucketSize*n))
   194  	return bucket
   195  }
   196  
   197  //go:inline
   198  func hashmapBucketAddrForHash(m *hashmap, hash uint32) *hashmapBucket {
   199  	numBuckets := uintptr(1) << m.bucketBits
   200  	bucketNumber := (uintptr(hash) & (numBuckets - 1))
   201  	return hashmapBucketAddr(m, m.buckets, bucketNumber)
   202  }
   203  
   204  //go:inline
   205  func hashmapSlotKey(m *hashmap, bucket *hashmapBucket, slot uint8) unsafe.Pointer {
   206  	slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(slot)
   207  	slotKey := unsafe.Add(unsafe.Pointer(bucket), slotKeyOffset)
   208  	return slotKey
   209  }
   210  
   211  //go:inline
   212  func hashmapSlotValue(m *hashmap, bucket *hashmapBucket, slot uint8) unsafe.Pointer {
   213  	slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(slot)
   214  	slotValue := unsafe.Add(unsafe.Pointer(bucket), slotValueOffset)
   215  	return slotValue
   216  }
   217  
   218  // Set a specified key to a given value. Grow the map if necessary.
   219  //
   220  //go:nobounds
   221  func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32) {
   222  	if hashmapHasSpaceToGrow(m.bucketBits) && hashmapOverLoadFactor(m.count, m.bucketBits) {
   223  		hashmapGrow(m)
   224  		// seed changed when we grew; rehash key with new seed
   225  		hash = m.keyHash(key, m.keySize, m.seed)
   226  	}
   227  
   228  	tophash := hashmapTopHash(hash)
   229  	bucket := hashmapBucketAddrForHash(m, hash)
   230  	var lastBucket *hashmapBucket
   231  
   232  	// See whether the key already exists somewhere.
   233  	var emptySlotKey unsafe.Pointer
   234  	var emptySlotValue unsafe.Pointer
   235  	var emptySlotTophash *byte
   236  	for bucket != nil {
   237  		for i := uint8(0); i < 8; i++ {
   238  			slotKey := hashmapSlotKey(m, bucket, i)
   239  			slotValue := hashmapSlotValue(m, bucket, i)
   240  			if bucket.tophash[i] == 0 && emptySlotKey == nil {
   241  				// Found an empty slot, store it for if we couldn't find an
   242  				// existing slot.
   243  				emptySlotKey = slotKey
   244  				emptySlotValue = slotValue
   245  				emptySlotTophash = &bucket.tophash[i]
   246  			}
   247  			if bucket.tophash[i] == tophash {
   248  				// Could be an existing key that's the same.
   249  				if m.keyEqual(key, slotKey, m.keySize) {
   250  					// found same key, replace it
   251  					memcpy(slotValue, value, m.valueSize)
   252  					return
   253  				}
   254  			}
   255  		}
   256  		lastBucket = bucket
   257  		bucket = bucket.next
   258  	}
   259  	if emptySlotKey == nil {
   260  		// Add a new bucket to the bucket chain.
   261  		// TODO: rebalance if necessary to avoid O(n) insert and lookup time.
   262  		lastBucket.next = (*hashmapBucket)(hashmapInsertIntoNewBucket(m, key, value, tophash))
   263  		return
   264  	}
   265  	m.count++
   266  	memcpy(emptySlotKey, key, m.keySize)
   267  	memcpy(emptySlotValue, value, m.valueSize)
   268  	*emptySlotTophash = tophash
   269  }
   270  
   271  func hashmapSetUnsafePointer(m unsafe.Pointer, key unsafe.Pointer, value unsafe.Pointer, hash uint32) {
   272  	hashmapSet((*hashmap)(m), key, value, hash)
   273  }
   274  
   275  // hashmapInsertIntoNewBucket creates a new bucket, inserts the given key and
   276  // value into the bucket, and returns a pointer to this bucket.
   277  func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket {
   278  	bucketBufSize := hashmapBucketSize(m)
   279  	bucketBuf := alloc(bucketBufSize, nil)
   280  	bucket := (*hashmapBucket)(bucketBuf)
   281  
   282  	// Insert into the first slot, which is empty as it has just been allocated.
   283  	slotKey := hashmapSlotKey(m, bucket, 0)
   284  	slotValue := hashmapSlotValue(m, bucket, 0)
   285  	m.count++
   286  	memcpy(slotKey, key, m.keySize)
   287  	memcpy(slotValue, value, m.valueSize)
   288  	bucket.tophash[0] = tophash
   289  	return bucket
   290  }
   291  
   292  func hashmapGrow(m *hashmap) {
   293  	// clone map as empty
   294  	n := *m
   295  	n.count = 0
   296  	n.seed = uintptr(fastrand())
   297  
   298  	// allocate our new buckets twice as big
   299  	n.bucketBits = m.bucketBits + 1
   300  	numBuckets := uintptr(1) << n.bucketBits
   301  	bucketBufSize := hashmapBucketSize(m)
   302  	n.buckets = alloc(bucketBufSize*numBuckets, nil)
   303  
   304  	// use a hashmap iterator to go through the old map
   305  	var it hashmapIterator
   306  
   307  	var key = alloc(m.keySize, nil)
   308  	var value = alloc(m.valueSize, nil)
   309  
   310  	for hashmapNext(m, &it, key, value) {
   311  		h := n.keyHash(key, uintptr(n.keySize), n.seed)
   312  		hashmapSet(&n, key, value, h)
   313  	}
   314  
   315  	*m = n
   316  }
   317  
   318  // Get the value of a specified key, or zero the value if not found.
   319  //
   320  //go:nobounds
   321  func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash uint32) bool {
   322  	if m == nil {
   323  		// Getting a value out of a nil map is valid. From the spec:
   324  		// > if the map is nil or does not contain such an entry, a[x] is the
   325  		// > zero value for the element type of M
   326  		memzero(value, uintptr(valueSize))
   327  		return false
   328  	}
   329  
   330  	tophash := hashmapTopHash(hash)
   331  	bucket := hashmapBucketAddrForHash(m, hash)
   332  
   333  	// Try to find the key.
   334  	for bucket != nil {
   335  		for i := uint8(0); i < 8; i++ {
   336  			slotKey := hashmapSlotKey(m, bucket, i)
   337  			slotValue := hashmapSlotValue(m, bucket, i)
   338  			if bucket.tophash[i] == tophash {
   339  				// This could be the key we're looking for.
   340  				if m.keyEqual(key, slotKey, m.keySize) {
   341  					// Found the key, copy it.
   342  					memcpy(value, slotValue, m.valueSize)
   343  					return true
   344  				}
   345  			}
   346  		}
   347  		bucket = bucket.next
   348  	}
   349  
   350  	// Did not find the key.
   351  	memzero(value, m.valueSize)
   352  	return false
   353  }
   354  
   355  func hashmapGetUnsafePointer(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr, hash uint32) bool {
   356  	return hashmapGet((*hashmap)(m), key, value, valueSize, hash)
   357  }
   358  
   359  // Delete a given key from the map. No-op when the key does not exist in the
   360  // map.
   361  //
   362  //go:nobounds
   363  func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32) {
   364  	if m == nil {
   365  		// The delete builtin is defined even when the map is nil. From the spec:
   366  		// > If the map m is nil or the element m[k] does not exist, delete is a
   367  		// > no-op.
   368  		return
   369  	}
   370  
   371  	tophash := hashmapTopHash(hash)
   372  	bucket := hashmapBucketAddrForHash(m, hash)
   373  
   374  	// Try to find the key.
   375  	for bucket != nil {
   376  		for i := uint8(0); i < 8; i++ {
   377  			slotKey := hashmapSlotKey(m, bucket, i)
   378  			if bucket.tophash[i] == tophash {
   379  				// This could be the key we're looking for.
   380  				if m.keyEqual(key, slotKey, m.keySize) {
   381  					// Found the key, delete it.
   382  					bucket.tophash[i] = 0
   383  					// Zero out the key and value so garbage collector doesn't pin the allocations.
   384  					memzero(slotKey, m.keySize)
   385  					slotValue := hashmapSlotValue(m, bucket, i)
   386  					memzero(slotValue, m.valueSize)
   387  					m.count--
   388  					return
   389  				}
   390  			}
   391  		}
   392  		bucket = bucket.next
   393  	}
   394  }
   395  
   396  // Iterate over a hashmap.
   397  //
   398  //go:nobounds
   399  func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) bool {
   400  	if m == nil {
   401  		// From the spec: If the map is nil, the number of iterations is 0.
   402  		return false
   403  	}
   404  
   405  	if it.buckets == nil {
   406  		// initialize iterator
   407  		it.buckets = m.buckets
   408  		it.numBuckets = uintptr(1) << m.bucketBits
   409  	}
   410  
   411  	for {
   412  		if it.bucketIndex >= 8 {
   413  			// end of bucket, move to the next in the chain
   414  			it.bucketIndex = 0
   415  			it.bucket = it.bucket.next
   416  		}
   417  		if it.bucket == nil {
   418  			if it.bucketNumber >= it.numBuckets {
   419  				// went through all buckets
   420  				return false
   421  			}
   422  			it.bucket = hashmapBucketAddr(m, it.buckets, it.bucketNumber)
   423  			it.bucketNumber++ // next bucket
   424  		}
   425  		if it.bucket.tophash[it.bucketIndex] == 0 {
   426  			// slot is empty - move on
   427  			it.bucketIndex++
   428  			continue
   429  		}
   430  
   431  		slotKey := hashmapSlotKey(m, it.bucket, it.bucketIndex)
   432  		memcpy(key, slotKey, m.keySize)
   433  
   434  		if it.buckets == m.buckets {
   435  			// Our view of the buckets is the same as the parent map.
   436  			// Just copy the value we have
   437  			slotValue := hashmapSlotValue(m, it.bucket, it.bucketIndex)
   438  			memcpy(value, slotValue, m.valueSize)
   439  			it.bucketIndex++
   440  		} else {
   441  			it.bucketIndex++
   442  
   443  			// Our view of the buckets doesn't match the parent map.
   444  			// Look up the key in the new buckets and return that value if it exists
   445  			hash := m.keyHash(key, m.keySize, m.seed)
   446  			ok := hashmapGet(m, key, value, m.valueSize, hash)
   447  			if !ok {
   448  				// doesn't exist in parent map; try next key
   449  				continue
   450  			}
   451  
   452  			// All good.
   453  		}
   454  
   455  		return true
   456  	}
   457  }
   458  
   459  func hashmapNextUnsafePointer(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool {
   460  	return hashmapNext((*hashmap)(m), (*hashmapIterator)(it), key, value)
   461  }
   462  
   463  // Hashmap with plain binary data keys (not containing strings etc.).
   464  func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
   465  	if m == nil {
   466  		nilMapPanic()
   467  	}
   468  	hash := hash32(key, m.keySize, m.seed)
   469  	hashmapSet(m, key, value, hash)
   470  }
   471  
   472  func hashmapBinarySetUnsafePointer(m unsafe.Pointer, key, value unsafe.Pointer) {
   473  	hashmapBinarySet((*hashmap)(m), key, value)
   474  }
   475  
   476  func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool {
   477  	if m == nil {
   478  		memzero(value, uintptr(valueSize))
   479  		return false
   480  	}
   481  	hash := hash32(key, m.keySize, m.seed)
   482  	return hashmapGet(m, key, value, valueSize, hash)
   483  }
   484  
   485  func hashmapBinaryGetUnsafePointer(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool {
   486  	return hashmapBinaryGet((*hashmap)(m), key, value, valueSize)
   487  }
   488  
   489  func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
   490  	if m == nil {
   491  		return
   492  	}
   493  	hash := hash32(key, m.keySize, m.seed)
   494  	hashmapDelete(m, key, hash)
   495  }
   496  
   497  func hashmapBinaryDeleteUnsafePointer(m unsafe.Pointer, key unsafe.Pointer) {
   498  	hashmapBinaryDelete((*hashmap)(m), key)
   499  }
   500  
   501  // Hashmap with string keys (a common case).
   502  
   503  func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
   504  	return *(*string)(x) == *(*string)(y)
   505  }
   506  
   507  func hashmapStringHash(s string, seed uintptr) uint32 {
   508  	_s := (*_string)(unsafe.Pointer(&s))
   509  	return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length), seed)
   510  }
   511  
   512  func hashmapStringPtrHash(sptr unsafe.Pointer, size uintptr, seed uintptr) uint32 {
   513  	_s := *(*_string)(sptr)
   514  	return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length), seed)
   515  }
   516  
   517  func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) {
   518  	if m == nil {
   519  		nilMapPanic()
   520  	}
   521  	hash := hashmapStringHash(key, m.seed)
   522  	hashmapSet(m, unsafe.Pointer(&key), value, hash)
   523  }
   524  
   525  func hashmapStringSetUnsafePointer(m unsafe.Pointer, key string, value unsafe.Pointer) {
   526  	hashmapStringSet((*hashmap)(m), key, value)
   527  }
   528  
   529  func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool {
   530  	if m == nil {
   531  		memzero(value, uintptr(valueSize))
   532  		return false
   533  	}
   534  	hash := hashmapStringHash(key, m.seed)
   535  	return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash)
   536  }
   537  
   538  func hashmapStringGetUnsafePointer(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool {
   539  	return hashmapStringGet((*hashmap)(m), key, value, valueSize)
   540  }
   541  
   542  func hashmapStringDelete(m *hashmap, key string) {
   543  	if m == nil {
   544  		return
   545  	}
   546  	hash := hashmapStringHash(key, m.seed)
   547  	hashmapDelete(m, unsafe.Pointer(&key), hash)
   548  }
   549  
   550  func hashmapStringDeleteUnsafePointer(m unsafe.Pointer, key string) {
   551  	hashmapStringDelete((*hashmap)(m), key)
   552  }
   553  
   554  // Hashmap with interface keys (for everything else).
   555  
   556  // This is a method that is intentionally unexported in the reflect package. It
   557  // is identical to the Interface() method call, except it doesn't check whether
   558  // a field is exported and thus allows circumventing the type system.
   559  // The hash function needs it as it also needs to hash unexported struct fields.
   560  //
   561  //go:linkname valueInterfaceUnsafe reflect.valueInterfaceUnsafe
   562  func valueInterfaceUnsafe(v reflect.Value) interface{}
   563  
   564  func hashmapFloat32Hash(ptr unsafe.Pointer, seed uintptr) uint32 {
   565  	f := *(*uint32)(ptr)
   566  	if f == 0x80000000 {
   567  		// convert -0 to 0 for hashing
   568  		f = 0
   569  	}
   570  	return hash32(unsafe.Pointer(&f), 4, seed)
   571  }
   572  
   573  func hashmapFloat64Hash(ptr unsafe.Pointer, seed uintptr) uint32 {
   574  	f := *(*uint64)(ptr)
   575  	if f == 0x8000000000000000 {
   576  		// convert -0 to 0 for hashing
   577  		f = 0
   578  	}
   579  	return hash32(unsafe.Pointer(&f), 8, seed)
   580  }
   581  
   582  func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
   583  	x := reflect.ValueOf(itf)
   584  	if x.RawType() == nil {
   585  		return 0 // nil interface
   586  	}
   587  
   588  	value := (*_interface)(unsafe.Pointer(&itf)).value
   589  	ptr := value
   590  	if x.RawType().Size() <= unsafe.Sizeof(uintptr(0)) {
   591  		// Value fits in pointer, so it's directly stored in the pointer.
   592  		ptr = unsafe.Pointer(&value)
   593  	}
   594  
   595  	switch x.RawType().Kind() {
   596  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   597  		return hash32(ptr, x.RawType().Size(), seed)
   598  	case reflect.Bool, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   599  		return hash32(ptr, x.RawType().Size(), seed)
   600  	case reflect.Float32:
   601  		// It should be possible to just has the contents. However, NaN != NaN
   602  		// so if you're using lots of NaNs as map keys (you shouldn't) then hash
   603  		// time may become exponential. To fix that, it would be better to
   604  		// return a random number instead:
   605  		// https://research.swtch.com/randhash
   606  		return hashmapFloat32Hash(ptr, seed)
   607  	case reflect.Float64:
   608  		return hashmapFloat64Hash(ptr, seed)
   609  	case reflect.Complex64:
   610  		rptr, iptr := ptr, unsafe.Add(ptr, 4)
   611  		return hashmapFloat32Hash(rptr, seed) ^ hashmapFloat32Hash(iptr, seed)
   612  	case reflect.Complex128:
   613  		rptr, iptr := ptr, unsafe.Add(ptr, 8)
   614  		return hashmapFloat64Hash(rptr, seed) ^ hashmapFloat64Hash(iptr, seed)
   615  	case reflect.String:
   616  		return hashmapStringHash(x.String(), seed)
   617  	case reflect.Chan, reflect.Ptr, reflect.UnsafePointer:
   618  		// It might seem better to just return the pointer, but that won't
   619  		// result in an evenly distributed hashmap. Instead, hash the pointer
   620  		// like most other types.
   621  		return hash32(ptr, x.RawType().Size(), seed)
   622  	case reflect.Array:
   623  		var hash uint32
   624  		for i := 0; i < x.Len(); i++ {
   625  			hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Index(i)), seed)
   626  		}
   627  		return hash
   628  	case reflect.Struct:
   629  		var hash uint32
   630  		for i := 0; i < x.NumField(); i++ {
   631  			hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Field(i)), seed)
   632  		}
   633  		return hash
   634  	default:
   635  		runtimePanic("comparing un-comparable type")
   636  		return 0 // unreachable
   637  	}
   638  }
   639  
   640  func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr, seed uintptr) uint32 {
   641  	_i := *(*interface{})(iptr)
   642  	return hashmapInterfaceHash(_i, seed)
   643  }
   644  
   645  func hashmapInterfaceEqual(x, y unsafe.Pointer, n uintptr) bool {
   646  	return *(*interface{})(x) == *(*interface{})(y)
   647  }
   648  
   649  func hashmapInterfaceSet(m *hashmap, key interface{}, value unsafe.Pointer) {
   650  	if m == nil {
   651  		nilMapPanic()
   652  	}
   653  	hash := hashmapInterfaceHash(key, m.seed)
   654  	hashmapSet(m, unsafe.Pointer(&key), value, hash)
   655  }
   656  
   657  func hashmapInterfaceSetUnsafePointer(m unsafe.Pointer, key interface{}, value unsafe.Pointer) {
   658  	hashmapInterfaceSet((*hashmap)(m), key, value)
   659  }
   660  
   661  func hashmapInterfaceGet(m *hashmap, key interface{}, value unsafe.Pointer, valueSize uintptr) bool {
   662  	if m == nil {
   663  		memzero(value, uintptr(valueSize))
   664  		return false
   665  	}
   666  	hash := hashmapInterfaceHash(key, m.seed)
   667  	return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash)
   668  }
   669  
   670  func hashmapInterfaceGetUnsafePointer(m unsafe.Pointer, key interface{}, value unsafe.Pointer, valueSize uintptr) bool {
   671  	return hashmapInterfaceGet((*hashmap)(m), key, value, valueSize)
   672  }
   673  
   674  func hashmapInterfaceDelete(m *hashmap, key interface{}) {
   675  	if m == nil {
   676  		return
   677  	}
   678  	hash := hashmapInterfaceHash(key, m.seed)
   679  	hashmapDelete(m, unsafe.Pointer(&key), hash)
   680  }
   681  
   682  func hashmapInterfaceDeleteUnsafePointer(m unsafe.Pointer, key interface{}) {
   683  	hashmapInterfaceDelete((*hashmap)(m), key)
   684  }