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.
@@ -1,9 +1,41 @@
1
- import { a as readHeader, d as hash32x2Into, i as assertMinBodyLength, m as reduce, n as UnknownHashVariantError, o as writeHeader, r as assertBodyLength, t as SerializationError } from "../serialize-BQ2UzZqq.js";
2
- import { a as assertUint32, i as assertProbability, n as assertPositiveFinite, r as assertPositiveInt, t as ParamError } from "../params-ChTRNxM9.js";
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 ~20-30% more space for cache-friendly throughput.
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: Math.max(1, Math.ceil(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) !== 1) throw new UnknownHashVariantError(`unsupported hash variant ${String(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 = new BlockedBloomFilter({
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
- const body = new Uint8Array(12 + lanes.length);
127
- const dv = new DataView(body.buffer);
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: 1
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 = new BlockedBloomFilter({
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 };
@@ -1,18 +1,13 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_serialize = require("../serialize-fa-pEUGq.cjs");
3
- const require_params = require("../params-J8p3bKq5.cjs");
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 BitSet {
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 optimal(n, epsilon) {
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
- const f = new BloomFilter(optimal(n, epsilon));
87
- f.#n = n;
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) !== 1) throw new require_serialize.UnknownHashVariantError(`unsupported hash variant ${String(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 = new BloomFilter({
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
- const body = new Uint8Array(14 + payload.length);
169
- const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
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: 1
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 = new BloomFilter({
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;
@@ -1,4 +1,4 @@
1
- import { t as BytesLike } from "../bytes-DCuYtUVS.cjs";
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
- export { BloomFilter, BloomParamMismatchError, type BloomParams, ParamError };
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 };
@@ -1,4 +1,4 @@
1
- import { t as BytesLike } from "../bytes-DCuYtUVS.js";
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
- export { BloomFilter, BloomParamMismatchError, type BloomParams, ParamError };
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 };