typedef struct Bucket Bucket;
struct Bucket
{
- uint8 tophash[BUCKETSIZE]; // top 8 bits of hash of each entry (0 = empty)
+ uint8 tophash[BUCKETSIZE]; // top 8 bits of hash of each entry (0 = empty)
Bucket *overflow; // overflow bucket, if any
- byte data[1]; // BUCKETSIZE keys followed by BUCKETSIZE values
+ byte data[1]; // BUCKETSIZE keys followed by BUCKETSIZE values
};
// NOTE: packing all the keys together and then all the values together makes the
// code a bit more complicated than alternating key/value/key/value/... but it allows
uint16 bucketsize; // bucket size in bytes
uintptr hash0; // hash seed
- byte *buckets; // array of 2^B Buckets
+ byte *buckets; // array of 2^B Buckets. may be nil if count==0.
byte *oldbuckets; // previous bucket array of half the size, non-nil only when growing
uintptr nevacuate; // progress counter for evacuation (buckets less than this have been evacuated)
};
static uint8 empty_value[MAXVALUESIZE];
// Specialized versions of mapaccess1 for specific types.
-// See ./hashmap_fast and ../../cmd/gc/walk.c.
+// See ./hashmap_fast.c and ../../cmd/gc/walk.c.
#define HASH_LOOKUP1 runtime·mapaccess1_fast32
#define HASH_LOOKUP2 runtime·mapaccess2_fast32
#define KEYTYPE uint32
#define HASHFUNC runtime·algarray[AMEM32].hash
#define EQFUNC(x,y) ((x) == (y))
+#define EQMAYBE(x,y) ((x) == (y))
+#define HASMAYBE false
#define QUICKEQ(x) true
#include "hashmap_fast.c"
#undef KEYTYPE
#undef HASHFUNC
#undef EQFUNC
+#undef EQMAYBE
+#undef HASMAYBE
#undef QUICKEQ
#define HASH_LOOKUP1 runtime·mapaccess1_fast64
#define KEYTYPE uint64
#define HASHFUNC runtime·algarray[AMEM64].hash
#define EQFUNC(x,y) ((x) == (y))
+#define EQMAYBE(x,y) ((x) == (y))
+#define HASMAYBE false
#define QUICKEQ(x) true
#include "hashmap_fast.c"
#undef KEYTYPE
#undef HASHFUNC
#undef EQFUNC
+#undef EQMAYBE
+#undef HASMAYBE
#undef QUICKEQ
#define HASH_LOOKUP1 runtime·mapaccess1_faststr
#define KEYTYPE String
#define HASHFUNC runtime·algarray[ASTRING].hash
#define EQFUNC(x,y) ((x).len == (y).len && ((x).str == (y).str || runtime·memeq((x).str, (y).str, (x).len)))
+#define EQMAYBE(x,y) ((x).len == (y).len)
+#define HASMAYBE true
#define QUICKEQ(x) ((x).len < 32)
#include "hashmap_fast.c"
uintptr hash;
uintptr bucket, oldbucket;
Bucket *b;
- uint8 top;
uintptr i;
KEYTYPE *k;
byte *v;
+ uint8 top;
+ int8 keymaybe;
+ bool quickkey;
if(debug) {
runtime·prints("runtime.mapaccess1_fastXXX: map=");
if(docheck)
check(t, h);
- if(h->B == 0 && (h->count == 1 || QUICKEQ(key))) {
- // One-bucket table. Don't hash, just check each bucket entry.
+ if(h->B == 0) {
+ // One-bucket table. Don't hash, just check each bucket entry.
+ if(HASMAYBE) {
+ keymaybe = -1;
+ }
+ quickkey = QUICKEQ(key);
b = (Bucket*)h->buckets;
for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
- if(b->tophash[i] != 0 && EQFUNC(key, *k)) {
- value = v;
+ if(b->tophash[i] != 0) {
+ if(quickkey && EQFUNC(key, *k)) {
+ value = v;
+ FLUSH(&value);
+ return;
+ }
+ if(HASMAYBE && EQMAYBE(key, *k)) {
+ // TODO: check if key.str matches. Add EQFUNCFAST?
+ if(keymaybe >= 0) {
+ // Two same-length strings in this bucket.
+ // use slow path.
+ // TODO: keep track of more than just 1. Especially
+ // if doing the TODO above.
+ goto dohash;
+ }
+ keymaybe = i;
+ }
+ }
+ }
+ if(HASMAYBE && keymaybe >= 0) {
+ k = (KEYTYPE*)b->data + keymaybe;
+ if(EQFUNC(key, *k)) {
+ value = (byte*)((KEYTYPE*)b->data + BUCKETSIZE) + keymaybe * h->valuesize;
FLUSH(&value);
return;
}
}
} else {
+dohash:
hash = h->hash0;
HASHFUNC(&hash, sizeof(KEYTYPE), &key);
bucket = hash & (((uintptr)1 << h->B) - 1);
uintptr hash;
uintptr bucket, oldbucket;
Bucket *b;
- uint8 top;
uintptr i;
KEYTYPE *k;
byte *v;
+ uint8 top;
+ int8 keymaybe;
+ bool quickkey;
if(debug) {
runtime·prints("runtime.mapaccess2_fastXXX: map=");
if(docheck)
check(t, h);
- if(h->B == 0 && (h->count == 1 || QUICKEQ(key))) {
+ if(h->B == 0) {
// One-bucket table. Don't hash, just check each bucket entry.
+ if(HASMAYBE) {
+ keymaybe = -1;
+ }
+ quickkey = QUICKEQ(key);
b = (Bucket*)h->buckets;
for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
- if(b->tophash[i] != 0 && EQFUNC(key, *k)) {
- value = v;
+ if(b->tophash[i] != 0) {
+ if(quickkey && EQFUNC(key, *k)) {
+ value = v;
+ res = true;
+ FLUSH(&value);
+ FLUSH(&res);
+ return;
+ }
+ if(HASMAYBE && EQMAYBE(key, *k)) {
+ // TODO: check if key.str matches. Add EQFUNCFAST?
+ if(keymaybe >= 0) {
+ // Two same-length strings in this bucket.
+ // use slow path.
+ // TODO: keep track of more than just 1. Especially
+ // if doing the TODO above.
+ goto dohash;
+ }
+ keymaybe = i;
+ }
+ }
+ }
+ if(HASMAYBE && keymaybe >= 0) {
+ k = (KEYTYPE*)b->data + keymaybe;
+ if(EQFUNC(key, *k)) {
+ value = (byte*)((KEYTYPE*)b->data + BUCKETSIZE) + keymaybe * h->valuesize;
res = true;
FLUSH(&value);
FLUSH(&res);
}
}
} else {
+dohash:
hash = h->hash0;
HASHFUNC(&hash, sizeof(KEYTYPE), &key);
bucket = hash & (((uintptr)1 << h->B) - 1);