2013-03-20 14:51:29 -06:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
// Note that this code differs from hash_lookup in that
|
|
|
|
// it returns a pointer to the result, not the result itself.
|
|
|
|
// The returned pointer is only valid until the next GC
|
|
|
|
// point, so the caller must dereference it before then.
|
|
|
|
|
|
|
|
// +build ignore
|
|
|
|
|
|
|
|
#pragma textflag 7
|
|
|
|
void
|
|
|
|
HASH_LOOKUP1(MapType *t, Hmap *h, KEYTYPE key, byte *value)
|
|
|
|
{
|
2013-06-28 14:37:07 -06:00
|
|
|
uintptr bucket, i;
|
2013-03-20 14:51:29 -06:00
|
|
|
Bucket *b;
|
|
|
|
KEYTYPE *k;
|
|
|
|
byte *v;
|
2013-04-02 21:58:25 -06:00
|
|
|
uint8 top;
|
|
|
|
int8 keymaybe;
|
2013-03-20 14:51:29 -06:00
|
|
|
|
|
|
|
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 = empty_value;
|
|
|
|
FLUSH(&value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(raceenabled)
|
|
|
|
runtime·racereadpc(h, runtime·getcallerpc(&t), HASH_LOOKUP1);
|
|
|
|
if(docheck)
|
|
|
|
check(t, h);
|
|
|
|
|
2013-04-02 21:58:25 -06:00
|
|
|
if(h->B == 0) {
|
|
|
|
// One-bucket table. Don't hash, just check each bucket entry.
|
2013-03-20 14:51:29 -06:00
|
|
|
b = (Bucket*)h->buckets;
|
2013-07-30 22:39:57 -06:00
|
|
|
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] == 0)
|
|
|
|
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] == 0)
|
|
|
|
continue;
|
|
|
|
if(QUICK_NE(key, *k))
|
|
|
|
continue;
|
|
|
|
if(QUICK_EQ(key, *k)) {
|
2013-04-02 21:58:25 -06:00
|
|
|
value = v;
|
|
|
|
FLUSH(&value);
|
|
|
|
return;
|
|
|
|
}
|
2013-07-30 22:39:57 -06:00
|
|
|
if(MAYBE_EQ(key, *k)) {
|
2013-04-02 21:58:25 -06:00
|
|
|
if(keymaybe >= 0) {
|
|
|
|
// Two same-length strings in this bucket.
|
|
|
|
// use slow path.
|
2013-07-30 22:39:57 -06:00
|
|
|
// 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.
|
2013-04-02 21:58:25 -06:00
|
|
|
goto dohash;
|
|
|
|
}
|
|
|
|
keymaybe = i;
|
|
|
|
}
|
|
|
|
}
|
2013-07-30 22:39:57 -06:00
|
|
|
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;
|
|
|
|
}
|
2013-03-20 14:51:29 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2013-04-02 21:58:25 -06:00
|
|
|
dohash:
|
2013-06-28 14:37:07 -06:00
|
|
|
bucket = h->hash0;
|
|
|
|
HASHFUNC(&bucket, sizeof(KEYTYPE), &key);
|
|
|
|
top = bucket >> (sizeof(uintptr)*8 - 8);
|
|
|
|
if(top == 0)
|
|
|
|
top = 1;
|
|
|
|
bucket &= (((uintptr)1 << h->B) - 1);
|
2013-05-31 22:44:32 -06:00
|
|
|
if(h->oldbuckets != nil) {
|
2013-06-28 14:37:07 -06:00
|
|
|
i = bucket & (((uintptr)1 << (h->B - 1)) - 1);
|
|
|
|
b = (Bucket*)(h->oldbuckets + i * h->bucketsize);
|
2013-05-31 22:44:32 -06:00
|
|
|
if(evacuated(b)) {
|
|
|
|
b = (Bucket*)(h->buckets + bucket * h->bucketsize);
|
|
|
|
}
|
2013-04-01 19:59:58 -06:00
|
|
|
} else {
|
|
|
|
b = (Bucket*)(h->buckets + bucket * h->bucketsize);
|
|
|
|
}
|
2013-03-20 14:51:29 -06:00
|
|
|
do {
|
|
|
|
for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
|
2013-07-30 22:39:57 -06:00
|
|
|
if(b->tophash[i] != top)
|
|
|
|
continue;
|
|
|
|
if(QUICK_NE(key, *k))
|
|
|
|
continue;
|
|
|
|
if(QUICK_EQ(key, *k) || SLOW_EQ(key, *k)) {
|
2013-03-20 14:51:29 -06:00
|
|
|
value = v;
|
|
|
|
FLUSH(&value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2013-05-31 22:44:32 -06:00
|
|
|
b = b->overflow;
|
2013-03-20 14:51:29 -06:00
|
|
|
} while(b != nil);
|
|
|
|
}
|
|
|
|
value = empty_value;
|
|
|
|
FLUSH(&value);
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma textflag 7
|
|
|
|
void
|
|
|
|
HASH_LOOKUP2(MapType *t, Hmap *h, KEYTYPE key, byte *value, bool res)
|
|
|
|
{
|
2013-06-28 14:37:07 -06:00
|
|
|
uintptr bucket, i;
|
2013-03-20 14:51:29 -06:00
|
|
|
Bucket *b;
|
|
|
|
KEYTYPE *k;
|
|
|
|
byte *v;
|
2013-04-02 21:58:25 -06:00
|
|
|
uint8 top;
|
|
|
|
int8 keymaybe;
|
2013-03-20 14:51:29 -06:00
|
|
|
|
|
|
|
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 = empty_value;
|
|
|
|
res = false;
|
|
|
|
FLUSH(&value);
|
|
|
|
FLUSH(&res);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(raceenabled)
|
|
|
|
runtime·racereadpc(h, runtime·getcallerpc(&t), HASH_LOOKUP2);
|
|
|
|
if(docheck)
|
|
|
|
check(t, h);
|
|
|
|
|
2013-04-02 21:58:25 -06:00
|
|
|
if(h->B == 0) {
|
2013-07-30 22:39:57 -06:00
|
|
|
// One-bucket table. Don't hash, just check each bucket entry.
|
2013-03-20 14:51:29 -06:00
|
|
|
b = (Bucket*)h->buckets;
|
2013-07-30 22:39:57 -06:00
|
|
|
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] == 0)
|
|
|
|
continue;
|
|
|
|
if(QUICK_NE(key, *k))
|
|
|
|
continue;
|
|
|
|
if(QUICK_EQ(key, *k) || SLOW_EQ(key, *k)) {
|
2013-04-02 21:58:25 -06:00
|
|
|
value = v;
|
|
|
|
res = true;
|
|
|
|
FLUSH(&value);
|
|
|
|
FLUSH(&res);
|
|
|
|
return;
|
|
|
|
}
|
2013-07-30 22:39:57 -06:00
|
|
|
}
|
|
|
|
} 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] == 0)
|
|
|
|
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)) {
|
2013-04-02 21:58:25 -06:00
|
|
|
if(keymaybe >= 0) {
|
|
|
|
// Two same-length strings in this bucket.
|
|
|
|
// use slow path.
|
2013-07-30 22:39:57 -06:00
|
|
|
// 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.
|
2013-04-02 21:58:25 -06:00
|
|
|
goto dohash;
|
|
|
|
}
|
|
|
|
keymaybe = i;
|
|
|
|
}
|
|
|
|
}
|
2013-07-30 22:39:57 -06:00
|
|
|
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;
|
|
|
|
}
|
2013-03-20 14:51:29 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2013-04-02 21:58:25 -06:00
|
|
|
dohash:
|
2013-06-28 14:37:07 -06:00
|
|
|
bucket = h->hash0;
|
|
|
|
HASHFUNC(&bucket, sizeof(KEYTYPE), &key);
|
|
|
|
top = bucket >> (sizeof(uintptr)*8 - 8);
|
|
|
|
if(top == 0)
|
|
|
|
top = 1;
|
|
|
|
bucket &= (((uintptr)1 << h->B) - 1);
|
2013-05-31 22:44:32 -06:00
|
|
|
if(h->oldbuckets != nil) {
|
2013-06-28 14:37:07 -06:00
|
|
|
i = bucket & (((uintptr)1 << (h->B - 1)) - 1);
|
|
|
|
b = (Bucket*)(h->oldbuckets + i * h->bucketsize);
|
2013-05-31 22:44:32 -06:00
|
|
|
if(evacuated(b)) {
|
|
|
|
b = (Bucket*)(h->buckets + bucket * h->bucketsize);
|
|
|
|
}
|
2013-04-01 19:59:58 -06:00
|
|
|
} else {
|
|
|
|
b = (Bucket*)(h->buckets + bucket * h->bucketsize);
|
|
|
|
}
|
2013-03-20 14:51:29 -06:00
|
|
|
do {
|
|
|
|
for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
|
2013-07-30 22:39:57 -06:00
|
|
|
if(b->tophash[i] != top)
|
|
|
|
continue;
|
|
|
|
if(QUICK_NE(key, *k))
|
|
|
|
continue;
|
|
|
|
if(QUICK_EQ(key, *k) || SLOW_EQ(key, *k)) {
|
2013-03-20 14:51:29 -06:00
|
|
|
value = v;
|
|
|
|
res = true;
|
|
|
|
FLUSH(&value);
|
|
|
|
FLUSH(&res);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2013-05-31 22:44:32 -06:00
|
|
|
b = b->overflow;
|
2013-03-20 14:51:29 -06:00
|
|
|
} while(b != nil);
|
|
|
|
}
|
|
|
|
value = empty_value;
|
|
|
|
res = false;
|
|
|
|
FLUSH(&value);
|
|
|
|
FLUSH(&res);
|
|
|
|
}
|