std/hashes: hash(ref|ptr|pointer) + other improvements (#17731)

This commit is contained in:
Timothee Cour 2021-04-16 05:21:26 -07:00 • committed by GitHub
commit d19e4310dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 96 additions and 36 deletions

View file

@ -182,7 +182,7 @@ proc hashData*(data: pointer, size: int): Hash =
var h: Hash = 0
when defined(js):
var p: cstring
asm """`p` = `Data`;"""
asm """`p` = `Data`"""
else:
var p = cast[cstring](data)
var i = 0
@ -193,32 +193,6 @@ proc hashData*(data: pointer, size: int): Hash =
dec(s)
result = !$h
when defined(js):
var objectID = 0
proc hash*(x: pointer): Hash {.inline.} =
## Efficient hashing of pointers.
when defined(js):
asm """
if (typeof `x` == "object") {
if ("_NimID" in `x`)
`result` = `x`["_NimID"];
else {
`result` = ++`objectID`;
`x`["_NimID"] = `result`;
}
}
"""
else:
result = cast[Hash](cast[uint](x) shr 3) # skip the alignment
proc hash*[T: proc](x: T): Hash {.inline.} =
## Efficient hashing of proc vars. Closures are supported too.
when T is "closure":
result = hash(rawProc(x)) !& hash(rawEnv(x))
else:
result = hash(pointer(x))
proc hashIdentity*[T: Ordinal|enum](x: T): Hash {.inline, since: (1, 3).} =
## The identity hash, i.e. `hashIdentity(x) = x`.
cast[Hash](ord(x))
@ -232,6 +206,59 @@ else:
## Efficient hashing of integers.
hashWangYi1(uint64(ord(x)))
when defined(js):
var objectID = 0
proc getObjectId(x: pointer): int =
asm """
if (typeof `x` == "object") {
if ("_NimID" in `x`)
`result` = `x`["_NimID"];
else {
`result` = ++`objectID`;
`x`["_NimID"] = `result`;
}
}
"""
proc hash*(x: pointer): Hash {.inline.} =
## Efficient `hash` overload.
when defined(js):
let y = getObjectId(x)
else:
let y = cast[int](x)
hash(y) # consistent with code expecting scrambled hashes depending on `nimIntHash1`.
proc hash*[T](x: ref[T] | ptr[T]): Hash {.inline.} =
## Efficient `hash` overload.
runnableExamples:
var a: array[10, uint8]
assert a[0].addr.hash != a[1].addr.hash
assert cast[pointer](a[0].addr).hash == a[0].addr.hash
runnableExamples:
type A = ref object
x: int
let a = A(x: 3)
let ha = a.hash
assert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`.
a.x = 4
assert ha == a.hash # the hash only depends on the address
runnableExamples:
# you can overload `hash` if you want to customize semantics
type A[T] = ref object
x, y: T
proc hash(a: A): Hash = hash(a.x)
assert A[int](x: 3, y: 4).hash == A[int](x: 3, y: 5).hash
# xxx pending bug #17733, merge as `proc hash*(pointer | ref | ptr): Hash`
# or `proc hash*[T: ref | ptr](x: T): Hash`
hash(cast[pointer](x))
proc hash*[T: proc](x: T): Hash {.inline.} =
## Efficient hashing of proc vars. Closures are supported too.
when T is "closure":
result = hash((rawProc(x), rawEnv(x)))
else:
result = hash(pointer(x))
proc hash*(x: float): Hash {.inline.} =
## Efficient hashing of floats.
let y = x + 0.0 # for denormalization
@ -484,10 +511,9 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash =
h = h !& ord(c)
result = !$h
proc hash*[T: tuple | object](x: T): Hash =
## Efficient hashing of tuples and objects.
## There must be a `hash` proc defined for each of the field types.
## Efficient `hash` overload.
## `hash` must be defined for each component of `x`.
runnableExamples:
type Obj = object
x: int