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 }