UNPKG

@harishreddym/baqend

Version:

Baqend JavaScript SDK

167 lines (146 loc) 4.64 kB
/* eslint-disable no-bitwise,default-case,no-fallthrough */ 'use strict'; const atob = require('../util/atob').atob; /** * A Bloom Filter is a client-side kept cache sketch of the server cache * * @alias caching.BloomFilter */ class BloomFilter { /** * @param {Object} bloomFilter The raw Bloom filter. * @param {number} bloomFilter.m The raw Bloom filter bits. * @param {number} bloomFilter.h The raw Bloom filter hashes. * @param {string} bloomFilter.b The Base64-encoded raw Bloom filter bytes. */ constructor(bloomFilter) { /** * The raw bytes of this Bloom filter. * @type string * @readonly */ this.bytes = atob(bloomFilter.b); /** * The amount of bits. * @type number * @readonly */ this.bits = bloomFilter.m; /** * The amount of hashes. * @type number * @readonly */ this.hashes = bloomFilter.h; /** * The creation timestamp of this bloom filter. * @type number * @readonly */ this.creation = Date.now(); } /** * Returns whether this Bloom filter contains the given element. * * @param {string} element The element to check if it is contained. * @return {boolean} True, if the element is contained in this Bloom filter. */ contains(element) { const hashes = BloomFilter.getHashes(element, this.bits, this.hashes); for (let i = 0, len = hashes.length; i < len; i += 1) { if (!this.isSet(hashes[i])) { return false; } } return true; } /** * Checks whether a bit is set at a given position. * * @param {number} index The position index to check. * @return {boolean} True, if the bit is set at the given position. * @private */ isSet(index) { const pos = Math.floor(index / 8); const bit = 1 << (index % 8); // Extract byte as int or NaN if out of range const byte = this.bytes.charCodeAt(pos); // Bit-wise AND should be non-zero (NaN always yields false) return (byte & bit) !== 0; } /** * Returns the hases of a given element in the Bloom filter. * * @param {string} element The element to check. * @param {number} bits The amount of bits. * @param {number} hashes The amount of hashes. * @return {number[]} The hashes of an element in the Bloom filter. * @private */ static getHashes(element, bits, hashes) { const hashValues = new Array(this.hashes); const hash1 = BloomFilter.murmur3(0, element); const hash2 = BloomFilter.murmur3(hash1, element); for (let i = 0; i < hashes; i += 1) { hashValues[i] = (hash1 + (i * hash2)) % bits; } return hashValues; } /** * Calculate a Murmur3 hash. * * @param {number} seed A seed to use for the hashing. * @param {string} key A key to check. * @return {number} A hashed value of key. * @private */ static murmur3(seed, key) { const remainder = key.length & 3; const bytes = key.length - remainder; const c1 = 0xcc9e2d51; const c2 = 0x1b873593; let h1; let h1b; let k1; let i; h1 = seed; i = 0; while (i < bytes) { k1 = ((key.charCodeAt(i) & 0xff)) | ((key.charCodeAt(i += 1) & 0xff) << 8) | ((key.charCodeAt(i += 1) & 0xff) << 16) | ((key.charCodeAt(i += 1) & 0xff) << 24); i += 1; 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); h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff; h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16)); } k1 = 0; switch (remainder) { case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16; case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8; case 1: k1 ^= (key.charCodeAt(i) & 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; } h1 ^= key.length; 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; } } module.exports = BloomFilter;