diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 9edb54cb5b3cb..1ab480ea895d6 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -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,43 @@ proc hash*(x: pointer): Hash {.inline.} = } } """ + +proc hash*(x: pointer | ref | ptr): 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 + + when defined(js): + let id = getObjectId(cast[pointer](x)) + result = hash(id) + # consistent with c backend and code expecting scrambled + # hashes depending on `nimIntHash1`. else: - result = cast[Hash](cast[uint](x) shr 3) # skip the alignment + result = hash(cast[int](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 +508,10 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash = h = h !& ord(c) result = !$h -proc hash*[T: tuple | object | ref | ptr](x: T): Hash = +proc hash*[T: tuple | object](x: T): Hash = ## Efficient hashing overload. + ## `hash` must be defined for each component of `x`. runnableExamples: - # For tuple | object, `hash` should be defined for each of the field types. type Obj = object x: int y: string @@ -498,21 +522,9 @@ proc hash*[T: tuple | object | ref | ptr](x: T): Hash = # you can define custom hashes for objects (even if they're generic): proc hash(a: Obj2): Hash = hash((a.x)) assert hash(Obj2[float](x: 520, y: "Nim")) == hash(Obj2[float](x: 520, y: "Nim2")) - runnableExamples: - # For ref | ptr, `hash(cast[int](x))` is used. - discard - when T is tuple | object: - for f in fields(x): - result = result !& hash(f) - result = !$result - elif T is ref: result = hash(cast[int](x)) - elif T is ptr: - when defined(nimLegacyHashPtr): - result = hash(cast[cstring](x)) - else: - result = hash(cast[int](x)) - else: - static: doAssert(false, $T) + for f in fields(x): + result = result !& hash(f) + result = !$result proc hash*[A](x: openArray[A]): Hash = ## Efficient hashing of arrays and sequences. diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index 61197e9f0edf2..46f04815d8ad2 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -7,7 +7,9 @@ And we get here 3 ''' joinable: false +targets: "c cpp js" """ + import hashes, sequtils, tables, algorithm proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted @@ -444,3 +446,13 @@ block emptyOrdered: var t2: OrderedTable[int, string] 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