feat: 添加常用hash函数实现

This commit is contained in:
= 2024-06-05 20:40:52 +08:00
parent 304450ba6a
commit 8280cca142
6 changed files with 319 additions and 0 deletions

View File

@ -20,6 +20,8 @@
"func-names": "off", //
"no-restricted-syntax": "off", // for of
"no-constant-condition": "off", // while(true)
"default-case": "off", // switchdefault
"no-fallthrough": "off", // case穿
"import/extensions": [
"error",
"ignorePackages", // node_modules

26
hash/city-hash.js Normal file
View File

@ -0,0 +1,26 @@
/**
* cityHash实现
* @param {string} s 需要计算hash的字符串
*/
export default function cityHash32(s) {
const seed = 0;
const len = s.length;
let h = len;
let g = seed;
let f = 0;
for (let i = 0; i < len; i++) {
f += s.charCodeAt(i);
g += f;
h ^= g;
g = ((g << 10) | (g >>> (32 - 10))) >>> 0;
g = ((g + f) >>> 0) * 9 >>> 0;
}
h ^= g;
h = ((h ^ (h >>> 16)) >>> 0) * 0x85ebca6b >>> 0;
h = ((h ^ (h >>> 13)) >>> 0) * 0xc2b2ae35 >>> 0;
h ^= h >>> 16;
return h >>> 0;
}

11
hash/index.js Normal file
View File

@ -0,0 +1,11 @@
import cityHash32 from './city-hash.js';
import murmurhash3 from './murmur-hash3.js';
import md5 from './md5.js';
import hashString from './string-hash.js';
export {
cityHash32,
murmurhash3,
md5,
hashString,
};

191
hash/md5.js Normal file
View File

@ -0,0 +1,191 @@
/*
MD5 算法原理
MD5 算法将任意长度的数据输入转换为 128 位的哈希值其主要步骤如下
填充消息
消息首先被填充使其长度在模 512 后余数为 448这通过在消息后添加一个 '1' 接着添加必要数量的 '0' 位完成
接下来消息长度以比特为单位被附加到消息末尾这样总长度现在是 512 的倍数
初始化 MD 缓冲区
一个 128 位的缓冲区4 32 位寄存器用于存储中间结果和最终的 MD5 哈希值缓冲区初始化为 4 个特定的常数
处理消息的每一个 512 位块
对每个 512 位块使用一系列的位运算包括左旋转加法等和非线性函数对缓冲区进行迭代更新
这个过程包括 4 轮操作每轮使用不同的非线性函数和一个常数表
输出结果
最终缓冲区的值被连接起来形成一个 128 位的哈希值
具体操作
函数 md5cycle对每个 512 位块进行 64 步操作这包括4轮非线性函数应用ffgghhii每轮16步
函数 cmnffgghhii用于不同轮次的非线性变换
函数 md51处理消息将其分块并循环调用 md5cycle
函数 md5blk将消息分块转换为 16 32 位整数
函数 rhexhex将结果转换为 16 进制字符串表示
函数 md5主函数调用 md51 处理消息并输出最终哈希值
希望这个解释能够帮助你理解 MD5 算法的实现及其原理如果有任何疑问或需要进一步的信息请告诉我
*/
function md5cycle(x, k) {
let [a, b, c, d] = x;
a = ff(a, b, c, d, k[0], 7, -680876936);
d = ff(d, a, b, c, k[1], 12, -389564586);
c = ff(c, d, a, b, k[2], 17, 606105819);
b = ff(b, c, d, a, k[3], 22, -1044525330);
a = ff(a, b, c, d, k[4], 7, -176418897);
d = ff(d, a, b, c, k[5], 12, 1200080426);
c = ff(c, d, a, b, k[6], 17, -1473231341);
b = ff(b, c, d, a, k[7], 22, -45705983);
a = ff(a, b, c, d, k[8], 7, 1770035416);
d = ff(d, a, b, c, k[9], 12, -1958414417);
c = ff(c, d, a, b, k[10], 17, -42063);
b = ff(b, c, d, a, k[11], 22, -1990404162);
a = ff(a, b, c, d, k[12], 7, 1804603682);
d = ff(d, a, b, c, k[13], 12, -40341101);
c = ff(c, d, a, b, k[14], 17, -1502002290);
b = ff(b, c, d, a, k[15], 22, 1236535329);
a = gg(a, b, c, d, k[1], 5, -165796510);
d = gg(d, a, b, c, k[6], 9, -1069501632);
c = gg(c, d, a, b, k[11], 14, 643717713);
b = gg(b, c, d, a, k[0], 20, -373897302);
a = gg(a, b, c, d, k[5], 5, -701558691);
d = gg(d, a, b, c, k[10], 9, 38016083);
c = gg(c, d, a, b, k[15], 14, -660478335);
b = gg(b, c, d, a, k[4], 20, -405537848);
a = gg(a, b, c, d, k[9], 5, 568446438);
d = gg(d, a, b, c, k[14], 9, -1019803690);
c = gg(c, d, a, b, k[3], 14, -187363961);
b = gg(b, c, d, a, k[8], 20, 1163531501);
a = gg(a, b, c, d, k[13], 5, -1444681467);
d = gg(d, a, b, c, k[2], 9, -51403784);
c = gg(c, d, a, b, k[7], 14, 1735328473);
b = gg(b, c, d, a, k[12], 20, -1926607734);
a = hh(a, b, c, d, k[5], 4, -378558);
d = hh(d, a, b, c, k[8], 11, -2022574463);
c = hh(c, d, a, b, k[11], 16, 1839030562);
b = hh(b, c, d, a, k[14], 23, -35309556);
a = hh(a, b, c, d, k[1], 4, -1530992060);
d = hh(d, a, b, c, k[4], 11, 1272893353);
c = hh(c, d, a, b, k[7], 16, -155497632);
b = hh(b, c, d, a, k[10], 23, -1094730640);
a = hh(a, b, c, d, k[13], 4, 681279174);
d = hh(d, a, b, c, k[0], 11, -358537222);
c = hh(c, d, a, b, k[3], 16, -722521979);
b = hh(b, c, d, a, k[6], 23, 76029189);
a = hh(a, b, c, d, k[9], 4, -640364487);
d = hh(d, a, b, c, k[12], 11, -421815835);
c = hh(c, d, a, b, k[15], 16, 530742520);
b = hh(b, c, d, a, k[2], 23, -995338651);
a = ii(a, b, c, d, k[0], 6, -198630844);
d = ii(d, a, b, c, k[7], 10, 1126891415);
c = ii(c, d, a, b, k[14], 15, -1416354905);
b = ii(b, c, d, a, k[5], 21, -57434055);
a = ii(a, b, c, d, k[12], 6, 1700485571);
d = ii(d, a, b, c, k[3], 10, -1894986606);
c = ii(c, d, a, b, k[10], 15, -1051523);
b = ii(b, c, d, a, k[1], 21, -2054922799);
a = ii(a, b, c, d, k[8], 6, 1873313359);
d = ii(d, a, b, c, k[15], 10, -30611744);
c = ii(c, d, a, b, k[6], 15, -1560198380);
b = ii(b, c, d, a, k[13], 21, 1309151649);
a = ii(a, b, c, d, k[4], 6, -145523070);
d = ii(d, a, b, c, k[11], 10, -1120210379);
c = ii(c, d, a, b, k[2], 15, 718787259);
b = ii(b, c, d, a, k[9], 21, -343485551);
x[0] = add32(a, x[0]);
x[1] = add32(b, x[1]);
x[2] = add32(c, x[2]);
x[3] = add32(d, x[3]);
}
function cmn(q, a, b, x, s, t) {
a = add32(add32(a, q), add32(x, t));
return add32((a << s) | (a >>> (32 - s)), b);
}
function ff(a, b, c, d, x, s, t) {
return cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function gg(a, b, c, d, x, s, t) {
return cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function hh(a, b, c, d, x, s, t) {
return cmn(b ^ c ^ d, a, b, x, s, t);
}
function ii(a, b, c, d, x, s, t) {
return cmn(c ^ (b | (~d)), a, b, x, s, t);
}
function md51(s) {
const n = s.length;
const state = [1732584193, -271733879, -1732584194, 271733878];
let i;
for (i = 64; i <= n; i += 64) {
md5cycle(state, md5blk(s.substring(i - 64, i)));
}
s = s.substring(i - 64);
const tail = new Array(16).fill(0);
for (i = 0; i < s.length; i++) {
tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
}
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
if (i > 55) {
md5cycle(state, tail);
tail.fill(0);
}
tail[14] = n * 8;
md5cycle(state, tail);
return state;
}
// 用于处理32位无符号整数加法
function add32(a, b) {
return (a + b) & 0xFFFFFFFF;
}
function md5blk(s) {
const md5blks = [];
for (let i = 0; i < 64; i += 4) {
md5blks[i >> 2] = s.charCodeAt(i)
+ (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
}
return md5blks;
}
function rhex(n) {
let s = '';
for (let j = 0; j < 4; j++) {
s += ((n >> (j * 8 + 4)) & 0x0F).toString(16) + ((n >> (j * 8)) & 0x0F).toString(16);
}
return s;
}
function hex(x) {
for (let i = 0; i < x.length; i++) {
x[i] = rhex(x[i]);
}
return x.join('');
}
/**
*
* @param {string} s 需要计算md5的字符串
* @returns
*/
export default function md5(s) {
return hex(md51(s));
}

53
hash/murmur-hash3.js Normal file
View File

@ -0,0 +1,53 @@
/**
*
* @param {string} key 需要做hash转换的数组
* @param {number} seed 随机种子
* @returns
*/
export default function murmurhash3(key, seed = 0) {
let h1 = seed;
const c1 = 0xcc9e2d51;
const c2 = 0x1b873593;
const len = key.length;
const roundedEnd = len & ~0x3; // round down to 4 byte block
for (let i = 0; i < roundedEnd; i += 4) {
// little endian load order
let k1 = (key.charCodeAt(i) & 0xff)
| ((key.charCodeAt(i + 1) & 0xff) << 8)
| ((key.charCodeAt(i + 2) & 0xff) << 16)
| ((key.charCodeAt(i + 3) & 0xff) << 24);
k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >>> 19);
h1 = (h1 * 5 + 0xe6546b64) & 0xffffffff;
}
// tail
let k1 = 0;
switch (len & 0x3) {
case 3: k1 = (key.charCodeAt(roundedEnd + 2) & 0xff) << 16;
case 2: k1 |= (key.charCodeAt(roundedEnd + 1) & 0xff) << 8;
case 1: k1 |= (key.charCodeAt(roundedEnd) & 0xff);
k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
h1 ^= k1;
}
// finalization
h1 ^= len;
h1 ^= h1 >>> 16;
h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
h1 ^= h1 >>> 13;
h1 = (((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16)) & 0xffffffff;
h1 ^= h1 >>> 16;
return h1 >>> 0;
}

36
hash/string-hash.js Normal file
View File

@ -0,0 +1,36 @@
/**
* 对字符串进行哈希处理生成一个哈希值
* @param {string} str - 要进行哈希处理的字符串
* @returns {number} - 生成的哈希值
*/
export default function hashString(str) {
let hash = 5381; // 初始化哈希值为一个较大的质数
for (let i = 0; i < str.length; i++) {
// 使用位运算和乘法混合哈希值,并引入额外的位移和位运算以增强离散性
hash = (hash * 15485863) ^ (str.charCodeAt(i) * 2654435761);
hash += hash << 13; // 添加额外的位移操作
hash ^= hash >>> 7; // 添加额外的位运算
hash += hash << 3; // 添加额外的位移操作
hash ^= hash >>> 17; // 添加额外的位运算
}
return hash >>> 0; // 确保结果为非负整数
}
/*
函数说明
这个函数用于对输入的字符串进行哈希处理生成一个哈希值哈希值是一个非负整数用于唯一标识输入字符串
参数
str要进行哈希处理的字符串
返回值
生成的哈希值是一个非负整数
算法说明
初始化哈希值为 5381这是一个较大的质数提供了良好的哈希起点
对字符串中的每个字符进行迭代处理使用位运算和乘法混合哈希值
引入额外的位移和位运算操作以增强哈希值的离散性
最后确保结果为非负整数并返回哈希值
魔幻数的选择
初始哈希值选取了质数 5381以提供良好的哈希起点
乘数 15485863 是一个较大的质数用于混合当前哈希值增加哈希的随机性
2654435761 是一个特殊的乘数通过与字符的 Unicode 编码值相乘将字符的分布范围扩大到更广的范围内以减少哈希冲突的可能性
额外的位移和位运算操作帮助进一步混合哈希值增强其离散性从而减少冲突的可能性
*/