std/hashes: hash(ref|ptr|pointer) + other improvements (#17731)
This commit is contained in:
parent
611b88763f
commit
d19e4310dc
5 changed files with 96 additions and 36 deletions
|
|
@ -182,7 +182,7 @@ proc hashData*(data: pointer, size: int): Hash =
|
||||||
var h: Hash = 0
|
var h: Hash = 0
|
||||||
when defined(js):
|
when defined(js):
|
||||||
var p: cstring
|
var p: cstring
|
||||||
asm """`p` = `Data`;"""
|
asm """`p` = `Data`"""
|
||||||
else:
|
else:
|
||||||
var p = cast[cstring](data)
|
var p = cast[cstring](data)
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -193,32 +193,6 @@ proc hashData*(data: pointer, size: int): Hash =
|
||||||
dec(s)
|
dec(s)
|
||||||
result = !$h
|
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).} =
|
proc hashIdentity*[T: Ordinal|enum](x: T): Hash {.inline, since: (1, 3).} =
|
||||||
## The identity hash, i.e. `hashIdentity(x) = x`.
|
## The identity hash, i.e. `hashIdentity(x) = x`.
|
||||||
cast[Hash](ord(x))
|
cast[Hash](ord(x))
|
||||||
|
|
@ -232,6 +206,59 @@ else:
|
||||||
## Efficient hashing of integers.
|
## Efficient hashing of integers.
|
||||||
hashWangYi1(uint64(ord(x)))
|
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.} =
|
proc hash*(x: float): Hash {.inline.} =
|
||||||
## Efficient hashing of floats.
|
## Efficient hashing of floats.
|
||||||
let y = x + 0.0 # for denormalization
|
let y = x + 0.0 # for denormalization
|
||||||
|
|
@ -484,10 +511,9 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash =
|
||||||
h = h !& ord(c)
|
h = h !& ord(c)
|
||||||
result = !$h
|
result = !$h
|
||||||
|
|
||||||
|
|
||||||
proc hash*[T: tuple | object](x: T): Hash =
|
proc hash*[T: tuple | object](x: T): Hash =
|
||||||
## Efficient hashing of tuples and objects.
|
## Efficient `hash` overload.
|
||||||
## There must be a `hash` proc defined for each of the field types.
|
## `hash` must be defined for each component of `x`.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
type Obj = object
|
type Obj = object
|
||||||
x: int
|
x: int
|
||||||
|
|
|
||||||
|
|
@ -85,3 +85,7 @@ template accept*(a) =
|
||||||
|
|
||||||
template reject*(a) =
|
template reject*(a) =
|
||||||
doAssert not compiles(a)
|
doAssert not compiles(a)
|
||||||
|
|
||||||
|
template disableVm*(body) =
|
||||||
|
when nimvm: discard
|
||||||
|
else: body
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,11 @@ And we get here
|
||||||
3
|
3
|
||||||
'''
|
'''
|
||||||
joinable: false
|
joinable: false
|
||||||
|
targets: "c cpp js"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# xxx wrap in a template to test in VM, see https://github.com/timotheecour/Nim/issues/534#issuecomment-769565033
|
||||||
|
|
||||||
import hashes, sequtils, tables, algorithm
|
import hashes, sequtils, tables, algorithm
|
||||||
|
|
||||||
proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted
|
proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted
|
||||||
|
|
@ -444,3 +448,13 @@ block emptyOrdered:
|
||||||
var t2: OrderedTable[int, string]
|
var t2: OrderedTable[int, string]
|
||||||
doAssert t1 == t2
|
doAssert t1 == t2
|
||||||
|
|
||||||
|
block: # Table[ref, int]
|
||||||
|
type A = ref object
|
||||||
|
x: int
|
||||||
|
var t: OrderedTable[A, int]
|
||||||
|
let a1 = A(x: 3)
|
||||||
|
let a2 = A(x: 3)
|
||||||
|
t[a1] = 10
|
||||||
|
t[a2] = 11
|
||||||
|
doAssert t[a1] == 10
|
||||||
|
doAssert t[a2] == 11
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ discard """
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import std/hashes
|
import std/hashes
|
||||||
|
from stdtest/testutils import disableVm, whenVMorJs
|
||||||
|
|
||||||
when not defined(js) and not defined(cpp):
|
when not defined(js) and not defined(cpp):
|
||||||
block:
|
block:
|
||||||
|
|
@ -177,5 +177,25 @@ proc main() =
|
||||||
doAssert hash(Obj5(t: false, x: 1)) == hash(Obj5(t: true, y: 1))
|
doAssert hash(Obj5(t: false, x: 1)) == hash(Obj5(t: true, y: 1))
|
||||||
doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: true, y: 2))
|
doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: true, y: 2))
|
||||||
|
|
||||||
|
block: # hash(ref|ptr|pointer)
|
||||||
|
var a: array[10, uint8]
|
||||||
|
# disableVm:
|
||||||
|
whenVMorJs:
|
||||||
|
# pending fix proposed in https://github.com/nim-lang/Nim/issues/15952#issuecomment-786312417
|
||||||
|
discard
|
||||||
|
do:
|
||||||
|
assert a[0].addr.hash != a[1].addr.hash
|
||||||
|
assert cast[pointer](a[0].addr).hash == a[0].addr.hash
|
||||||
|
|
||||||
|
block: # hash(ref)
|
||||||
|
type A = ref object
|
||||||
|
x: int
|
||||||
|
let a = A(x: 3)
|
||||||
|
disableVm: # xxx Error: VM does not support 'cast' from tyRef to tyPointer
|
||||||
|
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
|
||||||
|
|
||||||
static: main()
|
static: main()
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ discard """
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import std/strutils
|
import std/strutils
|
||||||
|
from stdtest/testutils import disableVm
|
||||||
# xxx each instance of `disableVm` and `when not defined js:` should eventually be fixed
|
# xxx each instance of `disableVm` and `when not defined js:` should eventually be fixed
|
||||||
|
|
||||||
template rejectParse(e) =
|
template rejectParse(e) =
|
||||||
|
|
@ -12,10 +12,6 @@ template rejectParse(e) =
|
||||||
raise newException(AssertionDefect, "This was supposed to fail: $#!" % astToStr(e))
|
raise newException(AssertionDefect, "This was supposed to fail: $#!" % astToStr(e))
|
||||||
except ValueError: discard
|
except ValueError: discard
|
||||||
|
|
||||||
template disableVm(body) =
|
|
||||||
when nimvm: discard
|
|
||||||
else: body
|
|
||||||
|
|
||||||
template main() =
|
template main() =
|
||||||
block: # strip
|
block: # strip
|
||||||
doAssert strip(" ha ") == "ha"
|
doAssert strip(" ha ") == "ha"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue