refactor hash in JS backend (#16863)
This commit is contained in:
parent
b8e8eaae66
commit
111092e8aa
2 changed files with 39 additions and 29 deletions
|
|
@ -123,6 +123,32 @@ proc hiXorLo(a, b: uint64): uint64 {.inline.} =
|
||||||
else:
|
else:
|
||||||
result = hiXorLoFallback64(a, b)
|
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.} =
|
proc hashWangYi1*(x: int64|uint64|Hash): Hash {.inline.} =
|
||||||
## Wang Yi's hash_v1 for 64-bit ints (see https://github.com/rurban/smhasher for
|
## 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.
|
## 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))
|
result = cast[Hash](h(x))
|
||||||
else:
|
else:
|
||||||
when defined(js):
|
when defined(js):
|
||||||
asm """
|
if hasJsBigInt():
|
||||||
if (typeof BigInt == 'undefined') {
|
result = hashWangYiJS(big(x))
|
||||||
`result` = `x`; // For Node < 10.4, etc. we do the old identity hash
|
else:
|
||||||
} else { // Otherwise we match the low 32-bits of C/C++ hash
|
result = cast[Hash](x) and cast[Hash](0xFFFFFFFF)
|
||||||
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)
|
|
||||||
else:
|
else:
|
||||||
result = cast[Hash](h(x))
|
result = cast[Hash](h(x))
|
||||||
|
|
||||||
|
|
@ -213,18 +227,6 @@ else:
|
||||||
## Efficient hashing of integers.
|
## Efficient hashing of integers.
|
||||||
hashWangYi1(uint64(ord(x)))
|
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.} =
|
proc hash*(x: float): Hash {.inline.} =
|
||||||
## Efficient hashing of floats.
|
## Efficient hashing of floats.
|
||||||
let y = x + 0.0 # for denormalization
|
let y = x + 0.0 # for denormalization
|
||||||
|
|
@ -235,7 +237,7 @@ proc hash*(x: float): Hash {.inline.} =
|
||||||
when not defined(js):
|
when not defined(js):
|
||||||
result = hashWangYi1(cast[Hash](y))
|
result = hashWangYi1(cast[Hash](y))
|
||||||
else:
|
else:
|
||||||
result = hashWangYi1(asBigInt(y))
|
result = hashWangYiJS(asBigInt(y))
|
||||||
|
|
||||||
# Forward declarations before methods that hash containers. This allows
|
# Forward declarations before methods that hash containers. This allows
|
||||||
# containers to contain other containers
|
# containers to contain other containers
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,20 @@
|
||||||
when defined(js):
|
when defined(js):
|
||||||
|
import std/jsbigints
|
||||||
|
|
||||||
type
|
type
|
||||||
ArrayBuffer* = ref object of JsRoot
|
ArrayBuffer* = ref object of JsRoot
|
||||||
Float64Array* = ref object of JsRoot
|
Float64Array* = ref object of JsRoot
|
||||||
Uint32Array* = ref object of JsRoot
|
Uint32Array* = ref object of JsRoot
|
||||||
|
BigUint64Array* = ref object of JsRoot
|
||||||
|
|
||||||
func newArrayBuffer*(n: int): ArrayBuffer {.importjs: "new ArrayBuffer(#)".}
|
func newArrayBuffer*(n: int): ArrayBuffer {.importjs: "new ArrayBuffer(#)".}
|
||||||
func newFloat64Array*(buffer: ArrayBuffer): Float64Array {.importjs: "new Float64Array(#)".}
|
func newFloat64Array*(buffer: ArrayBuffer): Float64Array {.importjs: "new Float64Array(#)".}
|
||||||
func newUint32Array*(buffer: ArrayBuffer): Uint32Array {.importjs: "new Uint32Array(#)".}
|
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: Uint32Array, i: int): uint32 {.importjs: "#[#]".}
|
||||||
|
func `[]`*(arr: BigUint64Array, i: int): JsBigInt {.importjs: "#[#]".}
|
||||||
func `[]=`*(arr: Float64Array, i: int, v: float) {.importjs: "#[#] = #".}
|
func `[]=`*(arr: Float64Array, i: int, v: float) {.importjs: "#[#] = #".}
|
||||||
|
|
||||||
|
proc hasJsBigInt*(): bool =
|
||||||
|
asm """`result` = typeof BigInt != 'undefined'"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue