Merge pull request #7 from jgarzik/bloom

Rough draft of bloom filter module
This commit is contained in:
Stephen Pair 2013-07-17 11:10:36 -07:00
commit e08661cbb9
1 changed files with 116 additions and 0 deletions

116
Bloom.js Normal file
View File

@ -0,0 +1,116 @@
require('classtool');
function ClassSpec(b) {
var MAX_BLOOM_FILTER_SIZE = 36000; // bytes
var MAX_HASH_FUNCS = 50;
var LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455;
var LN2 = 0.6931471805599453094172321214581765680755001343602552;
var bit_mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80];
function Bloom() {
this.data = '';
this.hashFuncs = 0;
};
function ROTL32(x, r) {
return (x << r) | (x >> (32 - r));
};
function getBlockU32(blockIdx, data) {
var idx = blockIdx * 4;
var v = (data[idx + 0] << (0 * 8)) |
(data[idx + 1] << (1 * 8)) |
(data[idx + 2] << (2 * 8)) |
(data[idx + 3] << (3 * 8));
return v;
};
Bloom.prototype.hash = function(hashNum, data) {
var h1 = hashNum * (0xffffffff / (this.hashFuncs - 1));
var c1 = 0xcc9e2d51;
var c2 = 0x1b873593;
var nBlocks = data.length / 4;
// data body
for (var i = -nBlocks; i; i++) {
var k1 = getBlockU32(i);
k1 *= c1;
k1 = ROTLF32(k1, 15);
k1 *= c2;
h1 ^= k1;
h1 = ROTFL(h1, 13);
h1 = h1 * 5 + 0xe6546b64;
}
// tail (trailing 1-3 bytes)
var tail = data.slice(nBlocks * 4);
var k1 = 0;
switch (data.length & 3) {
case 3: k1 ^= tail[2] << 16;
case 2: k1 ^= tail[1] << 8;
case 1: k1 ^= tail[0];
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
}
// finalize
h1 ^= data.length;
h1 ^= h1 >> 16;
h1 *= 0x85ebca6b;
h1 ^= h1 >> 13;
h1 *= 0xc2b2ae35;
h1 ^= h1 >> 16;
return h1 % (this.data.length * 8);
};
Bloom.prototype.insert = function(data) {
for (var i = 0; i < this.hashFuncs; i++) {
var index = this.hash(i, data);
this.data[index >> 3] |= bit_mask[7 & index];
}
};
Bloom.prototype.contains = function(data) {
for (var i = 0; i < this.hashFuncs; i++) {
var index = this.hash(i, data);
if (!(this.data[index >> 3] & bit_mask[7 & index]))
return false;
}
return true;
};
Bloom.prototype.sizeOk = function() {
return this.data.length <= MAX_BLOOM_FILTER_SIZE &&
this.hashFuncs <= MAX_HASH_FUNCS;
};
function toInt(v) {
return ~~v;
}
function min(a, b) {
if (a < b)
return a;
return b;
}
Bloom.prototype.init = function(elements, FPRate) {
var filterSize = min(toInt(-1.0 / LN2SQUARED * elements * Math.log(FPRate)),
MAX_BLOOM_FILTER_SIZE * 8) / 8;
this.data[filterSize] = 0;
this.hashFuncs = min(toInt(this.data.length * 8 / elements * LN2),
MAX_HASH_FUNCS);
};
return Bloom;
};
module.defineClass(ClassSpec);