distillate 0.5.0 → 0.7.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 +9 -9
- package/dist/blocked/index.cjs +113 -38
- package/dist/blocked/index.d.cts +55 -3
- package/dist/blocked/index.d.ts +55 -3
- package/dist/blocked/index.js +112 -39
- package/dist/bloom/index.cjs +71 -28
- package/dist/bloom/index.d.cts +45 -2
- package/dist/bloom/index.d.ts +45 -2
- package/dist/bloom/index.js +71 -29
- package/dist/fuse/index.cjs +77 -19
- package/dist/fuse/index.d.cts +44 -2
- package/dist/fuse/index.d.ts +44 -2
- package/dist/fuse/index.js +77 -20
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{params-J8p3bKq5.cjs → params--8CNXYWu.cjs} +10 -0
- package/dist/{params-ChTRNxM9.js → params-BrWuBC2A.js} +5 -1
- package/dist/serialize-BIIKUHH6.cjs +515 -0
- package/dist/serialize-BnwtzcMw.js +414 -0
- package/dist/serialize-DRKh6QOr.d.cts +15 -0
- package/dist/serialize-DRKh6QOr.d.ts +15 -0
- package/package.json +8 -31
- package/dist/bytes-DCuYtUVS.d.cts +0 -4
- package/dist/bytes-DCuYtUVS.d.ts +0 -4
- package/dist/serialize-BQ2UzZqq.js +0 -354
- package/dist/serialize-fa-pEUGq.cjs +0 -437
package/dist/blocked/index.js
CHANGED
|
@@ -1,9 +1,41 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import {
|
|
1
|
+
import { _ as reduce, a as bytesEqual, c as toJSONEnvelope, i as assertMinBodyLength, l as writeFrame, m as hash32x2Into, n as UnknownHashVariantError, o as fromJSONEnvelope, r as assertBodyLength, s as readHeader, t as SerializationError } from "../serialize-BnwtzcMw.js";
|
|
2
|
+
import { i as assertProbability, n as assertPositiveFinite, o as assertUint32, r as assertPositiveInt, t as ParamError } from "../params-BrWuBC2A.js";
|
|
3
3
|
//#region src/blocked/blocked.ts
|
|
4
4
|
const TYPE = 2;
|
|
5
5
|
const SALT = Uint32Array.of(1203114875, 1150766481, 2284105051, 2729912477, 1884591559, 770785867, 2667333959, 1550580529);
|
|
6
6
|
const scratch2 = /* @__PURE__ */ new Uint32Array(2);
|
|
7
|
+
const BLOCK_BITS = 256;
|
|
8
|
+
const LANE_BITS = 32;
|
|
9
|
+
const LANES = 8;
|
|
10
|
+
/**
|
|
11
|
+
* Modeled false-positive rate of a split-block filter at `bitsPerKey`. A block
|
|
12
|
+
* holding `j` keys has FPR `(1 - (1 - 1/32)^j)^8` (8 lanes of 32 bits, one probe
|
|
13
|
+
* each); the filter's rate averages that over the Poisson block load
|
|
14
|
+
* `lambda = 256 / bitsPerKey`. This clustering average is why the blocked curve
|
|
15
|
+
* is not linear in `log10(1/epsilon)`.
|
|
16
|
+
*/
|
|
17
|
+
function blockedFprAt(bitsPerKey) {
|
|
18
|
+
const lambda = BLOCK_BITS / bitsPerKey;
|
|
19
|
+
let fpr = 0;
|
|
20
|
+
let p = Math.exp(-lambda);
|
|
21
|
+
for (let j = 0;; j++) {
|
|
22
|
+
if (j > 0) p *= lambda / j;
|
|
23
|
+
fpr += p * (1 - (1 - 1 / LANE_BITS) ** j) ** LANES;
|
|
24
|
+
if (j > lambda && p < 1e-15) break;
|
|
25
|
+
}
|
|
26
|
+
return fpr;
|
|
27
|
+
}
|
|
28
|
+
const MAX_BITS_PER_KEY = 128;
|
|
29
|
+
/**
|
|
30
|
+
* Minimal integer bits-per-key whose modeled split-block FPR is at or below
|
|
31
|
+
* `epsilon`. Throws {@link ParamError} when even the densest supported filter
|
|
32
|
+
* cannot reach the target, so callers get a typed rejection instead of a
|
|
33
|
+
* silently under-provisioned filter.
|
|
34
|
+
*/
|
|
35
|
+
function blockedBitsPerKey(epsilon) {
|
|
36
|
+
for (let bpk = 1; bpk <= MAX_BITS_PER_KEY; bpk++) if (blockedFprAt(bpk) <= epsilon) return bpk;
|
|
37
|
+
throw new ParamError(`epsilon ${String(epsilon)} is below the blocked-filter floor; use a classic or fuse filter`);
|
|
38
|
+
}
|
|
7
39
|
/** Thrown when an operation requires two filters built with identical parameters. */
|
|
8
40
|
var BlockedBloomParamMismatchError = class extends Error {
|
|
9
41
|
/** Discriminates this error from other `Error`s. */
|
|
@@ -11,7 +43,7 @@ var BlockedBloomParamMismatchError = class extends Error {
|
|
|
11
43
|
};
|
|
12
44
|
/**
|
|
13
45
|
* A blocked (split-block) Bloom filter: confines every lookup to a single cache
|
|
14
|
-
* line, trading ~
|
|
46
|
+
* line, trading ~15% more space for higher lookup throughput and a lower FPR.
|
|
15
47
|
*
|
|
16
48
|
* @example
|
|
17
49
|
* ```ts
|
|
@@ -27,11 +59,6 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
27
59
|
#n;
|
|
28
60
|
#words = /* @__PURE__ */ new Uint32Array(8);
|
|
29
61
|
#bits = /* @__PURE__ */ new Uint32Array(8);
|
|
30
|
-
static #ANCHORS = [
|
|
31
|
-
[2, 10.5],
|
|
32
|
-
[3, 16.9],
|
|
33
|
-
[4, 26.4]
|
|
34
|
-
];
|
|
35
62
|
/**
|
|
36
63
|
* Creates a filter sized for `n` expected keys at a target false-positive rate.
|
|
37
64
|
*
|
|
@@ -42,35 +69,62 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
42
69
|
static create(n, epsilon) {
|
|
43
70
|
assertPositiveInt(n, "n");
|
|
44
71
|
assertProbability(epsilon, "epsilon");
|
|
45
|
-
const t = Math.log10(1 / epsilon);
|
|
46
|
-
const a = BlockedBloomFilter.#ANCHORS;
|
|
47
|
-
let seg = a.findIndex((p) => t <= p[0]);
|
|
48
|
-
if (seg < 1) seg = seg === -1 ? a.length - 1 : 1;
|
|
49
|
-
const [t0, b0] = a[seg - 1] ?? [0, 0];
|
|
50
|
-
const [t1, b1] = a[seg] ?? [0, 0];
|
|
51
|
-
const bitsPerKey = b0 + (b1 - b0) / (t1 - t0) * (t - t0);
|
|
52
72
|
return new BlockedBloomFilter({
|
|
53
|
-
bitsPerKey:
|
|
73
|
+
bitsPerKey: blockedBitsPerKey(epsilon),
|
|
54
74
|
capacity: n
|
|
55
75
|
});
|
|
56
76
|
}
|
|
57
77
|
/**
|
|
78
|
+
* Builds a filter from `keys`, sized for their count at the target
|
|
79
|
+
* false-positive rate. The ergonomic entry point when the key set is already
|
|
80
|
+
* in hand; use {@link BlockedBloomFilter.create} to size for a count known
|
|
81
|
+
* ahead.
|
|
82
|
+
*
|
|
83
|
+
* @param keys - The keys to insert.
|
|
84
|
+
* @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
|
|
85
|
+
* @returns A new filter containing every key.
|
|
86
|
+
*/
|
|
87
|
+
static from(keys, epsilon) {
|
|
88
|
+
const arr = [...keys];
|
|
89
|
+
const f = BlockedBloomFilter.create(Math.max(1, arr.length), epsilon);
|
|
90
|
+
for (const k of arr) f.add(k);
|
|
91
|
+
return f;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
58
94
|
* Constructs a filter from low-level {@link BlockedBloomParams}. Prefer
|
|
59
95
|
* {@link BlockedBloomFilter.create} unless restoring a specific configuration.
|
|
60
96
|
*/
|
|
61
97
|
constructor({ bitsPerKey, capacity, seed = 0 }) {
|
|
62
98
|
assertPositiveFinite(bitsPerKey, "bitsPerKey");
|
|
63
99
|
assertPositiveInt(capacity, "capacity");
|
|
100
|
+
assertUint32(capacity, "capacity");
|
|
64
101
|
assertUint32(seed, "seed");
|
|
65
102
|
this.#numBlocks = Math.max(1, Math.ceil(bitsPerKey * capacity / 256));
|
|
66
103
|
this.#lanes = new Uint32Array(this.#numBlocks * 8);
|
|
67
104
|
this.#seed = seed;
|
|
68
105
|
this.#n = capacity;
|
|
69
106
|
}
|
|
107
|
+
static #fromNumBlocks(numBlocks, seed, n) {
|
|
108
|
+
const f = new BlockedBloomFilter({
|
|
109
|
+
bitsPerKey: 256,
|
|
110
|
+
capacity: numBlocks,
|
|
111
|
+
seed
|
|
112
|
+
});
|
|
113
|
+
f.#n = n;
|
|
114
|
+
return f;
|
|
115
|
+
}
|
|
70
116
|
/** Actual bits allocated per key (`total bits / capacity`). */
|
|
71
117
|
get bitsPerKey() {
|
|
72
118
|
return this.#numBlocks * 256 / this.#n;
|
|
73
119
|
}
|
|
120
|
+
/** Number of 256-bit blocks; one of the two fields `union` requires to match. */
|
|
121
|
+
get numBlocks() {
|
|
122
|
+
return this.#numBlocks;
|
|
123
|
+
}
|
|
124
|
+
/** Hash seed; the other field `union` requires to match. */
|
|
125
|
+
get seed() {
|
|
126
|
+
return this.#seed;
|
|
127
|
+
}
|
|
74
128
|
/** Number of bits currently set across all lanes. */
|
|
75
129
|
get length() {
|
|
76
130
|
let bits = 0;
|
|
@@ -100,19 +154,15 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
100
154
|
static fromBytes(bytes) {
|
|
101
155
|
const { type, flags, body } = readHeader(bytes);
|
|
102
156
|
if (type !== TYPE) throw new SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
103
|
-
if ((flags & 15) !==
|
|
157
|
+
if ((flags & 15) !== 0) throw new UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
|
|
104
158
|
assertMinBodyLength(body.length, 12, "blocked");
|
|
105
159
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
106
160
|
const numBlocks = dv.getUint32(0, true);
|
|
107
161
|
const seed = dv.getUint32(4, true);
|
|
108
162
|
const n = dv.getUint32(8, true);
|
|
163
|
+
if (numBlocks === 0 || n === 0) throw new SerializationError(`blocked frame declares numBlocks=${String(numBlocks)}, n=${String(n)}; both must be positive`);
|
|
109
164
|
assertBodyLength(body.length, 12 + numBlocks * 32, "blocked");
|
|
110
|
-
const f =
|
|
111
|
-
bitsPerKey: 256,
|
|
112
|
-
capacity: numBlocks,
|
|
113
|
-
seed
|
|
114
|
-
});
|
|
115
|
-
f.#n = n;
|
|
165
|
+
const f = BlockedBloomFilter.#fromNumBlocks(numBlocks, seed, n);
|
|
116
166
|
new Uint8Array(f.#lanes.buffer).set(body.subarray(12));
|
|
117
167
|
return f;
|
|
118
168
|
}
|
|
@@ -123,17 +173,44 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
123
173
|
*/
|
|
124
174
|
toBytes() {
|
|
125
175
|
const lanes = new Uint8Array(this.#lanes.buffer, this.#lanes.byteOffset, this.#lanes.byteLength);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
dv.setUint32(0, this.#numBlocks, true);
|
|
129
|
-
dv.setUint32(4, this.#seed, true);
|
|
130
|
-
dv.setUint32(8, this.#n, true);
|
|
131
|
-
body.set(lanes, 12);
|
|
132
|
-
return writeHeader({
|
|
133
|
-
version: 2,
|
|
176
|
+
return writeFrame({
|
|
177
|
+
version: 3,
|
|
134
178
|
type: TYPE,
|
|
135
|
-
flags:
|
|
136
|
-
}, body)
|
|
179
|
+
flags: 0
|
|
180
|
+
}, 12 + lanes.length, (body, dv) => {
|
|
181
|
+
dv.setUint32(0, this.#numBlocks, true);
|
|
182
|
+
dv.setUint32(4, this.#seed, true);
|
|
183
|
+
dv.setUint32(8, this.#n, true);
|
|
184
|
+
body.set(lanes, 12);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Tests structural equality: `true` when `other` serializes to identical
|
|
189
|
+
* bytes, meaning identical parameters and set bits.
|
|
190
|
+
*
|
|
191
|
+
* @param other - The filter to compare against.
|
|
192
|
+
* @returns `true` if the two filters are byte-for-byte identical.
|
|
193
|
+
*/
|
|
194
|
+
equals(other) {
|
|
195
|
+
return bytesEqual(this.toBytes(), other.toBytes());
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
199
|
+
* {@link BlockedBloomFilter.toBytes}.
|
|
200
|
+
*
|
|
201
|
+
* @returns The envelope, readable by {@link BlockedBloomFilter.fromJSON}.
|
|
202
|
+
*/
|
|
203
|
+
toJSON() {
|
|
204
|
+
return toJSONEnvelope(this.toBytes());
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Restores a filter from its {@link BlockedBloomFilter.toJSON} envelope.
|
|
208
|
+
*
|
|
209
|
+
* @param value - The JSON envelope.
|
|
210
|
+
* @returns The reconstructed filter.
|
|
211
|
+
*/
|
|
212
|
+
static fromJSON(value) {
|
|
213
|
+
return BlockedBloomFilter.fromBytes(fromJSONEnvelope(value));
|
|
137
214
|
}
|
|
138
215
|
/**
|
|
139
216
|
* Returns a new filter containing the union of this filter and `other`.
|
|
@@ -144,11 +221,7 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
144
221
|
*/
|
|
145
222
|
union(other) {
|
|
146
223
|
if (this.#numBlocks !== other.#numBlocks || this.#seed !== other.#seed) throw new BlockedBloomParamMismatchError("cannot union blocked Bloom filters whose parameters do not match");
|
|
147
|
-
const r =
|
|
148
|
-
bitsPerKey: this.bitsPerKey,
|
|
149
|
-
capacity: this.#n,
|
|
150
|
-
seed: this.#seed
|
|
151
|
-
});
|
|
224
|
+
const r = BlockedBloomFilter.#fromNumBlocks(this.#numBlocks, this.#seed, this.#n);
|
|
152
225
|
for (let i = 0; i < this.#lanes.length; i++) r.#lanes[i] = (this.#lanes[i] ?? 0) | (other.#lanes[i] ?? 0);
|
|
153
226
|
return r;
|
|
154
227
|
}
|
|
@@ -190,4 +263,4 @@ function fillBlock(key, numBlocks, seed, outWords, outBits) {
|
|
|
190
263
|
}
|
|
191
264
|
}
|
|
192
265
|
//#endregion
|
|
193
|
-
export { BlockedBloomFilter, BlockedBloomParamMismatchError, ParamError };
|
|
266
|
+
export { BlockedBloomFilter, BlockedBloomParamMismatchError, ParamError, blockedBitsPerKey, blockedFprAt };
|
package/dist/bloom/index.cjs
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
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-BIIKUHH6.cjs");
|
|
3
|
+
const require_params = require("../params--8CNXYWu.cjs");
|
|
4
4
|
//#region src/core/bitset.ts
|
|
5
5
|
var BitSetRangeError = class extends RangeError {
|
|
6
6
|
name = "BitSetRangeError";
|
|
7
7
|
};
|
|
8
8
|
const MAX_BITS = 2 ** 32;
|
|
9
|
-
var BitSet = class
|
|
9
|
+
var BitSet = class {
|
|
10
10
|
#bits;
|
|
11
|
-
static fromBytes(bytes) {
|
|
12
|
-
const bs = new BitSet(bytes.length * 8);
|
|
13
|
-
bs.#bits.set(bytes);
|
|
14
|
-
return bs;
|
|
15
|
-
}
|
|
16
11
|
constructor(nbits) {
|
|
17
12
|
if (nbits > MAX_BITS) throw new BitSetRangeError(`BitSet capacity ${String(nbits)} exceeds the 2^32-bit limit`);
|
|
18
13
|
this.#bits = new Uint8Array(Math.ceil(nbits / 8));
|
|
@@ -39,7 +34,7 @@ var BitSet = class BitSet {
|
|
|
39
34
|
//#endregion
|
|
40
35
|
//#region src/core/sizing.ts
|
|
41
36
|
/** Optimal Bloom-filter sizing: `m` bits and `k` hashes for `n` items at target FPR `epsilon`. */
|
|
42
|
-
function
|
|
37
|
+
function bloomSizing(n, epsilon) {
|
|
43
38
|
const m = Math.ceil(-n * Math.log(epsilon) / (Math.LN2 * Math.LN2));
|
|
44
39
|
return {
|
|
45
40
|
m,
|
|
@@ -82,9 +77,23 @@ var BloomFilter = class BloomFilter {
|
|
|
82
77
|
*/
|
|
83
78
|
static create(n, epsilon) {
|
|
84
79
|
require_params.assertPositiveInt(n, "n");
|
|
80
|
+
require_params.assertUint32(n, "n");
|
|
85
81
|
require_params.assertProbability(epsilon, "epsilon");
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
return BloomFilter.#withN(bloomSizing(n, epsilon), n);
|
|
83
|
+
}
|
|
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
|
+
static from(keys, epsilon) {
|
|
94
|
+
const arr = [...keys];
|
|
95
|
+
const f = BloomFilter.create(Math.max(1, arr.length), epsilon);
|
|
96
|
+
for (const k of arr) f.add(k);
|
|
88
97
|
return f;
|
|
89
98
|
}
|
|
90
99
|
/**
|
|
@@ -96,7 +105,7 @@ var BloomFilter = class BloomFilter {
|
|
|
96
105
|
static fromBytes(bytes) {
|
|
97
106
|
const { type, flags, body } = require_serialize.readHeader(bytes);
|
|
98
107
|
if (type !== TYPE) throw new require_serialize.SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
99
|
-
if ((flags & 15) !==
|
|
108
|
+
if ((flags & 15) !== 0) throw new require_serialize.UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
|
|
100
109
|
require_serialize.assertMinBodyLength(body.length, 14, "bloom");
|
|
101
110
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
102
111
|
const m = dv.getUint32(0, true);
|
|
@@ -104,12 +113,11 @@ var BloomFilter = class BloomFilter {
|
|
|
104
113
|
const seed = dv.getUint32(6, true);
|
|
105
114
|
const n = dv.getUint32(10, true);
|
|
106
115
|
require_serialize.assertBodyLength(body.length, 14 + Math.ceil(m / 8), "bloom");
|
|
107
|
-
const f =
|
|
116
|
+
const f = BloomFilter.#withN({
|
|
108
117
|
m,
|
|
109
118
|
k,
|
|
110
119
|
seed
|
|
111
|
-
});
|
|
112
|
-
f.#n = n;
|
|
120
|
+
}, n);
|
|
113
121
|
f.#bits.bytes.set(body.subarray(14));
|
|
114
122
|
return f;
|
|
115
123
|
}
|
|
@@ -119,7 +127,9 @@ var BloomFilter = class BloomFilter {
|
|
|
119
127
|
*/
|
|
120
128
|
constructor({ m, k, seed = 0 }) {
|
|
121
129
|
require_params.assertPositiveInt(m, "m");
|
|
130
|
+
require_params.assertUint32(m, "m");
|
|
122
131
|
require_params.assertPositiveInt(k, "k");
|
|
132
|
+
require_params.assertUint16(k, "k");
|
|
123
133
|
require_params.assertUint32(seed, "seed");
|
|
124
134
|
this.#bits = new BitSet(m);
|
|
125
135
|
this.#m = m;
|
|
@@ -128,6 +138,11 @@ var BloomFilter = class BloomFilter {
|
|
|
128
138
|
this.#scratch = new Uint32Array(k);
|
|
129
139
|
this.#n = Math.round(m * Math.LN2 / k);
|
|
130
140
|
}
|
|
141
|
+
static #withN(params, n) {
|
|
142
|
+
const f = new BloomFilter(params);
|
|
143
|
+
f.#n = n;
|
|
144
|
+
return f;
|
|
145
|
+
}
|
|
131
146
|
/** Number of bits in the filter. */
|
|
132
147
|
get m() {
|
|
133
148
|
return this.#m;
|
|
@@ -165,18 +180,45 @@ var BloomFilter = class BloomFilter {
|
|
|
165
180
|
*/
|
|
166
181
|
toBytes() {
|
|
167
182
|
const payload = this.#bits.bytes;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
dv.setUint32(0, this.#m, true);
|
|
171
|
-
dv.setUint16(4, this.#k, true);
|
|
172
|
-
dv.setUint32(6, this.#seed, true);
|
|
173
|
-
dv.setUint32(10, this.#n, true);
|
|
174
|
-
body.set(payload, 14);
|
|
175
|
-
return require_serialize.writeHeader({
|
|
176
|
-
version: 2,
|
|
183
|
+
return require_serialize.writeFrame({
|
|
184
|
+
version: 3,
|
|
177
185
|
type: TYPE,
|
|
178
|
-
flags:
|
|
179
|
-
}, body)
|
|
186
|
+
flags: 0
|
|
187
|
+
}, 14 + payload.length, (body, dv) => {
|
|
188
|
+
dv.setUint32(0, this.#m, true);
|
|
189
|
+
dv.setUint16(4, this.#k, true);
|
|
190
|
+
dv.setUint32(6, this.#seed, true);
|
|
191
|
+
dv.setUint32(10, this.#n, true);
|
|
192
|
+
body.set(payload, 14);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
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
|
+
equals(other) {
|
|
203
|
+
return require_serialize.bytesEqual(this.toBytes(), other.toBytes());
|
|
204
|
+
}
|
|
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
|
+
toJSON() {
|
|
212
|
+
return require_serialize.toJSONEnvelope(this.toBytes());
|
|
213
|
+
}
|
|
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
|
+
static fromJSON(value) {
|
|
221
|
+
return BloomFilter.fromBytes(require_serialize.fromJSONEnvelope(value));
|
|
180
222
|
}
|
|
181
223
|
/**
|
|
182
224
|
* Returns a new filter containing the union of this filter and `other`.
|
|
@@ -191,11 +233,11 @@ var BloomFilter = class BloomFilter {
|
|
|
191
233
|
const b = other.#bits.bytes;
|
|
192
234
|
const merged = new Uint8Array(a.length);
|
|
193
235
|
for (let i = 0; i < a.length; i++) merged[i] = (a[i] ?? 0) | (b[i] ?? 0);
|
|
194
|
-
const r =
|
|
236
|
+
const r = BloomFilter.#withN({
|
|
195
237
|
m: this.#m,
|
|
196
238
|
k: this.#k,
|
|
197
239
|
seed: this.#seed
|
|
198
|
-
});
|
|
240
|
+
}, this.#n);
|
|
199
241
|
r.#bits.bytes.set(merged);
|
|
200
242
|
return r;
|
|
201
243
|
}
|
|
@@ -224,3 +266,4 @@ var BloomFilter = class BloomFilter {
|
|
|
224
266
|
exports.BloomFilter = BloomFilter;
|
|
225
267
|
exports.BloomParamMismatchError = BloomParamMismatchError;
|
|
226
268
|
exports.ParamError = require_params.ParamError;
|
|
269
|
+
exports.bloomSizing = bloomSizing;
|
package/dist/bloom/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as BytesLike, t as FilterJSON } from "../serialize-DRKh6QOr.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. */
|
|
@@ -37,6 +37,16 @@ declare class BloomFilter {
|
|
|
37
37
|
* @returns A new, empty filter.
|
|
38
38
|
*/
|
|
39
39
|
static create(n: number, epsilon: number): BloomFilter;
|
|
40
|
+
/**
|
|
41
|
+
* Builds a filter from `keys`, sized for their count at the target
|
|
42
|
+
* false-positive rate. The ergonomic entry point when the key set is already
|
|
43
|
+
* in hand; use {@link BloomFilter.create} to size for a count known ahead.
|
|
44
|
+
*
|
|
45
|
+
* @param keys - The keys to insert.
|
|
46
|
+
* @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
|
|
47
|
+
* @returns A new filter containing every key.
|
|
48
|
+
*/
|
|
49
|
+
static from(keys: Iterable<BytesLike>, epsilon: number): BloomFilter;
|
|
40
50
|
/**
|
|
41
51
|
* Restores a filter from its {@link BloomFilter.toBytes} serialization.
|
|
42
52
|
*
|
|
@@ -73,6 +83,28 @@ declare class BloomFilter {
|
|
|
73
83
|
* @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
|
|
74
84
|
*/
|
|
75
85
|
toBytes(): Uint8Array;
|
|
86
|
+
/**
|
|
87
|
+
* Tests structural equality: `true` when `other` serializes to identical
|
|
88
|
+
* bytes, meaning identical parameters and set bits.
|
|
89
|
+
*
|
|
90
|
+
* @param other - The filter to compare against.
|
|
91
|
+
* @returns `true` if the two filters are byte-for-byte identical.
|
|
92
|
+
*/
|
|
93
|
+
equals(other: BloomFilter): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
96
|
+
* {@link BloomFilter.toBytes}.
|
|
97
|
+
*
|
|
98
|
+
* @returns The envelope, readable by {@link BloomFilter.fromJSON}.
|
|
99
|
+
*/
|
|
100
|
+
toJSON(): FilterJSON;
|
|
101
|
+
/**
|
|
102
|
+
* Restores a filter from its {@link BloomFilter.toJSON} envelope.
|
|
103
|
+
*
|
|
104
|
+
* @param value - The JSON envelope.
|
|
105
|
+
* @returns The reconstructed filter.
|
|
106
|
+
*/
|
|
107
|
+
static fromJSON(value: unknown): BloomFilter;
|
|
76
108
|
/**
|
|
77
109
|
* Returns a new filter containing the union of this filter and `other`.
|
|
78
110
|
*
|
|
@@ -96,4 +128,15 @@ declare class BloomFilter {
|
|
|
96
128
|
has(key: BytesLike): boolean;
|
|
97
129
|
}
|
|
98
130
|
//#endregion
|
|
99
|
-
|
|
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 { BloomFilter, BloomParamMismatchError, type BloomParams, type BloomSizing, type FilterJSON, ParamError, bloomSizing };
|
package/dist/bloom/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as BytesLike, t as FilterJSON } from "../serialize-DRKh6QOr.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. */
|
|
@@ -37,6 +37,16 @@ declare class BloomFilter {
|
|
|
37
37
|
* @returns A new, empty filter.
|
|
38
38
|
*/
|
|
39
39
|
static create(n: number, epsilon: number): BloomFilter;
|
|
40
|
+
/**
|
|
41
|
+
* Builds a filter from `keys`, sized for their count at the target
|
|
42
|
+
* false-positive rate. The ergonomic entry point when the key set is already
|
|
43
|
+
* in hand; use {@link BloomFilter.create} to size for a count known ahead.
|
|
44
|
+
*
|
|
45
|
+
* @param keys - The keys to insert.
|
|
46
|
+
* @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
|
|
47
|
+
* @returns A new filter containing every key.
|
|
48
|
+
*/
|
|
49
|
+
static from(keys: Iterable<BytesLike>, epsilon: number): BloomFilter;
|
|
40
50
|
/**
|
|
41
51
|
* Restores a filter from its {@link BloomFilter.toBytes} serialization.
|
|
42
52
|
*
|
|
@@ -73,6 +83,28 @@ declare class BloomFilter {
|
|
|
73
83
|
* @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
|
|
74
84
|
*/
|
|
75
85
|
toBytes(): Uint8Array;
|
|
86
|
+
/**
|
|
87
|
+
* Tests structural equality: `true` when `other` serializes to identical
|
|
88
|
+
* bytes, meaning identical parameters and set bits.
|
|
89
|
+
*
|
|
90
|
+
* @param other - The filter to compare against.
|
|
91
|
+
* @returns `true` if the two filters are byte-for-byte identical.
|
|
92
|
+
*/
|
|
93
|
+
equals(other: BloomFilter): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
96
|
+
* {@link BloomFilter.toBytes}.
|
|
97
|
+
*
|
|
98
|
+
* @returns The envelope, readable by {@link BloomFilter.fromJSON}.
|
|
99
|
+
*/
|
|
100
|
+
toJSON(): FilterJSON;
|
|
101
|
+
/**
|
|
102
|
+
* Restores a filter from its {@link BloomFilter.toJSON} envelope.
|
|
103
|
+
*
|
|
104
|
+
* @param value - The JSON envelope.
|
|
105
|
+
* @returns The reconstructed filter.
|
|
106
|
+
*/
|
|
107
|
+
static fromJSON(value: unknown): BloomFilter;
|
|
76
108
|
/**
|
|
77
109
|
* Returns a new filter containing the union of this filter and `other`.
|
|
78
110
|
*
|
|
@@ -96,4 +128,15 @@ declare class BloomFilter {
|
|
|
96
128
|
has(key: BytesLike): boolean;
|
|
97
129
|
}
|
|
98
130
|
//#endregion
|
|
99
|
-
|
|
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 { BloomFilter, BloomParamMismatchError, type BloomParams, type BloomSizing, type FilterJSON, ParamError, bloomSizing };
|