diff --git a/quickjs.c b/quickjs.c index 6b5541e28..5cc08a6e6 100644 --- a/quickjs.c +++ b/quickjs.c @@ -53369,6 +53369,18 @@ static JSValueConst map_normalize_key_const(JSContext *ctx, JSValueConst key) return safe_const(map_normalize_key(ctx, unsafe_unconst(key))); } +/* lowbias32 integer finalizer. A bijection on uint32_t that mixes the high + bits into the low bits, which are the ones that select the bucket. */ +static inline uint32_t map_hash_mix(uint32_t h) +{ + h ^= h >> 16; + h *= 0x7feb352d; + h ^= h >> 15; + h *= 0x846ca68b; + h ^= h >> 16; + return h; +} + /* XXX: better hash ? */ static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) { @@ -53391,6 +53403,8 @@ static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) case JS_TAG_OBJECT: case JS_TAG_SYMBOL: h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163; + /* heap pointers are aligned, their low bits are always zero */ + h = map_hash_mix(h); break; case JS_TAG_INT: d = JS_VALUE_GET_INT(key); @@ -53409,7 +53423,8 @@ static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) d = NAN; hash_float64: u.d = d; - h = (u.u32[0] ^ u.u32[1]) * 3163; + /* small integers all have the same low bits here */ + h = map_hash_mix((u.u32[0] ^ u.u32[1]) * 3163); tag = JS_TAG_FLOAT64; break; default: diff --git a/tests/test_builtin.js b/tests/test_builtin.js index 55931fb48..e220c7dbd 100644 --- a/tests/test_builtin.js +++ b/tests/test_builtin.js @@ -990,6 +990,14 @@ function test_map() for (var i = 0; i < n; i++) { a.set(i, i); } + for (var i = 0; i < n; i++) { + assert(a.get(i), i); + } + assert(a.get(-0), 0); + assert(a.get(1.0), 1); + a.set(NaN, 2); + assert(a.get(0 / 0), 2); + assert(a.size, n + 1); a.set(-2147483648, 1); assert(a.get(-2147483648), 1); assert(a.get(-2147483647 - 1), 1);