diff --git a/changelog.md b/changelog.md index 1fc6d19c2..d75e8d17b 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,9 @@ - `uri` adds Data URI Base64, implements RFC-2397. - Add [DOM Parser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) to the `dom` module for the JavaScript target. +- The default hash for `Ordinal` has changed to something more bit-scrambling. + `import hashes; proc hash(x: myInt): Hash = hashIdentity(x)` recovers the old + one in an instantiation context while `-d:nimIntHash1` recovers it globally. ## Language changes diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 8c04a05e3..11639df3b 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -334,8 +334,7 @@ proc pop*[A](s: var HashSet[A]): A = ## * `clear proc <#clear,HashSet[A]>`_ runnableExamples: var s = toHashSet([2, 1]) - assert s.pop == 1 - assert s.pop == 2 + assert [s.pop, s.pop] in [[1, 2], [2,1]] # order unspecified doAssertRaises(KeyError, echo s.pop) for h in 0 .. high(s.data): @@ -1008,7 +1007,7 @@ when isMainModule and not defined(release): block toSeqAndString: var a = toHashSet([2, 7, 5]) - var b = initHashSet[int]() + var b = initHashSet[int](rightSize(a.len)) for x in [2, 7, 5]: b.incl(x) assert($a == $b) #echo a diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index d799fb062..d6a7c7e41 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -70,6 +70,66 @@ proc `!$`*(h: Hash): Hash {.inline.} = res = res + res shl 15 result = cast[Hash](res) +proc hiXorLoFallback64(a, b: uint64): uint64 {.inline.} = + let # Fall back in 64-bit arithmetic + aH = a shr 32 + aL = a and 0xFFFFFFFF'u64 + bH = b shr 32 + bL = b and 0xFFFFFFFF'u64 + rHH = aH * bH + rHL = aH * bL + rLH = aL * bH + rLL = aL * bL + t = rLL + (rHL shl 32) + var c = if t < rLL: 1'u64 else: 0'u64 + let lo = t + (rLH shl 32) + c += (if lo < t: 1'u64 else: 0'u64) + let hi = rHH + (rHL shr 32) + (rLH shr 32) + c + return hi xor lo + +proc hiXorLo(a, b: uint64): uint64 {.inline.} = + # Xor of high & low 8B of full 16B product + when nimvm: + result = hiXorLoFallback64(a, b) # `result =` is necessary here. + else: + when Hash.sizeof < 8: + result = hiXorLoFallback64(a, b) + elif defined(gcc) or defined(llvm_gcc) or defined(clang): + {.emit: """__uint128_t r = a; r *= b; `result` = (r >> 64) ^ r;""".} + elif defined(windows) and not defined(tcc): + {.emit: """a = _umul128(a, b, &b); `result` = a ^ b;""".} + else: + result = hiXorLoFallback64(a, b) + +proc hashWangYi1*(x: int64|uint64|Hash): Hash {.inline.} = + ## Wang Yi's hash_v1 for 8B int. https://github.com/rurban/smhasher has more + ## details. This passed all scrambling tests in Spring 2019 and is simple. + ## NOTE: It's ok to define ``proc(x: int16): Hash = hashWangYi1(Hash(x))``. + const P0 = 0xa0761d6478bd642f'u64 + const P1 = 0xe7037ed1a0b428db'u64 + const P58 = 0xeb44accab455d165'u64 xor 8'u64 + when nimvm: + cast[Hash](hiXorLo(hiXorLo(P0, uint64(x) xor P1), P58)) + 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)<