diff --git a/quickjs.c b/quickjs.c index 9a834a78d..ffb91df81 100644 --- a/quickjs.c +++ b/quickjs.c @@ -52665,6 +52665,16 @@ static JSValueConst map_normalize_key_const(JSContext *ctx, JSValueConst key) } /* XXX: better hash ? */ +static uint32_t map_hash_u64(uint64_t h) +{ + h ^= h >> 33; + h *= 0xff51afd7ed558ccdULL; + h ^= h >> 33; + h *= 0xc4ceb9fe1a85ec53ULL; + h ^= h >> 33; + return (uint32_t)(h ^ (h >> 32)); +} + static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) { uint32_t tag = JS_VALUE_GET_NORM_TAG(key); @@ -52678,10 +52688,10 @@ static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) h = JS_VALUE_GET_INT(key); break; case JS_TAG_STRING: - h = hash_string(JS_VALUE_GET_STRING(key), 0); + h = hash_string(JS_VALUE_GET_STRING(key), JS_VALUE_GET_STRING(key)->len); break; case JS_TAG_STRING_ROPE: - h = hash_string_rope(key, 0); + h = hash_string_rope(key, JS_VALUE_GET_STRING_ROPE(key)->len); break; case JS_TAG_OBJECT: case JS_TAG_SYMBOL: @@ -52704,7 +52714,7 @@ 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; + h = map_hash_u64(u.u64); return h ^= JS_TAG_FLOAT64; default: h = 0; diff --git a/tests/test_builtin.js b/tests/test_builtin.js index fb93e6206..f22d35e6d 100644 --- a/tests/test_builtin.js +++ b/tests/test_builtin.js @@ -959,6 +959,23 @@ function test_symbol() assert(b.toString(), "Symbol(aaa)"); } +function float64_from_words(hi, lo) +{ + var buffer, view; + buffer = new ArrayBuffer(8); + view = new DataView(buffer); + view.setUint32(0, hi); + view.setUint32(4, lo); + return view.getFloat64(0); +} + +function float64_map_hash_regression_value(i) +{ + var word; + word = (0x3ff00000 + (i << 10)) >>> 0; + return float64_from_words(word, word); +} + function test_map() { var a, i, n, tab, o, v; @@ -973,6 +990,54 @@ function test_map() assert(a.get(-2147483647 - 1), 1); assert(a.get(-2147483647.5 - 0.5), 1); + a = new Map(); + tab = ["k", "\0k", "\0\0k"]; + for(i = 0; i < tab.length; i++) { + a.set(tab[i], i); + } + assert(a.size, tab.length); + for(i = 0; i < tab.length; i++) { + assert(a.get(tab[i]), i); + } + + a = new Map(); + tab = [ + [0, "zero"], + [float64_from_words(0x00000001, 0x00000001), "subnormal"], + [float64_from_words(0x3ff00000, 0x3ff00000), "normal1"], + [float64_from_words(0x40080000, 0x40080000), "normal2"], + ]; + for(i = 0; i < tab.length; i++) { + a.set(tab[i][0], tab[i][1]); + } + assert(a.size, tab.length); + for(i = 0; i < tab.length; i++) { + assert(a.get(tab[i][0]), tab[i][1]); + } + + a = new Map(); + n = 4096; + for(i = 0; i < n; i++) { + v = float64_map_hash_regression_value(i); + a.set(v, i); + } + assert(a.size, n); + for(i = 0; i < n; i++) { + v = float64_map_hash_regression_value(i); + assert(a.get(v), i); + } + + a = new Map(); + a.set(NaN, 1); + a.set(Number.NaN, 2); + assert(a.size, 1); + assert(a.get(NaN), 2); + a.set(-0, 3); + a.set(+0, 4); + assert(a.size, 2); + assert(a.get(-0), 4); + assert(a.get(+0), 4); + a = new Map(); tab = []; for(i = 0; i < n; i++) { @@ -1034,6 +1099,7 @@ function test_weak_map() function test_set() { + var i, set, tab; const iter = { a: [4, 5, 6], nextCalls: 0, @@ -1101,6 +1167,41 @@ function test_set() keys() { return [].values() }, }) } + + tab = ["k", "\0k", "\0\0k"]; + set = new Set(tab); + assert(set.size, tab.length); + for(i = 0; i < tab.length; i++) { + assert(set.has(tab[i])); + } + + tab = [ + 0, + float64_from_words(0x00000001, 0x00000001), + float64_from_words(0x3ff00000, 0x3ff00000), + float64_from_words(0x40080000, 0x40080000), + ]; + set = new Set(tab); + assert(set.size, tab.length); + for(i = 0; i < tab.length; i++) { + assert(set.has(tab[i])); + } + + tab = []; + for(i = 0; i < 4096; i++) { + tab.push(float64_map_hash_regression_value(i)); + } + set = new Set(tab); + assert(set.size, tab.length); + for(i = 0; i < tab.length; i++) { + assert(set.has(tab[i])); + } + + set = new Set([NaN, Number.NaN, -0, +0]); + assert(set.size, 2); + assert(set.has(NaN)); + assert(set.has(-0)); + assert(set.has(+0)); } function test_weak_set()