distillate 0.9.0 → 0.11.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/README.md +44 -6
- package/dist/bitset-Cf2jwRJX.js +32 -0
- package/dist/bitset-QYGqxnEf.cjs +37 -0
- package/dist/blocked/index.cjs +14 -9
- package/dist/blocked/index.d.cts +7 -6
- package/dist/blocked/index.d.ts +7 -6
- package/dist/blocked/index.js +12 -8
- package/dist/bloom/index.cjs +16 -41
- package/dist/bloom/index.d.cts +5 -4
- package/dist/bloom/index.d.ts +5 -4
- package/dist/bloom/index.js +13 -39
- package/dist/bytes-DCuYtUVS.d.cts +4 -0
- package/dist/bytes-DCuYtUVS.d.ts +4 -0
- package/dist/frame/index.cjs +9 -0
- package/dist/frame/index.d.cts +2 -0
- package/dist/frame/index.d.ts +2 -0
- package/dist/frame/index.js +2 -0
- package/dist/fuse/index.cjs +26 -22
- package/dist/fuse/index.d.cts +7 -6
- package/dist/fuse/index.d.ts +7 -6
- package/dist/fuse/index.js +14 -11
- package/dist/hasher-3Cyn7Yg-.js +230 -0
- package/dist/hasher-MhY7aQ8F.cjs +289 -0
- package/dist/hll/index.cjs +12 -8
- package/dist/hll/index.d.cts +4 -3
- package/dist/hll/index.d.ts +4 -3
- package/dist/hll/index.js +11 -8
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js +1 -1
- package/dist/scalable/index.cjs +340 -0
- package/dist/scalable/index.d.cts +173 -0
- package/dist/scalable/index.d.ts +173 -0
- package/dist/scalable/index.js +330 -0
- package/dist/{serialize-Bv1zWzrh.d.cts → serialize--xR6vGSW.d.cts} +44 -8
- package/dist/{serialize-Bv1zWzrh.d.ts → serialize--xR6vGSW.d.ts} +44 -8
- package/dist/serialize-CHikZ4GH.cjs +296 -0
- package/dist/serialize-CMSYFym-.js +201 -0
- package/package.json +25 -9
- package/dist/serialize-D59Ri81a.js +0 -392
- package/dist/serialize-Ul2LKFJU.cjs +0 -517
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_bitset = require("../bitset-QYGqxnEf.cjs");
|
|
3
|
+
const require_hasher = require("../hasher-MhY7aQ8F.cjs");
|
|
4
|
+
const require_serialize = require("../serialize-CHikZ4GH.cjs");
|
|
5
|
+
const require_params = require("../params-UTZbJ22c.cjs");
|
|
6
|
+
const require_sizing = require("../sizing-BLzZk2zr.cjs");
|
|
7
|
+
//#region src/scalable/scalable.ts
|
|
8
|
+
const MAX_BITS = 4294967295;
|
|
9
|
+
const TYPE = 6;
|
|
10
|
+
const PARAMS_SIZE = 40;
|
|
11
|
+
const ENTRY_SIZE = 16;
|
|
12
|
+
const padded8 = (bytes) => Math.ceil(bytes / 8) * 8;
|
|
13
|
+
let restoring;
|
|
14
|
+
var ScalableParamMismatchError = class extends Error {
|
|
15
|
+
name = "ScalableParamMismatchError";
|
|
16
|
+
};
|
|
17
|
+
function readTable(view, stageCount) {
|
|
18
|
+
const entries = [];
|
|
19
|
+
for (let i = 0; i < stageCount; i++) {
|
|
20
|
+
const at = PARAMS_SIZE + ENTRY_SIZE * i;
|
|
21
|
+
const entry = {
|
|
22
|
+
m: view.getUint32(at, true),
|
|
23
|
+
k: view.getUint16(at + 4, true),
|
|
24
|
+
capacity: view.getUint32(at + 8, true),
|
|
25
|
+
count: view.getUint32(at + 12, true)
|
|
26
|
+
};
|
|
27
|
+
if (view.getUint16(at + 6, true) !== 0) throw new require_serialize.SerializationError(`scalable: stage ${String(i)} table padding is not zero`);
|
|
28
|
+
if (entry.m === 0 || entry.k === 0 || entry.capacity === 0) throw new require_serialize.SerializationError(`scalable: stage ${String(i)} declares m=${String(entry.m)}, k=${String(entry.k)}, capacity=${String(entry.capacity)}; all must be positive`);
|
|
29
|
+
if (entry.count > entry.capacity) throw new require_serialize.SerializationError(`scalable: stage ${String(i)} holds ${String(entry.count)} keys, above its capacity of ${String(entry.capacity)}`);
|
|
30
|
+
entries.push(entry);
|
|
31
|
+
}
|
|
32
|
+
return entries;
|
|
33
|
+
}
|
|
34
|
+
function readStages(body, entries, start) {
|
|
35
|
+
let at = start;
|
|
36
|
+
return entries.map((entry, i) => {
|
|
37
|
+
const length = Math.ceil(entry.m / 8);
|
|
38
|
+
const bits = new require_bitset.BitSet(entry.m);
|
|
39
|
+
bits.bytes.set(body.subarray(at, at + length));
|
|
40
|
+
for (let j = at + length; j < at + padded8(length); j++) if (body[j] !== 0) throw new require_serialize.SerializationError(`scalable: stage ${String(i)} bit padding is not zero`);
|
|
41
|
+
at += padded8(length);
|
|
42
|
+
return {
|
|
43
|
+
...entry,
|
|
44
|
+
bits
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
var ScalableBloomFilter = class ScalableBloomFilter {
|
|
49
|
+
#n;
|
|
50
|
+
#epsilon;
|
|
51
|
+
#growth;
|
|
52
|
+
#tightening;
|
|
53
|
+
#seed;
|
|
54
|
+
#stages = [];
|
|
55
|
+
#hash = {
|
|
56
|
+
w0: 0,
|
|
57
|
+
w1: 0,
|
|
58
|
+
w2: 0,
|
|
59
|
+
w3: 0
|
|
60
|
+
};
|
|
61
|
+
#probes = new Uint32Array(0);
|
|
62
|
+
#newest;
|
|
63
|
+
static create(n, epsilon, options = {}) {
|
|
64
|
+
return new ScalableBloomFilter({
|
|
65
|
+
...options,
|
|
66
|
+
n,
|
|
67
|
+
epsilon
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
static from(keys, epsilon, options = {}) {
|
|
71
|
+
const arr = [...keys];
|
|
72
|
+
const f = ScalableBloomFilter.create(Math.max(1, arr.length), epsilon, options);
|
|
73
|
+
for (const key of arr) f.add(key);
|
|
74
|
+
return f;
|
|
75
|
+
}
|
|
76
|
+
constructor({ n, epsilon, growth = 2, tightening = .85, seed = 0 }) {
|
|
77
|
+
require_params.assertPositiveInt(n, "n");
|
|
78
|
+
require_params.assertUint32(n, "n");
|
|
79
|
+
require_params.assertProbability(epsilon, "epsilon");
|
|
80
|
+
if (!Number.isFinite(growth) || growth <= 1) throw new require_params.ParamError(`growth must be a finite number greater than 1, got ${String(growth)}`);
|
|
81
|
+
require_params.assertProbability(tightening, "tightening");
|
|
82
|
+
require_params.assertUint32(seed, "seed");
|
|
83
|
+
this.#n = n;
|
|
84
|
+
this.#epsilon = epsilon;
|
|
85
|
+
this.#growth = growth;
|
|
86
|
+
this.#tightening = tightening;
|
|
87
|
+
this.#seed = seed;
|
|
88
|
+
if (restoring) {
|
|
89
|
+
this.#newest = this.#adopt(restoring);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const first = this.#geometry(0);
|
|
93
|
+
if (first.m > MAX_BITS) throw new require_params.ParamError(`the first stage needs ${String(first.m)} bits, more than ${String(MAX_BITS)}`);
|
|
94
|
+
this.#newest = this.#open(first);
|
|
95
|
+
}
|
|
96
|
+
#geometry(i) {
|
|
97
|
+
const capacity = Math.ceil(this.#n * this.#growth ** i);
|
|
98
|
+
const target = this.#epsilon * (1 - this.#tightening) * this.#tightening ** i;
|
|
99
|
+
return {
|
|
100
|
+
capacity,
|
|
101
|
+
...require_sizing.bloomSizing(capacity, target)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
#open({ capacity, m, k }) {
|
|
105
|
+
const stage = {
|
|
106
|
+
bits: new require_bitset.BitSet(m),
|
|
107
|
+
m,
|
|
108
|
+
k,
|
|
109
|
+
capacity,
|
|
110
|
+
count: 0
|
|
111
|
+
};
|
|
112
|
+
this.#stages.push(stage);
|
|
113
|
+
if (k > this.#probes.length) this.#probes = new Uint32Array(k);
|
|
114
|
+
return stage;
|
|
115
|
+
}
|
|
116
|
+
#settingPairs(other) {
|
|
117
|
+
return [
|
|
118
|
+
[
|
|
119
|
+
"n",
|
|
120
|
+
this.#n,
|
|
121
|
+
other.#n
|
|
122
|
+
],
|
|
123
|
+
[
|
|
124
|
+
"epsilon",
|
|
125
|
+
this.#epsilon,
|
|
126
|
+
other.#epsilon
|
|
127
|
+
],
|
|
128
|
+
[
|
|
129
|
+
"growth",
|
|
130
|
+
this.#growth,
|
|
131
|
+
other.#growth
|
|
132
|
+
],
|
|
133
|
+
[
|
|
134
|
+
"tightening",
|
|
135
|
+
this.#tightening,
|
|
136
|
+
other.#tightening
|
|
137
|
+
],
|
|
138
|
+
[
|
|
139
|
+
"seed",
|
|
140
|
+
this.#seed,
|
|
141
|
+
other.#seed
|
|
142
|
+
]
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
#assertMergeable(other) {
|
|
146
|
+
for (const [name, a, b] of this.#settingPairs(other)) if (a !== b) throw new ScalableParamMismatchError(`cannot union scalable Bloom filters whose ${name} differs (${String(a)} vs ${String(b)})`);
|
|
147
|
+
this.#stages.forEach((s, i) => {
|
|
148
|
+
const t = other.#stages[i];
|
|
149
|
+
if (!t) return;
|
|
150
|
+
for (const field of [
|
|
151
|
+
"m",
|
|
152
|
+
"k",
|
|
153
|
+
"capacity"
|
|
154
|
+
]) if (s[field] !== t[field]) throw new ScalableParamMismatchError(`cannot union scalable Bloom filters whose stage ${String(i)} ${field} differs (${String(s[field])} vs ${String(t[field])})`);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
#adopt(stages) {
|
|
158
|
+
this.#stages.length = 0;
|
|
159
|
+
this.#stages.push(...stages);
|
|
160
|
+
const k = Math.max(...stages.map((s) => s.k));
|
|
161
|
+
if (k > this.#probes.length) this.#probes = new Uint32Array(k);
|
|
162
|
+
return stages.reduce((_, s) => s);
|
|
163
|
+
}
|
|
164
|
+
#probe(stage) {
|
|
165
|
+
require_hasher.probeLanesInto(this.#hash.w0, this.#hash.w1, stage.k, stage.m, this.#probes);
|
|
166
|
+
}
|
|
167
|
+
#holds(stage) {
|
|
168
|
+
const { w0, w1 } = this.#hash;
|
|
169
|
+
for (let i = 0; i < stage.k; i++) if (!stage.bits.get(require_hasher.probeAt(w0, w1, i, stage.m))) return false;
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
#anyHolds() {
|
|
173
|
+
for (let i = this.#stages.length - 1; i >= 0; i--) {
|
|
174
|
+
const stage = this.#stages[i];
|
|
175
|
+
if (stage && this.#holds(stage)) return true;
|
|
176
|
+
}
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
add(key) {
|
|
180
|
+
require_hasher.hash128KeyInto(key, this.#seed, this.#hash);
|
|
181
|
+
if (this.#anyHolds()) return;
|
|
182
|
+
if (this.#newest.count >= this.#newest.capacity) {
|
|
183
|
+
const i = this.#stages.length;
|
|
184
|
+
const next = this.#geometry(i);
|
|
185
|
+
if (next.m > MAX_BITS) throw new RangeError(`stage ${String(i)} needs ${String(next.m)} bits, more than ${String(MAX_BITS)}; the filter cannot grow further`);
|
|
186
|
+
this.#newest = this.#open(next);
|
|
187
|
+
}
|
|
188
|
+
const stage = this.#newest;
|
|
189
|
+
this.#probe(stage);
|
|
190
|
+
for (let i = 0; i < stage.k; i++) stage.bits.set(this.#probes[i] ?? 0);
|
|
191
|
+
stage.count++;
|
|
192
|
+
}
|
|
193
|
+
has(key) {
|
|
194
|
+
require_hasher.hash128KeyInto(key, this.#seed, this.#hash);
|
|
195
|
+
return this.#anyHolds();
|
|
196
|
+
}
|
|
197
|
+
rate() {
|
|
198
|
+
let miss = 1;
|
|
199
|
+
for (const s of this.#stages) miss *= 1 - (s.bits.count() / s.m) ** s.k;
|
|
200
|
+
return 1 - miss;
|
|
201
|
+
}
|
|
202
|
+
union(other) {
|
|
203
|
+
this.#assertMergeable(other);
|
|
204
|
+
const result = new ScalableBloomFilter({
|
|
205
|
+
n: this.#n,
|
|
206
|
+
epsilon: this.#epsilon,
|
|
207
|
+
growth: this.#growth,
|
|
208
|
+
tightening: this.#tightening,
|
|
209
|
+
seed: this.#seed
|
|
210
|
+
});
|
|
211
|
+
const [longer, shorter] = this.#stages.length >= other.#stages.length ? [this.#stages, other.#stages] : [other.#stages, this.#stages];
|
|
212
|
+
const stages = longer.map((stage, i) => {
|
|
213
|
+
const bits = new require_bitset.BitSet(stage.m);
|
|
214
|
+
bits.bytes.set(stage.bits.bytes);
|
|
215
|
+
const twin = shorter[i];
|
|
216
|
+
if (twin) {
|
|
217
|
+
const out = bits.bytes;
|
|
218
|
+
const bytes = twin.bits.bytes;
|
|
219
|
+
for (let j = 0; j < out.length; j++) out[j] = (out[j] ?? 0) | (bytes[j] ?? 0);
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
bits,
|
|
223
|
+
m: stage.m,
|
|
224
|
+
k: stage.k,
|
|
225
|
+
capacity: stage.capacity,
|
|
226
|
+
count: Math.min(stage.capacity, stage.count + (twin?.count ?? 0))
|
|
227
|
+
};
|
|
228
|
+
});
|
|
229
|
+
result.#newest = result.#adopt(stages);
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
equals(other) {
|
|
233
|
+
if (this.#settingPairs(other).some(([, a, b]) => a !== b)) return false;
|
|
234
|
+
if (this.#stages.length !== other.#stages.length) return false;
|
|
235
|
+
return this.#stages.every((s, i) => {
|
|
236
|
+
const t = other.#stages[i];
|
|
237
|
+
if (!t) return false;
|
|
238
|
+
return s.m === t.m && s.k === t.k && s.capacity === t.capacity && s.count === t.count && require_serialize.bytesEqual(s.bits.bytes, t.bits.bytes);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
static fromBytes(bytes) {
|
|
242
|
+
const { type, flags, body } = require_serialize.readHeader(bytes);
|
|
243
|
+
if (type !== TYPE) throw new require_serialize.SerializationError(`expected DSTL type ${String(TYPE)}, got ${String(type)}`);
|
|
244
|
+
if ((flags & 15) !== 0) throw new require_serialize.UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
|
|
245
|
+
require_serialize.assertMinBodyLength(body.length, PARAMS_SIZE, "scalable");
|
|
246
|
+
require_serialize.assertParamsPadding(body, 36, PARAMS_SIZE, "scalable");
|
|
247
|
+
const view = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
248
|
+
const stageCount = view.getUint32(32, true);
|
|
249
|
+
if (stageCount === 0) throw new require_serialize.SerializationError("scalable: frame declares no stages");
|
|
250
|
+
const tableEnd = PARAMS_SIZE + ENTRY_SIZE * stageCount;
|
|
251
|
+
require_serialize.assertMinBodyLength(body.length, tableEnd, "scalable");
|
|
252
|
+
const entries = readTable(view, stageCount);
|
|
253
|
+
const expected = entries.reduce((sum, e) => sum + padded8(Math.ceil(e.m / 8)), tableEnd);
|
|
254
|
+
require_serialize.assertBodyLength(body.length, expected, "scalable");
|
|
255
|
+
restoring = readStages(body, entries, tableEnd);
|
|
256
|
+
try {
|
|
257
|
+
return new ScalableBloomFilter({
|
|
258
|
+
n: view.getUint32(0, true),
|
|
259
|
+
seed: view.getUint32(4, true),
|
|
260
|
+
epsilon: view.getFloat64(8, true),
|
|
261
|
+
growth: view.getFloat64(16, true),
|
|
262
|
+
tightening: view.getFloat64(24, true)
|
|
263
|
+
});
|
|
264
|
+
} catch (err) {
|
|
265
|
+
if (err instanceof require_params.ParamError) throw new require_serialize.SerializationError(`scalable: ${err.message}`);
|
|
266
|
+
throw err;
|
|
267
|
+
} finally {
|
|
268
|
+
restoring = void 0;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
toBytes() {
|
|
272
|
+
const payload = this.#stages.reduce((sum, s) => sum + ENTRY_SIZE + padded8(s.bits.bytes.length), 0);
|
|
273
|
+
return require_serialize.writeFrame({
|
|
274
|
+
version: 5,
|
|
275
|
+
type: TYPE,
|
|
276
|
+
flags: 0
|
|
277
|
+
}, PARAMS_SIZE, payload, (body, view) => {
|
|
278
|
+
view.setUint32(0, this.#n, true);
|
|
279
|
+
view.setUint32(4, this.#seed, true);
|
|
280
|
+
view.setFloat64(8, this.#epsilon, true);
|
|
281
|
+
view.setFloat64(16, this.#growth, true);
|
|
282
|
+
view.setFloat64(24, this.#tightening, true);
|
|
283
|
+
view.setUint32(32, this.#stages.length, true);
|
|
284
|
+
let at = PARAMS_SIZE + ENTRY_SIZE * this.#stages.length;
|
|
285
|
+
this.#stages.forEach((s, i) => {
|
|
286
|
+
const entry = PARAMS_SIZE + ENTRY_SIZE * i;
|
|
287
|
+
view.setUint32(entry, s.m, true);
|
|
288
|
+
view.setUint16(entry + 4, s.k, true);
|
|
289
|
+
view.setUint32(entry + 8, s.capacity, true);
|
|
290
|
+
view.setUint32(entry + 12, s.count, true);
|
|
291
|
+
body.set(s.bits.bytes, at);
|
|
292
|
+
at += padded8(s.bits.bytes.length);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
toJSON() {
|
|
297
|
+
return require_serialize.toJSONEnvelope(this.toBytes());
|
|
298
|
+
}
|
|
299
|
+
static fromJSON(value) {
|
|
300
|
+
return ScalableBloomFilter.fromBytes(require_serialize.fromJSONEnvelope(value));
|
|
301
|
+
}
|
|
302
|
+
get count() {
|
|
303
|
+
return this.#stages.reduce((sum, s) => sum + s.count, 0);
|
|
304
|
+
}
|
|
305
|
+
get capacity() {
|
|
306
|
+
return this.#stages.reduce((sum, s) => sum + s.capacity, 0);
|
|
307
|
+
}
|
|
308
|
+
get stages() {
|
|
309
|
+
return this.#stages.length;
|
|
310
|
+
}
|
|
311
|
+
get m() {
|
|
312
|
+
return this.#stages.reduce((sum, s) => sum + s.m, 0);
|
|
313
|
+
}
|
|
314
|
+
get bitsPerKey() {
|
|
315
|
+
return this.m / this.capacity;
|
|
316
|
+
}
|
|
317
|
+
get seed() {
|
|
318
|
+
return this.#seed;
|
|
319
|
+
}
|
|
320
|
+
get epsilon() {
|
|
321
|
+
return this.#epsilon;
|
|
322
|
+
}
|
|
323
|
+
get growth() {
|
|
324
|
+
return this.#growth;
|
|
325
|
+
}
|
|
326
|
+
get tightening() {
|
|
327
|
+
return this.#tightening;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
//#endregion
|
|
331
|
+
exports.BadMagicError = require_serialize.BadMagicError;
|
|
332
|
+
exports.ChecksumError = require_serialize.ChecksumError;
|
|
333
|
+
exports.ParamError = require_params.ParamError;
|
|
334
|
+
exports.ReservedBitsError = require_serialize.ReservedBitsError;
|
|
335
|
+
exports.ScalableBloomFilter = ScalableBloomFilter;
|
|
336
|
+
exports.ScalableParamMismatchError = ScalableParamMismatchError;
|
|
337
|
+
exports.SerializationError = require_serialize.SerializationError;
|
|
338
|
+
exports.TruncatedError = require_serialize.TruncatedError;
|
|
339
|
+
exports.UnknownHashVariantError = require_serialize.UnknownHashVariantError;
|
|
340
|
+
exports.UnknownVersionError = require_serialize.UnknownVersionError;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { t as BytesLike } from "../bytes-DCuYtUVS.cjs";
|
|
2
|
+
import { a as ReservedBitsError, c as UnknownHashVariantError, l as UnknownVersionError, n as ChecksumError, o as SerializationError, r as FilterJSON, s as TruncatedError, t as BadMagicError } from "../serialize--xR6vGSW.cjs";
|
|
3
|
+
import { t as ParamError } from "../params-DnqJBqLS.cjs";
|
|
4
|
+
//#region src/scalable/scalable.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Thrown by {@link ScalableBloomFilter.union} when the two filters were built
|
|
7
|
+
* with different settings. Stage `i` has the same geometry in both only when
|
|
8
|
+
* every setting matches, so no stage-by-stage merge exists otherwise.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ScalableParamMismatchError extends Error {
|
|
11
|
+
/** Discriminates this error from other `Error`s. */
|
|
12
|
+
override readonly name = "ScalableParamMismatchError";
|
|
13
|
+
}
|
|
14
|
+
/** Settings for a {@link ScalableBloomFilter}. */
|
|
15
|
+
interface ScalableBloomParams {
|
|
16
|
+
/** Keys the first stage holds before the next one opens. */
|
|
17
|
+
n: number;
|
|
18
|
+
/** Target false-positive rate for the whole chain, e.g. `0.01` for 1%. */
|
|
19
|
+
epsilon: number;
|
|
20
|
+
/** Capacity multiplier from one stage to the next; defaults to `2`. */
|
|
21
|
+
growth?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Multiplier on each stage's false-positive target relative to the one
|
|
24
|
+
* before; defaults to `0.85`. Lower spends more bits per stage to open
|
|
25
|
+
* fewer of them.
|
|
26
|
+
*/
|
|
27
|
+
tightening?: number;
|
|
28
|
+
/** Hash seed; defaults to `0`. */
|
|
29
|
+
seed?: number;
|
|
30
|
+
}
|
|
31
|
+
/** The optional {@link ScalableBloomParams}, as `create` and `from` take them. */
|
|
32
|
+
type ScalableBloomOptions = Omit<ScalableBloomParams, "n" | "epsilon">;
|
|
33
|
+
/**
|
|
34
|
+
* A Bloom filter that grows with its key count and keeps its false-positive
|
|
35
|
+
* rate under `epsilon`, for when the number of keys is not known up front.
|
|
36
|
+
*
|
|
37
|
+
* It is a chain of Bloom stages. Stage `i` holds `n * growth ** i` keys at a
|
|
38
|
+
* false-positive target of `epsilon * (1 - tightening) * tightening ** i`;
|
|
39
|
+
* those targets sum to at most `epsilon`, however many stages open.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const seen = ScalableBloomFilter.create(1000, 0.01);
|
|
44
|
+
* seen.add("alice");
|
|
45
|
+
* seen.has("alice"); // true
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare class ScalableBloomFilter {
|
|
49
|
+
#private;
|
|
50
|
+
/**
|
|
51
|
+
* Creates a filter whose first stage holds `n` keys, for a chain-wide
|
|
52
|
+
* false-positive target.
|
|
53
|
+
*
|
|
54
|
+
* @param n - Keys the first stage holds.
|
|
55
|
+
* @param epsilon - Target false-positive rate for the whole chain.
|
|
56
|
+
* @param options - Optional growth, tightening and seed.
|
|
57
|
+
* @returns A new, empty filter.
|
|
58
|
+
*/
|
|
59
|
+
static create(n: number, epsilon: number, options?: ScalableBloomOptions): ScalableBloomFilter;
|
|
60
|
+
/**
|
|
61
|
+
* Builds a filter from `keys`, with the first stage sized for their count;
|
|
62
|
+
* it keeps growing as more keys arrive. Duplicates only oversize the first
|
|
63
|
+
* stage, since a key already held is not counted again.
|
|
64
|
+
*
|
|
65
|
+
* @param keys - The keys to insert.
|
|
66
|
+
* @param epsilon - Target false-positive rate for the whole chain.
|
|
67
|
+
* @param options - Optional growth, tightening and seed.
|
|
68
|
+
* @returns A new filter containing every key.
|
|
69
|
+
*/
|
|
70
|
+
static from(keys: Iterable<BytesLike>, epsilon: number, options?: ScalableBloomOptions): ScalableBloomFilter;
|
|
71
|
+
/**
|
|
72
|
+
* @param params - The filter's settings.
|
|
73
|
+
* @throws {@link ParamError} if a setting is out of range, or the first
|
|
74
|
+
* stage would need more than `2^32 - 1` bits.
|
|
75
|
+
*/
|
|
76
|
+
constructor({ n, epsilon, growth, tightening, seed }: ScalableBloomParams);
|
|
77
|
+
/**
|
|
78
|
+
* Adds a key. A key the filter already holds is not counted again, so
|
|
79
|
+
* duplicates never use up a stage's capacity.
|
|
80
|
+
*
|
|
81
|
+
* @param key - The key to insert, as a string or bytes.
|
|
82
|
+
* @throws RangeError if the next stage would need more than `2^32 - 1`
|
|
83
|
+
* bits; the filter is left unchanged and still answers queries.
|
|
84
|
+
*/
|
|
85
|
+
add(key: BytesLike): void;
|
|
86
|
+
/**
|
|
87
|
+
* Tests whether a key is in the set.
|
|
88
|
+
*
|
|
89
|
+
* @param key - The key to test.
|
|
90
|
+
* @returns `true` if present (possibly a false positive); `false` guarantees absence.
|
|
91
|
+
*/
|
|
92
|
+
has(key: BytesLike): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Estimates the chain's current false-positive rate from each stage's actual
|
|
95
|
+
* fill: a key is a false positive if any stage wrongly holds it, so this is
|
|
96
|
+
* `1 - prod(1 - (setBits / m) ** k)`. It rises as keys are added and stays
|
|
97
|
+
* under `epsilon` while every stage is within its capacity.
|
|
98
|
+
*
|
|
99
|
+
* @returns The estimated false-positive rate, in `[0, 1]`.
|
|
100
|
+
*/
|
|
101
|
+
rate(): number;
|
|
102
|
+
/**
|
|
103
|
+
* Returns a new filter holding every key of this filter and `other`. Stage
|
|
104
|
+
* `i` of the result is the OR of both inputs' stage `i`, and the result has
|
|
105
|
+
* the longer of the two chains. Each stage's count becomes the smaller of
|
|
106
|
+
* its capacity and the two counts summed: an upper bound, so the merged
|
|
107
|
+
* chain opens its next stage early rather than late.
|
|
108
|
+
*
|
|
109
|
+
* @param other - A filter built with identical settings.
|
|
110
|
+
* @returns A new filter; neither input is changed.
|
|
111
|
+
* @throws {@link ScalableParamMismatchError} if any setting, or the
|
|
112
|
+
* geometry of a stage both filters have, differs.
|
|
113
|
+
*/
|
|
114
|
+
union(other: ScalableBloomFilter): ScalableBloomFilter;
|
|
115
|
+
/**
|
|
116
|
+
* Tests structural equality: identical settings, the same stages with the
|
|
117
|
+
* same counts, and identical bits, which is exactly when the two serialize
|
|
118
|
+
* to the same bytes. Which stage a key lands in depends on when it arrived,
|
|
119
|
+
* so two filters given the same keys in a different order can be unequal.
|
|
120
|
+
*
|
|
121
|
+
* @param other - The filter to compare against.
|
|
122
|
+
* @returns `true` if the two filters are identical.
|
|
123
|
+
*/
|
|
124
|
+
equals(other: ScalableBloomFilter): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Restores a filter from its {@link ScalableBloomFilter.toBytes} serialization.
|
|
127
|
+
*
|
|
128
|
+
* @param bytes - A frame produced by `toBytes`.
|
|
129
|
+
* @returns The reconstructed filter.
|
|
130
|
+
* @throws {@link SerializationError} (or a subclass) if the frame is malformed.
|
|
131
|
+
*/
|
|
132
|
+
static fromBytes(bytes: Uint8Array): ScalableBloomFilter;
|
|
133
|
+
/**
|
|
134
|
+
* Serializes the filter to a portable little-endian frame, DSTL type 6.
|
|
135
|
+
*
|
|
136
|
+
* @returns The serialized filter, readable by {@link ScalableBloomFilter.fromBytes}.
|
|
137
|
+
*/
|
|
138
|
+
toBytes(): Uint8Array;
|
|
139
|
+
/**
|
|
140
|
+
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
141
|
+
* {@link ScalableBloomFilter.toBytes}.
|
|
142
|
+
*
|
|
143
|
+
* @returns The JSON envelope.
|
|
144
|
+
*/
|
|
145
|
+
toJSON(): FilterJSON;
|
|
146
|
+
/**
|
|
147
|
+
* Restores a filter from its {@link ScalableBloomFilter.toJSON} envelope.
|
|
148
|
+
*
|
|
149
|
+
* @param value - The JSON envelope.
|
|
150
|
+
* @returns The reconstructed filter.
|
|
151
|
+
*/
|
|
152
|
+
static fromJSON(value: unknown): ScalableBloomFilter;
|
|
153
|
+
/** Keys added that the filter did not already hold. */
|
|
154
|
+
get count(): number;
|
|
155
|
+
/** Keys the open stages hold between them before the next one opens. */
|
|
156
|
+
get capacity(): number;
|
|
157
|
+
/** Number of open stages. */
|
|
158
|
+
get stages(): number;
|
|
159
|
+
/** Total bits across every stage. */
|
|
160
|
+
get m(): number;
|
|
161
|
+
/** Analytic design bits-per-key, `m / capacity`. */
|
|
162
|
+
get bitsPerKey(): number;
|
|
163
|
+
/** Hash seed shared by every stage. */
|
|
164
|
+
get seed(): number;
|
|
165
|
+
/** Target false-positive rate for the whole chain. */
|
|
166
|
+
get epsilon(): number;
|
|
167
|
+
/** Capacity multiplier from one stage to the next. */
|
|
168
|
+
get growth(): number;
|
|
169
|
+
/** False-positive target multiplier from one stage to the next. */
|
|
170
|
+
get tightening(): number;
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
export { BadMagicError, ChecksumError, type FilterJSON, ParamError, ReservedBitsError, type ScalableBloomOptions, type ScalableBloomParams, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError };
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { t as BytesLike } from "../bytes-DCuYtUVS.js";
|
|
2
|
+
import { a as ReservedBitsError, c as UnknownHashVariantError, l as UnknownVersionError, n as ChecksumError, o as SerializationError, r as FilterJSON, s as TruncatedError, t as BadMagicError } from "../serialize--xR6vGSW.js";
|
|
3
|
+
import { t as ParamError } from "../params-DnqJBqLS.js";
|
|
4
|
+
//#region src/scalable/scalable.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Thrown by {@link ScalableBloomFilter.union} when the two filters were built
|
|
7
|
+
* with different settings. Stage `i` has the same geometry in both only when
|
|
8
|
+
* every setting matches, so no stage-by-stage merge exists otherwise.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ScalableParamMismatchError extends Error {
|
|
11
|
+
/** Discriminates this error from other `Error`s. */
|
|
12
|
+
override readonly name = "ScalableParamMismatchError";
|
|
13
|
+
}
|
|
14
|
+
/** Settings for a {@link ScalableBloomFilter}. */
|
|
15
|
+
interface ScalableBloomParams {
|
|
16
|
+
/** Keys the first stage holds before the next one opens. */
|
|
17
|
+
n: number;
|
|
18
|
+
/** Target false-positive rate for the whole chain, e.g. `0.01` for 1%. */
|
|
19
|
+
epsilon: number;
|
|
20
|
+
/** Capacity multiplier from one stage to the next; defaults to `2`. */
|
|
21
|
+
growth?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Multiplier on each stage's false-positive target relative to the one
|
|
24
|
+
* before; defaults to `0.85`. Lower spends more bits per stage to open
|
|
25
|
+
* fewer of them.
|
|
26
|
+
*/
|
|
27
|
+
tightening?: number;
|
|
28
|
+
/** Hash seed; defaults to `0`. */
|
|
29
|
+
seed?: number;
|
|
30
|
+
}
|
|
31
|
+
/** The optional {@link ScalableBloomParams}, as `create` and `from` take them. */
|
|
32
|
+
type ScalableBloomOptions = Omit<ScalableBloomParams, "n" | "epsilon">;
|
|
33
|
+
/**
|
|
34
|
+
* A Bloom filter that grows with its key count and keeps its false-positive
|
|
35
|
+
* rate under `epsilon`, for when the number of keys is not known up front.
|
|
36
|
+
*
|
|
37
|
+
* It is a chain of Bloom stages. Stage `i` holds `n * growth ** i` keys at a
|
|
38
|
+
* false-positive target of `epsilon * (1 - tightening) * tightening ** i`;
|
|
39
|
+
* those targets sum to at most `epsilon`, however many stages open.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const seen = ScalableBloomFilter.create(1000, 0.01);
|
|
44
|
+
* seen.add("alice");
|
|
45
|
+
* seen.has("alice"); // true
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare class ScalableBloomFilter {
|
|
49
|
+
#private;
|
|
50
|
+
/**
|
|
51
|
+
* Creates a filter whose first stage holds `n` keys, for a chain-wide
|
|
52
|
+
* false-positive target.
|
|
53
|
+
*
|
|
54
|
+
* @param n - Keys the first stage holds.
|
|
55
|
+
* @param epsilon - Target false-positive rate for the whole chain.
|
|
56
|
+
* @param options - Optional growth, tightening and seed.
|
|
57
|
+
* @returns A new, empty filter.
|
|
58
|
+
*/
|
|
59
|
+
static create(n: number, epsilon: number, options?: ScalableBloomOptions): ScalableBloomFilter;
|
|
60
|
+
/**
|
|
61
|
+
* Builds a filter from `keys`, with the first stage sized for their count;
|
|
62
|
+
* it keeps growing as more keys arrive. Duplicates only oversize the first
|
|
63
|
+
* stage, since a key already held is not counted again.
|
|
64
|
+
*
|
|
65
|
+
* @param keys - The keys to insert.
|
|
66
|
+
* @param epsilon - Target false-positive rate for the whole chain.
|
|
67
|
+
* @param options - Optional growth, tightening and seed.
|
|
68
|
+
* @returns A new filter containing every key.
|
|
69
|
+
*/
|
|
70
|
+
static from(keys: Iterable<BytesLike>, epsilon: number, options?: ScalableBloomOptions): ScalableBloomFilter;
|
|
71
|
+
/**
|
|
72
|
+
* @param params - The filter's settings.
|
|
73
|
+
* @throws {@link ParamError} if a setting is out of range, or the first
|
|
74
|
+
* stage would need more than `2^32 - 1` bits.
|
|
75
|
+
*/
|
|
76
|
+
constructor({ n, epsilon, growth, tightening, seed }: ScalableBloomParams);
|
|
77
|
+
/**
|
|
78
|
+
* Adds a key. A key the filter already holds is not counted again, so
|
|
79
|
+
* duplicates never use up a stage's capacity.
|
|
80
|
+
*
|
|
81
|
+
* @param key - The key to insert, as a string or bytes.
|
|
82
|
+
* @throws RangeError if the next stage would need more than `2^32 - 1`
|
|
83
|
+
* bits; the filter is left unchanged and still answers queries.
|
|
84
|
+
*/
|
|
85
|
+
add(key: BytesLike): void;
|
|
86
|
+
/**
|
|
87
|
+
* Tests whether a key is in the set.
|
|
88
|
+
*
|
|
89
|
+
* @param key - The key to test.
|
|
90
|
+
* @returns `true` if present (possibly a false positive); `false` guarantees absence.
|
|
91
|
+
*/
|
|
92
|
+
has(key: BytesLike): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Estimates the chain's current false-positive rate from each stage's actual
|
|
95
|
+
* fill: a key is a false positive if any stage wrongly holds it, so this is
|
|
96
|
+
* `1 - prod(1 - (setBits / m) ** k)`. It rises as keys are added and stays
|
|
97
|
+
* under `epsilon` while every stage is within its capacity.
|
|
98
|
+
*
|
|
99
|
+
* @returns The estimated false-positive rate, in `[0, 1]`.
|
|
100
|
+
*/
|
|
101
|
+
rate(): number;
|
|
102
|
+
/**
|
|
103
|
+
* Returns a new filter holding every key of this filter and `other`. Stage
|
|
104
|
+
* `i` of the result is the OR of both inputs' stage `i`, and the result has
|
|
105
|
+
* the longer of the two chains. Each stage's count becomes the smaller of
|
|
106
|
+
* its capacity and the two counts summed: an upper bound, so the merged
|
|
107
|
+
* chain opens its next stage early rather than late.
|
|
108
|
+
*
|
|
109
|
+
* @param other - A filter built with identical settings.
|
|
110
|
+
* @returns A new filter; neither input is changed.
|
|
111
|
+
* @throws {@link ScalableParamMismatchError} if any setting, or the
|
|
112
|
+
* geometry of a stage both filters have, differs.
|
|
113
|
+
*/
|
|
114
|
+
union(other: ScalableBloomFilter): ScalableBloomFilter;
|
|
115
|
+
/**
|
|
116
|
+
* Tests structural equality: identical settings, the same stages with the
|
|
117
|
+
* same counts, and identical bits, which is exactly when the two serialize
|
|
118
|
+
* to the same bytes. Which stage a key lands in depends on when it arrived,
|
|
119
|
+
* so two filters given the same keys in a different order can be unequal.
|
|
120
|
+
*
|
|
121
|
+
* @param other - The filter to compare against.
|
|
122
|
+
* @returns `true` if the two filters are identical.
|
|
123
|
+
*/
|
|
124
|
+
equals(other: ScalableBloomFilter): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Restores a filter from its {@link ScalableBloomFilter.toBytes} serialization.
|
|
127
|
+
*
|
|
128
|
+
* @param bytes - A frame produced by `toBytes`.
|
|
129
|
+
* @returns The reconstructed filter.
|
|
130
|
+
* @throws {@link SerializationError} (or a subclass) if the frame is malformed.
|
|
131
|
+
*/
|
|
132
|
+
static fromBytes(bytes: Uint8Array): ScalableBloomFilter;
|
|
133
|
+
/**
|
|
134
|
+
* Serializes the filter to a portable little-endian frame, DSTL type 6.
|
|
135
|
+
*
|
|
136
|
+
* @returns The serialized filter, readable by {@link ScalableBloomFilter.fromBytes}.
|
|
137
|
+
*/
|
|
138
|
+
toBytes(): Uint8Array;
|
|
139
|
+
/**
|
|
140
|
+
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
141
|
+
* {@link ScalableBloomFilter.toBytes}.
|
|
142
|
+
*
|
|
143
|
+
* @returns The JSON envelope.
|
|
144
|
+
*/
|
|
145
|
+
toJSON(): FilterJSON;
|
|
146
|
+
/**
|
|
147
|
+
* Restores a filter from its {@link ScalableBloomFilter.toJSON} envelope.
|
|
148
|
+
*
|
|
149
|
+
* @param value - The JSON envelope.
|
|
150
|
+
* @returns The reconstructed filter.
|
|
151
|
+
*/
|
|
152
|
+
static fromJSON(value: unknown): ScalableBloomFilter;
|
|
153
|
+
/** Keys added that the filter did not already hold. */
|
|
154
|
+
get count(): number;
|
|
155
|
+
/** Keys the open stages hold between them before the next one opens. */
|
|
156
|
+
get capacity(): number;
|
|
157
|
+
/** Number of open stages. */
|
|
158
|
+
get stages(): number;
|
|
159
|
+
/** Total bits across every stage. */
|
|
160
|
+
get m(): number;
|
|
161
|
+
/** Analytic design bits-per-key, `m / capacity`. */
|
|
162
|
+
get bitsPerKey(): number;
|
|
163
|
+
/** Hash seed shared by every stage. */
|
|
164
|
+
get seed(): number;
|
|
165
|
+
/** Target false-positive rate for the whole chain. */
|
|
166
|
+
get epsilon(): number;
|
|
167
|
+
/** Capacity multiplier from one stage to the next. */
|
|
168
|
+
get growth(): number;
|
|
169
|
+
/** False-positive target multiplier from one stage to the next. */
|
|
170
|
+
get tightening(): number;
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
export { BadMagicError, ChecksumError, type FilterJSON, ParamError, ReservedBitsError, type ScalableBloomOptions, type ScalableBloomParams, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError };
|