summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorTimothee Cour <timothee.cour2@gmail.com>2021-04-16 05:21:26 -0700
committerGitHub <noreply@github.com>2021-04-16 14:21:26 +0200
commitd19e4310dc16cae2329c55dfa8feb94e0981dc0c (patch)
tree0fd3df253460a37aef246621538e50c351a5a0f3 /lib
parent611b88763f8ec88889b14da31ff220cb47789846 (diff)
downloadNim-d19e4310dc16cae2329c55dfa8feb94e0981dc0c.tar.gz
std/hashes: hash(ref|ptr|pointer) + other improvements (#17731)
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/hashes.nim72
1 files changed, 49 insertions, 23 deletions
diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim
index 3339adba2..2ef0e2454 100644
--- a/lib/pure/hashes.nim
+++ b/lib/pure/hashes.nim
@@ -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,12 +193,22 @@ proc hashData*(data: pointer, size: int): Hash =
     dec(s)
   result = !$h
 
+proc hashIdentity*[T: Ordinal|enum](x: T): Hash {.inline, since: (1, 3).} =
+  ## The identity hash, i.e. `hashIdentity(x) = x`.
+  cast[Hash](ord(x))
+
+when defined(nimIntHash1):
+  proc hash*[T: Ordinal|enum](x: T): Hash {.inline.} =
+    ## Efficient hashing of integers.
+    cast[Hash](ord(x))
+else:
+  proc hash*[T: Ordinal|enum](x: T): Hash {.inline.} =
+    ## Efficient hashing of integers.
+    hashWangYi1(uint64(ord(x)))
+
 when defined(js):
   var objectID = 0
-
-proc hash*(x: pointer): Hash {.inline.} =
-  ## Efficient hashing of pointers.
-  when defined(js):
+  proc getObjectId(x: pointer): int =
     asm """
       if (typeof `x` == "object") {
         if ("_NimID" in `x`)
@@ -209,29 +219,46 @@ proc hash*(x: pointer): Hash {.inline.} =
         }
       }
     """
+
+proc hash*(x: pointer): Hash {.inline.} =
+  ## Efficient `hash` overload.
+  when defined(js):
+    let y = getObjectId(x)
   else:
-    result = cast[Hash](cast[uint](x) shr 3) # skip the alignment
+    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)) !& hash(rawEnv(x))
+    result = hash((rawProc(x), 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))
-
-when defined(nimIntHash1):
-  proc hash*[T: Ordinal|enum](x: T): Hash {.inline.} =
-    ## Efficient hashing of integers.
-    cast[Hash](ord(x))
-else:
-  proc hash*[T: Ordinal|enum](x: T): Hash {.inline.} =
-    ## Efficient hashing of integers.
-    hashWangYi1(uint64(ord(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