/*! \brief Hash functions. Inspired by Qt hash functions (\see https://github.com/qt/qtbase/blob/5.11/src/corelib/tools/qhashfunctions.h). All functions return Uint32 to fit max array length and indicies (\see https://stackoverflow.com/a/6155063/2895579). Seeds implied to be Uint32 too. \see http://www.eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx \see https://softwareengineering.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed/145633#145633 Benchmarks: \see https://github.com/evg656e/vbase-benchmarks/blob/master/benchmarks/hash/results.md */ import { convertFloat64ToUint32, fuzzyConvertFloat64ToUint32, isMinusZero } from '../../math/float'; export function hashInt32(key: number, seed = 0) { return (key ^ seed) >>> 0; } export { hashInt32 as hashUint32 }; export function hashBoolean(key: boolean, seed = 0) { return (Number(key) ^ seed) >>> 0; } export function hashChar(key: string, seed = 0) { return (key.charCodeAt(0) ^ seed) >>> 0; } export function combineHash(seed: number, key: number) { // (nb!) seed first arg, compatible with array reduce return (seed ^ (key + 0x9e3779b9 + (seed << 6) + (seed >>> 2))) >>> 0; } export function hashUint32Array(key: number[], seed = 0) { let h = seed; for (let i = 0; i < key.length; i++) h ^= key[i] + 0x9e3779b9 + (h << 6) + (h >>> 2); // h = combineHash(h, key[i]) return h >>> 0; } export function hashUint32Pair(key: [number, number], seed = 0) { // unrolled hashUint32Array let h = seed; h ^= key[0] + 0x9e3779b9 + (h << 6) + (h >>> 2); h ^= key[1] + 0x9e3779b9 + (h << 6) + (h >>> 2); return h >>> 0; } export function hashUint8Array(key: Uint8Array, seed = 0) { let h = seed; for (let i = 0; i < key.length; ++i) h = (31 * h + key[i]) >>> 0; return h; } export function hashString(key: string, seed = 0) { let h = seed; for (let i = 0; i < key.length; ++i) h = (31 * h + key.charCodeAt(i)) >>> 0; return h; } export function hashAsciiString(key: string, seed = 0) { let h = seed; for (let i = 0; i < key.length; ++i) h = (31 * h + (key.charCodeAt(i) & 0xff)) >>> 0; return h; } export function hashFloat64(key: number, seed?: number) { // slow, precise return hashUint32Pair(convertFloat64ToUint32(key, true), seed); } export function fuzzyHashFloat64(key: number, seed?: number) { // fast, fuzzy return hashUint32Pair(fuzzyConvertFloat64ToUint32(key, true), seed); } export function hashNumber(key: number, seed?: number) { // slow, precise if ((((key | 0) === key) // isInt32(key) || ((key >>> 0) === key)) // isUint32(key) && !isMinusZero(key)) return hashInt32(key, seed); return hashFloat64(key, seed); } export function fuzzyHashNumber(key: number, seed?: number) { // fast, fuzzy if ((((key | 0) === key) // isInt32(key) || ((key >>> 0) === key)) // isUint32(key) && !isMinusZero(key)) return hashInt32(key, seed); return fuzzyHashFloat64(key, seed); }