/* This file is part of limb https://lila.oss/limb
* Copyright (C) 2023 Olivier Brunel jjk@jjacky.com */
/* Based on hashlittle2 (lookup3)
* Copyright (C) 2006 Bob Jenkins -- Public Domain */
/* SPDX-License-Identifier: ISC */
#include <limb/hlookup.h>
#include "config.h"
#if LIMB_ENDIAN == LIMB_LITTLE
# define HASH_LITTLE_ENDIAN 1
#else
# define HASH_LITTLE_ENDIAN 0
#endif
/* This is hashlittle2() by Bob Jenkins, May 2006, Public Domain
*
* These are functions for producing 32-bit hashes for hash table lookup.
* You can use this free for any purpose. It's in the public domain. It has no
* warranty.
*
* If you want to find a hash of, say, exactly 7 integers, do
* a = i1; b = i2; c = i3;
* mix(a,b,c);
* a += i4; b += i5; c += i6;
* mix(a,b,c);
* a += i7;
* final(a,b,c);
* then use c as the hash value.
*
* Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
* then mix those integers. This is fast (you can do a lot more thorough
* mixing with 12*3 instructions on 3 integers than you can with 3 instructions
* on 1 byte), but shoehorning those bytes into integers efficiently is messy.
*/
#define rot(x,k) ( ((x) << (k)) | ( (x) >> (32 - (k)) ) )
/* mix 3 32-bit values reversibly.
*
* This is reversible, so any information in (a,b,c) before mix() is still in
* (a,b,c) after mix().
*
* If four pairs of (a,b,c) inputs are run through mix(), or through mix() in
* reverse, there are at least 32 bits of the output that are sometimes the same
* for one pair and different for another pair. This was tested for:
* * pairs that differed by one bit, by two bits, in any combination of top bits
* of (a,b,c), or in any combination of bottom bits of (a,b,c).
* * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the
* output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly
* produced by subtraction) look like a single 1-bit difference.
* * the base values were pseudorandom, all zero but one bit set, or all zero
* plus a counter that starts at zero.
*
* Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that satisfy this
* are :
* 4 6 8 16 19 4
* 9 15 3 18 27 15
* 14 9 3 7 17 3
* Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing for "differ" defined
* as + with a one-bit base and a two-bit delta. I used
* http://burtleburtle.net/bob/hash/avalanche.html to choose the operations,
* constants, and arrangements of the variables.
*
* This does not achieve avalanche. There are input bits of (a,b,c) that fail
* to affect some output bits of (a,b,c), especially of a. The most thoroughly
* mixed value is c, but it doesn't really even achieve avalanche in c.
*
* This allows some parallelism. Read-after-writes are good at doubling the
* number of bits affected, so the goal of mixing pulls in the opposite
* direction as the goal of parallelism. I did what I could. Rotates seem to
* cost as much as shifts on every machine I could lay my hands on, and rotates
* are much kinder to the top and bottom bits, so I used rotates.
*/
#define mix(a,b,c) \
{ \
a -= c; a ^= rot(c, 4); c += b; \
b -= a; b ^= rot(a, 6); a += c; \
c -= b; c ^= rot(b, 8); b += a; \
a -= c; a ^= rot(c,16); c += b; \
b -= a; b ^= rot(a,19); a += c; \
c -= b; c ^= rot(b, 4); b += a; \
}
/* final mixing of 3 32-bit values (a,b,c) into c
*
* Pairs of (a,b,c) values differing in only a few bits will usually produce
* values of c that look totally different. This was tested for * pairs that
* differed by one bit, by two bits, in any combination of top bits of (a,b,c),
* or in any combination of bottom bits of (a,b,c).
* * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the
* output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly
* produced by subtraction) look like a single 1-bit difference.
* * the base values were pseudorandom, all zero but one bit set, or all zero
* plus a counter that starts at zero.
*
* These constants passed:
* 14 11 25 16 4 14 24
* 12 14 25 16 4 14 24
* and these came close:
* 4 8 15 26 3 22 24
* 10 8 15 26 3 22 24
* 11 8 15 26 3 22 24
*/
#define final(a,b,c) \
{ \
c ^= b; c -= rot(b,14); \
a ^= c; a -= rot(c,11); \
b ^= a; b -= rot(a,25); \
c ^= b; c -= rot(b,16); \
a ^= c; a -= rot(c, 4); \
b ^= a; b -= rot(a,14); \
c ^= b; c -= rot(b,24); \
}
void
hlookup(u32 *main, u32 *second, const void *data, size_t dlen)
{
u32 a, b, c;
/* needed for Mac Powerbook G4 */
union { const void *ptr; size_t i; } u;
/* Set up the internal state */
a = b = c = 0xdeadbeef + (u32) dlen + *main;
if (second)
c += *second;
u.ptr = data;
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
/* read 32bit chunks */
const u32 *k = (const u32 *) data;
/* all but last block: aligned reads and affect 32 bits of (a,b,c) */
while (dlen > 12) {
a += k[0];
b += k[1];
c += k[2];
mix(a,b,c);
dlen -= 12;
k += 3;
}
/* handle the last (probably partial) block */
#ifndef VALGRIND
/* "k[2] & 0x00ffffff" actually reads beyond the end of the data, but
* then masks off the part it's not allowed to read. Because the data
* is aligned, the masked-off tail is in the same word as the rest of
* it. Every machine with memory protection I've seen does it on word
* boundaries, so is OK with this. But VALGRIND will still catch it and
* complain. The masking trick does make the hash noticably faster for
* short data (e.g. English words).
*/
switch(dlen) {
case 12: c += k[2]; b += k[1]; a += k[0];
break;
case 11: c += k[2] & 0x00ffffff; b += k[1]; a += k[0];
break;
case 10: c += k[2] & 0x0000ffff; b += k[1]; a += k[0];
break;
case 9: c += k[2] & 0x000000ff; b += k[1]; a += k[0];
break;
case 8: b += k[1]; a += k[0];
break;
case 7: b += k[1] & 0x00ffffff; a += k[0];
break;
case 6: b += k[1] & 0x0000ffff; a += k[0];
break;
case 5: b += k[1] & 0x000000ff; a += k[0];
break;
case 4: a += k[0];
break;
case 3: a += k[0] & 0x00ffffff;
break;
case 2: a += k[0] & 0x0000ffff;
break;
case 1: a += k[0] & 0x000000ff;
break;
case 0:
/* zero length strings require no mixing */
*main = c;
if (second)
*second = b;
return;
}
#else
/* make valgrind happy - read 8bit chunks */
const u8 *k8 = (const u8 *) k;
switch(dlen) {
case 12: c += k[2]; b += k[1]; a += k[0];
break;
case 11: c += ((u32) k8[10]) << 16;
/* fall through */
case 10: c += ((u32) k8[ 9]) << 8;
/* fall through */
case 9: c += k8[8];
/* fall through */
case 8: b += k[1]; a += k[0];
break;
case 7: b += ((u32) k8[6]) << 16;
/* fall through */
case 6: b += ((u32) k8[5]) << 8;
/* fall through */
case 5: b += k8[4];
/* fall through */
case 4: a += k[0];
break;
case 3: a += ((u32) k8[2]) << 16;
/* fall through */
case 2: a += ((u32) k8[1]) << 8;
/* fall through */
case 1: a += k8[0];
break;
case 0:
/* zero length strings require no mixing */
*main = c;
if (second)
*second = b;
return;
}
#endif /* VALGRIND */
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
/* read 16-bit chunks */
const u16 *k = (const u16 *) data;
const u8 *k8;
/* all but last block: aligned reads and different mixing */
while (dlen > 12)
{
a += k[0] + (((u32) k[1]) << 16);
b += k[2] + (((u32) k[3]) << 16);
c += k[4] + (((u32) k[5]) << 16);
mix(a,b,c);
dlen -= 12;
k += 6;
}
/* handle the last (probably partial) block */
k8 = (const u8 *) k;
switch(dlen) {
case 12: c += k[4] + (((u32) k[5]) << 16);
b += k[2] + (((u32) k[3]) << 16);
a += k[0] + (((u32) k[1]) << 16);
break;
case 11: c += ((u32) k8[10]) << 16;
/* fall through */
case 10: c += k[4];
b += k[2] + (((u32) k[3]) << 16);
a += k[0] + (((u32) k[1]) << 16);
break;
case 9 : c += k8[8];
/* fall through */
case 8 : b += k[2] + (((u32) k[3]) << 16);
a += k[0] + (((u32) k[1]) << 16);
break;
case 7 : b += ((u32) k8[6]) << 16;
/* fall through */
case 6 : b += k[2];
a += k[0] + (((u32) k[1]) << 16);
break;
case 5 : b += k8[4];
/* fall through */
case 4 : a += k[0] + (((u32) k[1]) << 16);
break;
case 3 : a += ((u32) k8[2]) << 16;
/* fall through */
case 2 : a += k[0];
break;
case 1 : a += k8[0];
break;
case 0 :
/* zero length strings require no mixing */
*main = c;
if (second)
*second = b;
return;
}
} else {
/* need to read the key one byte at a time */
const u8 *k = (const u8 *) data;
/* all but the last block: affect some 32 bits of (a,b,c) */
while (dlen > 12)
{
a += k[0];
a += ((u32) k[ 1]) << 8;
a += ((u32) k[ 2]) << 16;
a += ((u32) k[ 3]) << 24;
b += k[4];
b += ((u32) k[ 5]) << 8;
b += ((u32) k[ 6]) << 16;
b += ((u32) k[ 7]) << 24;
c += k[8];
c += ((u32) k[ 9]) << 8;
c += ((u32) k[10]) << 16;
c += ((u32) k[11]) << 24;
mix(a,b,c);
dlen -= 12;
k += 12;
}
/* last block: affect all 32 bits of (c) */
switch(dlen) {
case 12: c += ((u32) k[11]) << 24; /* fallthrough */
case 11: c += ((u32) k[10]) << 16; /* fallthrough */
case 10: c += ((u32) k[ 9]) << 8; /* fallthrough */
case 9: c += k[ 8]; /* fallthrough */
case 8: b += ((u32) k[ 7]) << 24; /* fallthrough */
case 7: b += ((u32) k[ 6]) << 16; /* fallthrough */
case 6: b += ((u32) k[ 5]) << 8; /* fallthrough */
case 5: b += k[ 4]; /* fallthrough */
case 4: a += ((u32) k[ 3]) << 24; /* fallthrough */
case 3: a += ((u32) k[ 2]) << 16; /* fallthrough */
case 2: a += ((u32) k[ 1]) << 8; /* fallthrough */
case 1: a += k[ 0];
break;
case 0 :
/* zero length strings require no mixing */
*main = c;
if (second)
*second = b;
return;
}
}
final(a,b,c);
*main = c;
if (second)
*second = b;
}