From 63f1c38f4e0c6f8700a8877d5f3f6f2ac0fc16e6 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 25 Feb 2021 23:45:37 -0800 Subject: [PATCH] hashes: support object default hash (#17175) --- changelog.md | 4 +++- lib/pure/hashes.nim | 30 ++++++++++++-------------- tests/stdlib/thashes.nim | 46 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/changelog.md b/changelog.md index 6b2c412f06..db5b3c14b4 100644 --- a/changelog.md +++ b/changelog.md @@ -173,14 +173,16 @@ provided by the operating system. dumping (on select signals) and notifying the parent process about the cause of termination. -- Added `strip` and `setSlice` to `std/strbasics`. +- `hashes.hash` now supports `object`, but can be overloaded. +- Added `strip` and `setSlice` to `std/strbasics`. - Added to `wrapnils` an option-like API via `??.`, `isSome`, `get`. - `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"` and `$none(int)` to `"none(int)"` instead of `"None[int]"`. + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 54e083d251..c526c976fe 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -54,20 +54,6 @@ runnableExamples: ## **Note:** If the type has a `==` operator, the following must hold: ## If two values compare equal, their hashes must also be equal. ## -## You can hash an object by all of its fields with the `fields` iterator: -runnableExamples: - proc hash(x: object): Hash = - for f in fields(x): - result = result !& hash(f) - result = !$result - - type - Obj = object - x: int - y: string - - doAssert hash(Obj(x: 520, y: "Nim")) != hash(Obj(x: 520, y: "Nim2")) - ## See also ## ======== ## * `md5 module `_ for the MD5 checksum algorithm @@ -499,14 +485,24 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash = result = !$h -proc hash*[T: tuple](x: T): Hash = - ## Efficient hashing of tuples. +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. + runnableExamples: + type Obj = object + x: int + y: string + type Obj2[T] = object + x: int + y: string + assert hash(Obj(x: 520, y: "Nim")) != hash(Obj(x: 520, y: "Nim2")) + # 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")) for f in fields(x): result = result !& hash(f) result = !$result - proc hash*[A](x: openArray[A]): Hash = ## Efficient hashing of arrays and sequences. ## There must be a `hash` proc defined for the element type `A`. diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index ce7bb7d8c2..a4487c8c0b 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -130,5 +130,51 @@ proc main() = # run at CT, expecting c semantics. discard + block: # hash(object) + type + Obj = object + x: int + y: string + Obj2[T] = object + x: int + y: string + Obj3 = object + x: int + y: string + Obj4 = object + case t: bool + of false: + x: int + of true: + y: int + z: int + Obj5 = object + case t: bool + of false: + x: int + of true: + y: int + z: int + + proc hash(a: Obj2): Hash = hash(a.x) + proc hash(a: Obj3): Hash = hash((a.x,)) + proc hash(a: Obj5): Hash = + case a.t + of false: hash(a.x) + of true: hash(a.y) + + doAssert hash(Obj(x: 520, y: "Nim")) != hash(Obj(x: 520, y: "Nim2")) + doAssert hash(Obj2[float](x: 520, y: "Nim")) == hash(Obj2[float](x: 520, y: "Nim2")) + doAssert hash(Obj2[float](x: 520, y: "Nim")) != hash(Obj2[float](x: 521, y: "Nim2")) + doAssert hash(Obj3(x: 520, y: "Nim")) == hash(Obj3(x: 520, y: "Nim2")) + + doAssert hash(Obj4(t: false, x: 1)) == hash(Obj4(t: false, x: 1)) + doAssert hash(Obj4(t: false, x: 1)) != hash(Obj4(t: false, x: 2)) + doAssert hash(Obj4(t: false, x: 1)) != hash(Obj4(t: true, y: 1)) + + doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: false, x: 2)) + 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)) + static: main() main()