distillate 0.6.0 → 0.8.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 +22 -19
- package/dist/blocked/index.cjs +13 -112
- package/dist/blocked/index.d.cts +17 -2
- package/dist/blocked/index.d.ts +17 -2
- package/dist/blocked/index.js +6 -113
- package/dist/bloom/index.cjs +11 -99
- package/dist/bloom/index.d.cts +13 -2
- package/dist/bloom/index.d.ts +13 -2
- package/dist/bloom/index.js +5 -100
- package/dist/fuse/index.cjs +15 -102
- package/dist/fuse/index.d.cts +12 -2
- package/dist/fuse/index.d.ts +12 -2
- package/dist/fuse/index.js +9 -103
- package/dist/index.cjs +1 -2
- package/dist/index.js +1 -2
- package/dist/{params-BrWuBC2A.js → params-CeajSrwv.js} +0 -7
- package/dist/{params--8CNXYWu.cjs → params-UTZbJ22c.cjs} +0 -7
- package/dist/{serialize-BnwtzcMw.js → serialize-BqIcsR2J.js} +3 -29
- package/dist/{serialize-BIIKUHH6.cjs → serialize-CXnRWItH.cjs} +26 -28
- package/dist/serialize-ChyWpB9F.d.cts +73 -0
- package/dist/serialize-ChyWpB9F.d.ts +73 -0
- package/package.json +5 -6
- package/dist/serialize-DRKh6QOr.d.cts +0 -15
- package/dist/serialize-DRKh6QOr.d.ts +0 -15
package/dist/bloom/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_serialize = require("../serialize-
|
|
3
|
-
const require_params = require("../params
|
|
2
|
+
const require_serialize = require("../serialize-CXnRWItH.cjs");
|
|
3
|
+
const require_params = require("../params-UTZbJ22c.cjs");
|
|
4
4
|
//#region src/core/bitset.ts
|
|
5
5
|
var BitSetRangeError = class extends RangeError {
|
|
6
6
|
name = "BitSetRangeError";
|
|
@@ -33,8 +33,7 @@ var BitSet = class {
|
|
|
33
33
|
};
|
|
34
34
|
//#endregion
|
|
35
35
|
//#region src/core/sizing.ts
|
|
36
|
-
|
|
37
|
-
function optimal(n, epsilon) {
|
|
36
|
+
function bloomSizing(n, epsilon) {
|
|
38
37
|
const m = Math.ceil(-n * Math.log(epsilon) / (Math.LN2 * Math.LN2));
|
|
39
38
|
return {
|
|
40
39
|
m,
|
|
@@ -44,23 +43,9 @@ function optimal(n, epsilon) {
|
|
|
44
43
|
//#endregion
|
|
45
44
|
//#region src/bloom/bloom.ts
|
|
46
45
|
const TYPE = 1;
|
|
47
|
-
/** Thrown when an operation requires two filters built with identical parameters. */
|
|
48
46
|
var BloomParamMismatchError = class extends Error {
|
|
49
|
-
/** Discriminates this error from other `Error`s. */
|
|
50
47
|
name = "BloomParamMismatchError";
|
|
51
48
|
};
|
|
52
|
-
/**
|
|
53
|
-
* A classic Bloom filter: a space-efficient set with a tunable false-positive
|
|
54
|
-
* rate and zero false negatives.
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```ts
|
|
58
|
-
* const filter = BloomFilter.create(100_000, 0.01);
|
|
59
|
-
* filter.add("alice");
|
|
60
|
-
* filter.has("alice"); // true
|
|
61
|
-
* filter.has("bob"); // false (or a ~1% false positive)
|
|
62
|
-
* ```
|
|
63
|
-
*/
|
|
64
49
|
var BloomFilter = class BloomFilter {
|
|
65
50
|
#bits;
|
|
66
51
|
#m;
|
|
@@ -68,40 +53,18 @@ var BloomFilter = class BloomFilter {
|
|
|
68
53
|
#seed;
|
|
69
54
|
#scratch;
|
|
70
55
|
#n;
|
|
71
|
-
/**
|
|
72
|
-
* Creates a filter sized for `n` expected keys at a target false-positive rate.
|
|
73
|
-
*
|
|
74
|
-
* @param n - Expected number of keys.
|
|
75
|
-
* @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
|
|
76
|
-
* @returns A new, empty filter.
|
|
77
|
-
*/
|
|
78
56
|
static create(n, epsilon) {
|
|
79
57
|
require_params.assertPositiveInt(n, "n");
|
|
80
58
|
require_params.assertUint32(n, "n");
|
|
81
59
|
require_params.assertProbability(epsilon, "epsilon");
|
|
82
|
-
return BloomFilter.#withN(
|
|
60
|
+
return BloomFilter.#withN(bloomSizing(n, epsilon), n);
|
|
83
61
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Builds a filter from `keys`, sized for their count at the target
|
|
86
|
-
* false-positive rate. The ergonomic entry point when the key set is already
|
|
87
|
-
* in hand; use {@link BloomFilter.create} to size for a count known ahead.
|
|
88
|
-
*
|
|
89
|
-
* @param keys - The keys to insert.
|
|
90
|
-
* @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
|
|
91
|
-
* @returns A new filter containing every key.
|
|
92
|
-
*/
|
|
93
62
|
static from(keys, epsilon) {
|
|
94
63
|
const arr = [...keys];
|
|
95
64
|
const f = BloomFilter.create(Math.max(1, arr.length), epsilon);
|
|
96
65
|
for (const k of arr) f.add(k);
|
|
97
66
|
return f;
|
|
98
67
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Restores a filter from its {@link BloomFilter.toBytes} serialization.
|
|
101
|
-
*
|
|
102
|
-
* @param bytes - The serialized filter.
|
|
103
|
-
* @returns The reconstructed filter.
|
|
104
|
-
*/
|
|
105
68
|
static fromBytes(bytes) {
|
|
106
69
|
const { type, flags, body } = require_serialize.readHeader(bytes);
|
|
107
70
|
if (type !== TYPE) throw new require_serialize.SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
@@ -121,10 +84,6 @@ var BloomFilter = class BloomFilter {
|
|
|
121
84
|
f.#bits.bytes.set(body.subarray(14));
|
|
122
85
|
return f;
|
|
123
86
|
}
|
|
124
|
-
/**
|
|
125
|
-
* Constructs a filter from low-level {@link BloomParams}. Prefer
|
|
126
|
-
* {@link BloomFilter.create} unless restoring a specific configuration.
|
|
127
|
-
*/
|
|
128
87
|
constructor({ m, k, seed = 0 }) {
|
|
129
88
|
require_params.assertPositiveInt(m, "m");
|
|
130
89
|
require_params.assertUint32(m, "m");
|
|
@@ -143,41 +102,24 @@ var BloomFilter = class BloomFilter {
|
|
|
143
102
|
f.#n = n;
|
|
144
103
|
return f;
|
|
145
104
|
}
|
|
146
|
-
/** Number of bits in the filter. */
|
|
147
105
|
get m() {
|
|
148
106
|
return this.#m;
|
|
149
107
|
}
|
|
150
|
-
/** Number of hash probes per key. */
|
|
151
108
|
get k() {
|
|
152
109
|
return this.#k;
|
|
153
110
|
}
|
|
154
|
-
/** Hash seed. */
|
|
155
111
|
get seed() {
|
|
156
112
|
return this.#seed;
|
|
157
113
|
}
|
|
158
|
-
/** Number of bits currently set. */
|
|
159
114
|
get length() {
|
|
160
115
|
return this.#bits.count();
|
|
161
116
|
}
|
|
162
|
-
/** Analytic design bits-per-key `m / n`. */
|
|
163
117
|
get bitsPerKey() {
|
|
164
118
|
return this.#m / this.#n;
|
|
165
119
|
}
|
|
166
|
-
/**
|
|
167
|
-
* Estimates the current false-positive rate from the actual fill,
|
|
168
|
-
* `(length / m) ** k`. This reflects how full the filter is right now, not
|
|
169
|
-
* the design target; it rises as keys are added.
|
|
170
|
-
*
|
|
171
|
-
* @returns The estimated false-positive rate, `0` for an empty filter.
|
|
172
|
-
*/
|
|
173
120
|
rate() {
|
|
174
121
|
return (this.length / this.#m) ** this.#k;
|
|
175
122
|
}
|
|
176
|
-
/**
|
|
177
|
-
* Serializes the filter to a portable little-endian byte layout.
|
|
178
|
-
*
|
|
179
|
-
* @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
|
|
180
|
-
*/
|
|
181
123
|
toBytes() {
|
|
182
124
|
const payload = this.#bits.bytes;
|
|
183
125
|
return require_serialize.writeFrame({
|
|
@@ -192,41 +134,15 @@ var BloomFilter = class BloomFilter {
|
|
|
192
134
|
body.set(payload, 14);
|
|
193
135
|
});
|
|
194
136
|
}
|
|
195
|
-
/**
|
|
196
|
-
* Tests structural equality: `true` when `other` serializes to identical
|
|
197
|
-
* bytes, meaning identical parameters and set bits.
|
|
198
|
-
*
|
|
199
|
-
* @param other - The filter to compare against.
|
|
200
|
-
* @returns `true` if the two filters are byte-for-byte identical.
|
|
201
|
-
*/
|
|
202
137
|
equals(other) {
|
|
203
138
|
return require_serialize.bytesEqual(this.toBytes(), other.toBytes());
|
|
204
139
|
}
|
|
205
|
-
/**
|
|
206
|
-
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
207
|
-
* {@link BloomFilter.toBytes}.
|
|
208
|
-
*
|
|
209
|
-
* @returns The envelope, readable by {@link BloomFilter.fromJSON}.
|
|
210
|
-
*/
|
|
211
140
|
toJSON() {
|
|
212
141
|
return require_serialize.toJSONEnvelope(this.toBytes());
|
|
213
142
|
}
|
|
214
|
-
/**
|
|
215
|
-
* Restores a filter from its {@link BloomFilter.toJSON} envelope.
|
|
216
|
-
*
|
|
217
|
-
* @param value - The JSON envelope.
|
|
218
|
-
* @returns The reconstructed filter.
|
|
219
|
-
*/
|
|
220
143
|
static fromJSON(value) {
|
|
221
144
|
return BloomFilter.fromBytes(require_serialize.fromJSONEnvelope(value));
|
|
222
145
|
}
|
|
223
|
-
/**
|
|
224
|
-
* Returns a new filter containing the union of this filter and `other`.
|
|
225
|
-
*
|
|
226
|
-
* @param other - A filter built with identical parameters.
|
|
227
|
-
* @returns A new filter reporting membership for keys in either input.
|
|
228
|
-
* @throws {@link BloomParamMismatchError} if the parameters differ.
|
|
229
|
-
*/
|
|
230
146
|
union(other) {
|
|
231
147
|
if (this.#m !== other.#m || this.#k !== other.#k || this.#seed !== other.#seed) throw new BloomParamMismatchError("cannot union Bloom filters whose parameters do not match");
|
|
232
148
|
const a = this.#bits.bytes;
|
|
@@ -241,21 +157,10 @@ var BloomFilter = class BloomFilter {
|
|
|
241
157
|
r.#bits.bytes.set(merged);
|
|
242
158
|
return r;
|
|
243
159
|
}
|
|
244
|
-
/**
|
|
245
|
-
* Adds a key to the set.
|
|
246
|
-
*
|
|
247
|
-
* @param key - The key to insert, as a string or bytes.
|
|
248
|
-
*/
|
|
249
160
|
add(key) {
|
|
250
161
|
require_serialize.probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
|
|
251
162
|
for (let i = 0; i < this.#k; i++) this.#bits.set(this.#scratch[i] ?? 0);
|
|
252
163
|
}
|
|
253
|
-
/**
|
|
254
|
-
* Tests whether a key is in the set.
|
|
255
|
-
*
|
|
256
|
-
* @param key - The key to test.
|
|
257
|
-
* @returns `true` if present (possibly a false positive); `false` guarantees absence.
|
|
258
|
-
*/
|
|
259
164
|
has(key) {
|
|
260
165
|
require_serialize.probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
|
|
261
166
|
for (let i = 0; i < this.#k; i++) if (!this.#bits.get(this.#scratch[i] ?? 0)) return false;
|
|
@@ -263,6 +168,13 @@ var BloomFilter = class BloomFilter {
|
|
|
263
168
|
}
|
|
264
169
|
};
|
|
265
170
|
//#endregion
|
|
171
|
+
exports.BadMagicError = require_serialize.BadMagicError;
|
|
266
172
|
exports.BloomFilter = BloomFilter;
|
|
267
173
|
exports.BloomParamMismatchError = BloomParamMismatchError;
|
|
174
|
+
exports.ChecksumError = require_serialize.ChecksumError;
|
|
268
175
|
exports.ParamError = require_params.ParamError;
|
|
176
|
+
exports.SerializationError = require_serialize.SerializationError;
|
|
177
|
+
exports.TruncatedError = require_serialize.TruncatedError;
|
|
178
|
+
exports.UnknownHashVariantError = require_serialize.UnknownHashVariantError;
|
|
179
|
+
exports.UnknownVersionError = require_serialize.UnknownVersionError;
|
|
180
|
+
exports.bloomSizing = bloomSizing;
|
package/dist/bloom/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as TruncatedError, c as BytesLike, i as SerializationError, n as ChecksumError, o as UnknownHashVariantError, r as FilterJSON, s as UnknownVersionError, t as BadMagicError } from "../serialize-ChyWpB9F.cjs";
|
|
2
2
|
import { t as ParamError } from "../params-DnqJBqLS.cjs";
|
|
3
3
|
//#region src/bloom/bloom.d.ts
|
|
4
4
|
/** Thrown when an operation requires two filters built with identical parameters. */
|
|
@@ -128,4 +128,15 @@ declare class BloomFilter {
|
|
|
128
128
|
has(key: BytesLike): boolean;
|
|
129
129
|
}
|
|
130
130
|
//#endregion
|
|
131
|
-
|
|
131
|
+
//#region src/core/sizing.d.ts
|
|
132
|
+
/** Bloom filter geometry: the `BloomParams` fields a sizing solve determines. */
|
|
133
|
+
interface BloomSizing {
|
|
134
|
+
/** Number of bits in the filter. */
|
|
135
|
+
m: number;
|
|
136
|
+
/** Number of hash probes per key. */
|
|
137
|
+
k: number;
|
|
138
|
+
}
|
|
139
|
+
/** Optimal Bloom-filter sizing: `m` bits and `k` hashes for `n` items at target FPR `epsilon`. */
|
|
140
|
+
declare function bloomSizing(n: number, epsilon: number): BloomSizing;
|
|
141
|
+
//#endregion
|
|
142
|
+
export { BadMagicError, BloomFilter, BloomParamMismatchError, type BloomParams, type BloomSizing, ChecksumError, type FilterJSON, ParamError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError, bloomSizing };
|
package/dist/bloom/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as TruncatedError, c as BytesLike, i as SerializationError, n as ChecksumError, o as UnknownHashVariantError, r as FilterJSON, s as UnknownVersionError, t as BadMagicError } from "../serialize-ChyWpB9F.js";
|
|
2
2
|
import { t as ParamError } from "../params-DnqJBqLS.js";
|
|
3
3
|
//#region src/bloom/bloom.d.ts
|
|
4
4
|
/** Thrown when an operation requires two filters built with identical parameters. */
|
|
@@ -128,4 +128,15 @@ declare class BloomFilter {
|
|
|
128
128
|
has(key: BytesLike): boolean;
|
|
129
129
|
}
|
|
130
130
|
//#endregion
|
|
131
|
-
|
|
131
|
+
//#region src/core/sizing.d.ts
|
|
132
|
+
/** Bloom filter geometry: the `BloomParams` fields a sizing solve determines. */
|
|
133
|
+
interface BloomSizing {
|
|
134
|
+
/** Number of bits in the filter. */
|
|
135
|
+
m: number;
|
|
136
|
+
/** Number of hash probes per key. */
|
|
137
|
+
k: number;
|
|
138
|
+
}
|
|
139
|
+
/** Optimal Bloom-filter sizing: `m` bits and `k` hashes for `n` items at target FPR `epsilon`. */
|
|
140
|
+
declare function bloomSizing(n: number, epsilon: number): BloomSizing;
|
|
141
|
+
//#endregion
|
|
142
|
+
export { BadMagicError, BloomFilter, BloomParamMismatchError, type BloomParams, type BloomSizing, ChecksumError, type FilterJSON, ParamError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError, bloomSizing };
|
package/dist/bloom/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { a as assertUint16, i as assertProbability, o as assertUint32, r as assertPositiveInt, t as ParamError } from "../params-
|
|
1
|
+
import { a as UnknownHashVariantError, b as probeInto, c as assertMinBodyLength, d as readHeader, f as toJSONEnvelope, i as TruncatedError, l as bytesEqual, n as ChecksumError, o as UnknownVersionError, p as writeFrame, r as SerializationError, s as assertBodyLength, t as BadMagicError, u as fromJSONEnvelope } from "../serialize-BqIcsR2J.js";
|
|
2
|
+
import { a as assertUint16, i as assertProbability, o as assertUint32, r as assertPositiveInt, t as ParamError } from "../params-CeajSrwv.js";
|
|
3
3
|
//#region src/core/bitset.ts
|
|
4
4
|
var BitSetRangeError = class extends RangeError {
|
|
5
5
|
name = "BitSetRangeError";
|
|
@@ -32,8 +32,7 @@ var BitSet = class {
|
|
|
32
32
|
};
|
|
33
33
|
//#endregion
|
|
34
34
|
//#region src/core/sizing.ts
|
|
35
|
-
|
|
36
|
-
function optimal(n, epsilon) {
|
|
35
|
+
function bloomSizing(n, epsilon) {
|
|
37
36
|
const m = Math.ceil(-n * Math.log(epsilon) / (Math.LN2 * Math.LN2));
|
|
38
37
|
return {
|
|
39
38
|
m,
|
|
@@ -43,23 +42,9 @@ function optimal(n, epsilon) {
|
|
|
43
42
|
//#endregion
|
|
44
43
|
//#region src/bloom/bloom.ts
|
|
45
44
|
const TYPE = 1;
|
|
46
|
-
/** Thrown when an operation requires two filters built with identical parameters. */
|
|
47
45
|
var BloomParamMismatchError = class extends Error {
|
|
48
|
-
/** Discriminates this error from other `Error`s. */
|
|
49
46
|
name = "BloomParamMismatchError";
|
|
50
47
|
};
|
|
51
|
-
/**
|
|
52
|
-
* A classic Bloom filter: a space-efficient set with a tunable false-positive
|
|
53
|
-
* rate and zero false negatives.
|
|
54
|
-
*
|
|
55
|
-
* @example
|
|
56
|
-
* ```ts
|
|
57
|
-
* const filter = BloomFilter.create(100_000, 0.01);
|
|
58
|
-
* filter.add("alice");
|
|
59
|
-
* filter.has("alice"); // true
|
|
60
|
-
* filter.has("bob"); // false (or a ~1% false positive)
|
|
61
|
-
* ```
|
|
62
|
-
*/
|
|
63
48
|
var BloomFilter = class BloomFilter {
|
|
64
49
|
#bits;
|
|
65
50
|
#m;
|
|
@@ -67,40 +52,18 @@ var BloomFilter = class BloomFilter {
|
|
|
67
52
|
#seed;
|
|
68
53
|
#scratch;
|
|
69
54
|
#n;
|
|
70
|
-
/**
|
|
71
|
-
* Creates a filter sized for `n` expected keys at a target false-positive rate.
|
|
72
|
-
*
|
|
73
|
-
* @param n - Expected number of keys.
|
|
74
|
-
* @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
|
|
75
|
-
* @returns A new, empty filter.
|
|
76
|
-
*/
|
|
77
55
|
static create(n, epsilon) {
|
|
78
56
|
assertPositiveInt(n, "n");
|
|
79
57
|
assertUint32(n, "n");
|
|
80
58
|
assertProbability(epsilon, "epsilon");
|
|
81
|
-
return BloomFilter.#withN(
|
|
59
|
+
return BloomFilter.#withN(bloomSizing(n, epsilon), n);
|
|
82
60
|
}
|
|
83
|
-
/**
|
|
84
|
-
* Builds a filter from `keys`, sized for their count at the target
|
|
85
|
-
* false-positive rate. The ergonomic entry point when the key set is already
|
|
86
|
-
* in hand; use {@link BloomFilter.create} to size for a count known ahead.
|
|
87
|
-
*
|
|
88
|
-
* @param keys - The keys to insert.
|
|
89
|
-
* @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
|
|
90
|
-
* @returns A new filter containing every key.
|
|
91
|
-
*/
|
|
92
61
|
static from(keys, epsilon) {
|
|
93
62
|
const arr = [...keys];
|
|
94
63
|
const f = BloomFilter.create(Math.max(1, arr.length), epsilon);
|
|
95
64
|
for (const k of arr) f.add(k);
|
|
96
65
|
return f;
|
|
97
66
|
}
|
|
98
|
-
/**
|
|
99
|
-
* Restores a filter from its {@link BloomFilter.toBytes} serialization.
|
|
100
|
-
*
|
|
101
|
-
* @param bytes - The serialized filter.
|
|
102
|
-
* @returns The reconstructed filter.
|
|
103
|
-
*/
|
|
104
67
|
static fromBytes(bytes) {
|
|
105
68
|
const { type, flags, body } = readHeader(bytes);
|
|
106
69
|
if (type !== TYPE) throw new SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
@@ -120,10 +83,6 @@ var BloomFilter = class BloomFilter {
|
|
|
120
83
|
f.#bits.bytes.set(body.subarray(14));
|
|
121
84
|
return f;
|
|
122
85
|
}
|
|
123
|
-
/**
|
|
124
|
-
* Constructs a filter from low-level {@link BloomParams}. Prefer
|
|
125
|
-
* {@link BloomFilter.create} unless restoring a specific configuration.
|
|
126
|
-
*/
|
|
127
86
|
constructor({ m, k, seed = 0 }) {
|
|
128
87
|
assertPositiveInt(m, "m");
|
|
129
88
|
assertUint32(m, "m");
|
|
@@ -142,41 +101,24 @@ var BloomFilter = class BloomFilter {
|
|
|
142
101
|
f.#n = n;
|
|
143
102
|
return f;
|
|
144
103
|
}
|
|
145
|
-
/** Number of bits in the filter. */
|
|
146
104
|
get m() {
|
|
147
105
|
return this.#m;
|
|
148
106
|
}
|
|
149
|
-
/** Number of hash probes per key. */
|
|
150
107
|
get k() {
|
|
151
108
|
return this.#k;
|
|
152
109
|
}
|
|
153
|
-
/** Hash seed. */
|
|
154
110
|
get seed() {
|
|
155
111
|
return this.#seed;
|
|
156
112
|
}
|
|
157
|
-
/** Number of bits currently set. */
|
|
158
113
|
get length() {
|
|
159
114
|
return this.#bits.count();
|
|
160
115
|
}
|
|
161
|
-
/** Analytic design bits-per-key `m / n`. */
|
|
162
116
|
get bitsPerKey() {
|
|
163
117
|
return this.#m / this.#n;
|
|
164
118
|
}
|
|
165
|
-
/**
|
|
166
|
-
* Estimates the current false-positive rate from the actual fill,
|
|
167
|
-
* `(length / m) ** k`. This reflects how full the filter is right now, not
|
|
168
|
-
* the design target; it rises as keys are added.
|
|
169
|
-
*
|
|
170
|
-
* @returns The estimated false-positive rate, `0` for an empty filter.
|
|
171
|
-
*/
|
|
172
119
|
rate() {
|
|
173
120
|
return (this.length / this.#m) ** this.#k;
|
|
174
121
|
}
|
|
175
|
-
/**
|
|
176
|
-
* Serializes the filter to a portable little-endian byte layout.
|
|
177
|
-
*
|
|
178
|
-
* @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
|
|
179
|
-
*/
|
|
180
122
|
toBytes() {
|
|
181
123
|
const payload = this.#bits.bytes;
|
|
182
124
|
return writeFrame({
|
|
@@ -191,41 +133,15 @@ var BloomFilter = class BloomFilter {
|
|
|
191
133
|
body.set(payload, 14);
|
|
192
134
|
});
|
|
193
135
|
}
|
|
194
|
-
/**
|
|
195
|
-
* Tests structural equality: `true` when `other` serializes to identical
|
|
196
|
-
* bytes, meaning identical parameters and set bits.
|
|
197
|
-
*
|
|
198
|
-
* @param other - The filter to compare against.
|
|
199
|
-
* @returns `true` if the two filters are byte-for-byte identical.
|
|
200
|
-
*/
|
|
201
136
|
equals(other) {
|
|
202
137
|
return bytesEqual(this.toBytes(), other.toBytes());
|
|
203
138
|
}
|
|
204
|
-
/**
|
|
205
|
-
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
206
|
-
* {@link BloomFilter.toBytes}.
|
|
207
|
-
*
|
|
208
|
-
* @returns The envelope, readable by {@link BloomFilter.fromJSON}.
|
|
209
|
-
*/
|
|
210
139
|
toJSON() {
|
|
211
140
|
return toJSONEnvelope(this.toBytes());
|
|
212
141
|
}
|
|
213
|
-
/**
|
|
214
|
-
* Restores a filter from its {@link BloomFilter.toJSON} envelope.
|
|
215
|
-
*
|
|
216
|
-
* @param value - The JSON envelope.
|
|
217
|
-
* @returns The reconstructed filter.
|
|
218
|
-
*/
|
|
219
142
|
static fromJSON(value) {
|
|
220
143
|
return BloomFilter.fromBytes(fromJSONEnvelope(value));
|
|
221
144
|
}
|
|
222
|
-
/**
|
|
223
|
-
* Returns a new filter containing the union of this filter and `other`.
|
|
224
|
-
*
|
|
225
|
-
* @param other - A filter built with identical parameters.
|
|
226
|
-
* @returns A new filter reporting membership for keys in either input.
|
|
227
|
-
* @throws {@link BloomParamMismatchError} if the parameters differ.
|
|
228
|
-
*/
|
|
229
145
|
union(other) {
|
|
230
146
|
if (this.#m !== other.#m || this.#k !== other.#k || this.#seed !== other.#seed) throw new BloomParamMismatchError("cannot union Bloom filters whose parameters do not match");
|
|
231
147
|
const a = this.#bits.bytes;
|
|
@@ -240,21 +156,10 @@ var BloomFilter = class BloomFilter {
|
|
|
240
156
|
r.#bits.bytes.set(merged);
|
|
241
157
|
return r;
|
|
242
158
|
}
|
|
243
|
-
/**
|
|
244
|
-
* Adds a key to the set.
|
|
245
|
-
*
|
|
246
|
-
* @param key - The key to insert, as a string or bytes.
|
|
247
|
-
*/
|
|
248
159
|
add(key) {
|
|
249
160
|
probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
|
|
250
161
|
for (let i = 0; i < this.#k; i++) this.#bits.set(this.#scratch[i] ?? 0);
|
|
251
162
|
}
|
|
252
|
-
/**
|
|
253
|
-
* Tests whether a key is in the set.
|
|
254
|
-
*
|
|
255
|
-
* @param key - The key to test.
|
|
256
|
-
* @returns `true` if present (possibly a false positive); `false` guarantees absence.
|
|
257
|
-
*/
|
|
258
163
|
has(key) {
|
|
259
164
|
probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
|
|
260
165
|
for (let i = 0; i < this.#k; i++) if (!this.#bits.get(this.#scratch[i] ?? 0)) return false;
|
|
@@ -262,4 +167,4 @@ var BloomFilter = class BloomFilter {
|
|
|
262
167
|
}
|
|
263
168
|
};
|
|
264
169
|
//#endregion
|
|
265
|
-
export { BloomFilter, BloomParamMismatchError, ParamError };
|
|
170
|
+
export { BadMagicError, BloomFilter, BloomParamMismatchError, ChecksumError, ParamError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError, bloomSizing };
|
package/dist/fuse/index.cjs
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_serialize = require("../serialize-
|
|
2
|
+
const require_serialize = require("../serialize-CXnRWItH.cjs");
|
|
3
3
|
//#region src/fuse/fuse.ts
|
|
4
4
|
const ARITY = 3;
|
|
5
5
|
const TYPE_FUSE8 = 3;
|
|
6
6
|
const TYPE_FUSE16 = 4;
|
|
7
|
-
/** Thrown when binary fuse construction fails to converge on the key set. */
|
|
8
7
|
var BinaryFuseBuildError = class extends Error {
|
|
9
|
-
/** Discriminates this error from other `Error`s. */
|
|
10
8
|
name = "BinaryFuseBuildError";
|
|
11
9
|
};
|
|
12
10
|
const scratchHash = {
|
|
@@ -59,11 +57,10 @@ function computeParams(size) {
|
|
|
59
57
|
arrayLength
|
|
60
58
|
};
|
|
61
59
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
*/
|
|
60
|
+
function fuseBitsPerKey(n, width) {
|
|
61
|
+
if (n === 0) return 0;
|
|
62
|
+
return computeParams(n).arrayLength * width / n;
|
|
63
|
+
}
|
|
67
64
|
function buildFingerprints(fp, hashes, params, maxAttempts = 100) {
|
|
68
65
|
const size = hashes.length / 2;
|
|
69
66
|
const { seg, segMask, segCountLen, arrayLength } = params;
|
|
@@ -74,7 +71,7 @@ function buildFingerprints(fp, hashes, params, maxAttempts = 100) {
|
|
|
74
71
|
const orderLo = new Uint32Array(size);
|
|
75
72
|
const orderHi = new Uint32Array(size);
|
|
76
73
|
const orderIdx = new Uint32Array(size);
|
|
77
|
-
const pos =
|
|
74
|
+
const pos = new Uint32Array(3);
|
|
78
75
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
79
76
|
const seed = attempt;
|
|
80
77
|
counts.fill(0);
|
|
@@ -133,7 +130,7 @@ function buildFingerprints(fp, hashes, params, maxAttempts = 100) {
|
|
|
133
130
|
}
|
|
134
131
|
function buildState(keys, alloc) {
|
|
135
132
|
const hashList = [];
|
|
136
|
-
const seen =
|
|
133
|
+
const seen = new Set();
|
|
137
134
|
for (const key of keys) {
|
|
138
135
|
require_serialize.hash128KeyInto(key, 0, scratchHash);
|
|
139
136
|
const lo = scratchHash.w0 >>> 0;
|
|
@@ -191,10 +188,6 @@ function fuseStateFromBytes(bytes, expectedType) {
|
|
|
191
188
|
size
|
|
192
189
|
};
|
|
193
190
|
}
|
|
194
|
-
/**
|
|
195
|
-
* Shared behavior for the static binary fuse filters: an immutable,
|
|
196
|
-
* space-efficient membership filter built once from a fixed key set.
|
|
197
|
-
*/
|
|
198
191
|
var BinaryFuse = class {
|
|
199
192
|
#fp;
|
|
200
193
|
#seed;
|
|
@@ -202,7 +195,7 @@ var BinaryFuse = class {
|
|
|
202
195
|
#segMask;
|
|
203
196
|
#segCountLen;
|
|
204
197
|
#size;
|
|
205
|
-
#pos =
|
|
198
|
+
#pos = new Uint32Array(3);
|
|
206
199
|
constructor(state) {
|
|
207
200
|
this.#fp = state.fp;
|
|
208
201
|
this.#seed = state.seed;
|
|
@@ -211,23 +204,15 @@ var BinaryFuse = class {
|
|
|
211
204
|
this.#segCountLen = state.params.segCountLen;
|
|
212
205
|
this.#size = state.size;
|
|
213
206
|
}
|
|
214
|
-
/** Number of distinct keys the filter was built from. */
|
|
215
207
|
get size() {
|
|
216
208
|
return this.#size;
|
|
217
209
|
}
|
|
218
|
-
/** Hash seed selected during construction (may differ from 0 after a peel retry). */
|
|
219
210
|
get seed() {
|
|
220
211
|
return this.#seed;
|
|
221
212
|
}
|
|
222
|
-
/** Actual bits stored per key (`0` for an empty filter). */
|
|
223
213
|
get bitsPerKey() {
|
|
224
214
|
return this.#size === 0 ? 0 : this.#fp.byteLength * 8 / this.#size;
|
|
225
215
|
}
|
|
226
|
-
/**
|
|
227
|
-
* Serializes the filter to a portable little-endian byte layout.
|
|
228
|
-
*
|
|
229
|
-
* @returns The serialized filter, readable by the matching `fromBytes`.
|
|
230
|
-
*/
|
|
231
216
|
toBytes() {
|
|
232
217
|
const laneBytes = new Uint8Array(this.#fp.buffer, this.#fp.byteOffset, this.#fp.byteLength);
|
|
233
218
|
return require_serialize.writeFrame({
|
|
@@ -242,12 +227,6 @@ var BinaryFuse = class {
|
|
|
242
227
|
body.set(laneBytes, 16);
|
|
243
228
|
});
|
|
244
229
|
}
|
|
245
|
-
/**
|
|
246
|
-
* Tests whether a key is in the set.
|
|
247
|
-
*
|
|
248
|
-
* @param key - The key to test.
|
|
249
|
-
* @returns `true` if present (possibly a false positive); `false` guarantees absence.
|
|
250
|
-
*/
|
|
251
230
|
has(key) {
|
|
252
231
|
if (this.#fp.length === 0) return false;
|
|
253
232
|
require_serialize.hash128KeyInto(key, 0, scratchHash);
|
|
@@ -261,109 +240,43 @@ var BinaryFuse = class {
|
|
|
261
240
|
const p2 = this.#pos[2] ?? 0;
|
|
262
241
|
return ((mlo ^ mhi) & mask) === (((this.#fp[p0] ?? 0) ^ (this.#fp[p1] ?? 0) ^ (this.#fp[p2] ?? 0)) & mask);
|
|
263
242
|
}
|
|
264
|
-
/**
|
|
265
|
-
* Tests structural equality: `true` when `other` serializes to identical
|
|
266
|
-
* bytes. A {@link BinaryFuse8} and a {@link BinaryFuse16} are never equal,
|
|
267
|
-
* since their frames carry different type bytes.
|
|
268
|
-
*
|
|
269
|
-
* @param other - The filter to compare against.
|
|
270
|
-
* @returns `true` if the two filters are byte-for-byte identical.
|
|
271
|
-
*/
|
|
272
243
|
equals(other) {
|
|
273
244
|
return require_serialize.bytesEqual(this.toBytes(), other.toBytes());
|
|
274
245
|
}
|
|
275
|
-
/**
|
|
276
|
-
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
277
|
-
* the `toBytes` frame.
|
|
278
|
-
*
|
|
279
|
-
* @returns The envelope, readable by the matching `fromJSON`.
|
|
280
|
-
*/
|
|
281
246
|
toJSON() {
|
|
282
247
|
return require_serialize.toJSONEnvelope(this.toBytes());
|
|
283
248
|
}
|
|
284
249
|
};
|
|
285
|
-
/**
|
|
286
|
-
* A static 8-bit binary fuse filter: built once from a key set, then immutable.
|
|
287
|
-
* The most space-efficient option (~9 bits/key at ~0.39% false-positive rate).
|
|
288
|
-
*
|
|
289
|
-
* @example
|
|
290
|
-
* ```ts
|
|
291
|
-
* const filter = BinaryFuse8.from(["alice", "bob", "carol"]);
|
|
292
|
-
* filter.has("alice"); // true
|
|
293
|
-
* filter.size; // 3
|
|
294
|
-
* ```
|
|
295
|
-
*/
|
|
296
250
|
var BinaryFuse8 = class BinaryFuse8 extends BinaryFuse {
|
|
297
|
-
/**
|
|
298
|
-
* Builds a filter from the given keys; duplicates are ignored.
|
|
299
|
-
*
|
|
300
|
-
* @param keys - The complete set of keys to store.
|
|
301
|
-
* @returns A new immutable filter.
|
|
302
|
-
* @throws {@link BinaryFuseBuildError} if construction fails to converge.
|
|
303
|
-
*/
|
|
304
251
|
static from(keys) {
|
|
305
252
|
return new BinaryFuse8(buildState(keys, (n) => new Uint8Array(n)));
|
|
306
253
|
}
|
|
307
|
-
/**
|
|
308
|
-
* Restores a filter from its {@link BinaryFuse8.toBytes} serialization.
|
|
309
|
-
*
|
|
310
|
-
* @param bytes - The serialized filter.
|
|
311
|
-
* @returns The reconstructed filter.
|
|
312
|
-
*/
|
|
313
254
|
static fromBytes(bytes) {
|
|
314
255
|
return new BinaryFuse8(fuseStateFromBytes(bytes, TYPE_FUSE8));
|
|
315
256
|
}
|
|
316
|
-
/**
|
|
317
|
-
* Restores a filter from its {@link BinaryFuse8.toJSON} envelope.
|
|
318
|
-
*
|
|
319
|
-
* @param value - The JSON envelope.
|
|
320
|
-
* @returns The reconstructed filter.
|
|
321
|
-
*/
|
|
322
257
|
static fromJSON(value) {
|
|
323
258
|
return BinaryFuse8.fromBytes(require_serialize.fromJSONEnvelope(value));
|
|
324
259
|
}
|
|
325
260
|
};
|
|
326
|
-
/**
|
|
327
|
-
* A static 16-bit binary fuse filter: like {@link BinaryFuse8} but twice the
|
|
328
|
-
* space (~18 bits/key) for a far lower false-positive rate (~1/65536).
|
|
329
|
-
*
|
|
330
|
-
* @example
|
|
331
|
-
* ```ts
|
|
332
|
-
* const filter = BinaryFuse16.from(["alice", "bob", "carol"]);
|
|
333
|
-
* filter.has("alice"); // true
|
|
334
|
-
* ```
|
|
335
|
-
*/
|
|
336
261
|
var BinaryFuse16 = class BinaryFuse16 extends BinaryFuse {
|
|
337
|
-
/**
|
|
338
|
-
* Builds a filter from the given keys; duplicates are ignored.
|
|
339
|
-
*
|
|
340
|
-
* @param keys - The complete set of keys to store.
|
|
341
|
-
* @returns A new immutable filter.
|
|
342
|
-
* @throws {@link BinaryFuseBuildError} if construction fails to converge.
|
|
343
|
-
*/
|
|
344
262
|
static from(keys) {
|
|
345
263
|
return new BinaryFuse16(buildState(keys, (n) => new Uint16Array(n)));
|
|
346
264
|
}
|
|
347
|
-
/**
|
|
348
|
-
* Restores a filter from its {@link BinaryFuse16.toBytes} serialization.
|
|
349
|
-
*
|
|
350
|
-
* @param bytes - The serialized filter.
|
|
351
|
-
* @returns The reconstructed filter.
|
|
352
|
-
*/
|
|
353
265
|
static fromBytes(bytes) {
|
|
354
266
|
return new BinaryFuse16(fuseStateFromBytes(bytes, TYPE_FUSE16));
|
|
355
267
|
}
|
|
356
|
-
/**
|
|
357
|
-
* Restores a filter from its {@link BinaryFuse16.toJSON} envelope.
|
|
358
|
-
*
|
|
359
|
-
* @param value - The JSON envelope.
|
|
360
|
-
* @returns The reconstructed filter.
|
|
361
|
-
*/
|
|
362
268
|
static fromJSON(value) {
|
|
363
269
|
return BinaryFuse16.fromBytes(require_serialize.fromJSONEnvelope(value));
|
|
364
270
|
}
|
|
365
271
|
};
|
|
366
272
|
//#endregion
|
|
273
|
+
exports.BadMagicError = require_serialize.BadMagicError;
|
|
367
274
|
exports.BinaryFuse16 = BinaryFuse16;
|
|
368
275
|
exports.BinaryFuse8 = BinaryFuse8;
|
|
369
276
|
exports.BinaryFuseBuildError = BinaryFuseBuildError;
|
|
277
|
+
exports.ChecksumError = require_serialize.ChecksumError;
|
|
278
|
+
exports.SerializationError = require_serialize.SerializationError;
|
|
279
|
+
exports.TruncatedError = require_serialize.TruncatedError;
|
|
280
|
+
exports.UnknownHashVariantError = require_serialize.UnknownHashVariantError;
|
|
281
|
+
exports.UnknownVersionError = require_serialize.UnknownVersionError;
|
|
282
|
+
exports.fuseBitsPerKey = fuseBitsPerKey;
|