hashmap.mx raw

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