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.
Files changed (42) hide show
  1. package/README.md +44 -6
  2. package/dist/bitset-Cf2jwRJX.js +32 -0
  3. package/dist/bitset-QYGqxnEf.cjs +37 -0
  4. package/dist/blocked/index.cjs +14 -9
  5. package/dist/blocked/index.d.cts +7 -6
  6. package/dist/blocked/index.d.ts +7 -6
  7. package/dist/blocked/index.js +12 -8
  8. package/dist/bloom/index.cjs +16 -41
  9. package/dist/bloom/index.d.cts +5 -4
  10. package/dist/bloom/index.d.ts +5 -4
  11. package/dist/bloom/index.js +13 -39
  12. package/dist/bytes-DCuYtUVS.d.cts +4 -0
  13. package/dist/bytes-DCuYtUVS.d.ts +4 -0
  14. package/dist/frame/index.cjs +9 -0
  15. package/dist/frame/index.d.cts +2 -0
  16. package/dist/frame/index.d.ts +2 -0
  17. package/dist/frame/index.js +2 -0
  18. package/dist/fuse/index.cjs +26 -22
  19. package/dist/fuse/index.d.cts +7 -6
  20. package/dist/fuse/index.d.ts +7 -6
  21. package/dist/fuse/index.js +14 -11
  22. package/dist/hasher-3Cyn7Yg-.js +230 -0
  23. package/dist/hasher-MhY7aQ8F.cjs +289 -0
  24. package/dist/hll/index.cjs +12 -8
  25. package/dist/hll/index.d.cts +4 -3
  26. package/dist/hll/index.d.ts +4 -3
  27. package/dist/hll/index.js +11 -8
  28. package/dist/index.cjs +1 -1
  29. package/dist/index.d.cts +2 -3
  30. package/dist/index.d.ts +2 -3
  31. package/dist/index.js +1 -1
  32. package/dist/scalable/index.cjs +340 -0
  33. package/dist/scalable/index.d.cts +173 -0
  34. package/dist/scalable/index.d.ts +173 -0
  35. package/dist/scalable/index.js +330 -0
  36. package/dist/{serialize-Bv1zWzrh.d.cts → serialize--xR6vGSW.d.cts} +44 -8
  37. package/dist/{serialize-Bv1zWzrh.d.ts → serialize--xR6vGSW.d.ts} +44 -8
  38. package/dist/serialize-CHikZ4GH.cjs +296 -0
  39. package/dist/serialize-CMSYFym-.js +201 -0
  40. package/package.json +25 -9
  41. package/dist/serialize-D59Ri81a.js +0 -392
  42. package/dist/serialize-Ul2LKFJU.cjs +0 -517
package/README.md CHANGED
@@ -42,12 +42,13 @@ Every push runs a CI smoke matrix that imports the built package on Node 22/24,
42
42
 
43
43
  Each structure ships as its own subpath, so you only bundle what you import.
44
44
 
45
- | Import | Structure | Answers | Use for |
46
- | -------------------- | ------------- | ------------------ | -------------------------------------------------------- |
47
- | `distillate/bloom` | Classic Bloom | seen this key? | Familiar default, migration from `bloom-filters` |
48
- | `distillate/blocked` | Blocked Bloom | seen this key? | Faster lookups and a lower FPR for a small space premium |
49
- | `distillate/fuse` | Binary Fuse | seen this key? | Static set built once and queried a lot; least space |
50
- | `distillate/hll` | HyperLogLog | how many distinct? | Counting distinct users, IPs, or keys in fixed space |
45
+ | Import | Structure | Answers | Use for |
46
+ | --------------------- | -------------- | ------------------ | -------------------------------------------------------- |
47
+ | `distillate/bloom` | Classic Bloom | seen this key? | Familiar default, migration from `bloom-filters` |
48
+ | `distillate/scalable` | Scalable Bloom | seen this key? | Key count unknown or growing; keeps its FPR bound |
49
+ | `distillate/blocked` | Blocked Bloom | seen this key? | Faster lookups and a lower FPR for a small space premium |
50
+ | `distillate/fuse` | Binary Fuse | seen this key? | Static set built once and queried a lot; least space |
51
+ | `distillate/hll` | HyperLogLog | how many distinct? | Counting distinct users, IPs, or keys in fixed space |
51
52
 
52
53
  The filters are mutable except Binary Fuse, which is built once from the whole
53
54
  key set. HyperLogLog is not a filter: it counts distinct keys and cannot report
@@ -69,6 +70,24 @@ const restored = BloomFilter.fromBytes(bytes);
69
70
 
70
71
  Also: `union(other)` (merge equal-parameter filters), `bitsPerKey`, and a low-level `new BloomFilter({ m, k, seed })`.
71
72
 
73
+ ### Scalable Bloom (`distillate/scalable`)
74
+
75
+ For when you cannot say how many keys are coming. It grows by opening larger, tighter Bloom stages as keys arrive, and keeps its false-positive rate under the target however many open. Full API and trade-offs: [Scalable Bloom guide](https://distillate.akxp.net/guides/scalable/).
76
+
77
+ ```ts
78
+ import { ScalableBloomFilter } from "distillate/scalable";
79
+
80
+ const seen = ScalableBloomFilter.create(10, 0.01); // first stage, target FPR
81
+ for (let i = 0; i < 30; i++) seen.add(`key-${String(i)}`);
82
+
83
+ seen.stages; // 2
84
+ seen.has("key-29"); // true
85
+ ```
86
+
87
+ A key already held is not counted again, so duplicates never use up capacity. It costs more bits per key than a Classic Bloom sized for a known `n`; if you know `n`, use that.
88
+
89
+ Also: `from(keys, epsilon)`, `union`, `equals`, `rate`, `toBytes` / `fromBytes`, `toJSON` / `fromJSON`, and `growth` / `tightening` / `seed` options.
90
+
72
91
  ### Blocked Bloom (`distillate/blocked`)
73
92
 
74
93
  ```ts
@@ -135,6 +154,25 @@ Classic Bloom head-to-head at a **matched 1% false-positive rate** over the same
135
154
 
136
155
  Same space, same accuracy, **~75x the lookup throughput** of [`bloom-filters`](https://www.npmjs.com/package/bloom-filters) (the package distillate replaces), while hashing UTF-8 bytes with MurmurHash3 so filters stay portable and cross-language readable.
137
156
 
157
+ Cardinality is a separate head-to-head, at a **matched register count** (`m = 2 ** p`, `p = 14`, the configuration Redis uses) over the same keys, swept from 1k to 10M distinct:
158
+
159
+ | p = 14, m = 16384 | distillate | bloom-filters |
160
+ | -------------------- | --------------- | ---------------- |
161
+ | rel. error, n=1k | 0.00% | 49.63% |
162
+ | rel. error, n=100k | 0.82% | 0.78% |
163
+ | rel. error, n=10M | 0.15% | 0.41% |
164
+ | serialized, n=10M | 12,314 B binary | 40,247 B JSON |
165
+ | build 10M keys | 0.63 s | 1,165 s (19 min) |
166
+ | sustained add, n=10M | ~16 M ops/s | ~9 k ops/s |
167
+
168
+ Both sketches carry the same theoretical error at this precision (`1.04 / sqrt(m)`, about 0.81%), and once `n` is well past `m` both stay inside it. The differences are elsewhere.
169
+
170
+ **Small counts.** `bloom-filters` has no working small-range correction, so below roughly `2.5 * m` it is off by about half: counting a few thousand distinct keys in a 16k-register sketch, it answers 504 for 1,000. distillate is exact there because it is still holding sparse entries, not because its estimator is better.
171
+
172
+ **Memory does not grow.** From 10k distinct keys to 10M, distillate stays at exactly 12,314 bytes. The incumbent's JSON grows from 32,904 to 40,247 bytes over the same range, because larger register values take more digits to spell out.
173
+
174
+ **Building the sketch.** `bloom-filters` holds a flat 9k adds/sec at every size, so its cost is purely linear: 10.6 seconds for 100k distinct keys, 109 seconds for 1M, and 1,165 seconds for 10M. distillate counts the same 10M in 0.63 seconds, and its rate climbs with `n` rather than staying flat, reaching about 16 M ops/s once the registers are dense.
175
+
138
176
  These are a point-in-time snapshot on one machine. The full report (blocked/fuse, 1M capacity, the `bloomfilter` micro-package) and exactly how it is measured live in the [`apps/bench`](https://github.com/akshay-xp/distillate/tree/main/apps/bench) workspace: [RESULTS.md](https://github.com/akshay-xp/distillate/blob/main/apps/bench/RESULTS.md), [METHODOLOGY.md](https://github.com/akshay-xp/distillate/blob/main/apps/bench/METHODOLOGY.md).
139
177
 
140
178
  ## Docs
@@ -0,0 +1,32 @@
1
+ //#region src/core/bitset.ts
2
+ var BitSetRangeError = class extends RangeError {
3
+ name = "BitSetRangeError";
4
+ };
5
+ const MAX_BITS = 2 ** 32;
6
+ var BitSet = class {
7
+ #bits;
8
+ constructor(nbits) {
9
+ if (nbits > MAX_BITS) throw new BitSetRangeError(`BitSet capacity ${String(nbits)} exceeds the 2^32-bit limit`);
10
+ this.#bits = new Uint8Array(Math.ceil(nbits / 8));
11
+ }
12
+ set(i) {
13
+ const idx = i >>> 3;
14
+ this.#bits[idx] = (this.#bits[idx] ?? 0) | 1 << (i & 7);
15
+ }
16
+ get(i) {
17
+ return ((this.#bits[i >>> 3] ?? 0) & 1 << (i & 7)) !== 0;
18
+ }
19
+ get bytes() {
20
+ return this.#bits;
21
+ }
22
+ count() {
23
+ let total = 0;
24
+ for (let byte of this.#bits) while (byte) {
25
+ byte &= byte - 1;
26
+ total++;
27
+ }
28
+ return total;
29
+ }
30
+ };
31
+ //#endregion
32
+ export { BitSet as t };
@@ -0,0 +1,37 @@
1
+ //#region src/core/bitset.ts
2
+ var BitSetRangeError = class extends RangeError {
3
+ name = "BitSetRangeError";
4
+ };
5
+ const MAX_BITS = 2 ** 32;
6
+ var BitSet = class {
7
+ #bits;
8
+ constructor(nbits) {
9
+ if (nbits > MAX_BITS) throw new BitSetRangeError(`BitSet capacity ${String(nbits)} exceeds the 2^32-bit limit`);
10
+ this.#bits = new Uint8Array(Math.ceil(nbits / 8));
11
+ }
12
+ set(i) {
13
+ const idx = i >>> 3;
14
+ this.#bits[idx] = (this.#bits[idx] ?? 0) | 1 << (i & 7);
15
+ }
16
+ get(i) {
17
+ return ((this.#bits[i >>> 3] ?? 0) & 1 << (i & 7)) !== 0;
18
+ }
19
+ get bytes() {
20
+ return this.#bits;
21
+ }
22
+ count() {
23
+ let total = 0;
24
+ for (let byte of this.#bits) while (byte) {
25
+ byte &= byte - 1;
26
+ total++;
27
+ }
28
+ return total;
29
+ }
30
+ };
31
+ //#endregion
32
+ Object.defineProperty(exports, "BitSet", {
33
+ enumerable: true,
34
+ get: function() {
35
+ return BitSet;
36
+ }
37
+ });
@@ -1,8 +1,11 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_serialize = require("../serialize-Ul2LKFJU.cjs");
2
+ const require_hasher = require("../hasher-MhY7aQ8F.cjs");
3
+ const require_serialize = require("../serialize-CHikZ4GH.cjs");
3
4
  const require_params = require("../params-UTZbJ22c.cjs");
4
5
  //#region src/blocked/blocked.ts
5
6
  const TYPE = 2;
7
+ const PARAMS_SIZE = 16;
8
+ const PARAMS_FIELDS_END = 12;
6
9
  const SALT = Uint32Array.of(1203114875, 1150766481, 2284105051, 2729912477, 1884591559, 770785867, 2667333959, 1550580529);
7
10
  const scratch2 = new Uint32Array(2);
8
11
  const BLOCK_BITS = 256;
@@ -91,28 +94,29 @@ var BlockedBloomFilter = class BlockedBloomFilter {
91
94
  const { type, flags, body } = require_serialize.readHeader(bytes);
92
95
  if (type !== TYPE) throw new require_serialize.SerializationError(`expected DSTL type ${String(TYPE)}, got ${String(type)}`);
93
96
  if ((flags & 15) !== 0) throw new require_serialize.UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
94
- require_serialize.assertMinBodyLength(body.length, 12, "blocked");
97
+ require_serialize.assertMinBodyLength(body.length, PARAMS_SIZE, "blocked");
98
+ require_serialize.assertParamsPadding(body, PARAMS_FIELDS_END, PARAMS_SIZE, "blocked");
95
99
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
96
100
  const numBlocks = dv.getUint32(0, true);
97
101
  const seed = dv.getUint32(4, true);
98
102
  const n = dv.getUint32(8, true);
99
103
  if (numBlocks === 0 || n === 0) throw new require_serialize.SerializationError(`blocked frame declares numBlocks=${String(numBlocks)}, n=${String(n)}; both must be positive`);
100
- require_serialize.assertBodyLength(body.length, 12 + numBlocks * 32, "blocked");
104
+ require_serialize.assertBodyLength(body.length, PARAMS_SIZE + numBlocks * 32, "blocked");
101
105
  const f = BlockedBloomFilter.#fromNumBlocks(numBlocks, seed, n);
102
- new Uint8Array(f.#lanes.buffer).set(body.subarray(12));
106
+ new Uint8Array(f.#lanes.buffer).set(body.subarray(PARAMS_SIZE));
103
107
  return f;
104
108
  }
105
109
  toBytes() {
106
110
  const lanes = new Uint8Array(this.#lanes.buffer, this.#lanes.byteOffset, this.#lanes.byteLength);
107
111
  return require_serialize.writeFrame({
108
- version: 4,
112
+ version: 5,
109
113
  type: TYPE,
110
114
  flags: 0
111
- }, 12 + lanes.length, (body, dv) => {
115
+ }, PARAMS_SIZE, lanes.length, (body, dv) => {
112
116
  dv.setUint32(0, this.#numBlocks, true);
113
117
  dv.setUint32(4, this.#seed, true);
114
118
  dv.setUint32(8, this.#n, true);
115
- body.set(lanes, 12);
119
+ body.set(lanes, PARAMS_SIZE);
116
120
  });
117
121
  }
118
122
  equals(other) {
@@ -147,8 +151,8 @@ var BlockedBloomFilter = class BlockedBloomFilter {
147
151
  }
148
152
  };
149
153
  function fillBlock(key, numBlocks, seed, outWords, outBits) {
150
- require_serialize.hash32x2Into(key, seed, scratch2);
151
- const block = require_serialize.reduce(scratch2[0] ?? 0, numBlocks);
154
+ require_hasher.hash32x2Into(key, seed, scratch2);
155
+ const block = require_hasher.reduce(scratch2[0] ?? 0, numBlocks);
152
156
  const x = scratch2[1] ?? 0;
153
157
  const base = block * 8;
154
158
  for (let i = 0; i < 8; i++) {
@@ -162,6 +166,7 @@ exports.BlockedBloomFilter = BlockedBloomFilter;
162
166
  exports.BlockedBloomParamMismatchError = BlockedBloomParamMismatchError;
163
167
  exports.ChecksumError = require_serialize.ChecksumError;
164
168
  exports.ParamError = require_params.ParamError;
169
+ exports.ReservedBitsError = require_serialize.ReservedBitsError;
165
170
  exports.SerializationError = require_serialize.SerializationError;
166
171
  exports.TruncatedError = require_serialize.TruncatedError;
167
172
  exports.UnknownHashVariantError = require_serialize.UnknownHashVariantError;
@@ -1,4 +1,5 @@
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-Bv1zWzrh.cjs";
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";
2
3
  import { t as ParamError } from "../params-DnqJBqLS.cjs";
3
4
  //#region src/blocked/blocked.d.ts
4
5
  /**
@@ -8,16 +9,16 @@ import { t as ParamError } from "../params-DnqJBqLS.cjs";
8
9
  * `lambda = 256 / bitsPerKey`. This clustering average is why the blocked curve
9
10
  * is not linear in `log10(1/epsilon)`.
10
11
  */
11
- declare function blockedFprAt(bitsPerKey: number): number;
12
+ export declare function blockedFprAt(bitsPerKey: number): number;
12
13
  /**
13
14
  * Minimal integer bits-per-key whose modeled split-block FPR is at or below
14
15
  * `epsilon`. Throws {@link ParamError} when even the densest supported filter
15
16
  * cannot reach the target, so callers get a typed rejection instead of a
16
17
  * silently under-provisioned filter.
17
18
  */
18
- declare function blockedBitsPerKey(epsilon: number): number;
19
+ export declare function blockedBitsPerKey(epsilon: number): number;
19
20
  /** Thrown when an operation requires two filters built with identical parameters. */
20
- declare class BlockedBloomParamMismatchError extends Error {
21
+ export declare class BlockedBloomParamMismatchError extends Error {
21
22
  /** Discriminates this error from other `Error`s. */
22
23
  override readonly name = "BlockedBloomParamMismatchError";
23
24
  }
@@ -41,7 +42,7 @@ interface BlockedBloomParams {
41
42
  * filter.has("alice"); // true
42
43
  * ```
43
44
  */
44
- declare class BlockedBloomFilter {
45
+ export declare class BlockedBloomFilter {
45
46
  #private;
46
47
  /**
47
48
  * Creates a filter sized for `n` expected keys at a target false-positive rate.
@@ -142,4 +143,4 @@ declare class BlockedBloomFilter {
142
143
  has(key: BytesLike): boolean;
143
144
  }
144
145
  //#endregion
145
- export { BadMagicError, BlockedBloomFilter, BlockedBloomParamMismatchError, type BlockedBloomParams, ChecksumError, type FilterJSON, ParamError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError, blockedBitsPerKey, blockedFprAt };
146
+ export { BadMagicError, type BlockedBloomParams, ChecksumError, type FilterJSON, ParamError, ReservedBitsError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError };
@@ -1,4 +1,5 @@
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-Bv1zWzrh.js";
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";
2
3
  import { t as ParamError } from "../params-DnqJBqLS.js";
3
4
  //#region src/blocked/blocked.d.ts
4
5
  /**
@@ -8,16 +9,16 @@ import { t as ParamError } from "../params-DnqJBqLS.js";
8
9
  * `lambda = 256 / bitsPerKey`. This clustering average is why the blocked curve
9
10
  * is not linear in `log10(1/epsilon)`.
10
11
  */
11
- declare function blockedFprAt(bitsPerKey: number): number;
12
+ export declare function blockedFprAt(bitsPerKey: number): number;
12
13
  /**
13
14
  * Minimal integer bits-per-key whose modeled split-block FPR is at or below
14
15
  * `epsilon`. Throws {@link ParamError} when even the densest supported filter
15
16
  * cannot reach the target, so callers get a typed rejection instead of a
16
17
  * silently under-provisioned filter.
17
18
  */
18
- declare function blockedBitsPerKey(epsilon: number): number;
19
+ export declare function blockedBitsPerKey(epsilon: number): number;
19
20
  /** Thrown when an operation requires two filters built with identical parameters. */
20
- declare class BlockedBloomParamMismatchError extends Error {
21
+ export declare class BlockedBloomParamMismatchError extends Error {
21
22
  /** Discriminates this error from other `Error`s. */
22
23
  override readonly name = "BlockedBloomParamMismatchError";
23
24
  }
@@ -41,7 +42,7 @@ interface BlockedBloomParams {
41
42
  * filter.has("alice"); // true
42
43
  * ```
43
44
  */
44
- declare class BlockedBloomFilter {
45
+ export declare class BlockedBloomFilter {
45
46
  #private;
46
47
  /**
47
48
  * Creates a filter sized for `n` expected keys at a target false-positive rate.
@@ -142,4 +143,4 @@ declare class BlockedBloomFilter {
142
143
  has(key: BytesLike): boolean;
143
144
  }
144
145
  //#endregion
145
- export { BadMagicError, BlockedBloomFilter, BlockedBloomParamMismatchError, type BlockedBloomParams, ChecksumError, type FilterJSON, ParamError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError, blockedBitsPerKey, blockedFprAt };
146
+ export { BadMagicError, type BlockedBloomParams, ChecksumError, type FilterJSON, ParamError, ReservedBitsError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError };
@@ -1,7 +1,10 @@
1
- import { a as UnknownHashVariantError, 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, v as hash32x2Into, x as reduce } from "../serialize-D59Ri81a.js";
1
+ import { a as hash32x2Into, u as reduce } from "../hasher-3Cyn7Yg-.js";
2
+ import { a as TruncatedError, c as assertBodyLength, d as bytesEqual, f as fromJSONEnvelope, g as writeFrame, h as toJSONEnvelope, i as SerializationError, l as assertMinBodyLength, m as readHeader, n as ChecksumError, o as UnknownHashVariantError, r as ReservedBitsError, s as UnknownVersionError, t as BadMagicError, u as assertParamsPadding } from "../serialize-CMSYFym-.js";
2
3
  import { i as assertProbability, n as assertPositiveFinite, o as assertUint32, r as assertPositiveInt, t as ParamError } from "../params-CeajSrwv.js";
3
4
  //#region src/blocked/blocked.ts
4
5
  const TYPE = 2;
6
+ const PARAMS_SIZE = 16;
7
+ const PARAMS_FIELDS_END = 12;
5
8
  const SALT = Uint32Array.of(1203114875, 1150766481, 2284105051, 2729912477, 1884591559, 770785867, 2667333959, 1550580529);
6
9
  const scratch2 = new Uint32Array(2);
7
10
  const BLOCK_BITS = 256;
@@ -90,28 +93,29 @@ var BlockedBloomFilter = class BlockedBloomFilter {
90
93
  const { type, flags, body } = readHeader(bytes);
91
94
  if (type !== TYPE) throw new SerializationError(`expected DSTL type ${String(TYPE)}, got ${String(type)}`);
92
95
  if ((flags & 15) !== 0) throw new UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
93
- assertMinBodyLength(body.length, 12, "blocked");
96
+ assertMinBodyLength(body.length, PARAMS_SIZE, "blocked");
97
+ assertParamsPadding(body, PARAMS_FIELDS_END, PARAMS_SIZE, "blocked");
94
98
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
95
99
  const numBlocks = dv.getUint32(0, true);
96
100
  const seed = dv.getUint32(4, true);
97
101
  const n = dv.getUint32(8, true);
98
102
  if (numBlocks === 0 || n === 0) throw new SerializationError(`blocked frame declares numBlocks=${String(numBlocks)}, n=${String(n)}; both must be positive`);
99
- assertBodyLength(body.length, 12 + numBlocks * 32, "blocked");
103
+ assertBodyLength(body.length, PARAMS_SIZE + numBlocks * 32, "blocked");
100
104
  const f = BlockedBloomFilter.#fromNumBlocks(numBlocks, seed, n);
101
- new Uint8Array(f.#lanes.buffer).set(body.subarray(12));
105
+ new Uint8Array(f.#lanes.buffer).set(body.subarray(PARAMS_SIZE));
102
106
  return f;
103
107
  }
104
108
  toBytes() {
105
109
  const lanes = new Uint8Array(this.#lanes.buffer, this.#lanes.byteOffset, this.#lanes.byteLength);
106
110
  return writeFrame({
107
- version: 4,
111
+ version: 5,
108
112
  type: TYPE,
109
113
  flags: 0
110
- }, 12 + lanes.length, (body, dv) => {
114
+ }, PARAMS_SIZE, lanes.length, (body, dv) => {
111
115
  dv.setUint32(0, this.#numBlocks, true);
112
116
  dv.setUint32(4, this.#seed, true);
113
117
  dv.setUint32(8, this.#n, true);
114
- body.set(lanes, 12);
118
+ body.set(lanes, PARAMS_SIZE);
115
119
  });
116
120
  }
117
121
  equals(other) {
@@ -156,4 +160,4 @@ function fillBlock(key, numBlocks, seed, outWords, outBits) {
156
160
  }
157
161
  }
158
162
  //#endregion
159
- export { BadMagicError, BlockedBloomFilter, BlockedBloomParamMismatchError, ChecksumError, ParamError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError, blockedBitsPerKey, blockedFprAt };
163
+ export { BadMagicError, BlockedBloomFilter, BlockedBloomParamMismatchError, ChecksumError, ParamError, ReservedBitsError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError, blockedBitsPerKey, blockedFprAt };
@@ -1,40 +1,13 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_serialize = require("../serialize-Ul2LKFJU.cjs");
2
+ const require_bitset = require("../bitset-QYGqxnEf.cjs");
3
+ const require_hasher = require("../hasher-MhY7aQ8F.cjs");
4
+ const require_serialize = require("../serialize-CHikZ4GH.cjs");
3
5
  const require_params = require("../params-UTZbJ22c.cjs");
4
6
  const require_sizing = require("../sizing-BLzZk2zr.cjs");
5
- //#region src/core/bitset.ts
6
- var BitSetRangeError = class extends RangeError {
7
- name = "BitSetRangeError";
8
- };
9
- const MAX_BITS = 2 ** 32;
10
- var BitSet = class {
11
- #bits;
12
- constructor(nbits) {
13
- if (nbits > MAX_BITS) throw new BitSetRangeError(`BitSet capacity ${String(nbits)} exceeds the 2^32-bit limit`);
14
- this.#bits = new Uint8Array(Math.ceil(nbits / 8));
15
- }
16
- set(i) {
17
- const idx = i >>> 3;
18
- this.#bits[idx] = (this.#bits[idx] ?? 0) | 1 << (i & 7);
19
- }
20
- get(i) {
21
- return ((this.#bits[i >>> 3] ?? 0) & 1 << (i & 7)) !== 0;
22
- }
23
- get bytes() {
24
- return this.#bits;
25
- }
26
- count() {
27
- let total = 0;
28
- for (let byte of this.#bits) while (byte) {
29
- byte &= byte - 1;
30
- total++;
31
- }
32
- return total;
33
- }
34
- };
35
- //#endregion
36
7
  //#region src/bloom/bloom.ts
37
8
  const TYPE = 1;
9
+ const PARAMS_SIZE = 16;
10
+ const PARAMS_FIELDS_END = 14;
38
11
  var BloomParamMismatchError = class extends Error {
39
12
  name = "BloomParamMismatchError";
40
13
  };
@@ -64,20 +37,21 @@ var BloomFilter = class BloomFilter {
64
37
  const { type, flags, body } = require_serialize.readHeader(bytes);
65
38
  if (type !== TYPE) throw new require_serialize.SerializationError(`expected DSTL type ${String(TYPE)}, got ${String(type)}`);
66
39
  if ((flags & 15) !== 0) throw new require_serialize.UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
67
- require_serialize.assertMinBodyLength(body.length, 14, "bloom");
40
+ require_serialize.assertMinBodyLength(body.length, PARAMS_SIZE, "bloom");
41
+ require_serialize.assertParamsPadding(body, PARAMS_FIELDS_END, PARAMS_SIZE, "bloom");
68
42
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
69
43
  const m = dv.getUint32(0, true);
70
44
  const k = dv.getUint16(4, true);
71
45
  const seed = dv.getUint32(6, true);
72
46
  const n = dv.getUint32(10, true);
73
- require_serialize.assertBodyLength(body.length, 14 + Math.ceil(m / 8), "bloom");
47
+ require_serialize.assertBodyLength(body.length, PARAMS_SIZE + Math.ceil(m / 8), "bloom");
74
48
  const f = new BloomFilter({
75
49
  m,
76
50
  k,
77
51
  seed,
78
52
  n
79
53
  });
80
- f.#bits.bytes.set(body.subarray(14));
54
+ f.#bits.bytes.set(body.subarray(PARAMS_SIZE));
81
55
  return f;
82
56
  }
83
57
  constructor({ m, k, seed = 0, n }) {
@@ -90,7 +64,7 @@ var BloomFilter = class BloomFilter {
90
64
  require_params.assertPositiveInt(n, "n");
91
65
  require_params.assertUint32(n, "n");
92
66
  }
93
- this.#bits = new BitSet(m);
67
+ this.#bits = new require_bitset.BitSet(m);
94
68
  this.#m = m;
95
69
  this.#k = k;
96
70
  this.#seed = seed;
@@ -118,15 +92,15 @@ var BloomFilter = class BloomFilter {
118
92
  toBytes() {
119
93
  const payload = this.#bits.bytes;
120
94
  return require_serialize.writeFrame({
121
- version: 4,
95
+ version: 5,
122
96
  type: TYPE,
123
97
  flags: 0
124
- }, 14 + payload.length, (body, dv) => {
98
+ }, PARAMS_SIZE, payload.length, (body, dv) => {
125
99
  dv.setUint32(0, this.#m, true);
126
100
  dv.setUint16(4, this.#k, true);
127
101
  dv.setUint32(6, this.#seed, true);
128
102
  dv.setUint32(10, this.#n, true);
129
- body.set(payload, 14);
103
+ body.set(payload, PARAMS_SIZE);
130
104
  });
131
105
  }
132
106
  equals(other) {
@@ -154,11 +128,11 @@ var BloomFilter = class BloomFilter {
154
128
  return r;
155
129
  }
156
130
  add(key) {
157
- require_serialize.probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
131
+ require_hasher.probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
158
132
  for (let i = 0; i < this.#k; i++) this.#bits.set(this.#scratch[i] ?? 0);
159
133
  }
160
134
  has(key) {
161
- require_serialize.probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
135
+ require_hasher.probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
162
136
  for (let i = 0; i < this.#k; i++) if (!this.#bits.get(this.#scratch[i] ?? 0)) return false;
163
137
  return true;
164
138
  }
@@ -169,6 +143,7 @@ exports.BloomFilter = BloomFilter;
169
143
  exports.BloomParamMismatchError = BloomParamMismatchError;
170
144
  exports.ChecksumError = require_serialize.ChecksumError;
171
145
  exports.ParamError = require_params.ParamError;
146
+ exports.ReservedBitsError = require_serialize.ReservedBitsError;
172
147
  exports.SerializationError = require_serialize.SerializationError;
173
148
  exports.TruncatedError = require_serialize.TruncatedError;
174
149
  exports.UnknownHashVariantError = require_serialize.UnknownHashVariantError;
@@ -1,9 +1,10 @@
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-Bv1zWzrh.cjs";
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";
2
3
  import { t as ParamError } from "../params-DnqJBqLS.cjs";
3
4
  import { r as bloomSizing, t as BloomSizing } from "../sizing-wYOW27eY.cjs";
4
5
  //#region src/bloom/bloom.d.ts
5
6
  /** Thrown when an operation requires two filters built with identical parameters. */
6
- declare class BloomParamMismatchError extends Error {
7
+ export declare class BloomParamMismatchError extends Error {
7
8
  /** Discriminates this error from other `Error`s. */
8
9
  override readonly name = "BloomParamMismatchError";
9
10
  }
@@ -34,7 +35,7 @@ interface BloomParams {
34
35
  * filter.has("bob"); // false (or a ~1% false positive)
35
36
  * ```
36
37
  */
37
- declare class BloomFilter {
38
+ export declare class BloomFilter {
38
39
  #private;
39
40
  /**
40
41
  * Creates a filter sized for `n` expected keys at a target false-positive rate.
@@ -143,4 +144,4 @@ declare class BloomFilter {
143
144
  has(key: BytesLike): boolean;
144
145
  }
145
146
  //#endregion
146
- export { BadMagicError, BloomFilter, BloomParamMismatchError, type BloomParams, type BloomSizing, ChecksumError, type FilterJSON, ParamError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError, bloomSizing };
147
+ export { BadMagicError, type BloomParams, type BloomSizing, ChecksumError, type FilterJSON, ParamError, ReservedBitsError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError, bloomSizing };
@@ -1,9 +1,10 @@
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-Bv1zWzrh.js";
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";
2
3
  import { t as ParamError } from "../params-DnqJBqLS.js";
3
4
  import { r as bloomSizing, t as BloomSizing } from "../sizing-wYOW27eY.js";
4
5
  //#region src/bloom/bloom.d.ts
5
6
  /** Thrown when an operation requires two filters built with identical parameters. */
6
- declare class BloomParamMismatchError extends Error {
7
+ export declare class BloomParamMismatchError extends Error {
7
8
  /** Discriminates this error from other `Error`s. */
8
9
  override readonly name = "BloomParamMismatchError";
9
10
  }
@@ -34,7 +35,7 @@ interface BloomParams {
34
35
  * filter.has("bob"); // false (or a ~1% false positive)
35
36
  * ```
36
37
  */
37
- declare class BloomFilter {
38
+ export declare class BloomFilter {
38
39
  #private;
39
40
  /**
40
41
  * Creates a filter sized for `n` expected keys at a target false-positive rate.
@@ -143,4 +144,4 @@ declare class BloomFilter {
143
144
  has(key: BytesLike): boolean;
144
145
  }
145
146
  //#endregion
146
- export { BadMagicError, BloomFilter, BloomParamMismatchError, type BloomParams, type BloomSizing, ChecksumError, type FilterJSON, ParamError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError, bloomSizing };
147
+ export { BadMagicError, type BloomParams, type BloomSizing, ChecksumError, type FilterJSON, ParamError, ReservedBitsError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError, bloomSizing };
@@ -1,39 +1,12 @@
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-D59Ri81a.js";
1
+ import { t as BitSet } from "../bitset-Cf2jwRJX.js";
2
+ import { c as probeInto } from "../hasher-3Cyn7Yg-.js";
3
+ import { a as TruncatedError, c as assertBodyLength, d as bytesEqual, f as fromJSONEnvelope, g as writeFrame, h as toJSONEnvelope, i as SerializationError, l as assertMinBodyLength, m as readHeader, n as ChecksumError, o as UnknownHashVariantError, r as ReservedBitsError, s as UnknownVersionError, t as BadMagicError, u as assertParamsPadding } from "../serialize-CMSYFym-.js";
2
4
  import { a as assertUint16, i as assertProbability, o as assertUint32, r as assertPositiveInt, t as ParamError } from "../params-CeajSrwv.js";
3
5
  import { t as bloomSizing } from "../sizing-BtUrtvF_.js";
4
- //#region src/core/bitset.ts
5
- var BitSetRangeError = class extends RangeError {
6
- name = "BitSetRangeError";
7
- };
8
- const MAX_BITS = 2 ** 32;
9
- var BitSet = class {
10
- #bits;
11
- constructor(nbits) {
12
- if (nbits > MAX_BITS) throw new BitSetRangeError(`BitSet capacity ${String(nbits)} exceeds the 2^32-bit limit`);
13
- this.#bits = new Uint8Array(Math.ceil(nbits / 8));
14
- }
15
- set(i) {
16
- const idx = i >>> 3;
17
- this.#bits[idx] = (this.#bits[idx] ?? 0) | 1 << (i & 7);
18
- }
19
- get(i) {
20
- return ((this.#bits[i >>> 3] ?? 0) & 1 << (i & 7)) !== 0;
21
- }
22
- get bytes() {
23
- return this.#bits;
24
- }
25
- count() {
26
- let total = 0;
27
- for (let byte of this.#bits) while (byte) {
28
- byte &= byte - 1;
29
- total++;
30
- }
31
- return total;
32
- }
33
- };
34
- //#endregion
35
6
  //#region src/bloom/bloom.ts
36
7
  const TYPE = 1;
8
+ const PARAMS_SIZE = 16;
9
+ const PARAMS_FIELDS_END = 14;
37
10
  var BloomParamMismatchError = class extends Error {
38
11
  name = "BloomParamMismatchError";
39
12
  };
@@ -63,20 +36,21 @@ var BloomFilter = class BloomFilter {
63
36
  const { type, flags, body } = readHeader(bytes);
64
37
  if (type !== TYPE) throw new SerializationError(`expected DSTL type ${String(TYPE)}, got ${String(type)}`);
65
38
  if ((flags & 15) !== 0) throw new UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
66
- assertMinBodyLength(body.length, 14, "bloom");
39
+ assertMinBodyLength(body.length, PARAMS_SIZE, "bloom");
40
+ assertParamsPadding(body, PARAMS_FIELDS_END, PARAMS_SIZE, "bloom");
67
41
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
68
42
  const m = dv.getUint32(0, true);
69
43
  const k = dv.getUint16(4, true);
70
44
  const seed = dv.getUint32(6, true);
71
45
  const n = dv.getUint32(10, true);
72
- assertBodyLength(body.length, 14 + Math.ceil(m / 8), "bloom");
46
+ assertBodyLength(body.length, PARAMS_SIZE + Math.ceil(m / 8), "bloom");
73
47
  const f = new BloomFilter({
74
48
  m,
75
49
  k,
76
50
  seed,
77
51
  n
78
52
  });
79
- f.#bits.bytes.set(body.subarray(14));
53
+ f.#bits.bytes.set(body.subarray(PARAMS_SIZE));
80
54
  return f;
81
55
  }
82
56
  constructor({ m, k, seed = 0, n }) {
@@ -117,15 +91,15 @@ var BloomFilter = class BloomFilter {
117
91
  toBytes() {
118
92
  const payload = this.#bits.bytes;
119
93
  return writeFrame({
120
- version: 4,
94
+ version: 5,
121
95
  type: TYPE,
122
96
  flags: 0
123
- }, 14 + payload.length, (body, dv) => {
97
+ }, PARAMS_SIZE, payload.length, (body, dv) => {
124
98
  dv.setUint32(0, this.#m, true);
125
99
  dv.setUint16(4, this.#k, true);
126
100
  dv.setUint32(6, this.#seed, true);
127
101
  dv.setUint32(10, this.#n, true);
128
- body.set(payload, 14);
102
+ body.set(payload, PARAMS_SIZE);
129
103
  });
130
104
  }
131
105
  equals(other) {
@@ -163,4 +137,4 @@ var BloomFilter = class BloomFilter {
163
137
  }
164
138
  };
165
139
  //#endregion
166
- export { BadMagicError, BloomFilter, BloomParamMismatchError, ChecksumError, ParamError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError, bloomSizing };
140
+ export { BadMagicError, BloomFilter, BloomParamMismatchError, ChecksumError, ParamError, ReservedBitsError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError, bloomSizing };
@@ -0,0 +1,4 @@
1
+ //#region src/core/bytes.d.ts
2
+ type BytesLike = string | Uint8Array | ArrayBuffer;
3
+ //#endregion
4
+ export { BytesLike as t };
@@ -0,0 +1,4 @@
1
+ //#region src/core/bytes.d.ts
2
+ type BytesLike = string | Uint8Array | ArrayBuffer;
3
+ //#endregion
4
+ export { BytesLike as t };
@@ -0,0 +1,9 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_serialize = require("../serialize-CHikZ4GH.cjs");
3
+ exports.BadMagicError = require_serialize.BadMagicError;
4
+ exports.ChecksumError = require_serialize.ChecksumError;
5
+ exports.ReservedBitsError = require_serialize.ReservedBitsError;
6
+ exports.SerializationError = require_serialize.SerializationError;
7
+ exports.TruncatedError = require_serialize.TruncatedError;
8
+ exports.UnknownVersionError = require_serialize.UnknownVersionError;
9
+ exports.readFrameAt = require_serialize.readFrameAt;
@@ -0,0 +1,2 @@
1
+ import { a as ReservedBitsError, i as Frame, l as UnknownVersionError, n as ChecksumError, o as SerializationError, s as TruncatedError, t as BadMagicError, u as readFrameAt } from "../serialize--xR6vGSW.cjs";
2
+ export { BadMagicError, ChecksumError, type Frame, ReservedBitsError, SerializationError, TruncatedError, UnknownVersionError, readFrameAt };
@@ -0,0 +1,2 @@
1
+ import { a as ReservedBitsError, i as Frame, l as UnknownVersionError, n as ChecksumError, o as SerializationError, s as TruncatedError, t as BadMagicError, u as readFrameAt } from "../serialize--xR6vGSW.js";
2
+ export { BadMagicError, ChecksumError, type Frame, ReservedBitsError, SerializationError, TruncatedError, UnknownVersionError, readFrameAt };