Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor hash in JS backend #16863

Merged
merged 6 commits into from
Jan 30, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 32 additions & 29 deletions lib/pure/hashes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,33 @@ 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" # mask = (big"1" shl big"64") - big"1"
result = (prod shr big"64") xor (prod and mask)
ringabout marked this conversation as resolved.
Show resolved Hide resolved

template hashWangYiJS(x: JsBigInt): Hash =
let
P0 = big"0xa0761d6478bd642f"
P1 = big"0xe7037ed1a0b428db"
P58 = big"0xeb44accab455d165" xor big"8"
ringabout marked this conversation as resolved.
Show resolved Hide resolved
res = hiXorLoJs(hiXorLoJs(P0, x xor P1), P58)
maxSafeInterger = (big"1" shl big"53") - big"1"
ringabout marked this conversation as resolved.
Show resolved Hide resolved
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
cast[Hash](toNumber((res and maxSafeInterger))) and cast[Hash](0xFFFFFFFF)

template asBigInt(x: float): JsBigInt =
ringabout marked this conversation as resolved.
Show resolved Hide resolved
let
buffer = newArrayBuffer(8)
floatBuffer = newFloat64Array(buffer)
uintBuffer = newBigUint64Array(buffer)
floatBuffer[0] = x
uintBuffer[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.
Expand All @@ -139,22 +166,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))

Expand Down Expand Up @@ -213,18 +228,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
Expand All @@ -235,7 +238,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
Expand Down
8 changes: 8 additions & 0 deletions lib/std/private/jsutils.nim
Original file line number Diff line number Diff line change
@@ -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'"""