summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/hashes.nim60
-rw-r--r--lib/std/private/jsutils.nim8
2 files changed, 39 insertions, 29 deletions
diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim
index c6f7efc69..7ac62174c 100644
--- a/lib/pure/hashes.nim
+++ b/lib/pure/hashes.nim
@@ -123,6 +123,32 @@ proc hiXorLo(a, b: uint64): uint64 {.inline.} =
     else:
       result = hiXorLoFallback64(a, b)
 
+when defined(js):
+  import std/jsbigints
+  import std/private/jsutils
+
+  proc hiXorLoJs(a, b: JsBigInt): JsBigInt =
+    let
+      prod = a * b
+      mask = big"0xffffffffffffffff" # (big"1" shl big"64") - big"1"
+    result = (prod shr big"64") xor (prod and mask)
+
+  template hashWangYiJS(x: JsBigInt): Hash =
+    let
+      P0 = big"0xa0761d6478bd642f"
+      P1 = big"0xe7037ed1a0b428db"
+      P58 = big"0xeb44accab455d16d" # big"0xeb44accab455d165" xor big"8"
+      res = hiXorLoJs(hiXorLoJs(P0, x xor P1), P58)
+    cast[Hash](toNumber(wrapToInt(res, 32)))
+
+  template asBigInt(num: float): JsBigInt =
+    let
+      x = newArrayBuffer(8)
+      y = newFloat64Array(x)
+      z = newBigUint64Array(x)
+    y[0] = num
+    z[0]
+
 proc hashWangYi1*(x: int64|uint64|Hash): Hash {.inline.} =
   ## Wang Yi's hash_v1 for 64-bit ints (see https://github.com/rurban/smhasher for
   ## more details). This passed all scrambling tests in Spring 2019 and is simple.
@@ -139,22 +165,10 @@ proc hashWangYi1*(x: int64|uint64|Hash): Hash {.inline.} =
       result = cast[Hash](h(x))
   else:
     when defined(js):
-      asm """
-        if (typeof BigInt == 'undefined') {
-          `result` = `x`; // For Node < 10.4, etc. we do the old identity hash
-        } else {          // Otherwise we match the low 32-bits of C/C++ hash
-          function hi_xor_lo_js(a, b) {
-            const prod = BigInt(a) * BigInt(b);
-            const mask = (BigInt(1) << BigInt(64)) - BigInt(1);
-            return (prod >> BigInt(64)) ^ (prod & mask);
-          }
-          const P0  = BigInt(0xa0761d64)<<BigInt(32)|BigInt(0x78bd642f);
-          const P1  = BigInt(0xe7037ed1)<<BigInt(32)|BigInt(0xa0b428db);
-          const P58 = BigInt(0xeb44acca)<<BigInt(32)|BigInt(0xb455d165)^BigInt(8);
-          var res   = hi_xor_lo_js(hi_xor_lo_js(P0, BigInt(`x`) ^ P1), P58);
-          `result`  = Number(res & ((BigInt(1) << BigInt(53)) - BigInt(1)));
-        }"""
-      result = result and cast[Hash](0xFFFFFFFF)
+      if hasJsBigInt():
+        result = hashWangYiJS(big(x))
+      else:
+        result = cast[Hash](x) and cast[Hash](0xFFFFFFFF)
     else:
       result = cast[Hash](h(x))
 
@@ -213,18 +227,6 @@ else:
     ## Efficient hashing of integers.
     hashWangYi1(uint64(ord(x)))
 
-when defined(js):
-  proc asBigInt(x: float): int64 =
-    # result is a `BigInt` type in js, but we cheat the type system
-    # and say it is a `int64` type.
-    # TODO: refactor it using bigInt once jsBigInt is ready, pending pr #1640
-    asm """
-    const buffer = new ArrayBuffer(8);
-    const floatBuffer = new Float64Array(buffer);
-    const uintBuffer = new BigUint64Array(buffer);
-    floatBuffer[0] = `x`;
-    `result` = uintBuffer[0];"""
-
 proc hash*(x: float): Hash {.inline.} =
   ## Efficient hashing of floats.
   let y = x + 0.0 # for denormalization
@@ -235,7 +237,7 @@ proc hash*(x: float): Hash {.inline.} =
     when not defined(js):
       result = hashWangYi1(cast[Hash](y))
     else:
-      result = hashWangYi1(asBigInt(y))
+      result = hashWangYiJS(asBigInt(y))
 
 # Forward declarations before methods that hash containers. This allows
 # containers to contain other containers
diff --git a/lib/std/private/jsutils.nim b/lib/std/private/jsutils.nim
index cc463ac74..296533a49 100644
--- a/lib/std/private/jsutils.nim
+++ b/lib/std/private/jsutils.nim
@@ -1,12 +1,20 @@
 when defined(js):
+  import std/jsbigints
+
   type
     ArrayBuffer* = ref object of JsRoot
     Float64Array* = ref object of JsRoot
     Uint32Array* = ref object of JsRoot
+    BigUint64Array* = ref object of JsRoot
 
   func newArrayBuffer*(n: int): ArrayBuffer {.importjs: "new ArrayBuffer(#)".}
   func newFloat64Array*(buffer: ArrayBuffer): Float64Array {.importjs: "new Float64Array(#)".}
   func newUint32Array*(buffer: ArrayBuffer): Uint32Array {.importjs: "new Uint32Array(#)".}
+  func newBigUint64Array*(buffer: ArrayBuffer): BigUint64Array {.importjs: "new BigUint64Array(#)".}
 
   func `[]`*(arr: Uint32Array, i: int): uint32 {.importjs: "#[#]".}
+  func `[]`*(arr: BigUint64Array, i: int): JsBigInt {.importjs: "#[#]".}
   func `[]=`*(arr: Float64Array, i: int, v: float) {.importjs: "#[#] = #".}
+
+  proc hasJsBigInt*(): bool =
+    asm """`result` = typeof BigInt != 'undefined'"""