UNPKG

transitory

Version:

In-memory cache with high hit rates via LFU eviction. Supports time-based expiration, automatic loading and metrics.

116 lines 3.79 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CountMinSketch = void 0; var hashcode_1 = require("./hashcode"); /** * Helper function to calculate the closest power of two to N. * * @param n - * input * @returns * closest power of two to `n` */ function toPowerOfN(n) { return Math.pow(2, Math.ceil(Math.log(n) / Math.LN2)); } /** * Calculates a component of the hash. * * @param a0 - * @returns * hash */ function hash2(a0) { var a = (a0 ^ 61) ^ (a0 >>> 16); a = a + (a << 3); a = a ^ (a >>> 4); a = safeishMultiply(a, 0x27d4eb2d); a = a ^ (a >>> 15); return a; } /** * Tiny helper to perform a multiply that's slightly safer to use for hashing. * * @param a - * @param b - * @returns a * b */ function safeishMultiply(a, b) { return ((a & 0xffff) * b) + ((((a >>> 16) * b) & 0xffff) << 16); } /** * Count-min sketch suitable for use with W-TinyLFU. Similiar to a regular * count-min sketch but with a few important differences to achieve better * estimations: * * 1) Enforces that the width of the sketch is a power of 2. * 2) Uses a reset that decays all values by half when width * 10 additions * have been made. */ var CountMinSketch = /** @class */ (function () { function CountMinSketch(width, depth, decay) { this.width = toPowerOfN(width); this.depth = depth; // Get the maximum size of values, assuming unsigned ints this.maxSize = Math.pow(2, Uint8Array.BYTES_PER_ELEMENT * 8) - 1; this.halfMaxSize = this.maxSize / 2; this.slightlyLessThanHalfMaxSize = this.halfMaxSize - Math.max(this.halfMaxSize / 4, 1); // Track additions and when to reset this.additions = 0; this.resetAfter = decay ? width * 10 : -1; // Create the table to store data in this.table = new Uint8Array(this.width * depth); } CountMinSketch.prototype.findIndex = function (h1, h2, d) { var h = h1 + safeishMultiply(h2, d); return (d * this.width) + (h & (this.width - 1)); }; CountMinSketch.prototype.update = function (hashCode) { var table = this.table; var maxSize = this.maxSize; var estimate = this.estimate(hashCode); var h2 = hash2(hashCode); var added = false; for (var i = 0, n = this.depth; i < n; i++) { var idx = this.findIndex(hashCode, h2, i); var v = table[idx]; if (v + 1 < maxSize && v <= estimate) { table[idx] = v + 1; added = true; } } if (added && ++this.additions === this.resetAfter) { this.performReset(); } }; CountMinSketch.prototype.estimate = function (hashCode) { var table = this.table; var h2 = hash2(hashCode); var result = this.maxSize; for (var i = 0, n = this.depth; i < n; i++) { var value = table[this.findIndex(hashCode, h2, i)]; if (value < result) { result = value; } } return result; }; CountMinSketch.prototype.performReset = function () { var table = this.table; this.additions /= 2; for (var i = 0, n = table.length; i < n; i++) { this.additions -= table[i] & 1; table[i] = Math.floor(table[i] >>> 1); } }; CountMinSketch.hash = function (key) { return (0, hashcode_1.hashcode)(key); }; CountMinSketch.uint8 = function (width, depth, decay) { if (decay === void 0) { decay = true; } return new CountMinSketch(width, depth, decay); }; return CountMinSketch; }()); exports.CountMinSketch = CountMinSketch; //# sourceMappingURL=CountMinSketch.js.map