Skip to content

Commit 111092e

Browse files
authored
refactor hash in JS backend (#16863)
1 parent b8e8eaa commit 111092e

File tree

2 files changed

+39
-29
lines changed

2 files changed

+39
-29
lines changed

lib/pure/hashes.nim

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,32 @@ proc hiXorLo(a, b: uint64): uint64 {.inline.} =
123123
else:
124124
result = hiXorLoFallback64(a, b)
125125

126+
when defined(js):
127+
import std/jsbigints
128+
import std/private/jsutils
129+
130+
proc hiXorLoJs(a, b: JsBigInt): JsBigInt =
131+
let
132+
prod = a * b
133+
mask = big"0xffffffffffffffff" # (big"1" shl big"64") - big"1"
134+
result = (prod shr big"64") xor (prod and mask)
135+
136+
template hashWangYiJS(x: JsBigInt): Hash =
137+
let
138+
P0 = big"0xa0761d6478bd642f"
139+
P1 = big"0xe7037ed1a0b428db"
140+
P58 = big"0xeb44accab455d16d" # big"0xeb44accab455d165" xor big"8"
141+
res = hiXorLoJs(hiXorLoJs(P0, x xor P1), P58)
142+
cast[Hash](toNumber(wrapToInt(res, 32)))
143+
144+
template asBigInt(num: float): JsBigInt =
145+
let
146+
x = newArrayBuffer(8)
147+
y = newFloat64Array(x)
148+
z = newBigUint64Array(x)
149+
y[0] = num
150+
z[0]
151+
126152
proc hashWangYi1*(x: int64|uint64|Hash): Hash {.inline.} =
127153
## Wang Yi's hash_v1 for 64-bit ints (see https://github.com/rurban/smhasher for
128154
## 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.} =
139165
result = cast[Hash](h(x))
140166
else:
141167
when defined(js):
142-
asm """
143-
if (typeof BigInt == 'undefined') {
144-
`result` = `x`; // For Node < 10.4, etc. we do the old identity hash
145-
} else { // Otherwise we match the low 32-bits of C/C++ hash
146-
function hi_xor_lo_js(a, b) {
147-
const prod = BigInt(a) * BigInt(b);
148-
const mask = (BigInt(1) << BigInt(64)) - BigInt(1);
149-
return (prod >> BigInt(64)) ^ (prod & mask);
150-
}
151-
const P0 = BigInt(0xa0761d64)<<BigInt(32)|BigInt(0x78bd642f);
152-
const P1 = BigInt(0xe7037ed1)<<BigInt(32)|BigInt(0xa0b428db);
153-
const P58 = BigInt(0xeb44acca)<<BigInt(32)|BigInt(0xb455d165)^BigInt(8);
154-
var res = hi_xor_lo_js(hi_xor_lo_js(P0, BigInt(`x`) ^ P1), P58);
155-
`result` = Number(res & ((BigInt(1) << BigInt(53)) - BigInt(1)));
156-
}"""
157-
result = result and cast[Hash](0xFFFFFFFF)
168+
if hasJsBigInt():
169+
result = hashWangYiJS(big(x))
170+
else:
171+
result = cast[Hash](x) and cast[Hash](0xFFFFFFFF)
158172
else:
159173
result = cast[Hash](h(x))
160174

@@ -213,18 +227,6 @@ else:
213227
## Efficient hashing of integers.
214228
hashWangYi1(uint64(ord(x)))
215229

216-
when defined(js):
217-
proc asBigInt(x: float): int64 =
218-
# result is a `BigInt` type in js, but we cheat the type system
219-
# and say it is a `int64` type.
220-
# TODO: refactor it using bigInt once jsBigInt is ready, pending pr #1640
221-
asm """
222-
const buffer = new ArrayBuffer(8);
223-
const floatBuffer = new Float64Array(buffer);
224-
const uintBuffer = new BigUint64Array(buffer);
225-
floatBuffer[0] = `x`;
226-
`result` = uintBuffer[0];"""
227-
228230
proc hash*(x: float): Hash {.inline.} =
229231
## Efficient hashing of floats.
230232
let y = x + 0.0 # for denormalization
@@ -235,7 +237,7 @@ proc hash*(x: float): Hash {.inline.} =
235237
when not defined(js):
236238
result = hashWangYi1(cast[Hash](y))
237239
else:
238-
result = hashWangYi1(asBigInt(y))
240+
result = hashWangYiJS(asBigInt(y))
239241

240242
# Forward declarations before methods that hash containers. This allows
241243
# containers to contain other containers

lib/std/private/jsutils.nim

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
when defined(js):
2+
import std/jsbigints
3+
24
type
35
ArrayBuffer* = ref object of JsRoot
46
Float64Array* = ref object of JsRoot
57
Uint32Array* = ref object of JsRoot
8+
BigUint64Array* = ref object of JsRoot
69

710
func newArrayBuffer*(n: int): ArrayBuffer {.importjs: "new ArrayBuffer(#)".}
811
func newFloat64Array*(buffer: ArrayBuffer): Float64Array {.importjs: "new Float64Array(#)".}
912
func newUint32Array*(buffer: ArrayBuffer): Uint32Array {.importjs: "new Uint32Array(#)".}
13+
func newBigUint64Array*(buffer: ArrayBuffer): BigUint64Array {.importjs: "new BigUint64Array(#)".}
1014

1115
func `[]`*(arr: Uint32Array, i: int): uint32 {.importjs: "#[#]".}
16+
func `[]`*(arr: BigUint64Array, i: int): JsBigInt {.importjs: "#[#]".}
1217
func `[]=`*(arr: Float64Array, i: int, v: float) {.importjs: "#[#] = #".}
18+
19+
proc hasJsBigInt*(): bool =
20+
asm """`result` = typeof BigInt != 'undefined'"""

0 commit comments

Comments
 (0)