| // Copyright 2013 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Fast hashmap lookup specialized to a specific key type. | |
| // Included by hashmap.c once for each specialized type. | |
| // +build ignore | |
| #pragma textflag NOSPLIT | |
| void | |
| HASH_LOOKUP1(MapType *t, Hmap *h, KEYTYPE key, byte *value) | |
| { | |
| uintptr bucket, i; | |
| Bucket *b; | |
| KEYTYPE *k; | |
| byte *v; | |
| uint8 top; | |
| int8 keymaybe; | |
| if(debug) { | |
| runtime·prints("runtime.mapaccess1_fastXXX: map="); | |
| runtime·printpointer(h); | |
| runtime·prints("; key="); | |
| t->key->alg->print(t->key->size, &key); | |
| runtime·prints("\n"); | |
| } | |
| if(h == nil || h->count == 0) { | |
| value = t->elem->zero; | |
| FLUSH(&value); | |
| return; | |
| } | |
| if(raceenabled) | |
| runtime·racereadpc(h, runtime·getcallerpc(&t), HASH_LOOKUP1); | |
| if(docheck) | |
| check(t, h); | |
| if(h->B == 0) { | |
| // One-bucket table. Don't hash, just check each bucket entry. | |
| b = (Bucket*)h->buckets; | |
| if(FASTKEY(key)) { | |
| for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) { | |
| if(b->tophash[i] == Empty) | |
| continue; | |
| if(QUICK_NE(key, *k)) | |
| continue; | |
| if(QUICK_EQ(key, *k) || SLOW_EQ(key, *k)) { | |
| value = v; | |
| FLUSH(&value); | |
| return; | |
| } | |
| } | |
| } else { | |
| keymaybe = -1; | |
| for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) { | |
| if(b->tophash[i] == Empty) | |
| continue; | |
| if(QUICK_NE(key, *k)) | |
| continue; | |
| if(QUICK_EQ(key, *k)) { | |
| value = v; | |
| FLUSH(&value); | |
| return; | |
| } | |
| if(MAYBE_EQ(key, *k)) { | |
| if(keymaybe >= 0) { | |
| // Two same-length strings in this bucket. | |
| // use slow path. | |
| // TODO: keep track of more than just 1. We could | |
| // afford about 3 equals calls before it would be more | |
| // expensive than 1 hash + 1 equals. | |
| goto dohash; | |
| } | |
| keymaybe = i; | |
| } | |
| } | |
| if(keymaybe >= 0) { | |
| k = (KEYTYPE*)b->data + keymaybe; | |
| if(SLOW_EQ(key, *k)) { | |
| value = (byte*)((KEYTYPE*)b->data + BUCKETSIZE) + keymaybe * h->valuesize; | |
| FLUSH(&value); | |
| return; | |
| } | |
| } | |
| } | |
| } else { | |
| dohash: | |
| bucket = h->hash0; | |
| HASHFUNC(&bucket, sizeof(KEYTYPE), &key); | |
| top = bucket >> (sizeof(uintptr)*8 - 8); | |
| if(top < MinTopHash) | |
| top += MinTopHash; | |
| bucket &= (((uintptr)1 << h->B) - 1); | |
| if(h->oldbuckets != nil) { | |
| i = bucket & (((uintptr)1 << (h->B - 1)) - 1); | |
| b = (Bucket*)(h->oldbuckets + i * h->bucketsize); | |
| if(evacuated(b)) { | |
| b = (Bucket*)(h->buckets + bucket * h->bucketsize); | |
| } | |
| } else { | |
| b = (Bucket*)(h->buckets + bucket * h->bucketsize); | |
| } | |
| do { | |
| for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) { | |
| if(b->tophash[i] != top) | |
| continue; | |
| if(QUICK_NE(key, *k)) | |
| continue; | |
| if(QUICK_EQ(key, *k) || SLOW_EQ(key, *k)) { | |
| value = v; | |
| FLUSH(&value); | |
| return; | |
| } | |
| } | |
| b = b->overflow; | |
| } while(b != nil); | |
| } | |
| value = t->elem->zero; | |
| FLUSH(&value); | |
| } | |
| #pragma textflag NOSPLIT | |
| void | |
| HASH_LOOKUP2(MapType *t, Hmap *h, KEYTYPE key, byte *value, bool res) | |
| { | |
| uintptr bucket, i; | |
| Bucket *b; | |
| KEYTYPE *k; | |
| byte *v; | |
| uint8 top; | |
| int8 keymaybe; | |
| if(debug) { | |
| runtime·prints("runtime.mapaccess2_fastXXX: map="); | |
| runtime·printpointer(h); | |
| runtime·prints("; key="); | |
| t->key->alg->print(t->key->size, &key); | |
| runtime·prints("\n"); | |
| } | |
| if(h == nil || h->count == 0) { | |
| value = t->elem->zero; | |
| res = false; | |
| FLUSH(&value); | |
| FLUSH(&res); | |
| return; | |
| } | |
| if(raceenabled) | |
| runtime·racereadpc(h, runtime·getcallerpc(&t), HASH_LOOKUP2); | |
| if(docheck) | |
| check(t, h); | |
| if(h->B == 0) { | |
| // One-bucket table. Don't hash, just check each bucket entry. | |
| b = (Bucket*)h->buckets; | |
| if(FASTKEY(key)) { | |
| for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) { | |
| if(b->tophash[i] == Empty) | |
| continue; | |
| if(QUICK_NE(key, *k)) | |
| continue; | |
| if(QUICK_EQ(key, *k) || SLOW_EQ(key, *k)) { | |
| value = v; | |
| res = true; | |
| FLUSH(&value); | |
| FLUSH(&res); | |
| return; | |
| } | |
| } | |
| } else { | |
| keymaybe = -1; | |
| for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) { | |
| if(b->tophash[i] == Empty) | |
| continue; | |
| if(QUICK_NE(key, *k)) | |
| continue; | |
| if(QUICK_EQ(key, *k)) { | |
| value = v; | |
| res = true; | |
| FLUSH(&value); | |
| FLUSH(&res); | |
| return; | |
| } | |
| if(MAYBE_EQ(key, *k)) { | |
| if(keymaybe >= 0) { | |
| // Two same-length strings in this bucket. | |
| // use slow path. | |
| // TODO: keep track of more than just 1. We could | |
| // afford about 3 equals calls before it would be more | |
| // expensive than 1 hash + 1 equals. | |
| goto dohash; | |
| } | |
| keymaybe = i; | |
| } | |
| } | |
| if(keymaybe >= 0) { | |
| k = (KEYTYPE*)b->data + keymaybe; | |
| if(SLOW_EQ(key, *k)) { | |
| value = (byte*)((KEYTYPE*)b->data + BUCKETSIZE) + keymaybe * h->valuesize; | |
| res = true; | |
| FLUSH(&value); | |
| FLUSH(&res); | |
| return; | |
| } | |
| } | |
| } | |
| } else { | |
| dohash: | |
| bucket = h->hash0; | |
| HASHFUNC(&bucket, sizeof(KEYTYPE), &key); | |
| top = bucket >> (sizeof(uintptr)*8 - 8); | |
| if(top < MinTopHash) | |
| top += MinTopHash; | |
| bucket &= (((uintptr)1 << h->B) - 1); | |
| if(h->oldbuckets != nil) { | |
| i = bucket & (((uintptr)1 << (h->B - 1)) - 1); | |
| b = (Bucket*)(h->oldbuckets + i * h->bucketsize); | |
| if(evacuated(b)) { | |
| b = (Bucket*)(h->buckets + bucket * h->bucketsize); | |
| } | |
| } else { | |
| b = (Bucket*)(h->buckets + bucket * h->bucketsize); | |
| } | |
| do { | |
| for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) { | |
| if(b->tophash[i] != top) | |
| continue; | |
| if(QUICK_NE(key, *k)) | |
| continue; | |
| if(QUICK_EQ(key, *k) || SLOW_EQ(key, *k)) { | |
| value = v; | |
| res = true; | |
| FLUSH(&value); | |
| FLUSH(&res); | |
| return; | |
| } | |
| } | |
| b = b->overflow; | |
| } while(b != nil); | |
| } | |
| value = t->elem->zero; | |
| res = false; | |
| FLUSH(&value); | |
| FLUSH(&res); | |
| } |