The $hash identifier calculates a simple hash of the supplied text. The hash is shown as a decimal number in the range from 0 to 2^N-1 where N is the number of bits ranging from 2-32. Except for legacy scripts, you should probably avoid using this hash for reasons explained in the Notes section. It's possible that $hash() was once the method used by /hadd to put an item into one of the hash table's "buckets", but that was not the case at the point when it was changed to use 'modified FNV-1A'.

Contents

text text string or %variable to be hashed B Bit length for the returned hash. $hash returns $null if B is a number outside the range 2-32, and returns the original string if the B parameter isn't used. Returned hash is a decimal number in the range 0 to 2^B -1.

I have not been able to find a string where 'fakehash' returns a different number than $hash.

$hash uses a hash function that is very weak for several reasons. The weaknesses are more easily seen by inspecting the 'fakehash' alias which mimics $hash output, and by showing $hash output as hex instead of a base-10 number. These weaknesses could be why $hash was not also extended to accept binary variables as input when $crc was given this ability. Some weaknesses in $hash include:

1. Similar input have similar output hash. Strings of 1-3 bytes are most obvious:

2. When N is greater than 24, the rightmost extra bits above 24 are always zero, making it effectively a 24-bit hash not a 32-bit. Only 2^24 of the 2^32 values within the range of 0 - 2^32-1 can possibly be hashes. i.e. the least-significant 3 bits of a 27-bit hash are always zero:

3. One of the properties of good hash functions is that each changed bit of the input should change close to half the output bits in a seemingly-random pattern. Changing 1 bit of the input string has minimal change of the bits in the $hash output, often changing output by just 1 bit. $hash() processes the input string 1 byte at a time, and it often takes several additional bytes before the bits altered by the first byte are altered again by another byte.

As of v7.56, on a computer where the 1000 repetitions takes $crc approx 1/10th of a second, the same work using $hash takes longer than 30 seconds.

Instead of using $hash, you would be better off using other substitutes. For example, if you need it to be a decimal number with a variable 1-32 number of bits, use $crc then convert to decimal then reduce the number of bits:

The above borrows code from $hotp which uses the final digit of the hash to determine which digits within the SHA* hash are used. This next variant is faster, because it doesn't calculate the offset, and it also uses the faster MD5. Because of the 2^53 accuracy limit for $calc, this allows the hash to be up to 52 bits instead of the 32 bits for $hash, and you can easily substitute any of the SHA* identifiers in place of $md5.

The first parameter tells the number of bits in the output hash, which means there should be 2^N possible outputs.
The 2nd number is the length of random strings to be hashed.
The 3rd and 4th parameters give the option of changing the first and last characters of the random range away from being the range a-z.

Using "/hash_distribution 4 N a z" where N ranges from 8 through 12 shows a very uneven frequency count of hash output, and the quality of the distribution depends greatly on the string length. In this example, because the output is a 4-bit hash, there are 2^4=16 possible outputs, and this alias shows most of the 16 numbers never happen for this length of a-z input, while other outputs happen too frequently:

Increasing the number of bits above 4 helps smooth the distribution, and increasing the string length also helps, but even when the string is as long as 100 characters the distribution of hashing lower-case letters is uneven. Also helping to smooth the distribution is to change first/last characters in the random range to increase that range size. But even for using ! and ~ as the first/last characters of the range, which includes a lot of characters unlikely to be in real-world item names, it still has an uneven distribution until the string length increases sufficiently.

The 1024 outputs of a 10 bit hash of a length-100 input fits onto a length-4150 mIRC line, but only because too many of the tokens are single digits:

(Warning: This is slow, and is too long to display here. There's a very large area where consecutive outputs happen 0-3 times. 11 bit instead of 10 bit can be used in a length-8292 line, but is even SLOWER.)

If you edit the fakehash alias to use the above $crchash alias instead of $hash, the distribution is much better for all input lengths and range of characters. Repeating the a-z range with $crchash gives a much smoother distribution.

$crc is not of cryptographic quality, but at least it has a good distribution, and hash functions don't need a 1-way feature, they just need to be fast. A good distribution is not proof of a good hash, since even a repeating pattern of 1-through-10 has that.