Improve documentation for the hashes module (#16720)
* Improve documentation for hashes * Fix runnableExamples * Apply suggestions
This commit is contained in:
parent
fc9cf2088d
commit
7b632f9ccb
1 changed files with 73 additions and 51 deletions
|
|
@ -10,51 +10,68 @@
|
||||||
## This module implements efficient computations of hash values for diverse
|
## This module implements efficient computations of hash values for diverse
|
||||||
## Nim types. All the procs are based on these two building blocks:
|
## Nim types. All the procs are based on these two building blocks:
|
||||||
## - `!& proc <#!&,Hash,int>`_ used to start or mix a hash value, and
|
## - `!& proc <#!&,Hash,int>`_ used to start or mix a hash value, and
|
||||||
## - `!$ proc <#!$,Hash>`_ used to *finish* the hash value.
|
## - `!$ proc <#!$,Hash>`_ used to finish the hash value.
|
||||||
##
|
##
|
||||||
## If you want to implement hash procs for your custom types,
|
## If you want to implement hash procs for your custom types,
|
||||||
## you will end up writing the following kind of skeleton of code:
|
## you will end up writing the following kind of skeleton of code:
|
||||||
|
|
||||||
|
runnableExamples:
|
||||||
|
type
|
||||||
|
Something = object
|
||||||
|
foo: int
|
||||||
|
bar: string
|
||||||
|
|
||||||
|
iterator items(x: Something): int =
|
||||||
|
yield hash(x.foo)
|
||||||
|
yield hash(x.bar)
|
||||||
|
|
||||||
|
proc hash(x: Something): Hash =
|
||||||
|
## Computes a Hash from `x`.
|
||||||
|
var h: Hash = 0
|
||||||
|
# Iterate over parts of `x`.
|
||||||
|
for xAtom in x:
|
||||||
|
# Mix the atom with the partial hash.
|
||||||
|
h = h !& xAtom
|
||||||
|
# Finish the hash.
|
||||||
|
result = !$h
|
||||||
|
|
||||||
|
## If your custom types contain fields for which there already is a `hash` proc,
|
||||||
|
## you can simply hash together the hash values of the individual fields:
|
||||||
|
|
||||||
|
runnableExamples:
|
||||||
|
type
|
||||||
|
Something = object
|
||||||
|
foo: int
|
||||||
|
bar: string
|
||||||
|
|
||||||
|
proc hash(x: Something): Hash =
|
||||||
|
## Computes a Hash from `x`.
|
||||||
|
var h: Hash = 0
|
||||||
|
h = h !& hash(x.foo)
|
||||||
|
h = h !& hash(x.bar)
|
||||||
|
result = !$h
|
||||||
|
|
||||||
|
## **Note:** If the type has a `==` operator, the following must hold:
|
||||||
|
## If two values compare equal, their hashes must also be equal.
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## See also
|
||||||
## proc hash(x: Something): Hash =
|
## ========
|
||||||
## ## Computes a Hash from `x`.
|
## * `md5 module <md5.html>`_ for the MD5 checksum algorithm
|
||||||
## var h: Hash = 0
|
## * `base64 module <base64.html>`_ for a Base64 encoder and decoder
|
||||||
## # Iterate over parts of `x`.
|
## * `std/sha1 module <sha1.html>`_ for a SHA-1 encoder and decoder
|
||||||
## for xAtom in x:
|
|
||||||
## # Mix the atom with the partial hash.
|
|
||||||
## h = h !& xAtom
|
|
||||||
## # Finish the hash.
|
|
||||||
## result = !$h
|
|
||||||
##
|
|
||||||
## If your custom types contain fields for which there already is a hash proc,
|
|
||||||
## like for example objects made up of ``strings``, you can simply hash
|
|
||||||
## together the hash value of the individual fields:
|
|
||||||
##
|
|
||||||
## .. code-block:: Nim
|
|
||||||
## proc hash(x: Something): Hash =
|
|
||||||
## ## Computes a Hash from `x`.
|
|
||||||
## var h: Hash = 0
|
|
||||||
## h = h !& hash(x.foo)
|
|
||||||
## h = h !& hash(x.bar)
|
|
||||||
## result = !$h
|
|
||||||
##
|
|
||||||
## **See also:**
|
|
||||||
## * `md5 module <md5.html>`_ for MD5 checksum algorithm
|
|
||||||
## * `base64 module <base64.html>`_ for a base64 encoder and decoder
|
|
||||||
## * `std/sha1 module <sha1.html>`_ for a sha1 encoder and decoder
|
|
||||||
## * `tables module <tables.html>`_ for hash tables
|
## * `tables module <tables.html>`_ for hash tables
|
||||||
|
|
||||||
import std/private/since
|
import std/private/since
|
||||||
|
|
||||||
type
|
type
|
||||||
Hash* = int ## A hash value. Hash tables using these values should
|
Hash* = int ## A hash value. Hash tables using these values should
|
||||||
## always have a size of a power of two and can use the ``and``
|
## always have a size of a power of two so they can use the `and`
|
||||||
## operator instead of ``mod`` for truncation of the hash value.
|
## operator instead of `mod` for truncation of the hash value.
|
||||||
|
|
||||||
proc `!&`*(h: Hash, val: int): Hash {.inline.} =
|
proc `!&`*(h: Hash, val: int): Hash {.inline.} =
|
||||||
## Mixes a hash value `h` with `val` to produce a new hash value.
|
## Mixes a hash value `h` with `val` to produce a new hash value.
|
||||||
##
|
##
|
||||||
## This is only needed if you need to implement a hash proc for a new datatype.
|
## This is only needed if you need to implement a `hash` proc for a new datatype.
|
||||||
let h = cast[uint](h)
|
let h = cast[uint](h)
|
||||||
let val = cast[uint](val)
|
let val = cast[uint](val)
|
||||||
var res = h + val
|
var res = h + val
|
||||||
|
|
@ -65,7 +82,7 @@ proc `!&`*(h: Hash, val: int): Hash {.inline.} =
|
||||||
proc `!$`*(h: Hash): Hash {.inline.} =
|
proc `!$`*(h: Hash): Hash {.inline.} =
|
||||||
## Finishes the computation of the hash value.
|
## Finishes the computation of the hash value.
|
||||||
##
|
##
|
||||||
## This is only needed if you need to implement a hash proc for a new datatype.
|
## This is only needed if you need to implement a `hash` proc for a new datatype.
|
||||||
let h = cast[uint](h) # Hash is practically unsigned.
|
let h = cast[uint](h) # Hash is practically unsigned.
|
||||||
var res = h + h shl 3
|
var res = h + h shl 3
|
||||||
res = res xor (res shr 11)
|
res = res xor (res shr 11)
|
||||||
|
|
@ -90,7 +107,7 @@ proc hiXorLoFallback64(a, b: uint64): uint64 {.inline.} =
|
||||||
return hi xor lo
|
return hi xor lo
|
||||||
|
|
||||||
proc hiXorLo(a, b: uint64): uint64 {.inline.} =
|
proc hiXorLo(a, b: uint64): uint64 {.inline.} =
|
||||||
# Xor of high & low 8B of full 16B product
|
# XOR of the high & low 8 bytes of the full 16 byte product.
|
||||||
when nimvm:
|
when nimvm:
|
||||||
result = hiXorLoFallback64(a, b) # `result =` is necessary here.
|
result = hiXorLoFallback64(a, b) # `result =` is necessary here.
|
||||||
else:
|
else:
|
||||||
|
|
@ -107,9 +124,10 @@ proc hiXorLo(a, b: uint64): uint64 {.inline.} =
|
||||||
result = hiXorLoFallback64(a, b)
|
result = hiXorLoFallback64(a, b)
|
||||||
|
|
||||||
proc hashWangYi1*(x: int64|uint64|Hash): Hash {.inline.} =
|
proc hashWangYi1*(x: int64|uint64|Hash): Hash {.inline.} =
|
||||||
## Wang Yi's hash_v1 for 8B int. https://github.com/rurban/smhasher has more
|
## Wang Yi's hash_v1 for 64-bit ints (see https://github.com/rurban/smhasher for
|
||||||
## details. This passed all scrambling tests in Spring 2019 and is simple.
|
## more details). This passed all scrambling tests in Spring 2019 and is simple.
|
||||||
## NOTE: It's ok to define ``proc(x: int16): Hash = hashWangYi1(Hash(x))``.
|
##
|
||||||
|
## **Note:** It's ok to define `proc(x: int16): Hash = hashWangYi1(Hash(x))`.
|
||||||
const P0 = 0xa0761d6478bd642f'u64
|
const P0 = 0xa0761d6478bd642f'u64
|
||||||
const P1 = 0xe7037ed1a0b428db'u64
|
const P1 = 0xe7037ed1a0b428db'u64
|
||||||
const P58 = 0xeb44accab455d165'u64 xor 8'u64
|
const P58 = 0xeb44accab455d165'u64 xor 8'u64
|
||||||
|
|
@ -183,7 +201,7 @@ proc hash*[T: proc](x: T): Hash {.inline.} =
|
||||||
result = hash(pointer(x))
|
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))
|
||||||
|
|
||||||
when defined(nimIntHash1):
|
when defined(nimIntHash1):
|
||||||
|
|
@ -199,7 +217,7 @@ when defined(js):
|
||||||
proc asBigInt(x: float): int64 =
|
proc asBigInt(x: float): int64 =
|
||||||
# result is a `BigInt` type in js, but we cheat the type system
|
# result is a `BigInt` type in js, but we cheat the type system
|
||||||
# and say it is a `int64` type.
|
# and say it is a `int64` type.
|
||||||
# TODO refactor it using bigInt once jsBigInt is ready, pending pr #1640
|
# TODO: refactor it using bigInt once jsBigInt is ready, pending pr #1640
|
||||||
asm """
|
asm """
|
||||||
const buffer = new ArrayBuffer(8);
|
const buffer = new ArrayBuffer(8);
|
||||||
const floatBuffer = new Float64Array(buffer);
|
const floatBuffer = new Float64Array(buffer);
|
||||||
|
|
@ -312,7 +330,7 @@ proc hashVmImplByte(x: openArray[byte], sPos, ePos: int): Hash =
|
||||||
proc hash*(x: string): Hash =
|
proc hash*(x: string): Hash =
|
||||||
## Efficient hashing of strings.
|
## Efficient hashing of strings.
|
||||||
##
|
##
|
||||||
## See also:
|
## **See also:**
|
||||||
## * `hashIgnoreStyle <#hashIgnoreStyle,string>`_
|
## * `hashIgnoreStyle <#hashIgnoreStyle,string>`_
|
||||||
## * `hashIgnoreCase <#hashIgnoreCase,string>`_
|
## * `hashIgnoreCase <#hashIgnoreCase,string>`_
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
|
|
@ -357,7 +375,7 @@ proc hash*(sBuf: string, sPos, ePos: int): Hash =
|
||||||
## Efficient hashing of a string buffer, from starting
|
## Efficient hashing of a string buffer, from starting
|
||||||
## position `sPos` to ending position `ePos` (included).
|
## position `sPos` to ending position `ePos` (included).
|
||||||
##
|
##
|
||||||
## ``hash(myStr, 0, myStr.high)`` is equivalent to ``hash(myStr)``.
|
## `hash(myStr, 0, myStr.high)` is equivalent to `hash(myStr)`.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var a = "abracadabra"
|
var a = "abracadabra"
|
||||||
doAssert hash(a, 0, 3) == hash(a, 7, 10)
|
doAssert hash(a, 0, 3) == hash(a, 7, 10)
|
||||||
|
|
@ -373,9 +391,9 @@ proc hash*(sBuf: string, sPos, ePos: int): Hash =
|
||||||
proc hashIgnoreStyle*(x: string): Hash =
|
proc hashIgnoreStyle*(x: string): Hash =
|
||||||
## Efficient hashing of strings; style is ignored.
|
## Efficient hashing of strings; style is ignored.
|
||||||
##
|
##
|
||||||
## **Note:** This uses different hashing algorithm than `hash(string)`.
|
## **Note:** This uses a different hashing algorithm than `hash(string)`.
|
||||||
##
|
##
|
||||||
## See also:
|
## **See also:**
|
||||||
## * `hashIgnoreCase <#hashIgnoreCase,string>`_
|
## * `hashIgnoreCase <#hashIgnoreCase,string>`_
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
doAssert hashIgnoreStyle("aBr_aCa_dAB_ra") == hashIgnoreStyle("abracadabra")
|
doAssert hashIgnoreStyle("aBr_aCa_dAB_ra") == hashIgnoreStyle("abracadabra")
|
||||||
|
|
@ -399,10 +417,10 @@ proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash =
|
||||||
## Efficient hashing of a string buffer, from starting
|
## Efficient hashing of a string buffer, from starting
|
||||||
## position `sPos` to ending position `ePos` (included); style is ignored.
|
## position `sPos` to ending position `ePos` (included); style is ignored.
|
||||||
##
|
##
|
||||||
## **Note:** This uses different hashing algorithm than `hash(string)`.
|
## **Note:** This uses a different hashing algorithm than `hash(string)`.
|
||||||
##
|
##
|
||||||
## ``hashIgnoreStyle(myBuf, 0, myBuf.high)`` is equivalent
|
## `hashIgnoreStyle(myBuf, 0, myBuf.high)` is equivalent
|
||||||
## to ``hashIgnoreStyle(myBuf)``.
|
## to `hashIgnoreStyle(myBuf)`.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var a = "ABracada_b_r_a"
|
var a = "ABracada_b_r_a"
|
||||||
doAssert hashIgnoreStyle(a, 0, 3) == hashIgnoreStyle(a, 7, a.high)
|
doAssert hashIgnoreStyle(a, 0, 3) == hashIgnoreStyle(a, 7, a.high)
|
||||||
|
|
@ -423,9 +441,9 @@ proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash =
|
||||||
proc hashIgnoreCase*(x: string): Hash =
|
proc hashIgnoreCase*(x: string): Hash =
|
||||||
## Efficient hashing of strings; case is ignored.
|
## Efficient hashing of strings; case is ignored.
|
||||||
##
|
##
|
||||||
## **Note:** This uses different hashing algorithm than `hash(string)`.
|
## **Note:** This uses a different hashing algorithm than `hash(string)`.
|
||||||
##
|
##
|
||||||
## See also:
|
## **See also:**
|
||||||
## * `hashIgnoreStyle <#hashIgnoreStyle,string>`_
|
## * `hashIgnoreStyle <#hashIgnoreStyle,string>`_
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
doAssert hashIgnoreCase("ABRAcaDABRA") == hashIgnoreCase("abRACAdabra")
|
doAssert hashIgnoreCase("ABRAcaDABRA") == hashIgnoreCase("abRACAdabra")
|
||||||
|
|
@ -443,10 +461,10 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash =
|
||||||
## Efficient hashing of a string buffer, from starting
|
## Efficient hashing of a string buffer, from starting
|
||||||
## position `sPos` to ending position `ePos` (included); case is ignored.
|
## position `sPos` to ending position `ePos` (included); case is ignored.
|
||||||
##
|
##
|
||||||
## **Note:** This uses different hashing algorithm than `hash(string)`.
|
## **Note:** This uses a different hashing algorithm than `hash(string)`.
|
||||||
##
|
##
|
||||||
## ``hashIgnoreCase(myBuf, 0, myBuf.high)`` is equivalent
|
## `hashIgnoreCase(myBuf, 0, myBuf.high)` is equivalent
|
||||||
## to ``hashIgnoreCase(myBuf)``.
|
## to `hashIgnoreCase(myBuf)`.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var a = "ABracadabRA"
|
var a = "ABracadabRA"
|
||||||
doAssert hashIgnoreCase(a, 0, 3) == hashIgnoreCase(a, 7, 10)
|
doAssert hashIgnoreCase(a, 0, 3) == hashIgnoreCase(a, 7, 10)
|
||||||
|
|
@ -462,6 +480,7 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash =
|
||||||
|
|
||||||
proc hash*[T: tuple](x: T): Hash =
|
proc hash*[T: tuple](x: T): Hash =
|
||||||
## Efficient hashing of tuples.
|
## Efficient hashing of tuples.
|
||||||
|
## There must be a `hash` proc defined for each of the field types.
|
||||||
for f in fields(x):
|
for f in fields(x):
|
||||||
result = result !& hash(f)
|
result = result !& hash(f)
|
||||||
result = !$result
|
result = !$result
|
||||||
|
|
@ -469,6 +488,7 @@ proc hash*[T: tuple](x: T): Hash =
|
||||||
|
|
||||||
proc hash*[A](x: openArray[A]): Hash =
|
proc hash*[A](x: openArray[A]): Hash =
|
||||||
## Efficient hashing of arrays and sequences.
|
## Efficient hashing of arrays and sequences.
|
||||||
|
## There must be a `hash` proc defined for the element type `A`.
|
||||||
when A is byte:
|
when A is byte:
|
||||||
result = murmurHash(x)
|
result = murmurHash(x)
|
||||||
elif A is char:
|
elif A is char:
|
||||||
|
|
@ -484,8 +504,9 @@ proc hash*[A](x: openArray[A]): Hash =
|
||||||
proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash =
|
proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash =
|
||||||
## Efficient hashing of portions of arrays and sequences, from starting
|
## Efficient hashing of portions of arrays and sequences, from starting
|
||||||
## position `sPos` to ending position `ePos` (included).
|
## position `sPos` to ending position `ePos` (included).
|
||||||
|
## There must be a `hash` proc defined for the element type `A`.
|
||||||
##
|
##
|
||||||
## ``hash(myBuf, 0, myBuf.high)`` is equivalent to ``hash(myBuf)``.
|
## `hash(myBuf, 0, myBuf.high)` is equivalent to `hash(myBuf)`.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
let a = [1, 2, 5, 1, 2, 6]
|
let a = [1, 2, 5, 1, 2, 6]
|
||||||
doAssert hash(a, 0, 1) == hash(a, 3, 4)
|
doAssert hash(a, 0, 1) == hash(a, 3, 4)
|
||||||
|
|
@ -507,6 +528,7 @@ proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash =
|
||||||
|
|
||||||
proc hash*[A](x: set[A]): Hash =
|
proc hash*[A](x: set[A]): Hash =
|
||||||
## Efficient hashing of sets.
|
## Efficient hashing of sets.
|
||||||
|
## There must be a `hash` proc defined for the element type `A`.
|
||||||
for it in items(x):
|
for it in items(x):
|
||||||
result = result !& hash(it)
|
result = result !& hash(it)
|
||||||
result = !$result
|
result = !$result
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue