bloomkit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +207 -0
- package/dist/index.cjs +376 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +214 -0
- package/dist/index.d.ts +214 -0
- package/dist/index.js +341 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
// src/BitArray.ts
|
|
2
|
+
var BitArray = class _BitArray {
|
|
3
|
+
constructor(size) {
|
|
4
|
+
this.size = size;
|
|
5
|
+
this._buf = new Uint32Array(Math.ceil(size / 32));
|
|
6
|
+
}
|
|
7
|
+
set(index) {
|
|
8
|
+
this._buf[index >>> 5] |= 1 << (index & 31);
|
|
9
|
+
}
|
|
10
|
+
get(index) {
|
|
11
|
+
return (this._buf[index >>> 5] >>> (index & 31) & 1) === 1;
|
|
12
|
+
}
|
|
13
|
+
clear() {
|
|
14
|
+
this._buf.fill(0);
|
|
15
|
+
}
|
|
16
|
+
/** Number of bits set to 1 (popcount). */
|
|
17
|
+
popcount() {
|
|
18
|
+
let n = 0;
|
|
19
|
+
for (const w of this._buf) {
|
|
20
|
+
let v = w;
|
|
21
|
+
v = v - (v >>> 1 & 1431655765);
|
|
22
|
+
v = (v & 858993459) + (v >>> 2 & 858993459);
|
|
23
|
+
n += (v + (v >>> 4) & 252645135) * 16843009 >>> 24;
|
|
24
|
+
}
|
|
25
|
+
return n;
|
|
26
|
+
}
|
|
27
|
+
/** Export as a base64-encoded string for serialization. */
|
|
28
|
+
toBase64() {
|
|
29
|
+
const bytes = new Uint8Array(this._buf.buffer);
|
|
30
|
+
let s = "";
|
|
31
|
+
for (const b of bytes) s += String.fromCharCode(b);
|
|
32
|
+
return btoa(s);
|
|
33
|
+
}
|
|
34
|
+
/** Restore from a base64 string produced by `toBase64`. */
|
|
35
|
+
static fromBase64(b64, size) {
|
|
36
|
+
const arr = new _BitArray(size);
|
|
37
|
+
const raw = atob(b64);
|
|
38
|
+
const bytes = new Uint8Array(arr._buf.buffer);
|
|
39
|
+
for (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);
|
|
40
|
+
return arr;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/hash.ts
|
|
45
|
+
function murmur3(key, seed = 0) {
|
|
46
|
+
let h = seed >>> 0;
|
|
47
|
+
const len = key.length;
|
|
48
|
+
let i = 0;
|
|
49
|
+
while (i <= len - 4) {
|
|
50
|
+
let k = key.charCodeAt(i) & 255 | (key.charCodeAt(i + 1) & 255) << 8 | (key.charCodeAt(i + 2) & 255) << 16 | (key.charCodeAt(i + 3) & 255) << 24;
|
|
51
|
+
k = Math.imul(k, 3432918353);
|
|
52
|
+
k = k << 15 | k >>> 17;
|
|
53
|
+
k = Math.imul(k, 461845907);
|
|
54
|
+
h ^= k;
|
|
55
|
+
h = h << 13 | h >>> 19;
|
|
56
|
+
h = Math.imul(h, 5) + 3864292196 >>> 0;
|
|
57
|
+
i += 4;
|
|
58
|
+
}
|
|
59
|
+
let remaining = 0;
|
|
60
|
+
switch (len & 3) {
|
|
61
|
+
case 3:
|
|
62
|
+
remaining ^= (key.charCodeAt(i + 2) & 255) << 16;
|
|
63
|
+
// fall through
|
|
64
|
+
case 2:
|
|
65
|
+
remaining ^= (key.charCodeAt(i + 1) & 255) << 8;
|
|
66
|
+
// fall through
|
|
67
|
+
case 1:
|
|
68
|
+
remaining ^= key.charCodeAt(i) & 255;
|
|
69
|
+
remaining = Math.imul(remaining, 3432918353);
|
|
70
|
+
remaining = remaining << 15 | remaining >>> 17;
|
|
71
|
+
remaining = Math.imul(remaining, 461845907);
|
|
72
|
+
h ^= remaining;
|
|
73
|
+
}
|
|
74
|
+
h ^= len;
|
|
75
|
+
h ^= h >>> 16;
|
|
76
|
+
h = Math.imul(h, 2246822507);
|
|
77
|
+
h ^= h >>> 13;
|
|
78
|
+
h = Math.imul(h, 3266489909);
|
|
79
|
+
h ^= h >>> 16;
|
|
80
|
+
return h >>> 0;
|
|
81
|
+
}
|
|
82
|
+
function fnv1a(key, seed = 2166136261) {
|
|
83
|
+
let h = seed >>> 0;
|
|
84
|
+
for (let i = 0; i < key.length; i++) {
|
|
85
|
+
h ^= key.charCodeAt(i) & 255;
|
|
86
|
+
h = Math.imul(h, 16777619);
|
|
87
|
+
}
|
|
88
|
+
return h >>> 0;
|
|
89
|
+
}
|
|
90
|
+
function hashPositions(key, k, m) {
|
|
91
|
+
const h1 = murmur3(key);
|
|
92
|
+
const h2 = fnv1a(key);
|
|
93
|
+
const positions = new Array(k);
|
|
94
|
+
for (let i = 0; i < k; i++) {
|
|
95
|
+
positions[i] = (h1 + i * h2 >>> 0) % m;
|
|
96
|
+
}
|
|
97
|
+
return positions;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/BloomFilter.ts
|
|
101
|
+
function optimalM(n, fpr) {
|
|
102
|
+
return Math.ceil(-n * Math.log(fpr) / (Math.LN2 * Math.LN2));
|
|
103
|
+
}
|
|
104
|
+
function optimalK(m, n) {
|
|
105
|
+
return Math.max(1, Math.round(m / n * Math.LN2));
|
|
106
|
+
}
|
|
107
|
+
var BloomFilter = class _BloomFilter {
|
|
108
|
+
constructor(options) {
|
|
109
|
+
this._count = 0;
|
|
110
|
+
const { capacity, errorRate = 0.01 } = options;
|
|
111
|
+
if (capacity <= 0 || !Number.isFinite(capacity)) throw new RangeError("capacity must be a positive finite number");
|
|
112
|
+
if (errorRate <= 0 || errorRate >= 1) throw new RangeError("errorRate must be in (0, 1)");
|
|
113
|
+
this.capacity = capacity;
|
|
114
|
+
this.errorRate = errorRate;
|
|
115
|
+
this.m = optimalM(capacity, errorRate);
|
|
116
|
+
this.k = optimalK(this.m, capacity);
|
|
117
|
+
this._bits = new BitArray(this.m);
|
|
118
|
+
}
|
|
119
|
+
/** Add an item to the filter. */
|
|
120
|
+
add(item) {
|
|
121
|
+
for (const pos of hashPositions(item, this.k, this.m)) {
|
|
122
|
+
this._bits.set(pos);
|
|
123
|
+
}
|
|
124
|
+
this._count++;
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Test membership. Returns `true` if item *may* have been added,
|
|
129
|
+
* `false` if it *definitely* has not.
|
|
130
|
+
*/
|
|
131
|
+
has(item) {
|
|
132
|
+
for (const pos of hashPositions(item, this.k, this.m)) {
|
|
133
|
+
if (!this._bits.get(pos)) return false;
|
|
134
|
+
}
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
/** Number of items added (may exceed capacity). */
|
|
138
|
+
get size() {
|
|
139
|
+
return this._count;
|
|
140
|
+
}
|
|
141
|
+
/** Current estimated false-positive rate based on items inserted. */
|
|
142
|
+
get currentFPR() {
|
|
143
|
+
const fillRatio = this._bits.popcount() / this.m;
|
|
144
|
+
return Math.pow(fillRatio, this.k);
|
|
145
|
+
}
|
|
146
|
+
/** Reset the filter. */
|
|
147
|
+
clear() {
|
|
148
|
+
this._bits.clear();
|
|
149
|
+
this._count = 0;
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
/** Serialize to a plain object for JSON.stringify. */
|
|
153
|
+
toJSON() {
|
|
154
|
+
return {
|
|
155
|
+
type: "BloomFilter",
|
|
156
|
+
capacity: this.capacity,
|
|
157
|
+
errorRate: this.errorRate,
|
|
158
|
+
m: this.m,
|
|
159
|
+
k: this.k,
|
|
160
|
+
bits: this._bits.toBase64()
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/** Restore a BloomFilter from the output of `toJSON`. */
|
|
164
|
+
static fromJSON(data) {
|
|
165
|
+
const bf = new _BloomFilter({ capacity: data.capacity, errorRate: data.errorRate });
|
|
166
|
+
bf["_bits"] = BitArray.fromBase64(data.bits, data.m);
|
|
167
|
+
return bf;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// src/CountingBloomFilter.ts
|
|
172
|
+
var CountingBloomFilter = class {
|
|
173
|
+
constructor(options) {
|
|
174
|
+
this._count = 0;
|
|
175
|
+
const { capacity, errorRate = 0.01, counterBits = 4 } = options;
|
|
176
|
+
if (capacity <= 0 || !Number.isFinite(capacity)) throw new RangeError("capacity must be a positive finite number");
|
|
177
|
+
if (errorRate <= 0 || errorRate >= 1) throw new RangeError("errorRate must be in (0, 1)");
|
|
178
|
+
this.capacity = capacity;
|
|
179
|
+
this.errorRate = errorRate;
|
|
180
|
+
this.m = optimalM(capacity, errorRate);
|
|
181
|
+
this.k = optimalK(this.m, capacity);
|
|
182
|
+
this._counterBits = counterBits;
|
|
183
|
+
if (counterBits === 4) {
|
|
184
|
+
this._counters = new Uint8Array(Math.ceil(this.m / 2));
|
|
185
|
+
this._maxCount = 15;
|
|
186
|
+
} else {
|
|
187
|
+
this._counters = new Uint8Array(this.m);
|
|
188
|
+
this._maxCount = 255;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
_get(pos) {
|
|
192
|
+
if (this._counterBits === 4) {
|
|
193
|
+
const byte = this._counters[pos >>> 1];
|
|
194
|
+
return pos & 1 ? byte >>> 4 : byte & 15;
|
|
195
|
+
}
|
|
196
|
+
return this._counters[pos];
|
|
197
|
+
}
|
|
198
|
+
_increment(pos) {
|
|
199
|
+
if (this._counterBits === 4) {
|
|
200
|
+
const idx = pos >>> 1;
|
|
201
|
+
const byte = this._counters[idx];
|
|
202
|
+
if (pos & 1) {
|
|
203
|
+
const hi = byte >>> 4;
|
|
204
|
+
if (hi < this._maxCount) this._counters[idx] = byte & 15 | hi + 1 << 4;
|
|
205
|
+
} else {
|
|
206
|
+
const lo = byte & 15;
|
|
207
|
+
if (lo < this._maxCount) this._counters[idx] = byte & 240 | lo + 1;
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
const v = this._counters[pos];
|
|
211
|
+
if (v < this._maxCount) this._counters[pos] = v + 1;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
_decrement(pos) {
|
|
215
|
+
if (this._counterBits === 4) {
|
|
216
|
+
const idx = pos >>> 1;
|
|
217
|
+
const byte = this._counters[idx];
|
|
218
|
+
if (pos & 1) {
|
|
219
|
+
const hi = byte >>> 4;
|
|
220
|
+
if (hi > 0) this._counters[idx] = byte & 15 | hi - 1 << 4;
|
|
221
|
+
} else {
|
|
222
|
+
const lo = byte & 15;
|
|
223
|
+
if (lo > 0) this._counters[idx] = byte & 240 | lo - 1;
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
const v = this._counters[pos];
|
|
227
|
+
if (v > 0) this._counters[pos] = v - 1;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/** Add an item to the filter. */
|
|
231
|
+
add(item) {
|
|
232
|
+
for (const pos of hashPositions(item, this.k, this.m)) {
|
|
233
|
+
this._increment(pos);
|
|
234
|
+
}
|
|
235
|
+
this._count++;
|
|
236
|
+
return this;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Remove an item from the filter.
|
|
240
|
+
* Only remove items you previously added — removing items never added leads
|
|
241
|
+
* to false negatives.
|
|
242
|
+
*/
|
|
243
|
+
remove(item) {
|
|
244
|
+
for (const pos of hashPositions(item, this.k, this.m)) {
|
|
245
|
+
this._decrement(pos);
|
|
246
|
+
}
|
|
247
|
+
this._count = Math.max(0, this._count - 1);
|
|
248
|
+
return this;
|
|
249
|
+
}
|
|
250
|
+
/** Test membership. */
|
|
251
|
+
has(item) {
|
|
252
|
+
for (const pos of hashPositions(item, this.k, this.m)) {
|
|
253
|
+
if (this._get(pos) === 0) return false;
|
|
254
|
+
}
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
/** Number of items currently in the filter (approximate). */
|
|
258
|
+
get size() {
|
|
259
|
+
return this._count;
|
|
260
|
+
}
|
|
261
|
+
/** Reset the filter. */
|
|
262
|
+
clear() {
|
|
263
|
+
this._counters.fill(0);
|
|
264
|
+
this._count = 0;
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// src/ScalableBloomFilter.ts
|
|
270
|
+
var ScalableBloomFilter = class {
|
|
271
|
+
constructor(options = {}) {
|
|
272
|
+
this._count = 0;
|
|
273
|
+
this._initialCapacity = options.initialCapacity ?? 1e3;
|
|
274
|
+
this._errorRate = options.errorRate ?? 0.01;
|
|
275
|
+
this._scaleFactor = options.scaleFactor ?? 2;
|
|
276
|
+
this._tighteningRatio = options.tighteningRatio ?? 0.9;
|
|
277
|
+
if (this._errorRate <= 0 || this._errorRate >= 1) throw new RangeError("errorRate must be in (0, 1)");
|
|
278
|
+
if (this._scaleFactor < 2) throw new RangeError("scaleFactor must be >= 2");
|
|
279
|
+
if (this._tighteningRatio <= 0 || this._tighteningRatio >= 1) throw new RangeError("tighteningRatio must be in (0, 1)");
|
|
280
|
+
this._filters = [this._createFilter(0)];
|
|
281
|
+
}
|
|
282
|
+
_createFilter(index) {
|
|
283
|
+
const capacity = Math.ceil(this._initialCapacity * Math.pow(this._scaleFactor, index));
|
|
284
|
+
const errorRate = this._errorRate * Math.pow(this._tighteningRatio, index);
|
|
285
|
+
return new BloomFilter({ capacity, errorRate });
|
|
286
|
+
}
|
|
287
|
+
/** Add an item. The filter grows automatically when the current slice is full. */
|
|
288
|
+
add(item) {
|
|
289
|
+
const current = this._filters[this._filters.length - 1];
|
|
290
|
+
if (current.size >= current.capacity) {
|
|
291
|
+
this._filters.push(this._createFilter(this._filters.length));
|
|
292
|
+
}
|
|
293
|
+
this._filters[this._filters.length - 1].add(item);
|
|
294
|
+
this._count++;
|
|
295
|
+
return this;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Test membership — checks all sub-filters.
|
|
299
|
+
* Returns `true` if item may have been added, `false` if definitely not.
|
|
300
|
+
*/
|
|
301
|
+
has(item) {
|
|
302
|
+
for (const filter of this._filters) {
|
|
303
|
+
if (filter.has(item)) return true;
|
|
304
|
+
}
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
/** Total number of items added (approximate). */
|
|
308
|
+
get size() {
|
|
309
|
+
return this._count;
|
|
310
|
+
}
|
|
311
|
+
/** Number of internal sub-filters created so far. */
|
|
312
|
+
get filterCount() {
|
|
313
|
+
return this._filters.length;
|
|
314
|
+
}
|
|
315
|
+
/** Total bits allocated across all sub-filters. */
|
|
316
|
+
get bitsAllocated() {
|
|
317
|
+
return this._filters.reduce((s, f) => s + f.m, 0);
|
|
318
|
+
}
|
|
319
|
+
/** Target false-positive rate. */
|
|
320
|
+
get errorRate() {
|
|
321
|
+
return this._errorRate;
|
|
322
|
+
}
|
|
323
|
+
/** Reset the filter. */
|
|
324
|
+
clear() {
|
|
325
|
+
this._filters = [this._createFilter(0)];
|
|
326
|
+
this._count = 0;
|
|
327
|
+
return this;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
export {
|
|
331
|
+
BitArray,
|
|
332
|
+
BloomFilter,
|
|
333
|
+
CountingBloomFilter,
|
|
334
|
+
ScalableBloomFilter,
|
|
335
|
+
fnv1a,
|
|
336
|
+
hashPositions,
|
|
337
|
+
murmur3,
|
|
338
|
+
optimalK,
|
|
339
|
+
optimalM
|
|
340
|
+
};
|
|
341
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/BitArray.ts","../src/hash.ts","../src/BloomFilter.ts","../src/CountingBloomFilter.ts","../src/ScalableBloomFilter.ts"],"sourcesContent":["/** Compact fixed-size bit array backed by Uint32Array. */\nexport class BitArray {\n private readonly _buf: Uint32Array;\n readonly size: number;\n\n constructor(size: number) {\n this.size = size;\n this._buf = new Uint32Array(Math.ceil(size / 32));\n }\n\n set(index: number): void {\n this._buf[index >>> 5]! |= 1 << (index & 31);\n }\n\n get(index: number): boolean {\n return (this._buf[index >>> 5]! >>> (index & 31) & 1) === 1;\n }\n\n clear(): void {\n this._buf.fill(0);\n }\n\n /** Number of bits set to 1 (popcount). */\n popcount(): number {\n let n = 0;\n for (const w of this._buf) {\n let v = w;\n v = v - ((v >>> 1) & 0x55555555);\n v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);\n n += (((v + (v >>> 4)) & 0x0f0f0f0f) * 0x01010101) >>> 24;\n }\n return n;\n }\n\n /** Export as a base64-encoded string for serialization. */\n toBase64(): string {\n const bytes = new Uint8Array(this._buf.buffer);\n let s = \"\";\n for (const b of bytes) s += String.fromCharCode(b);\n return btoa(s);\n }\n\n /** Restore from a base64 string produced by `toBase64`. */\n static fromBase64(b64: string, size: number): BitArray {\n const arr = new BitArray(size);\n const raw = atob(b64);\n const bytes = new Uint8Array(arr._buf.buffer);\n for (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);\n return arr;\n }\n}\n","/**\n * MurmurHash3 (32-bit, x86) — fast non-cryptographic hash.\n * Returns an unsigned 32-bit integer.\n */\nexport function murmur3(key: string, seed = 0): number {\n let h = seed >>> 0;\n const len = key.length;\n let i = 0;\n\n while (i <= len - 4) {\n let k =\n ((key.charCodeAt(i) & 0xff)) |\n ((key.charCodeAt(i + 1) & 0xff) << 8) |\n ((key.charCodeAt(i + 2) & 0xff) << 16) |\n ((key.charCodeAt(i + 3) & 0xff) << 24);\n k = Math.imul(k, 0xcc9e2d51);\n k = (k << 15) | (k >>> 17);\n k = Math.imul(k, 0x1b873593);\n h ^= k;\n h = (h << 13) | (h >>> 19);\n h = (Math.imul(h, 5) + 0xe6546b64) >>> 0;\n i += 4;\n }\n\n let remaining = 0;\n switch (len & 3) {\n case 3: remaining ^= (key.charCodeAt(i + 2) & 0xff) << 16; // fall through\n case 2: remaining ^= (key.charCodeAt(i + 1) & 0xff) << 8; // fall through\n case 1:\n remaining ^= key.charCodeAt(i) & 0xff;\n remaining = Math.imul(remaining, 0xcc9e2d51);\n remaining = (remaining << 15) | (remaining >>> 17);\n remaining = Math.imul(remaining, 0x1b873593);\n h ^= remaining;\n }\n\n h ^= len;\n h ^= h >>> 16;\n h = Math.imul(h, 0x85ebca6b);\n h ^= h >>> 13;\n h = Math.imul(h, 0xc2b2ae35);\n h ^= h >>> 16;\n return h >>> 0;\n}\n\n/**\n * FNV-1a (32-bit) — second independent hash for double-hashing.\n */\nexport function fnv1a(key: string, seed = 0x811c9dc5): number {\n let h = seed >>> 0;\n for (let i = 0; i < key.length; i++) {\n h ^= key.charCodeAt(i) & 0xff;\n h = Math.imul(h, 0x01000193);\n }\n return h >>> 0;\n}\n\n/**\n * Generate `k` hash positions in `[0, m)` using double hashing.\n * gi(x) = (h1(x) + i * h2(x)) mod m\n */\nexport function hashPositions(key: string, k: number, m: number): number[] {\n const h1 = murmur3(key);\n const h2 = fnv1a(key);\n const positions: number[] = new Array(k);\n for (let i = 0; i < k; i++) {\n positions[i] = ((h1 + i * h2) >>> 0) % m;\n }\n return positions;\n}\n","import { BitArray } from \"./BitArray.js\";\nimport { hashPositions } from \"./hash.js\";\n\n/** @internal Compute optimal bit-array size m given n items and fpr. */\nexport function optimalM(n: number, fpr: number): number {\n return Math.ceil(-n * Math.log(fpr) / (Math.LN2 * Math.LN2));\n}\n\n/** @internal Compute optimal number of hash functions k given m bits and n items. */\nexport function optimalK(m: number, n: number): number {\n return Math.max(1, Math.round((m / n) * Math.LN2));\n}\n\nexport interface BloomFilterOptions {\n /**\n * Expected number of items that will be inserted.\n * Used to size the filter optimally.\n */\n capacity: number;\n /**\n * Target false-positive rate (0 < fpr < 1). Default: 0.01 (1%).\n */\n errorRate?: number;\n}\n\nexport interface BloomFilterJSON {\n type: \"BloomFilter\";\n capacity: number;\n errorRate: number;\n m: number;\n k: number;\n bits: string;\n}\n\n/**\n * Standard Bloom filter — space-efficient probabilistic set membership.\n *\n * - `add(item)` always works correctly.\n * - `has(item)` may return `true` for items not added (false positive, rate ≤ `errorRate`).\n * - `has(item)` never returns `false` for items that were added.\n * - Items cannot be removed (use `CountingBloomFilter` for deletions).\n *\n * @example\n * const bf = new BloomFilter({ capacity: 1_000_000, errorRate: 0.01 });\n * bf.add(\"hello\");\n * bf.has(\"hello\"); // true\n * bf.has(\"world\"); // false (with high probability)\n */\nexport class BloomFilter {\n readonly capacity: number;\n readonly errorRate: number;\n /** Bit-array size. */\n readonly m: number;\n /** Number of hash functions. */\n readonly k: number;\n\n private readonly _bits: BitArray;\n private _count = 0;\n\n constructor(options: BloomFilterOptions) {\n const { capacity, errorRate = 0.01 } = options;\n if (capacity <= 0 || !Number.isFinite(capacity)) throw new RangeError(\"capacity must be a positive finite number\");\n if (errorRate <= 0 || errorRate >= 1) throw new RangeError(\"errorRate must be in (0, 1)\");\n\n this.capacity = capacity;\n this.errorRate = errorRate;\n this.m = optimalM(capacity, errorRate);\n this.k = optimalK(this.m, capacity);\n this._bits = new BitArray(this.m);\n }\n\n /** Add an item to the filter. */\n add(item: string): this {\n for (const pos of hashPositions(item, this.k, this.m)) {\n this._bits.set(pos);\n }\n this._count++;\n return this;\n }\n\n /**\n * Test membership. Returns `true` if item *may* have been added,\n * `false` if it *definitely* has not.\n */\n has(item: string): boolean {\n for (const pos of hashPositions(item, this.k, this.m)) {\n if (!this._bits.get(pos)) return false;\n }\n return true;\n }\n\n /** Number of items added (may exceed capacity). */\n get size(): number {\n return this._count;\n }\n\n /** Current estimated false-positive rate based on items inserted. */\n get currentFPR(): number {\n const fillRatio = this._bits.popcount() / this.m;\n return Math.pow(fillRatio, this.k);\n }\n\n /** Reset the filter. */\n clear(): this {\n this._bits.clear();\n this._count = 0;\n return this;\n }\n\n /** Serialize to a plain object for JSON.stringify. */\n toJSON(): BloomFilterJSON {\n return {\n type: \"BloomFilter\",\n capacity: this.capacity,\n errorRate: this.errorRate,\n m: this.m,\n k: this.k,\n bits: this._bits.toBase64(),\n };\n }\n\n /** Restore a BloomFilter from the output of `toJSON`. */\n static fromJSON(data: BloomFilterJSON): BloomFilter {\n const bf = new BloomFilter({ capacity: data.capacity, errorRate: data.errorRate });\n (bf as unknown as { _bits: BitArray })[\"_bits\"] = BitArray.fromBase64(data.bits, data.m);\n return bf;\n }\n}\n","import { hashPositions } from \"./hash.js\";\nimport { optimalM, optimalK } from \"./BloomFilter.js\";\n\nexport interface CountingBloomFilterOptions {\n /** Expected number of items. */\n capacity: number;\n /** Target false-positive rate (default: 0.01). */\n errorRate?: number;\n /** Counter width in bits — 4 (default) supports up to 15 adds per cell. */\n counterBits?: 4 | 8;\n}\n\n/**\n * Counting Bloom filter — supports deletion by maintaining per-cell counters\n * instead of single bits.\n *\n * - `add(item)` increments k counters.\n * - `remove(item)` decrements k counters. Do NOT remove items that were never added.\n * - `has(item)` returns `true` iff all k counters are > 0.\n *\n * @example\n * const cbf = new CountingBloomFilter({ capacity: 10_000 });\n * cbf.add(\"user:42\");\n * cbf.has(\"user:42\"); // true\n * cbf.remove(\"user:42\");\n * cbf.has(\"user:42\"); // false\n */\nexport class CountingBloomFilter {\n readonly capacity: number;\n readonly errorRate: number;\n readonly m: number;\n readonly k: number;\n private readonly _counters: Uint8Array | Uint16Array;\n private readonly _counterBits: number;\n private readonly _maxCount: number;\n private _count = 0;\n\n constructor(options: CountingBloomFilterOptions) {\n const { capacity, errorRate = 0.01, counterBits = 4 } = options;\n if (capacity <= 0 || !Number.isFinite(capacity)) throw new RangeError(\"capacity must be a positive finite number\");\n if (errorRate <= 0 || errorRate >= 1) throw new RangeError(\"errorRate must be in (0, 1)\");\n\n this.capacity = capacity;\n this.errorRate = errorRate;\n this.m = optimalM(capacity, errorRate);\n this.k = optimalK(this.m, capacity);\n this._counterBits = counterBits;\n\n if (counterBits === 4) {\n // Pack two 4-bit counters per byte\n this._counters = new Uint8Array(Math.ceil(this.m / 2));\n this._maxCount = 15;\n } else {\n this._counters = new Uint8Array(this.m);\n this._maxCount = 255;\n }\n }\n\n private _get(pos: number): number {\n if (this._counterBits === 4) {\n const byte = this._counters[pos >>> 1]!;\n return pos & 1 ? byte >>> 4 : byte & 0x0f;\n }\n return (this._counters as Uint8Array)[pos]!;\n }\n\n private _increment(pos: number): void {\n if (this._counterBits === 4) {\n const idx = pos >>> 1;\n const byte = this._counters[idx]!;\n if (pos & 1) {\n const hi = byte >>> 4;\n if (hi < this._maxCount) this._counters[idx] = (byte & 0x0f) | ((hi + 1) << 4);\n } else {\n const lo = byte & 0x0f;\n if (lo < this._maxCount) this._counters[idx] = (byte & 0xf0) | (lo + 1);\n }\n } else {\n const v = (this._counters as Uint8Array)[pos]!;\n if (v < this._maxCount) (this._counters as Uint8Array)[pos] = v + 1;\n }\n }\n\n private _decrement(pos: number): void {\n if (this._counterBits === 4) {\n const idx = pos >>> 1;\n const byte = this._counters[idx]!;\n if (pos & 1) {\n const hi = byte >>> 4;\n if (hi > 0) this._counters[idx] = (byte & 0x0f) | ((hi - 1) << 4);\n } else {\n const lo = byte & 0x0f;\n if (lo > 0) this._counters[idx] = (byte & 0xf0) | (lo - 1);\n }\n } else {\n const v = (this._counters as Uint8Array)[pos]!;\n if (v > 0) (this._counters as Uint8Array)[pos] = v - 1;\n }\n }\n\n /** Add an item to the filter. */\n add(item: string): this {\n for (const pos of hashPositions(item, this.k, this.m)) {\n this._increment(pos);\n }\n this._count++;\n return this;\n }\n\n /**\n * Remove an item from the filter.\n * Only remove items you previously added — removing items never added leads\n * to false negatives.\n */\n remove(item: string): this {\n for (const pos of hashPositions(item, this.k, this.m)) {\n this._decrement(pos);\n }\n this._count = Math.max(0, this._count - 1);\n return this;\n }\n\n /** Test membership. */\n has(item: string): boolean {\n for (const pos of hashPositions(item, this.k, this.m)) {\n if (this._get(pos) === 0) return false;\n }\n return true;\n }\n\n /** Number of items currently in the filter (approximate). */\n get size(): number {\n return this._count;\n }\n\n /** Reset the filter. */\n clear(): this {\n this._counters.fill(0);\n this._count = 0;\n return this;\n }\n}\n","import { BloomFilter } from \"./BloomFilter.js\";\n\nexport interface ScalableBloomFilterOptions {\n /**\n * Initial capacity (items before first resize). Default: 1000.\n */\n initialCapacity?: number;\n /**\n * Target overall false-positive rate. Default: 0.01.\n * Each sub-filter uses `errorRate * tighteningRatio^i` so the series\n * converges to a total FPR ≤ `errorRate`.\n */\n errorRate?: number;\n /**\n * Scale factor: each new sub-filter has `scaleFactor × previous capacity`.\n * Common values: 2 (default) or 4.\n */\n scaleFactor?: number;\n /**\n * Ratio by which each sub-filter tightens its FPR. Default: 0.9.\n * Must be in (0, 1).\n */\n tighteningRatio?: number;\n}\n\n/**\n * Scalable Bloom filter — grows automatically as items are added, so you\n * don't need to know the final set size upfront.\n *\n * Maintains a series of standard BloomFilters; when the current one is full,\n * a new one is created with higher capacity and tighter FPR.\n *\n * Port of Python pybloom-live's `ScalableBloomFilter`.\n *\n * @example\n * const sbf = new ScalableBloomFilter({ errorRate: 0.01 });\n * for (const id of millionIds) sbf.add(id);\n * sbf.has(id); // reliable, even with 1M+ items\n */\nexport class ScalableBloomFilter {\n private readonly _initialCapacity: number;\n private readonly _errorRate: number;\n private readonly _scaleFactor: number;\n private readonly _tighteningRatio: number;\n private _filters: BloomFilter[];\n private _count = 0;\n\n constructor(options: ScalableBloomFilterOptions = {}) {\n this._initialCapacity = options.initialCapacity ?? 1000;\n this._errorRate = options.errorRate ?? 0.01;\n this._scaleFactor = options.scaleFactor ?? 2;\n this._tighteningRatio = options.tighteningRatio ?? 0.9;\n\n if (this._errorRate <= 0 || this._errorRate >= 1) throw new RangeError(\"errorRate must be in (0, 1)\");\n if (this._scaleFactor < 2) throw new RangeError(\"scaleFactor must be >= 2\");\n if (this._tighteningRatio <= 0 || this._tighteningRatio >= 1) throw new RangeError(\"tighteningRatio must be in (0, 1)\");\n\n this._filters = [this._createFilter(0)];\n }\n\n private _createFilter(index: number): BloomFilter {\n const capacity = Math.ceil(this._initialCapacity * Math.pow(this._scaleFactor, index));\n const errorRate = this._errorRate * Math.pow(this._tighteningRatio, index);\n return new BloomFilter({ capacity, errorRate });\n }\n\n /** Add an item. The filter grows automatically when the current slice is full. */\n add(item: string): this {\n const current = this._filters[this._filters.length - 1]!;\n\n // Grow when we'd exceed capacity (keeps FPR ≤ target)\n if (current.size >= current.capacity) {\n this._filters.push(this._createFilter(this._filters.length));\n }\n\n this._filters[this._filters.length - 1]!.add(item);\n this._count++;\n return this;\n }\n\n /**\n * Test membership — checks all sub-filters.\n * Returns `true` if item may have been added, `false` if definitely not.\n */\n has(item: string): boolean {\n for (const filter of this._filters) {\n if (filter.has(item)) return true;\n }\n return false;\n }\n\n /** Total number of items added (approximate). */\n get size(): number {\n return this._count;\n }\n\n /** Number of internal sub-filters created so far. */\n get filterCount(): number {\n return this._filters.length;\n }\n\n /** Total bits allocated across all sub-filters. */\n get bitsAllocated(): number {\n return this._filters.reduce((s, f) => s + f.m, 0);\n }\n\n /** Target false-positive rate. */\n get errorRate(): number {\n return this._errorRate;\n }\n\n /** Reset the filter. */\n clear(): this {\n this._filters = [this._createFilter(0)];\n this._count = 0;\n return this;\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bloomkit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Zero-dependency TypeScript Bloom filter: standard, counting, and scalable variants. Probabilistic set membership with tunable false-positive rate. Port of Python pybloom-live / Java Guava BloomFilter.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"test": "node --experimental-vm-modules node_modules/.bin/jest --forceExit",
|
|
25
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"bloom-filter",
|
|
29
|
+
"bloom",
|
|
30
|
+
"probabilistic",
|
|
31
|
+
"set",
|
|
32
|
+
"membership",
|
|
33
|
+
"deduplication",
|
|
34
|
+
"typescript",
|
|
35
|
+
"zero-dependencies"
|
|
36
|
+
],
|
|
37
|
+
"author": "trananhtung",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/trananhtung/bloomkit.git"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/jest": "^30.0.0",
|
|
45
|
+
"jest": "^30.4.2",
|
|
46
|
+
"ts-jest": "^29.4.11",
|
|
47
|
+
"tsup": "^8.5.1",
|
|
48
|
+
"typescript": "^6.0.3"
|
|
49
|
+
}
|
|
50
|
+
}
|