Compare commits

...
Sign in to create a new pull request.

3 commits

Author SHA1 Message Date
Joey Yakimowich-Payne
ae108f7a56 WIP 4 2020-02-13 08:02:20 -07:00
Joey Yakimowich-Payne
b3dad3007c WIP 3 2020-02-13 06:28:16 -07:00
Joey Yakimowich-Payne
437461d809 Add more robust hashing based on a paper
The paper in question can be referenced [here](https://courses.cs.washington.edu/courses/cse521/15sp/refs/thorup1.pdf)

This will compute a hash that is almost just as performant as the
previous hashing, but handles the case where if lower numbered bits
are all 0 and the highest bit of the length of the container is less
than the lowest bit of the key, the hash will always be 0.
2020-02-12 14:33:05 -07:00
4 changed files with 69 additions and 15 deletions

View file

@ -355,6 +355,15 @@ proc firstSetBit*(x: SomeInteger): int {.inline, noSideEffect.} =
when sizeof(x) <= 4: result = firstSetBitNim(x.uint32)
else: result = firstSetBitNim(x.uint64)
proc lastSetBit*(x: SomeUnsignedInt): uint {.inline, noSideEffect.} =
## Returns the 1-based index of the most significant set bit of x.
## If x == 0, the result will also be 0
var temp = x
result = 0
while temp != 0:
temp = temp shr 1
result += 1
proc fastLog2*(x: SomeInteger): int {.inline, noSideEffect.} =
## Quickly find the log base 2 of an integer.
## If `x` is zero, when ``noUndefinedBitOpts`` is set, result is -1,

View file

@ -9,6 +9,7 @@
# An ``include`` file which contains common code for
# hash sets and tables.
from bitops import lastSetBit
const
growthFactor = 2
@ -51,7 +52,7 @@ proc rawGetKnownHC[X, A](t: X, key: A, hc: Hash): int {.inline.} =
rawGetKnownHCImpl()
template genHashImpl(key, hc: typed) =
hc = hash(key)
hc = hash(key, targetBits=lastSetBit(maxHash(t).uint))
if hc == 0: # This almost never taken branch should be very predictable.
hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.

View file

@ -113,31 +113,65 @@ proc hash*[T: proc](x: T): Hash {.inline.} =
result = hash(pointer(x))
const
prime = uint(11)
defaultSeedA = (17316035218449499591'u64).uint
defaultSeedB = (1734880652122947187'u64).uint
proc hash*(x: int): Hash {.inline.} =
template computeIntegerHash(x, numBits, seedA, seedB): Hash =
cast[Hash]((seedA*cast[uint](x) + seedB) shr (sizeof(x)*8 - numBits*8))
proc hash*(
x: int,
targetBits: SomeUnsignedInt = sizeof(uint),
seedA = defaultSeedA,
seedB = defaultSeedB
): Hash {.inline.} =
## Efficient hashing of integers.
result = cast[Hash](cast[uint](x) * prime)
result = computeIntegerHash(x, targetBits, seedA, seedB)
proc hash*(x: int64): Hash {.inline.} =
proc hash*(
x: int64,
targetBits: SomeUnsignedInt = sizeof(uint),
seedA = defaultSeedA,
seedB = defaultSeedB
): Hash {.inline.} =
## Efficient hashing of `int64` integers.
result = cast[Hash](cast[uint](x) * prime)
result = computeIntegerHash(x, targetBits, seedA, seedB)
proc hash*(x: uint): Hash {.inline.} =
proc hash*(
x: uint,
targetBits: SomeUnsignedInt = sizeof(uint),
seedA = defaultSeedA,
seedB = defaultSeedB
): Hash {.inline.} =
## Efficient hashing of unsigned integers.
result = cast[Hash](x * prime)
result = computeIntegerHash(x, targetBits, seedA, seedB)
proc hash*(x: uint64): Hash {.inline.} =
proc hash*(
x: uint64,
targetBits: SomeUnsignedInt = sizeof(uint),
seedA = defaultSeedA,
seedB = defaultSeedB
): Hash {.inline.} =
## Efficient hashing of `uint64` integers.
result = cast[Hash](cast[uint](x) * prime)
result = computeIntegerHash(x, targetBits, seedA, seedB)
proc hash*(x: char): Hash {.inline.} =
proc hash*(
x: char,
targetBits: SomeUnsignedInt = sizeof(uint),
seedA = defaultSeedA,
seedB = defaultSeedB
): Hash {.inline.} =
## Efficient hashing of characters.
result = cast[Hash](cast[uint](ord(x)) * prime)
result = computeIntegerHash(ord(x), targetBits, seedA, seedB)
proc hash*[T: Ordinal](x: T): Hash {.inline.} =
proc hash*[T: Ordinal](
x: T,
targetBits: SomeUnsignedInt = sizeof(uint),
seedA = defaultSeedA,
seedB = defaultSeedB
): Hash {.inline.} =
## Efficient hashing of other ordinal types (e.g. enums).
result = cast[Hash](cast[uint](ord(x)) * prime)
result = computeIntegerHash(x, targetBits, seedA, seedB)
proc hash*(x: float): Hash {.inline.} =
## Efficient hashing of floats.

View file

@ -1,4 +1,4 @@
import sets, hashes, algorithm
import sets, hashes, algorithm, bitops
block setEquality:
@ -120,3 +120,13 @@ block hashForOrderdSet:
reversed = !$reversed
doAssert hash(r) == reversed
doAssert hash(s1) != reversed
block hashDoesntHaveLowerBitsCleared:
let num32 = 0xFFFF.uint32
let bits = num32.lastBitSet
let h1 = hash(0xFFFFFFFF00000000'u64, targetBits=bits)
let h2 = hash(0x2345123400000000'u64, targetBits=bits)
doAssert (h1 and num32) != (h2 and num32)