github.com/c9s/go@v0.0.0-20180120015821-984e81f64e0c/src/runtime/hashmap_fast.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime 6 7 import ( 8 "runtime/internal/sys" 9 "unsafe" 10 ) 11 12 func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer { 13 if raceenabled && h != nil { 14 callerpc := getcallerpc() 15 racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast32)) 16 } 17 if h == nil || h.count == 0 { 18 return unsafe.Pointer(&zeroVal[0]) 19 } 20 if h.flags&hashWriting != 0 { 21 throw("concurrent map read and map write") 22 } 23 var b *bmap 24 if h.B == 0 { 25 // One-bucket table. No need to hash. 26 b = (*bmap)(h.buckets) 27 } else { 28 hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) 29 m := bucketMask(h.B) 30 b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) 31 if c := h.oldbuckets; c != nil { 32 if !h.sameSizeGrow() { 33 // There used to be half as many buckets; mask down one more power of two. 34 m >>= 1 35 } 36 oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) 37 if !evacuated(oldb) { 38 b = oldb 39 } 40 } 41 } 42 for ; b != nil; b = b.overflow(t) { 43 for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 4) { 44 if *(*uint32)(k) == key && b.tophash[i] != empty { 45 return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize)) 46 } 47 } 48 } 49 return unsafe.Pointer(&zeroVal[0]) 50 } 51 52 func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) { 53 if raceenabled && h != nil { 54 callerpc := getcallerpc() 55 racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast32)) 56 } 57 if h == nil || h.count == 0 { 58 return unsafe.Pointer(&zeroVal[0]), false 59 } 60 if h.flags&hashWriting != 0 { 61 throw("concurrent map read and map write") 62 } 63 var b *bmap 64 if h.B == 0 { 65 // One-bucket table. No need to hash. 66 b = (*bmap)(h.buckets) 67 } else { 68 hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) 69 m := bucketMask(h.B) 70 b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) 71 if c := h.oldbuckets; c != nil { 72 if !h.sameSizeGrow() { 73 // There used to be half as many buckets; mask down one more power of two. 74 m >>= 1 75 } 76 oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) 77 if !evacuated(oldb) { 78 b = oldb 79 } 80 } 81 } 82 for ; b != nil; b = b.overflow(t) { 83 for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 4) { 84 if *(*uint32)(k) == key && b.tophash[i] != empty { 85 return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize)), true 86 } 87 } 88 } 89 return unsafe.Pointer(&zeroVal[0]), false 90 } 91 92 func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer { 93 if raceenabled && h != nil { 94 callerpc := getcallerpc() 95 racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast64)) 96 } 97 if h == nil || h.count == 0 { 98 return unsafe.Pointer(&zeroVal[0]) 99 } 100 if h.flags&hashWriting != 0 { 101 throw("concurrent map read and map write") 102 } 103 var b *bmap 104 if h.B == 0 { 105 // One-bucket table. No need to hash. 106 b = (*bmap)(h.buckets) 107 } else { 108 hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) 109 m := bucketMask(h.B) 110 b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) 111 if c := h.oldbuckets; c != nil { 112 if !h.sameSizeGrow() { 113 // There used to be half as many buckets; mask down one more power of two. 114 m >>= 1 115 } 116 oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) 117 if !evacuated(oldb) { 118 b = oldb 119 } 120 } 121 } 122 for ; b != nil; b = b.overflow(t) { 123 for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 8) { 124 if *(*uint64)(k) == key && b.tophash[i] != empty { 125 return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize)) 126 } 127 } 128 } 129 return unsafe.Pointer(&zeroVal[0]) 130 } 131 132 func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) { 133 if raceenabled && h != nil { 134 callerpc := getcallerpc() 135 racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast64)) 136 } 137 if h == nil || h.count == 0 { 138 return unsafe.Pointer(&zeroVal[0]), false 139 } 140 if h.flags&hashWriting != 0 { 141 throw("concurrent map read and map write") 142 } 143 var b *bmap 144 if h.B == 0 { 145 // One-bucket table. No need to hash. 146 b = (*bmap)(h.buckets) 147 } else { 148 hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) 149 m := bucketMask(h.B) 150 b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) 151 if c := h.oldbuckets; c != nil { 152 if !h.sameSizeGrow() { 153 // There used to be half as many buckets; mask down one more power of two. 154 m >>= 1 155 } 156 oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) 157 if !evacuated(oldb) { 158 b = oldb 159 } 160 } 161 } 162 for ; b != nil; b = b.overflow(t) { 163 for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 8) { 164 if *(*uint64)(k) == key && b.tophash[i] != empty { 165 return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize)), true 166 } 167 } 168 } 169 return unsafe.Pointer(&zeroVal[0]), false 170 } 171 172 func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer { 173 if raceenabled && h != nil { 174 callerpc := getcallerpc() 175 racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_faststr)) 176 } 177 if h == nil || h.count == 0 { 178 return unsafe.Pointer(&zeroVal[0]) 179 } 180 if h.flags&hashWriting != 0 { 181 throw("concurrent map read and map write") 182 } 183 key := stringStructOf(&ky) 184 if h.B == 0 { 185 // One-bucket table. 186 b := (*bmap)(h.buckets) 187 if key.len < 32 { 188 // short key, doing lots of comparisons is ok 189 for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { 190 k := (*stringStruct)(kptr) 191 if k.len != key.len || b.tophash[i] == empty { 192 continue 193 } 194 if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { 195 return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) 196 } 197 } 198 return unsafe.Pointer(&zeroVal[0]) 199 } 200 // long key, try not to do more comparisons than necessary 201 keymaybe := uintptr(bucketCnt) 202 for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { 203 k := (*stringStruct)(kptr) 204 if k.len != key.len || b.tophash[i] == empty { 205 continue 206 } 207 if k.str == key.str { 208 return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) 209 } 210 // check first 4 bytes 211 if *((*[4]byte)(key.str)) != *((*[4]byte)(k.str)) { 212 continue 213 } 214 // check last 4 bytes 215 if *((*[4]byte)(add(key.str, uintptr(key.len)-4))) != *((*[4]byte)(add(k.str, uintptr(key.len)-4))) { 216 continue 217 } 218 if keymaybe != bucketCnt { 219 // Two keys are potential matches. Use hash to distinguish them. 220 goto dohash 221 } 222 keymaybe = i 223 } 224 if keymaybe != bucketCnt { 225 k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*sys.PtrSize)) 226 if memequal(k.str, key.str, uintptr(key.len)) { 227 return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.valuesize)) 228 } 229 } 230 return unsafe.Pointer(&zeroVal[0]) 231 } 232 dohash: 233 hash := t.key.alg.hash(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0)) 234 m := bucketMask(h.B) 235 b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) 236 if c := h.oldbuckets; c != nil { 237 if !h.sameSizeGrow() { 238 // There used to be half as many buckets; mask down one more power of two. 239 m >>= 1 240 } 241 oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) 242 if !evacuated(oldb) { 243 b = oldb 244 } 245 } 246 top := tophash(hash) 247 for ; b != nil; b = b.overflow(t) { 248 for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { 249 k := (*stringStruct)(kptr) 250 if k.len != key.len || b.tophash[i] != top { 251 continue 252 } 253 if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { 254 return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) 255 } 256 } 257 } 258 return unsafe.Pointer(&zeroVal[0]) 259 } 260 261 func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { 262 if raceenabled && h != nil { 263 callerpc := getcallerpc() 264 racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr)) 265 } 266 if h == nil || h.count == 0 { 267 return unsafe.Pointer(&zeroVal[0]), false 268 } 269 if h.flags&hashWriting != 0 { 270 throw("concurrent map read and map write") 271 } 272 key := stringStructOf(&ky) 273 if h.B == 0 { 274 // One-bucket table. 275 b := (*bmap)(h.buckets) 276 if key.len < 32 { 277 // short key, doing lots of comparisons is ok 278 for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { 279 k := (*stringStruct)(kptr) 280 if k.len != key.len || b.tophash[i] == empty { 281 continue 282 } 283 if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { 284 return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true 285 } 286 } 287 return unsafe.Pointer(&zeroVal[0]), false 288 } 289 // long key, try not to do more comparisons than necessary 290 keymaybe := uintptr(bucketCnt) 291 for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { 292 k := (*stringStruct)(kptr) 293 if k.len != key.len || b.tophash[i] == empty { 294 continue 295 } 296 if k.str == key.str { 297 return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true 298 } 299 // check first 4 bytes 300 if *((*[4]byte)(key.str)) != *((*[4]byte)(k.str)) { 301 continue 302 } 303 // check last 4 bytes 304 if *((*[4]byte)(add(key.str, uintptr(key.len)-4))) != *((*[4]byte)(add(k.str, uintptr(key.len)-4))) { 305 continue 306 } 307 if keymaybe != bucketCnt { 308 // Two keys are potential matches. Use hash to distinguish them. 309 goto dohash 310 } 311 keymaybe = i 312 } 313 if keymaybe != bucketCnt { 314 k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*sys.PtrSize)) 315 if memequal(k.str, key.str, uintptr(key.len)) { 316 return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.valuesize)), true 317 } 318 } 319 return unsafe.Pointer(&zeroVal[0]), false 320 } 321 dohash: 322 hash := t.key.alg.hash(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0)) 323 m := bucketMask(h.B) 324 b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) 325 if c := h.oldbuckets; c != nil { 326 if !h.sameSizeGrow() { 327 // There used to be half as many buckets; mask down one more power of two. 328 m >>= 1 329 } 330 oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) 331 if !evacuated(oldb) { 332 b = oldb 333 } 334 } 335 top := tophash(hash) 336 for ; b != nil; b = b.overflow(t) { 337 for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { 338 k := (*stringStruct)(kptr) 339 if k.len != key.len || b.tophash[i] != top { 340 continue 341 } 342 if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { 343 return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true 344 } 345 } 346 } 347 return unsafe.Pointer(&zeroVal[0]), false 348 } 349 350 func mapassign_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer { 351 if h == nil { 352 panic(plainError("assignment to entry in nil map")) 353 } 354 if raceenabled { 355 callerpc := getcallerpc() 356 racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast32)) 357 } 358 if h.flags&hashWriting != 0 { 359 throw("concurrent map writes") 360 } 361 hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) 362 363 // Set hashWriting after calling alg.hash for consistency with mapassign. 364 h.flags |= hashWriting 365 366 if h.buckets == nil { 367 h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) 368 } 369 370 again: 371 bucket := hash & bucketMask(h.B) 372 if h.growing() { 373 growWork_fast32(t, h, bucket) 374 } 375 b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) 376 377 var insertb *bmap 378 var inserti uintptr 379 var insertk unsafe.Pointer 380 381 for { 382 for i := uintptr(0); i < bucketCnt; i++ { 383 if b.tophash[i] == empty { 384 if insertb == nil { 385 inserti = i 386 insertb = b 387 } 388 continue 389 } 390 k := *((*uint32)(add(unsafe.Pointer(b), dataOffset+i*4))) 391 if k != key { 392 continue 393 } 394 inserti = i 395 insertb = b 396 goto done 397 } 398 ovf := b.overflow(t) 399 if ovf == nil { 400 break 401 } 402 b = ovf 403 } 404 405 // Did not find mapping for key. Allocate new cell & add entry. 406 407 // If we hit the max load factor or we have too many overflow buckets, 408 // and we're not already in the middle of growing, start growing. 409 if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { 410 hashGrow(t, h) 411 goto again // Growing the table invalidates everything, so try again 412 } 413 414 if insertb == nil { 415 // all current buckets are full, allocate a new one. 416 insertb = h.newoverflow(t, b) 417 inserti = 0 // not necessary, but avoids needlessly spilling inserti 418 } 419 insertb.tophash[inserti&(bucketCnt-1)] = tophash(hash) // mask inserti to avoid bounds checks 420 421 insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*4) 422 // store new key at insert position 423 *(*uint32)(insertk) = key 424 425 h.count++ 426 427 done: 428 val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*4+inserti*uintptr(t.valuesize)) 429 if h.flags&hashWriting == 0 { 430 throw("concurrent map writes") 431 } 432 h.flags &^= hashWriting 433 return val 434 } 435 436 func mapassign_fast32ptr(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { 437 if h == nil { 438 panic(plainError("assignment to entry in nil map")) 439 } 440 if raceenabled { 441 callerpc := getcallerpc() 442 racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast32)) 443 } 444 if h.flags&hashWriting != 0 { 445 throw("concurrent map writes") 446 } 447 hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) 448 449 // Set hashWriting after calling alg.hash for consistency with mapassign. 450 h.flags |= hashWriting 451 452 if h.buckets == nil { 453 h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) 454 } 455 456 again: 457 bucket := hash & bucketMask(h.B) 458 if h.growing() { 459 growWork_fast32(t, h, bucket) 460 } 461 b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) 462 463 var insertb *bmap 464 var inserti uintptr 465 var insertk unsafe.Pointer 466 467 for { 468 for i := uintptr(0); i < bucketCnt; i++ { 469 if b.tophash[i] == empty { 470 if insertb == nil { 471 inserti = i 472 insertb = b 473 } 474 continue 475 } 476 k := *((*unsafe.Pointer)(add(unsafe.Pointer(b), dataOffset+i*4))) 477 if k != key { 478 continue 479 } 480 inserti = i 481 insertb = b 482 goto done 483 } 484 ovf := b.overflow(t) 485 if ovf == nil { 486 break 487 } 488 b = ovf 489 } 490 491 // Did not find mapping for key. Allocate new cell & add entry. 492 493 // If we hit the max load factor or we have too many overflow buckets, 494 // and we're not already in the middle of growing, start growing. 495 if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { 496 hashGrow(t, h) 497 goto again // Growing the table invalidates everything, so try again 498 } 499 500 if insertb == nil { 501 // all current buckets are full, allocate a new one. 502 insertb = h.newoverflow(t, b) 503 inserti = 0 // not necessary, but avoids needlessly spilling inserti 504 } 505 insertb.tophash[inserti&(bucketCnt-1)] = tophash(hash) // mask inserti to avoid bounds checks 506 507 insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*4) 508 // store new key at insert position 509 *(*unsafe.Pointer)(insertk) = key 510 511 h.count++ 512 513 done: 514 val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*4+inserti*uintptr(t.valuesize)) 515 if h.flags&hashWriting == 0 { 516 throw("concurrent map writes") 517 } 518 h.flags &^= hashWriting 519 return val 520 } 521 522 func mapassign_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer { 523 if h == nil { 524 panic(plainError("assignment to entry in nil map")) 525 } 526 if raceenabled { 527 callerpc := getcallerpc() 528 racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast64)) 529 } 530 if h.flags&hashWriting != 0 { 531 throw("concurrent map writes") 532 } 533 hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) 534 535 // Set hashWriting after calling alg.hash for consistency with mapassign. 536 h.flags |= hashWriting 537 538 if h.buckets == nil { 539 h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) 540 } 541 542 again: 543 bucket := hash & bucketMask(h.B) 544 if h.growing() { 545 growWork_fast64(t, h, bucket) 546 } 547 b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) 548 549 var insertb *bmap 550 var inserti uintptr 551 var insertk unsafe.Pointer 552 553 for { 554 for i := uintptr(0); i < bucketCnt; i++ { 555 if b.tophash[i] == empty { 556 if insertb == nil { 557 insertb = b 558 inserti = i 559 } 560 continue 561 } 562 k := *((*uint64)(add(unsafe.Pointer(b), dataOffset+i*8))) 563 if k != key { 564 continue 565 } 566 insertb = b 567 inserti = i 568 goto done 569 } 570 ovf := b.overflow(t) 571 if ovf == nil { 572 break 573 } 574 b = ovf 575 } 576 577 // Did not find mapping for key. Allocate new cell & add entry. 578 579 // If we hit the max load factor or we have too many overflow buckets, 580 // and we're not already in the middle of growing, start growing. 581 if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { 582 hashGrow(t, h) 583 goto again // Growing the table invalidates everything, so try again 584 } 585 586 if insertb == nil { 587 // all current buckets are full, allocate a new one. 588 insertb = h.newoverflow(t, b) 589 inserti = 0 // not necessary, but avoids needlessly spilling inserti 590 } 591 insertb.tophash[inserti&(bucketCnt-1)] = tophash(hash) // mask inserti to avoid bounds checks 592 593 insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*8) 594 // store new key at insert position 595 *(*uint64)(insertk) = key 596 597 h.count++ 598 599 done: 600 val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*8+inserti*uintptr(t.valuesize)) 601 if h.flags&hashWriting == 0 { 602 throw("concurrent map writes") 603 } 604 h.flags &^= hashWriting 605 return val 606 } 607 608 func mapassign_fast64ptr(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { 609 if h == nil { 610 panic(plainError("assignment to entry in nil map")) 611 } 612 if raceenabled { 613 callerpc := getcallerpc() 614 racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast64)) 615 } 616 if h.flags&hashWriting != 0 { 617 throw("concurrent map writes") 618 } 619 hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) 620 621 // Set hashWriting after calling alg.hash for consistency with mapassign. 622 h.flags |= hashWriting 623 624 if h.buckets == nil { 625 h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) 626 } 627 628 again: 629 bucket := hash & bucketMask(h.B) 630 if h.growing() { 631 growWork_fast64(t, h, bucket) 632 } 633 b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) 634 635 var insertb *bmap 636 var inserti uintptr 637 var insertk unsafe.Pointer 638 639 for { 640 for i := uintptr(0); i < bucketCnt; i++ { 641 if b.tophash[i] == empty { 642 if insertb == nil { 643 insertb = b 644 inserti = i 645 } 646 continue 647 } 648 k := *((*unsafe.Pointer)(add(unsafe.Pointer(b), dataOffset+i*8))) 649 if k != key { 650 continue 651 } 652 insertb = b 653 inserti = i 654 goto done 655 } 656 ovf := b.overflow(t) 657 if ovf == nil { 658 break 659 } 660 b = ovf 661 } 662 663 // Did not find mapping for key. Allocate new cell & add entry. 664 665 // If we hit the max load factor or we have too many overflow buckets, 666 // and we're not already in the middle of growing, start growing. 667 if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { 668 hashGrow(t, h) 669 goto again // Growing the table invalidates everything, so try again 670 } 671 672 if insertb == nil { 673 // all current buckets are full, allocate a new one. 674 insertb = h.newoverflow(t, b) 675 inserti = 0 // not necessary, but avoids needlessly spilling inserti 676 } 677 insertb.tophash[inserti&(bucketCnt-1)] = tophash(hash) // mask inserti to avoid bounds checks 678 679 insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*8) 680 // store new key at insert position 681 *(*unsafe.Pointer)(insertk) = key 682 683 h.count++ 684 685 done: 686 val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*8+inserti*uintptr(t.valuesize)) 687 if h.flags&hashWriting == 0 { 688 throw("concurrent map writes") 689 } 690 h.flags &^= hashWriting 691 return val 692 } 693 694 func mapassign_faststr(t *maptype, h *hmap, s string) unsafe.Pointer { 695 if h == nil { 696 panic(plainError("assignment to entry in nil map")) 697 } 698 if raceenabled { 699 callerpc := getcallerpc() 700 racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_faststr)) 701 } 702 if h.flags&hashWriting != 0 { 703 throw("concurrent map writes") 704 } 705 key := stringStructOf(&s) 706 hash := t.key.alg.hash(noescape(unsafe.Pointer(&s)), uintptr(h.hash0)) 707 708 // Set hashWriting after calling alg.hash for consistency with mapassign. 709 h.flags |= hashWriting 710 711 if h.buckets == nil { 712 h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) 713 } 714 715 again: 716 bucket := hash & bucketMask(h.B) 717 if h.growing() { 718 growWork_faststr(t, h, bucket) 719 } 720 b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) 721 top := tophash(hash) 722 723 var insertb *bmap 724 var inserti uintptr 725 var insertk unsafe.Pointer 726 727 for { 728 for i := uintptr(0); i < bucketCnt; i++ { 729 if b.tophash[i] != top { 730 if b.tophash[i] == empty && insertb == nil { 731 insertb = b 732 inserti = i 733 } 734 continue 735 } 736 k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*sys.PtrSize)) 737 if k.len != key.len { 738 continue 739 } 740 if k.str != key.str && !memequal(k.str, key.str, uintptr(key.len)) { 741 continue 742 } 743 // already have a mapping for key. Update it. 744 inserti = i 745 insertb = b 746 goto done 747 } 748 ovf := b.overflow(t) 749 if ovf == nil { 750 break 751 } 752 b = ovf 753 } 754 755 // Did not find mapping for key. Allocate new cell & add entry. 756 757 // If we hit the max load factor or we have too many overflow buckets, 758 // and we're not already in the middle of growing, start growing. 759 if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { 760 hashGrow(t, h) 761 goto again // Growing the table invalidates everything, so try again 762 } 763 764 if insertb == nil { 765 // all current buckets are full, allocate a new one. 766 insertb = h.newoverflow(t, b) 767 inserti = 0 // not necessary, but avoids needlessly spilling inserti 768 } 769 insertb.tophash[inserti&(bucketCnt-1)] = top // mask inserti to avoid bounds checks 770 771 insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*2*sys.PtrSize) 772 // store new key at insert position 773 *((*stringStruct)(insertk)) = *key 774 h.count++ 775 776 done: 777 val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*2*sys.PtrSize+inserti*uintptr(t.valuesize)) 778 if h.flags&hashWriting == 0 { 779 throw("concurrent map writes") 780 } 781 h.flags &^= hashWriting 782 return val 783 } 784 785 func mapdelete_fast32(t *maptype, h *hmap, key uint32) { 786 if raceenabled && h != nil { 787 callerpc := getcallerpc() 788 racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapdelete_fast32)) 789 } 790 if h == nil || h.count == 0 { 791 return 792 } 793 if h.flags&hashWriting != 0 { 794 throw("concurrent map writes") 795 } 796 797 hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) 798 799 // Set hashWriting after calling alg.hash for consistency with mapdelete 800 h.flags |= hashWriting 801 802 bucket := hash & bucketMask(h.B) 803 if h.growing() { 804 growWork_fast32(t, h, bucket) 805 } 806 b := (*bmap)(add(h.buckets, bucket*uintptr(t.bucketsize))) 807 search: 808 for ; b != nil; b = b.overflow(t) { 809 for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 4) { 810 if key != *(*uint32)(k) || b.tophash[i] == empty { 811 continue 812 } 813 // Only clear key if there are pointers in it. 814 if t.key.kind&kindNoPointers == 0 { 815 memclrHasPointers(k, t.key.size) 816 } 817 // Only clear value if there are pointers in it. 818 if t.elem.kind&kindNoPointers == 0 { 819 v := add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize)) 820 memclrHasPointers(v, t.elem.size) 821 } 822 b.tophash[i] = empty 823 h.count-- 824 break search 825 } 826 } 827 828 if h.flags&hashWriting == 0 { 829 throw("concurrent map writes") 830 } 831 h.flags &^= hashWriting 832 } 833 834 func mapdelete_fast64(t *maptype, h *hmap, key uint64) { 835 if raceenabled && h != nil { 836 callerpc := getcallerpc() 837 racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapdelete_fast64)) 838 } 839 if h == nil || h.count == 0 { 840 return 841 } 842 if h.flags&hashWriting != 0 { 843 throw("concurrent map writes") 844 } 845 846 hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) 847 848 // Set hashWriting after calling alg.hash for consistency with mapdelete 849 h.flags |= hashWriting 850 851 bucket := hash & bucketMask(h.B) 852 if h.growing() { 853 growWork_fast64(t, h, bucket) 854 } 855 b := (*bmap)(add(h.buckets, bucket*uintptr(t.bucketsize))) 856 search: 857 for ; b != nil; b = b.overflow(t) { 858 for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 8) { 859 if key != *(*uint64)(k) || b.tophash[i] == empty { 860 continue 861 } 862 // Only clear key if there are pointers in it. 863 if t.key.kind&kindNoPointers == 0 { 864 memclrHasPointers(k, t.key.size) 865 } 866 // Only clear value if there are pointers in it. 867 if t.elem.kind&kindNoPointers == 0 { 868 v := add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize)) 869 memclrHasPointers(v, t.elem.size) 870 } 871 b.tophash[i] = empty 872 h.count-- 873 break search 874 } 875 } 876 877 if h.flags&hashWriting == 0 { 878 throw("concurrent map writes") 879 } 880 h.flags &^= hashWriting 881 } 882 883 func mapdelete_faststr(t *maptype, h *hmap, ky string) { 884 if raceenabled && h != nil { 885 callerpc := getcallerpc() 886 racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapdelete_faststr)) 887 } 888 if h == nil || h.count == 0 { 889 return 890 } 891 if h.flags&hashWriting != 0 { 892 throw("concurrent map writes") 893 } 894 895 key := stringStructOf(&ky) 896 hash := t.key.alg.hash(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0)) 897 898 // Set hashWriting after calling alg.hash for consistency with mapdelete 899 h.flags |= hashWriting 900 901 bucket := hash & bucketMask(h.B) 902 if h.growing() { 903 growWork_faststr(t, h, bucket) 904 } 905 b := (*bmap)(add(h.buckets, bucket*uintptr(t.bucketsize))) 906 top := tophash(hash) 907 search: 908 for ; b != nil; b = b.overflow(t) { 909 for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { 910 k := (*stringStruct)(kptr) 911 if k.len != key.len || b.tophash[i] != top { 912 continue 913 } 914 if k.str != key.str && !memequal(k.str, key.str, uintptr(key.len)) { 915 continue 916 } 917 // Clear key's pointer. 918 k.str = nil 919 // Only clear value if there are pointers in it. 920 if t.elem.kind&kindNoPointers == 0 { 921 v := add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) 922 memclrHasPointers(v, t.elem.size) 923 } 924 b.tophash[i] = empty 925 h.count-- 926 break search 927 } 928 } 929 930 if h.flags&hashWriting == 0 { 931 throw("concurrent map writes") 932 } 933 h.flags &^= hashWriting 934 } 935 936 func growWork_fast32(t *maptype, h *hmap, bucket uintptr) { 937 // make sure we evacuate the oldbucket corresponding 938 // to the bucket we're about to use 939 evacuate_fast32(t, h, bucket&h.oldbucketmask()) 940 941 // evacuate one more oldbucket to make progress on growing 942 if h.growing() { 943 evacuate_fast32(t, h, h.nevacuate) 944 } 945 } 946 947 func evacuate_fast32(t *maptype, h *hmap, oldbucket uintptr) { 948 b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))) 949 newbit := h.noldbuckets() 950 if !evacuated(b) { 951 // TODO: reuse overflow buckets instead of using new ones, if there 952 // is no iterator using the old buckets. (If !oldIterator.) 953 954 // xy contains the x and y (low and high) evacuation destinations. 955 var xy [2]evacDst 956 x := &xy[0] 957 x.b = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize))) 958 x.k = add(unsafe.Pointer(x.b), dataOffset) 959 x.v = add(x.k, bucketCnt*4) 960 961 if !h.sameSizeGrow() { 962 // Only calculate y pointers if we're growing bigger. 963 // Otherwise GC can see bad pointers. 964 y := &xy[1] 965 y.b = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize))) 966 y.k = add(unsafe.Pointer(y.b), dataOffset) 967 y.v = add(y.k, bucketCnt*4) 968 } 969 970 for ; b != nil; b = b.overflow(t) { 971 k := add(unsafe.Pointer(b), dataOffset) 972 v := add(k, bucketCnt*4) 973 for i := 0; i < bucketCnt; i, k, v = i+1, add(k, 4), add(v, uintptr(t.valuesize)) { 974 top := b.tophash[i] 975 if top == empty { 976 b.tophash[i] = evacuatedEmpty 977 continue 978 } 979 if top < minTopHash { 980 throw("bad map state") 981 } 982 var useY uint8 983 if !h.sameSizeGrow() { 984 // Compute hash to make our evacuation decision (whether we need 985 // to send this key/value to bucket x or bucket y). 986 hash := t.key.alg.hash(k, uintptr(h.hash0)) 987 if hash&newbit != 0 { 988 useY = 1 989 } 990 } 991 992 b.tophash[i] = evacuatedX + useY // evacuatedX + 1 == evacuatedY, enforced in makemap 993 dst := &xy[useY] // evacuation destination 994 995 if dst.i == bucketCnt { 996 dst.b = h.newoverflow(t, dst.b) 997 dst.i = 0 998 dst.k = add(unsafe.Pointer(dst.b), dataOffset) 999 dst.v = add(dst.k, bucketCnt*4) 1000 } 1001 dst.b.tophash[dst.i&(bucketCnt-1)] = top // mask dst.i as an optimization, to avoid a bounds check 1002 1003 // Copy key. 1004 if sys.PtrSize == 4 && t.key.kind&kindNoPointers == 0 && writeBarrier.enabled { 1005 writebarrierptr((*uintptr)(dst.k), *(*uintptr)(k)) 1006 } else { 1007 *(*uint32)(dst.k) = *(*uint32)(k) 1008 } 1009 1010 typedmemmove(t.elem, dst.v, v) 1011 dst.i++ 1012 // These updates might push these pointers past the end of the 1013 // key or value arrays. That's ok, as we have the overflow pointer 1014 // at the end of the bucket to protect against pointing past the 1015 // end of the bucket. 1016 dst.k = add(dst.k, 4) 1017 dst.v = add(dst.v, uintptr(t.valuesize)) 1018 } 1019 } 1020 // Unlink the overflow buckets & clear key/value to help GC. 1021 if h.flags&oldIterator == 0 && t.bucket.kind&kindNoPointers == 0 { 1022 b := add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)) 1023 // Preserve b.tophash because the evacuation 1024 // state is maintained there. 1025 ptr := add(b, dataOffset) 1026 n := uintptr(t.bucketsize) - dataOffset 1027 memclrHasPointers(ptr, n) 1028 } 1029 } 1030 1031 if oldbucket == h.nevacuate { 1032 advanceEvacuationMark(h, t, newbit) 1033 } 1034 } 1035 1036 func growWork_fast64(t *maptype, h *hmap, bucket uintptr) { 1037 // make sure we evacuate the oldbucket corresponding 1038 // to the bucket we're about to use 1039 evacuate_fast64(t, h, bucket&h.oldbucketmask()) 1040 1041 // evacuate one more oldbucket to make progress on growing 1042 if h.growing() { 1043 evacuate_fast64(t, h, h.nevacuate) 1044 } 1045 } 1046 1047 func evacuate_fast64(t *maptype, h *hmap, oldbucket uintptr) { 1048 b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))) 1049 newbit := h.noldbuckets() 1050 if !evacuated(b) { 1051 // TODO: reuse overflow buckets instead of using new ones, if there 1052 // is no iterator using the old buckets. (If !oldIterator.) 1053 1054 // xy contains the x and y (low and high) evacuation destinations. 1055 var xy [2]evacDst 1056 x := &xy[0] 1057 x.b = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize))) 1058 x.k = add(unsafe.Pointer(x.b), dataOffset) 1059 x.v = add(x.k, bucketCnt*8) 1060 1061 if !h.sameSizeGrow() { 1062 // Only calculate y pointers if we're growing bigger. 1063 // Otherwise GC can see bad pointers. 1064 y := &xy[1] 1065 y.b = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize))) 1066 y.k = add(unsafe.Pointer(y.b), dataOffset) 1067 y.v = add(y.k, bucketCnt*8) 1068 } 1069 1070 for ; b != nil; b = b.overflow(t) { 1071 k := add(unsafe.Pointer(b), dataOffset) 1072 v := add(k, bucketCnt*8) 1073 for i := 0; i < bucketCnt; i, k, v = i+1, add(k, 8), add(v, uintptr(t.valuesize)) { 1074 top := b.tophash[i] 1075 if top == empty { 1076 b.tophash[i] = evacuatedEmpty 1077 continue 1078 } 1079 if top < minTopHash { 1080 throw("bad map state") 1081 } 1082 var useY uint8 1083 if !h.sameSizeGrow() { 1084 // Compute hash to make our evacuation decision (whether we need 1085 // to send this key/value to bucket x or bucket y). 1086 hash := t.key.alg.hash(k, uintptr(h.hash0)) 1087 if hash&newbit != 0 { 1088 useY = 1 1089 } 1090 } 1091 1092 b.tophash[i] = evacuatedX + useY // evacuatedX + 1 == evacuatedY, enforced in makemap 1093 dst := &xy[useY] // evacuation destination 1094 1095 if dst.i == bucketCnt { 1096 dst.b = h.newoverflow(t, dst.b) 1097 dst.i = 0 1098 dst.k = add(unsafe.Pointer(dst.b), dataOffset) 1099 dst.v = add(dst.k, bucketCnt*8) 1100 } 1101 dst.b.tophash[dst.i&(bucketCnt-1)] = top // mask dst.i as an optimization, to avoid a bounds check 1102 1103 // Copy key. 1104 if t.key.kind&kindNoPointers == 0 && writeBarrier.enabled { 1105 if sys.PtrSize == 8 { 1106 writebarrierptr((*uintptr)(dst.k), *(*uintptr)(k)) 1107 } else { 1108 // There are three ways to squeeze at least one 32 bit pointer into 64 bits. 1109 // Give up and call typedmemmove. 1110 typedmemmove(t.key, dst.k, k) 1111 } 1112 } else { 1113 *(*uint64)(dst.k) = *(*uint64)(k) 1114 } 1115 1116 typedmemmove(t.elem, dst.v, v) 1117 dst.i++ 1118 // These updates might push these pointers past the end of the 1119 // key or value arrays. That's ok, as we have the overflow pointer 1120 // at the end of the bucket to protect against pointing past the 1121 // end of the bucket. 1122 dst.k = add(dst.k, 8) 1123 dst.v = add(dst.v, uintptr(t.valuesize)) 1124 } 1125 } 1126 // Unlink the overflow buckets & clear key/value to help GC. 1127 if h.flags&oldIterator == 0 && t.bucket.kind&kindNoPointers == 0 { 1128 b := add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)) 1129 // Preserve b.tophash because the evacuation 1130 // state is maintained there. 1131 ptr := add(b, dataOffset) 1132 n := uintptr(t.bucketsize) - dataOffset 1133 memclrHasPointers(ptr, n) 1134 } 1135 } 1136 1137 if oldbucket == h.nevacuate { 1138 advanceEvacuationMark(h, t, newbit) 1139 } 1140 } 1141 1142 func growWork_faststr(t *maptype, h *hmap, bucket uintptr) { 1143 // make sure we evacuate the oldbucket corresponding 1144 // to the bucket we're about to use 1145 evacuate_faststr(t, h, bucket&h.oldbucketmask()) 1146 1147 // evacuate one more oldbucket to make progress on growing 1148 if h.growing() { 1149 evacuate_faststr(t, h, h.nevacuate) 1150 } 1151 } 1152 1153 func evacuate_faststr(t *maptype, h *hmap, oldbucket uintptr) { 1154 b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))) 1155 newbit := h.noldbuckets() 1156 if !evacuated(b) { 1157 // TODO: reuse overflow buckets instead of using new ones, if there 1158 // is no iterator using the old buckets. (If !oldIterator.) 1159 1160 // xy contains the x and y (low and high) evacuation destinations. 1161 var xy [2]evacDst 1162 x := &xy[0] 1163 x.b = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize))) 1164 x.k = add(unsafe.Pointer(x.b), dataOffset) 1165 x.v = add(x.k, bucketCnt*2*sys.PtrSize) 1166 1167 if !h.sameSizeGrow() { 1168 // Only calculate y pointers if we're growing bigger. 1169 // Otherwise GC can see bad pointers. 1170 y := &xy[1] 1171 y.b = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize))) 1172 y.k = add(unsafe.Pointer(y.b), dataOffset) 1173 y.v = add(y.k, bucketCnt*2*sys.PtrSize) 1174 } 1175 1176 for ; b != nil; b = b.overflow(t) { 1177 k := add(unsafe.Pointer(b), dataOffset) 1178 v := add(k, bucketCnt*2*sys.PtrSize) 1179 for i := 0; i < bucketCnt; i, k, v = i+1, add(k, 2*sys.PtrSize), add(v, uintptr(t.valuesize)) { 1180 top := b.tophash[i] 1181 if top == empty { 1182 b.tophash[i] = evacuatedEmpty 1183 continue 1184 } 1185 if top < minTopHash { 1186 throw("bad map state") 1187 } 1188 var useY uint8 1189 if !h.sameSizeGrow() { 1190 // Compute hash to make our evacuation decision (whether we need 1191 // to send this key/value to bucket x or bucket y). 1192 hash := t.key.alg.hash(k, uintptr(h.hash0)) 1193 if hash&newbit != 0 { 1194 useY = 1 1195 } 1196 } 1197 1198 b.tophash[i] = evacuatedX + useY // evacuatedX + 1 == evacuatedY, enforced in makemap 1199 dst := &xy[useY] // evacuation destination 1200 1201 if dst.i == bucketCnt { 1202 dst.b = h.newoverflow(t, dst.b) 1203 dst.i = 0 1204 dst.k = add(unsafe.Pointer(dst.b), dataOffset) 1205 dst.v = add(dst.k, bucketCnt*2*sys.PtrSize) 1206 } 1207 dst.b.tophash[dst.i&(bucketCnt-1)] = top // mask dst.i as an optimization, to avoid a bounds check 1208 1209 // Copy key. 1210 *(*string)(dst.k) = *(*string)(k) 1211 1212 typedmemmove(t.elem, dst.v, v) 1213 dst.i++ 1214 // These updates might push these pointers past the end of the 1215 // key or value arrays. That's ok, as we have the overflow pointer 1216 // at the end of the bucket to protect against pointing past the 1217 // end of the bucket. 1218 dst.k = add(dst.k, 2*sys.PtrSize) 1219 dst.v = add(dst.v, uintptr(t.valuesize)) 1220 } 1221 } 1222 // Unlink the overflow buckets & clear key/value to help GC. 1223 // Unlink the overflow buckets & clear key/value to help GC. 1224 if h.flags&oldIterator == 0 && t.bucket.kind&kindNoPointers == 0 { 1225 b := add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)) 1226 // Preserve b.tophash because the evacuation 1227 // state is maintained there. 1228 ptr := add(b, dataOffset) 1229 n := uintptr(t.bucketsize) - dataOffset 1230 memclrHasPointers(ptr, n) 1231 } 1232 } 1233 1234 if oldbucket == h.nevacuate { 1235 advanceEvacuationMark(h, t, newbit) 1236 } 1237 }