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); }; module.exports = require('soop')(Bloom);