【问题标题】:Javascript implementation of Jenkins Hash?Jenkins Hash 的 Javascript 实现?
【发布时间】:2011-05-13 04:19:13
【问题描述】:

有没有我可以使用的 Jenkins Hash 的 javascript 实现——而不是我自己实现它?

我知道有一个 python 实现可以用来编写我自己的 js。但我不是 Javascript 专家,因此我更喜欢别人的实现。


更新:(我确实尝试过转换http://www.burtleburtle.net/bob/c/lookup3.c) 更新 2:此代码为您提供与 lookup3.c 中 hashlittle2 相同的哈希

var Jenkins = {
rot: function(x,k) {
    return (x<<k) | (x>>>(32-k));
},

mix: function(a,b,c) {
    a = (a - c) | 0;  a ^= Jenkins.rot(c, 4);  c = (c + b) | 0;
    b = (b - a) | 0;  b ^= Jenkins.rot(a, 6);  a = (a + c) | 0;
    c = (c - b) | 0;  c ^= Jenkins.rot(b, 8);  b = (b + a) | 0;
    a = (a - c) | 0;  a ^= Jenkins.rot(c,16);  c = (c + b) | 0;
    b = (b - a) | 0;  b ^= Jenkins.rot(a,19);  a = (a + c) | 0;
    c = (c - b) | 0;  c ^= Jenkins.rot(b, 4);  b = (b + a) | 0;
    return {a : a, b : b, c : c};
},

final: function(a,b,c) {
   c ^= b; c -= Jenkins.rot(b,14) | 0;
   a ^= c; a -= Jenkins.rot(c,11) | 0;
   b ^= a; b -= Jenkins.rot(a,25) | 0;
   c ^= b; c -= Jenkins.rot(b,16) | 0;
   a ^= c; a -= Jenkins.rot(c,4) | 0;
   b ^= a; b -= Jenkins.rot(a,14) | 0;
   c ^= b; c -= Jenkins.rot(b,24) | 0;
   return {a : a, b : b, c : c};
},  

hashlittle2: function(k, initval, initval2) {
    var length = k.length;
    a = b = c = 0xdeadbeef + length + initval;
    c += initval2;

    offset = 0;
    while (length > 12) {
        a += (k.charCodeAt(offset+0) + (k.charCodeAt(offset+1)<<8) + (k.charCodeAt(offset+2)<<16) + (k.charCodeAt(offset+3)<<24)); a = a>>>0;
        b += (k.charCodeAt(offset+4) + (k.charCodeAt(offset+5)<<8) + (k.charCodeAt(offset+6)<<16) + (k.charCodeAt(offset+7)<<24)); b = b>>>0;
        c += (k.charCodeAt(offset+8) + (k.charCodeAt(offset+9)<<8) + (k.charCodeAt(offset+10)<<16) + (k.charCodeAt(offset+11)<<24)); c = c>>>0;
        o = Jenkins.mix(a,b,c);
        a = o.a; b = o.b; c = o.c;
        length -= 12;
        offset += 12;
    }

    switch(length) {
        case 12: c += (k.charCodeAt(offset+8) + (k.charCodeAt(offset+9)<<8) + (k.charCodeAt(offset+10)<<16) + (k.charCodeAt(offset+11)<<24)); b += (k.charCodeAt(offset+4) + (k.charCodeAt(offset+5)<<8) + (k.charCodeAt(offset+6)<<16) + (k.charCodeAt(offset+7)<<24)); a += (k.charCodeAt(offset+0) + (k.charCodeAt(offset+1)<<8) + (k.charCodeAt(offset+2)<<16) + (k.charCodeAt(offset+3)<<24)); break;
        case 11: c += (k.charCodeAt(offset+8) + (k.charCodeAt(offset+9)<<8) + (k.charCodeAt(offset+10)<<16)); b += (k.charCodeAt(offset+4) + (k.charCodeAt(offset+5)<<8) + (k.charCodeAt(offset+6)<<16) + (k.charCodeAt(offset+7)<<24)); a += (k.charCodeAt(offset+0) + (k.charCodeAt(offset+1)<<8) + (k.charCodeAt(offset+2)<<16) + (k.charCodeAt(offset+3)<<24)); break;
        case 10: c += (k.charCodeAt(offset+8) + (k.charCodeAt(offset+9)<<8)); b += (k.charCodeAt(offset+4) + (k.charCodeAt(offset+5)<<8) + (k.charCodeAt(offset+6)<<16) + (k.charCodeAt(offset+7)<<24)); a += (k.charCodeAt(offset+0) + (k.charCodeAt(offset+1)<<8) + (k.charCodeAt(offset+2)<<16) + (k.charCodeAt(offset+3)<<24)); break;
        case 9: c += (k.charCodeAt(offset+8)); b += (k.charCodeAt(offset+4) + (k.charCodeAt(offset+5)<<8) + (k.charCodeAt(offset+6)<<16) + (k.charCodeAt(offset+7)<<24)); a += (k.charCodeAt(offset+0) + (k.charCodeAt(offset+1)<<8) + (k.charCodeAt(offset+2)<<16) + (k.charCodeAt(offset+3)<<24)); break;
        case 8: b += (k.charCodeAt(offset+4) + (k.charCodeAt(offset+5)<<8) + (k.charCodeAt(offset+6)<<16) + (k.charCodeAt(offset+7)<<24)); a += (k.charCodeAt(offset+0) + (k.charCodeAt(offset+1)<<8) + (k.charCodeAt(offset+2)<<16) + (k.charCodeAt(offset+3)<<24)); break;
        case 7: b += (k.charCodeAt(offset+4) + (k.charCodeAt(offset+5)<<8) + (k.charCodeAt(offset+6)<<16)); a += (k.charCodeAt(offset+0) + (k.charCodeAt(offset+1)<<8) + (k.charCodeAt(offset+2)<<16) + (k.charCodeAt(offset+3)<<24)); break;
        case 6: b += ((k.charCodeAt(offset+5)<<8) + k.charCodeAt(offset+4)); a += (k.charCodeAt(offset+0) + (k.charCodeAt(offset+1)<<8) + (k.charCodeAt(offset+2)<<16) + (k.charCodeAt(offset+3)<<24)); break;
        case 5: b += (k.charCodeAt(offset+4)); a += (k.charCodeAt(offset+0) + (k.charCodeAt(offset+1)<<8) + (k.charCodeAt(offset+2)<<16) + (k.charCodeAt(offset+3)<<24)); break;
        case 4: a += (k.charCodeAt(offset+0) + (k.charCodeAt(offset+1)<<8) + (k.charCodeAt(offset+2)<<16) + (k.charCodeAt(offset+3)<<24)); break;
        case 3: a += (k.charCodeAt(offset+0) + (k.charCodeAt(offset+1)<<8) + (k.charCodeAt(offset+2)<<16)); break;
        case 2: a += (k.charCodeAt(offset+0) + (k.charCodeAt(offset+1)<<8)); break;
        case 1: a += (k.charCodeAt(offset+0)); break;
        case 0: return {b : b, c : c};
    }

    o = Jenkins.final(a,b,c);
    a = o.a; b = o.b; c = o.c;

    return {b : b>>>0, c : c>>>0};
}    

}

【问题讨论】:

  • 来吧,会很有趣的。此外,如果您有现有的有效测试数据来源......:p
  • 按位 OR 代替 + 在将字节组合成 32 位字时效率显着提高。尽你所能防止 JS 转换回浮点数。您可以在对其进行基准测试时看到差异。您还可以删除 break 语句并简单地添加相关部分而不是整个内容,因为 case 12case 11 可以实现为位掩码。

标签: javascript hash


【解决方案1】:

只需进行一些修改,C code on Wikipedia 就可以在 JavaScript 中完美运行。只需摆脱数据类型并使用key.length 而不必传递长度。

【讨论】:

  • 但这与詹金在burtleburtle.net/bob/hash/doobs.html 上展示的“我的哈希”相比是一个不同(并且更简单)的哈希,其中包含混合阶段以及其他差异。 (一次一个哈希只是所述链接中的讨论哈希之一。)
  • @pst:嗯,这仍然只是一堆移位和其他算术运算符,所以大部分在 JavaScript 中都不会改变。
  • @casablanca 请指出 :-) 但是,我相信差异可能在于溢出的处理方式——这必须手动添加到 JavaScript 逻辑中。 “我的哈希”假定为无符号 4 字节,我相信 2 的补码。
  • @pst:当然,谢谢。 :) 我会留给 OP 来选择适合他的。
  • @casablanca 其实溢出问题也困扰着wiki上C代码的“直接转换”。必须小心模仿。
【解决方案2】:

这是我为 lookup3.c 做的一个 JavaScript 端口,用于在 JavaScript 中实现 Bloom filter 的个人项目。我不能确定它是否会产生与 C 代码相同的结果。

不直接转换为 JavaScript 的主要内容之一是对指向键输入的指针执行的指针算术。在下面的代码中查找单词offset,看看我是如何处理的。

如果你想要一个整数像无符号一样的输出,你可以使用returnValue &gt;&gt;&gt; 0

var BloomFilter = {
    // Convert a JavaScript string into an array of 32-bit words.
    // This preserves the UTF-16 encoding, padding with the null character if necessary.
    stringToWords: function(s) {
        var b = [];
        if(s.length & 1) {
            s += "\u0000";
        }
        for (var i = 0; i < s.length; i += 2) {
            b.push((s.charCodeAt(i) << 16) | (s.charCodeAt(i + 1)));
        }
        return b;
    },

    // Hash an array of multiple 32-bit words to a single word.
    // Adapted from "lookup3.c, by Bob Jenkins, May 2006, Public Domain."
    // as retrieved 2010-07-03 from http://burtleburtle.net/bob/c/lookup3.c

    hashWord: function(k, initval) {
        // definition of bitwise rotate function
        function rot(x, k) {
            return (x << k) | (x >>> (32 - k));
        }

        // initialization
        var a, b, c, length = k.length, offset = 0;
        a = b = c = (0xdeadbeef + (length << 2) + initval) | 0;

        // handle most of the key
        while(length > 3) {
            a = (a + k[offset]) | 0;
            b = (b + k[offset + 1]) | 0;
            c = (c + k[offset + 2]) | 0;

            // mixing function
            a = (a - c) | 0;  a ^= rot(c, 4);  c = (c + b) | 0;
            b = (b - a) | 0;  b ^= rot(a, 6);  a = (a + c) | 0;
            c = (c - b) | 0;  c ^= rot(b, 8);  b = (b + a) | 0;
            a = (a - c) | 0;  a ^= rot(c,16);  c = (c + b) | 0;
            b = (b - a) | 0;  b ^= rot(a,19);  a = (a + c) | 0;
            c = (c - b) | 0;  c ^= rot(b, 4);  b = (b + a) | 0;

            length -= 3;
            offset += 3;
        }

        // handle the final words if left over; fall-through is intended
        switch(length) {
            case 3: c = (c + k[offset + 2]) | 0;
            case 2: b = (b + k[offset + 1]) | 0;
            case 1: a = (a + k[offset]) | 0;

            // final mixing
            c ^= b; c = (c - rot(b,14)) | 0;
            a ^= c; a = (a - rot(c,11)) | 0;
            b ^= a; b = (b - rot(a,25)) | 0;
            c ^= b; c = (c - rot(b,16)) | 0;
            a ^= c; a = (a - rot(c, 4)) | 0;
            b ^= a; b = (b - rot(a,14)) | 0;
            c ^= b; c = (c - rot(b,24)) | 0;

            case 0: break; // nothing left to do
        }

        // return the result
        return c;
    },

    // Hash a string by converting to UTF-16 before using the lookup3 algorithm.
    hashString: function(s) {
        return BloomFilter.hashWord(BloomFilter.stringToWords(s), 0);
    }
}

【讨论】:

  • 它肯定会产生不同的结果。 a,b,c 从相邻字节(每个循环 3 个)而不是每个字节 4 个(每个循环 12 个)获取输入,从而使计算不同。此外,length &lt;&lt; 2 的转移也不是必需的。
【解决方案3】:

这是我对 lookup3 (2006) 所做的优化端口:

function lookup3(k, init = 0, init2 = 0) {
    var len = k.length, o = 0,
        a = 0xdeadbeef + len + init | 0,
        b = 0xdeadbeef + len + init | 0,
        c = 0xdeadbeef + len + init + init2 | 0;

    while (len > 12) {
        a += k[o]   | k[o+1] << 8 | k[o+2]  << 16 | k[o+3]  << 24;
        b += k[o+4] | k[o+5] << 8 | k[o+6]  << 16 | k[o+7]  << 24;
        c += k[o+8] | k[o+9] << 8 | k[o+10] << 16 | k[o+11] << 24;

        a -= c; a ^= c<<4  | c>>>28; c = c+b | 0;
        b -= a; b ^= a<<6  | a>>>26; a = a+c | 0;
        c -= b; c ^= b<<8  | b>>>24; b = b+a | 0;
        a -= c; a ^= c<<16 | c>>>16; c = c+b | 0;
        b -= a; b ^= a<<19 | a>>>13; a = a+c | 0;
        c -= b; c ^= b<<4  | b>>>28; b = b+a | 0;

        len -= 12, o += 12;
    }

    if(len > 0) { // final mix only if len > 0
        switch (len) { // incorporate trailing bytes before fmix
            case 12: c += k[o+11] << 24;
            case 11: c += k[o+10] << 16;
            case 10: c += k[o+9] << 8;
            case  9: c += k[o+8];
            case  8: b += k[o+7] << 24;
            case  7: b += k[o+6] << 16;
            case  6: b += k[o+5] << 8;
            case  5: b += k[o+4];
            case  4: a += k[o+3] << 24;
            case  3: a += k[o+2] << 16;
            case  2: a += k[o+1] << 8;
            case  1: a += k[o];
        }

        c ^= b; c -= b<<14 | b>>>18;
        a ^= c; a -= c<<11 | c>>>21;
        b ^= a; b -= a<<25 | a>>>7;
        c ^= b; c -= b<<16 | b>>>16;
        a ^= c; a -= c<<4  | c>>>28;
        b ^= a; b -= a<<14 | a>>>18;
        c ^= b; c -= b<<24 | b>>>8;
    }
    // use c as 32-bit hash; add b for 64-bit hash. a is not mixed well.
    return [b >>> 0, c >>> 0];
}

这里是lookup2 (1996),它非常相似(但速度较慢):

function lookup2(k, init = 0) {
    var len = k.length, o = 0, 
        a = 0x9e3779b9 | 0,
        b = 0x9e3779b9 | 0,
        c = init | 0;

    while (len >= 12) {
        a += k[o]   | k[o+1] << 8 | k[o+2]  << 16 | k[o+3]  << 24;
        b += k[o+4] | k[o+5] << 8 | k[o+6]  << 16 | k[o+7]  << 24;
        c += k[o+8] | k[o+9] << 8 | k[o+10] << 16 | k[o+11] << 24;

        a -= b; a -= c; a ^= c >>> 13;
        b -= c; b -= a; b ^= a << 8;
        c -= a; c -= b; c ^= b >>> 13;
        a -= b; a -= c; a ^= c >>> 12;
        b -= c; b -= a; b ^= a << 16;
        c -= a; c -= b; c ^= b >>> 5;
        a -= b; a -= c; a ^= c >>> 3;
        b -= c; b -= a; b ^= a << 10;
        c -= a; c -= b; c ^= b >>> 15;

        len -= 12, o += 12;
    }

    c += k.length;
    switch(len) {
        case 11: c += k[o+10] << 24;
        case 10: c += k[o+9] << 16;
        case  9: c += k[o+8] << 8;
        // the first byte of c is reserved for the length
        case  8: b += k[o+7] << 24;
        case  7: b += k[o+6] << 16;
        case  6: b += k[o+5] << 8;
        case  5: b += k[o+4];
        case  4: a += k[o+3] << 24;
        case  3: a += k[o+2] << 16;
        case  2: a += k[o+1] << 8;
        case  1: a += k[o];
    }

    a -= b; a -= c; a ^= c >>> 13;
    b -= c; b -= a; b ^= a << 8;
    c -= a; c -= b; c ^= b >>> 13;
    a -= b; a -= c; a ^= c >>> 12;
    b -= c; b -= a; b ^= a << 16;
    c -= a; c -= b; c ^= b >>> 5;
    a -= b; a -= c; a ^= c >>> 3;
    b -= c; b -= a; b ^= a << 10;
    c -= a; c -= b; c ^= b >>> 15;
    // c is intended as 32-bit hash only
    return c >>> 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-07
    • 1970-01-01
    • 2010-10-17
    • 2015-05-18
    • 1970-01-01
    • 2016-07-28
    • 2013-08-02
    • 2014-07-30
    相关资源
    最近更新 更多