#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
+#define FASTKEY(x) true
+#define QUICK_NE(x,y) ((x) != (y))
+#define QUICK_EQ(x,y) true
+#define SLOW_EQ(x,y) true
+#define MAYBE_EQ(x,y) true
#include "hashmap_fast.c"
#undef HASH_LOOKUP1
#undef HASH_LOOKUP2
#undef KEYTYPE
#undef HASHFUNC
-#undef EQFUNC
-#undef EQMAYBE
-#undef HASMAYBE
-#undef QUICKEQ
+#undef FASTKEY
+#undef QUICK_NE
+#undef QUICK_EQ
+#undef SLOW_EQ
+#undef MAYBE_EQ
#define HASH_LOOKUP1 runtime·mapaccess1_fast64
#define HASH_LOOKUP2 runtime·mapaccess2_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
+#define FASTKEY(x) true
+#define QUICK_NE(x,y) ((x) != (y))
+#define QUICK_EQ(x,y) true
+#define SLOW_EQ(x,y) true
+#define MAYBE_EQ(x,y) true
#include "hashmap_fast.c"
#undef HASH_LOOKUP1
#undef HASH_LOOKUP2
#undef KEYTYPE
#undef HASHFUNC
-#undef EQFUNC
-#undef EQMAYBE
-#undef HASMAYBE
-#undef QUICKEQ
+#undef FASTKEY
+#undef QUICK_NE
+#undef QUICK_EQ
+#undef SLOW_EQ
+#undef MAYBE_EQ
+
+#ifdef GOARCH_amd64
+#define CHECKTYPE uint64
+#endif
+#ifdef GOARCH_386
+#define CHECKTYPE uint32
+#endif
+#ifdef GOARCH_arm
+// can't use uint32 on arm because our loads aren't aligned.
+// TODO: use uint32 for arm v6+?
+#define CHECKTYPE uint8
+#endif
#define HASH_LOOKUP1 runtime·mapaccess1_faststr
#define HASH_LOOKUP2 runtime·mapaccess2_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)
+#define FASTKEY(x) ((x).len < 32)
+#define QUICK_NE(x,y) ((x).len != (y).len)
+#define QUICK_EQ(x,y) ((x).str == (y).str)
+#define SLOW_EQ(x,y) runtime·memeq((x).str, (y).str, (x).len)
+#define MAYBE_EQ(x,y) (*(CHECKTYPE*)(x).str == *(CHECKTYPE*)(y).str && *(CHECKTYPE*)((x).str + (x).len - sizeof(CHECKTYPE)) == *(CHECKTYPE*)((y).str + (x).len - sizeof(CHECKTYPE)))
#include "hashmap_fast.c"
static void
byte *v;
uint8 top;
int8 keymaybe;
- bool quickkey;
if(debug) {
runtime·prints("runtime.mapaccess1_fastXXX: map=");
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) {
- if(quickkey && EQFUNC(key, *k)) {
+ 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)) {
value = v;
FLUSH(&value);
return;
}
- if(HASMAYBE && EQMAYBE(key, *k)) {
- // TODO: check if key.str matches. Add EQFUNCFAST?
+ 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. Especially
- // if doing the TODO above.
+ // 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(HASMAYBE && keymaybe >= 0) {
- k = (KEYTYPE*)b->data + keymaybe;
- if(EQFUNC(key, *k)) {
- value = (byte*)((KEYTYPE*)b->data + BUCKETSIZE) + keymaybe * h->valuesize;
- FLUSH(&value);
- return;
+ 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 {
}
do {
for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
- if(b->tophash[i] == top && EQFUNC(key, *k)) {
+ 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;
byte *v;
uint8 top;
int8 keymaybe;
- bool quickkey;
if(debug) {
runtime·prints("runtime.mapaccess2_fastXXX: map=");
check(t, h);
if(h->B == 0) {
- // One-bucket table. Don't hash, just check each bucket entry.
- if(HASMAYBE) {
- keymaybe = -1;
- }
- quickkey = QUICKEQ(key);
+ // One-bucket table. Don't hash, just check each bucket entry.
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) {
- if(quickkey && EQFUNC(key, *k)) {
+ 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;
res = true;
FLUSH(&value);
FLUSH(&res);
return;
}
- if(HASMAYBE && EQMAYBE(key, *k)) {
- // TODO: check if key.str matches. Add EQFUNCFAST?
+ }
+ } 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)) {
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.
+ // 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(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);
- return;
+ 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 {
}
do {
for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
- if(b->tophash[i] == top && EQFUNC(key, *k)) {
+ 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);