distillate 0.1.0 → 0.1.2

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 CHANGED
@@ -92,6 +92,19 @@ const precise = BinaryFuse16.from(["alice", "bob", "carol"]);
92
92
 
93
93
  Also: `toBytes` / `fromBytes`. No `add` / `delete`; rebuild `from` the new set to change membership.
94
94
 
95
+ ## Performance
96
+
97
+ Classic Bloom head-to-head at a **matched 1% false-positive rate** over the same 100k keys, measured by identical code (Node, Apple M5):
98
+
99
+ | Classic Bloom | bits/key | measured FPR | `has` throughput |
100
+ | -------------- | -------- | ------------ | ---------------- |
101
+ | **distillate** | 9.59 | 1.03% | ~7.0 M ops/s |
102
+ | bloom-filters | 9.59 | 0.99% | ~0.29 M ops/s |
103
+
104
+ Same space, same accuracy, **~24x 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.
105
+
106
+ 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 [distillate-bench](https://github.com/akshay-xp/distillate-bench) repo: [RESULTS.md](https://github.com/akshay-xp/distillate-bench/blob/main/RESULTS.md), [METHODOLOGY.md](https://github.com/akshay-xp/distillate-bench/blob/main/METHODOLOGY.md).
107
+
95
108
  ## Docs
96
109
 
97
110
  Design notes, the structure decision matrix, hashing, and the binary format live in [`docs/`](./docs):
@@ -99,6 +112,8 @@ Design notes, the structure decision matrix, hashing, and the binary format live
99
112
  - [overview](./docs/overview.md): what and why
100
113
  - [structures](./docs/structures.md): decision matrix and the full lineup
101
114
  - [architecture](./docs/architecture.md), [hashing](./docs/hashing.md), [serialization](./docs/serialization.md)
115
+ - [versioning](./docs/versioning.md): SemVer policy and supported-runtime baseline
116
+ - [API reference](./docs/api): generated from TSDoc (per entry point)
102
117
 
103
118
  ## License
104
119
 
@@ -1,17 +1,30 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_serialize = require("../serialize-C5rwVPvv.cjs");
2
+ const require_serialize = require("../serialize-CHHDM4TQ.cjs");
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
- /**
7
- * Split-block probe: derive one block index and 8 single-bit lane masks for
8
- * `key`. Writes lane word indices into `outWords` and their masks into
9
- * `outBits` (both length 8, caller-owned so no per-call allocation). A block is
10
- * 256 bits = 8 contiguous 32-bit lanes; word `block*8 + i` gets one bit set.
11
- */
6
+ const scratchHash = {
7
+ h1lo: 0,
8
+ h1hi: 0,
9
+ h2lo: 0,
10
+ h2hi: 0
11
+ };
12
+ /** Thrown when an operation requires two filters built with identical parameters. */
12
13
  var BlockedBloomParamMismatchError = class extends Error {
14
+ /** Discriminates this error from other `Error`s. */
13
15
  name = "BlockedBloomParamMismatchError";
14
16
  };
17
+ /**
18
+ * A blocked (split-block) Bloom filter: confines every lookup to a single cache
19
+ * line, trading ~20-30% more space for cache-friendly throughput.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const filter = BlockedBloomFilter.create(100_000, 0.01);
24
+ * filter.add("alice");
25
+ * filter.has("alice"); // true
26
+ * ```
27
+ */
15
28
  var BlockedBloomFilter = class BlockedBloomFilter {
16
29
  #lanes;
17
30
  #numBlocks;
@@ -24,6 +37,13 @@ var BlockedBloomFilter = class BlockedBloomFilter {
24
37
  [3, 16.9],
25
38
  [4, 26.4]
26
39
  ];
40
+ /**
41
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
42
+ *
43
+ * @param n - Expected number of keys.
44
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
45
+ * @returns A new, empty filter.
46
+ */
27
47
  static create(n, epsilon) {
28
48
  const t = Math.log10(1 / epsilon);
29
49
  const a = BlockedBloomFilter.#ANCHORS;
@@ -37,15 +57,26 @@ var BlockedBloomFilter = class BlockedBloomFilter {
37
57
  capacity: n
38
58
  });
39
59
  }
60
+ /**
61
+ * Constructs a filter from low-level {@link BlockedBloomParams}. Prefer
62
+ * {@link BlockedBloomFilter.create} unless restoring a specific configuration.
63
+ */
40
64
  constructor({ bitsPerKey, capacity, seed = 0 }) {
41
65
  this.#numBlocks = Math.max(1, Math.ceil(bitsPerKey * capacity / 256));
42
66
  this.#lanes = new Uint32Array(this.#numBlocks * 8);
43
67
  this.#seed = seed;
44
68
  this.#n = capacity;
45
69
  }
70
+ /** Actual bits allocated per key (`total bits / capacity`). */
46
71
  get bitsPerKey() {
47
72
  return this.#numBlocks * 256 / this.#n;
48
73
  }
74
+ /**
75
+ * Restores a filter from its {@link BlockedBloomFilter.toBytes} serialization.
76
+ *
77
+ * @param bytes - The serialized filter.
78
+ * @returns The reconstructed filter.
79
+ */
49
80
  static fromBytes(bytes) {
50
81
  const { body } = require_serialize.readHeader(bytes);
51
82
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
@@ -61,6 +92,11 @@ var BlockedBloomFilter = class BlockedBloomFilter {
61
92
  new Uint8Array(f.#lanes.buffer).set(body.subarray(12));
62
93
  return f;
63
94
  }
95
+ /**
96
+ * Serializes the filter to a portable little-endian byte layout.
97
+ *
98
+ * @returns The serialized filter, readable by {@link BlockedBloomFilter.fromBytes}.
99
+ */
64
100
  toBytes() {
65
101
  const lanes = new Uint8Array(this.#lanes.buffer, this.#lanes.byteOffset, this.#lanes.byteLength);
66
102
  const body = new Uint8Array(12 + lanes.length);
@@ -75,6 +111,13 @@ var BlockedBloomFilter = class BlockedBloomFilter {
75
111
  flags: 0
76
112
  }, body);
77
113
  }
114
+ /**
115
+ * Returns a new filter containing the union of this filter and `other`.
116
+ *
117
+ * @param other - A filter built with identical parameters.
118
+ * @returns A new filter reporting membership for keys in either input.
119
+ * @throws {@link BlockedBloomParamMismatchError} if the parameters differ.
120
+ */
78
121
  union(other) {
79
122
  if (this.#numBlocks !== other.#numBlocks || this.#seed !== other.#seed) throw new BlockedBloomParamMismatchError("cannot union blocked Bloom filters whose parameters do not match");
80
123
  const r = new BlockedBloomFilter({
@@ -85,6 +128,11 @@ var BlockedBloomFilter = class BlockedBloomFilter {
85
128
  for (let i = 0; i < this.#lanes.length; i++) r.#lanes[i] = (this.#lanes[i] ?? 0) | (other.#lanes[i] ?? 0);
86
129
  return r;
87
130
  }
131
+ /**
132
+ * Adds a key to the set.
133
+ *
134
+ * @param key - The key to insert, as a string or bytes.
135
+ */
88
136
  add(key) {
89
137
  fillBlock(key, this.#numBlocks, this.#seed, this.#words, this.#bits);
90
138
  for (let i = 0; i < 8; i++) {
@@ -92,6 +140,12 @@ var BlockedBloomFilter = class BlockedBloomFilter {
92
140
  this.#lanes[w] = (this.#lanes[w] ?? 0) | (this.#bits[i] ?? 0);
93
141
  }
94
142
  }
143
+ /**
144
+ * Tests whether a key is in the set.
145
+ *
146
+ * @param key - The key to test.
147
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
148
+ */
95
149
  has(key) {
96
150
  fillBlock(key, this.#numBlocks, this.#seed, this.#words, this.#bits);
97
151
  for (let i = 0; i < 8; i++) {
@@ -102,9 +156,9 @@ var BlockedBloomFilter = class BlockedBloomFilter {
102
156
  }
103
157
  };
104
158
  function fillBlock(key, numBlocks, seed, outWords, outBits) {
105
- const { h1lo, h1hi, h2lo, h2hi } = require_serialize.hash128(require_serialize.normalize(key), seed);
106
- const block = require_serialize.reduce((h1lo ^ h1hi) >>> 0, numBlocks);
107
- const x = (h2lo ^ h2hi) >>> 0;
159
+ require_serialize.hash128KeyInto(key, seed, scratchHash);
160
+ const block = require_serialize.reduce((scratchHash.h1lo ^ scratchHash.h1hi) >>> 0, numBlocks);
161
+ const x = (scratchHash.h2lo ^ scratchHash.h2hi) >>> 0;
108
162
  const base = block * 8;
109
163
  for (let i = 0; i < 8; i++) {
110
164
  outWords[i] = base + i;
@@ -1,28 +1,80 @@
1
1
  import { t as BytesLike } from "../bytes-DCuYtUVS.cjs";
2
2
  //#region src/blocked/blocked.d.ts
3
- /**
4
- * Split-block probe: derive one block index and 8 single-bit lane masks for
5
- * `key`. Writes lane word indices into `outWords` and their masks into
6
- * `outBits` (both length 8, caller-owned so no per-call allocation). A block is
7
- * 256 bits = 8 contiguous 32-bit lanes; word `block*8 + i` gets one bit set.
8
- */
3
+ /** Thrown when an operation requires two filters built with identical parameters. */
9
4
  declare class BlockedBloomParamMismatchError extends Error {
5
+ /** Discriminates this error from other `Error`s. */
10
6
  override readonly name = "BlockedBloomParamMismatchError";
11
7
  }
8
+ /** Low-level blocked Bloom filter parameters. */
12
9
  interface BlockedBloomParams {
10
+ /** Bits allocated per key; higher lowers the false-positive rate. */
13
11
  bitsPerKey: number;
12
+ /** Expected number of keys. */
14
13
  capacity: number;
14
+ /** Hash seed; defaults to `0`. */
15
15
  seed?: number;
16
16
  }
17
+ /**
18
+ * A blocked (split-block) Bloom filter: confines every lookup to a single cache
19
+ * line, trading ~20-30% more space for cache-friendly throughput.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const filter = BlockedBloomFilter.create(100_000, 0.01);
24
+ * filter.add("alice");
25
+ * filter.has("alice"); // true
26
+ * ```
27
+ */
17
28
  declare class BlockedBloomFilter {
18
29
  #private;
30
+ /**
31
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
32
+ *
33
+ * @param n - Expected number of keys.
34
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
35
+ * @returns A new, empty filter.
36
+ */
19
37
  static create(n: number, epsilon: number): BlockedBloomFilter;
38
+ /**
39
+ * Constructs a filter from low-level {@link BlockedBloomParams}. Prefer
40
+ * {@link BlockedBloomFilter.create} unless restoring a specific configuration.
41
+ */
20
42
  constructor({ bitsPerKey, capacity, seed }: BlockedBloomParams);
43
+ /** Actual bits allocated per key (`total bits / capacity`). */
21
44
  get bitsPerKey(): number;
45
+ /**
46
+ * Restores a filter from its {@link BlockedBloomFilter.toBytes} serialization.
47
+ *
48
+ * @param bytes - The serialized filter.
49
+ * @returns The reconstructed filter.
50
+ */
22
51
  static fromBytes(bytes: Uint8Array): BlockedBloomFilter;
52
+ /**
53
+ * Serializes the filter to a portable little-endian byte layout.
54
+ *
55
+ * @returns The serialized filter, readable by {@link BlockedBloomFilter.fromBytes}.
56
+ */
23
57
  toBytes(): Uint8Array;
58
+ /**
59
+ * Returns a new filter containing the union of this filter and `other`.
60
+ *
61
+ * @param other - A filter built with identical parameters.
62
+ * @returns A new filter reporting membership for keys in either input.
63
+ * @throws {@link BlockedBloomParamMismatchError} if the parameters differ.
64
+ */
24
65
  union(other: BlockedBloomFilter): BlockedBloomFilter;
66
+ /**
67
+ * Adds a key to the set.
68
+ *
69
+ * @param key - The key to insert, as a string or bytes.
70
+ */
25
71
  add(key: BytesLike): void;
72
+ /**
73
+ * Tests whether a key is in the set.
74
+ *
75
+ * @param key - The key to test.
76
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
77
+ */
26
78
  has(key: BytesLike): boolean;
27
79
  }
28
80
  //#endregion
@@ -1,28 +1,80 @@
1
1
  import { t as BytesLike } from "../bytes-DCuYtUVS.js";
2
2
  //#region src/blocked/blocked.d.ts
3
- /**
4
- * Split-block probe: derive one block index and 8 single-bit lane masks for
5
- * `key`. Writes lane word indices into `outWords` and their masks into
6
- * `outBits` (both length 8, caller-owned so no per-call allocation). A block is
7
- * 256 bits = 8 contiguous 32-bit lanes; word `block*8 + i` gets one bit set.
8
- */
3
+ /** Thrown when an operation requires two filters built with identical parameters. */
9
4
  declare class BlockedBloomParamMismatchError extends Error {
5
+ /** Discriminates this error from other `Error`s. */
10
6
  override readonly name = "BlockedBloomParamMismatchError";
11
7
  }
8
+ /** Low-level blocked Bloom filter parameters. */
12
9
  interface BlockedBloomParams {
10
+ /** Bits allocated per key; higher lowers the false-positive rate. */
13
11
  bitsPerKey: number;
12
+ /** Expected number of keys. */
14
13
  capacity: number;
14
+ /** Hash seed; defaults to `0`. */
15
15
  seed?: number;
16
16
  }
17
+ /**
18
+ * A blocked (split-block) Bloom filter: confines every lookup to a single cache
19
+ * line, trading ~20-30% more space for cache-friendly throughput.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const filter = BlockedBloomFilter.create(100_000, 0.01);
24
+ * filter.add("alice");
25
+ * filter.has("alice"); // true
26
+ * ```
27
+ */
17
28
  declare class BlockedBloomFilter {
18
29
  #private;
30
+ /**
31
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
32
+ *
33
+ * @param n - Expected number of keys.
34
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
35
+ * @returns A new, empty filter.
36
+ */
19
37
  static create(n: number, epsilon: number): BlockedBloomFilter;
38
+ /**
39
+ * Constructs a filter from low-level {@link BlockedBloomParams}. Prefer
40
+ * {@link BlockedBloomFilter.create} unless restoring a specific configuration.
41
+ */
20
42
  constructor({ bitsPerKey, capacity, seed }: BlockedBloomParams);
43
+ /** Actual bits allocated per key (`total bits / capacity`). */
21
44
  get bitsPerKey(): number;
45
+ /**
46
+ * Restores a filter from its {@link BlockedBloomFilter.toBytes} serialization.
47
+ *
48
+ * @param bytes - The serialized filter.
49
+ * @returns The reconstructed filter.
50
+ */
22
51
  static fromBytes(bytes: Uint8Array): BlockedBloomFilter;
52
+ /**
53
+ * Serializes the filter to a portable little-endian byte layout.
54
+ *
55
+ * @returns The serialized filter, readable by {@link BlockedBloomFilter.fromBytes}.
56
+ */
23
57
  toBytes(): Uint8Array;
58
+ /**
59
+ * Returns a new filter containing the union of this filter and `other`.
60
+ *
61
+ * @param other - A filter built with identical parameters.
62
+ * @returns A new filter reporting membership for keys in either input.
63
+ * @throws {@link BlockedBloomParamMismatchError} if the parameters differ.
64
+ */
24
65
  union(other: BlockedBloomFilter): BlockedBloomFilter;
66
+ /**
67
+ * Adds a key to the set.
68
+ *
69
+ * @param key - The key to insert, as a string or bytes.
70
+ */
25
71
  add(key: BytesLike): void;
72
+ /**
73
+ * Tests whether a key is in the set.
74
+ *
75
+ * @param key - The key to test.
76
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
77
+ */
26
78
  has(key: BytesLike): boolean;
27
79
  }
28
80
  //#endregion
@@ -1,16 +1,29 @@
1
- import { i as hash128, n as readHeader, o as reduce, r as writeHeader, s as normalize } from "../serialize-E_4WyueA.js";
1
+ import { i as hash128KeyInto, n as readHeader, o as reduce, r as writeHeader } from "../serialize-5XQ5y_R-.js";
2
2
  //#region src/blocked/blocked.ts
3
3
  const TYPE = 2;
4
4
  const SALT = Uint32Array.of(1203114875, 1150766481, 2284105051, 2729912477, 1884591559, 770785867, 2667333959, 1550580529);
5
- /**
6
- * Split-block probe: derive one block index and 8 single-bit lane masks for
7
- * `key`. Writes lane word indices into `outWords` and their masks into
8
- * `outBits` (both length 8, caller-owned so no per-call allocation). A block is
9
- * 256 bits = 8 contiguous 32-bit lanes; word `block*8 + i` gets one bit set.
10
- */
5
+ const scratchHash = {
6
+ h1lo: 0,
7
+ h1hi: 0,
8
+ h2lo: 0,
9
+ h2hi: 0
10
+ };
11
+ /** Thrown when an operation requires two filters built with identical parameters. */
11
12
  var BlockedBloomParamMismatchError = class extends Error {
13
+ /** Discriminates this error from other `Error`s. */
12
14
  name = "BlockedBloomParamMismatchError";
13
15
  };
16
+ /**
17
+ * A blocked (split-block) Bloom filter: confines every lookup to a single cache
18
+ * line, trading ~20-30% more space for cache-friendly throughput.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const filter = BlockedBloomFilter.create(100_000, 0.01);
23
+ * filter.add("alice");
24
+ * filter.has("alice"); // true
25
+ * ```
26
+ */
14
27
  var BlockedBloomFilter = class BlockedBloomFilter {
15
28
  #lanes;
16
29
  #numBlocks;
@@ -23,6 +36,13 @@ var BlockedBloomFilter = class BlockedBloomFilter {
23
36
  [3, 16.9],
24
37
  [4, 26.4]
25
38
  ];
39
+ /**
40
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
41
+ *
42
+ * @param n - Expected number of keys.
43
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
44
+ * @returns A new, empty filter.
45
+ */
26
46
  static create(n, epsilon) {
27
47
  const t = Math.log10(1 / epsilon);
28
48
  const a = BlockedBloomFilter.#ANCHORS;
@@ -36,15 +56,26 @@ var BlockedBloomFilter = class BlockedBloomFilter {
36
56
  capacity: n
37
57
  });
38
58
  }
59
+ /**
60
+ * Constructs a filter from low-level {@link BlockedBloomParams}. Prefer
61
+ * {@link BlockedBloomFilter.create} unless restoring a specific configuration.
62
+ */
39
63
  constructor({ bitsPerKey, capacity, seed = 0 }) {
40
64
  this.#numBlocks = Math.max(1, Math.ceil(bitsPerKey * capacity / 256));
41
65
  this.#lanes = new Uint32Array(this.#numBlocks * 8);
42
66
  this.#seed = seed;
43
67
  this.#n = capacity;
44
68
  }
69
+ /** Actual bits allocated per key (`total bits / capacity`). */
45
70
  get bitsPerKey() {
46
71
  return this.#numBlocks * 256 / this.#n;
47
72
  }
73
+ /**
74
+ * Restores a filter from its {@link BlockedBloomFilter.toBytes} serialization.
75
+ *
76
+ * @param bytes - The serialized filter.
77
+ * @returns The reconstructed filter.
78
+ */
48
79
  static fromBytes(bytes) {
49
80
  const { body } = readHeader(bytes);
50
81
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
@@ -60,6 +91,11 @@ var BlockedBloomFilter = class BlockedBloomFilter {
60
91
  new Uint8Array(f.#lanes.buffer).set(body.subarray(12));
61
92
  return f;
62
93
  }
94
+ /**
95
+ * Serializes the filter to a portable little-endian byte layout.
96
+ *
97
+ * @returns The serialized filter, readable by {@link BlockedBloomFilter.fromBytes}.
98
+ */
63
99
  toBytes() {
64
100
  const lanes = new Uint8Array(this.#lanes.buffer, this.#lanes.byteOffset, this.#lanes.byteLength);
65
101
  const body = new Uint8Array(12 + lanes.length);
@@ -74,6 +110,13 @@ var BlockedBloomFilter = class BlockedBloomFilter {
74
110
  flags: 0
75
111
  }, body);
76
112
  }
113
+ /**
114
+ * Returns a new filter containing the union of this filter and `other`.
115
+ *
116
+ * @param other - A filter built with identical parameters.
117
+ * @returns A new filter reporting membership for keys in either input.
118
+ * @throws {@link BlockedBloomParamMismatchError} if the parameters differ.
119
+ */
77
120
  union(other) {
78
121
  if (this.#numBlocks !== other.#numBlocks || this.#seed !== other.#seed) throw new BlockedBloomParamMismatchError("cannot union blocked Bloom filters whose parameters do not match");
79
122
  const r = new BlockedBloomFilter({
@@ -84,6 +127,11 @@ var BlockedBloomFilter = class BlockedBloomFilter {
84
127
  for (let i = 0; i < this.#lanes.length; i++) r.#lanes[i] = (this.#lanes[i] ?? 0) | (other.#lanes[i] ?? 0);
85
128
  return r;
86
129
  }
130
+ /**
131
+ * Adds a key to the set.
132
+ *
133
+ * @param key - The key to insert, as a string or bytes.
134
+ */
87
135
  add(key) {
88
136
  fillBlock(key, this.#numBlocks, this.#seed, this.#words, this.#bits);
89
137
  for (let i = 0; i < 8; i++) {
@@ -91,6 +139,12 @@ var BlockedBloomFilter = class BlockedBloomFilter {
91
139
  this.#lanes[w] = (this.#lanes[w] ?? 0) | (this.#bits[i] ?? 0);
92
140
  }
93
141
  }
142
+ /**
143
+ * Tests whether a key is in the set.
144
+ *
145
+ * @param key - The key to test.
146
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
147
+ */
94
148
  has(key) {
95
149
  fillBlock(key, this.#numBlocks, this.#seed, this.#words, this.#bits);
96
150
  for (let i = 0; i < 8; i++) {
@@ -101,9 +155,9 @@ var BlockedBloomFilter = class BlockedBloomFilter {
101
155
  }
102
156
  };
103
157
  function fillBlock(key, numBlocks, seed, outWords, outBits) {
104
- const { h1lo, h1hi, h2lo, h2hi } = hash128(normalize(key), seed);
105
- const block = reduce((h1lo ^ h1hi) >>> 0, numBlocks);
106
- const x = (h2lo ^ h2hi) >>> 0;
158
+ hash128KeyInto(key, seed, scratchHash);
159
+ const block = reduce((scratchHash.h1lo ^ scratchHash.h1hi) >>> 0, numBlocks);
160
+ const x = (scratchHash.h2lo ^ scratchHash.h2hi) >>> 0;
107
161
  const base = block * 8;
108
162
  for (let i = 0; i < 8; i++) {
109
163
  outWords[i] = base + i;
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_serialize = require("../serialize-C5rwVPvv.cjs");
2
+ const require_serialize = require("../serialize-CHHDM4TQ.cjs");
3
3
  //#region src/core/bitset.ts
4
4
  var BitSetRangeError = class extends RangeError {
5
5
  name = "BitSetRangeError";
@@ -48,9 +48,23 @@ function optimal(n, epsilon) {
48
48
  //#endregion
49
49
  //#region src/bloom/bloom.ts
50
50
  const TYPE = 1;
51
+ /** Thrown when an operation requires two filters built with identical parameters. */
51
52
  var BloomParamMismatchError = class extends Error {
53
+ /** Discriminates this error from other `Error`s. */
52
54
  name = "BloomParamMismatchError";
53
55
  };
56
+ /**
57
+ * A classic Bloom filter: a space-efficient set with a tunable false-positive
58
+ * rate and zero false negatives.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * const filter = BloomFilter.create(100_000, 0.01);
63
+ * filter.add("alice");
64
+ * filter.has("alice"); // true
65
+ * filter.has("bob"); // false (or a ~1% false positive)
66
+ * ```
67
+ */
54
68
  var BloomFilter = class BloomFilter {
55
69
  #bits;
56
70
  #m;
@@ -58,11 +72,24 @@ var BloomFilter = class BloomFilter {
58
72
  #seed;
59
73
  #scratch;
60
74
  #n;
75
+ /**
76
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
77
+ *
78
+ * @param n - Expected number of keys.
79
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
80
+ * @returns A new, empty filter.
81
+ */
61
82
  static create(n, epsilon) {
62
83
  const f = new BloomFilter(optimal(n, epsilon));
63
84
  f.#n = n;
64
85
  return f;
65
86
  }
87
+ /**
88
+ * Restores a filter from its {@link BloomFilter.toBytes} serialization.
89
+ *
90
+ * @param bytes - The serialized filter.
91
+ * @returns The reconstructed filter.
92
+ */
66
93
  static fromBytes(bytes) {
67
94
  const { body } = require_serialize.readHeader(bytes);
68
95
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
@@ -77,6 +104,10 @@ var BloomFilter = class BloomFilter {
77
104
  f.#bits.bytes.set(body.subarray(9));
78
105
  return f;
79
106
  }
107
+ /**
108
+ * Constructs a filter from low-level {@link BloomParams}. Prefer
109
+ * {@link BloomFilter.create} unless restoring a specific configuration.
110
+ */
80
111
  constructor({ m, k, seed = 0 }) {
81
112
  this.#bits = new BitSet(m);
82
113
  this.#m = m;
@@ -89,6 +120,11 @@ var BloomFilter = class BloomFilter {
89
120
  get bitsPerKey() {
90
121
  return this.#m / this.#n;
91
122
  }
123
+ /**
124
+ * Serializes the filter to a portable little-endian byte layout.
125
+ *
126
+ * @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
127
+ */
92
128
  toBytes() {
93
129
  const payload = this.#bits.bytes;
94
130
  const body = new Uint8Array(9 + payload.length);
@@ -103,6 +139,13 @@ var BloomFilter = class BloomFilter {
103
139
  flags: 0
104
140
  }, body);
105
141
  }
142
+ /**
143
+ * Returns a new filter containing the union of this filter and `other`.
144
+ *
145
+ * @param other - A filter built with identical parameters.
146
+ * @returns A new filter reporting membership for keys in either input.
147
+ * @throws {@link BloomParamMismatchError} if the parameters differ.
148
+ */
106
149
  union(other) {
107
150
  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");
108
151
  const a = this.#bits.bytes;
@@ -117,10 +160,21 @@ var BloomFilter = class BloomFilter {
117
160
  r.#bits.bytes.set(merged);
118
161
  return r;
119
162
  }
163
+ /**
164
+ * Adds a key to the set.
165
+ *
166
+ * @param key - The key to insert, as a string or bytes.
167
+ */
120
168
  add(key) {
121
169
  require_serialize.probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
122
170
  for (let i = 0; i < this.#k; i++) this.#bits.set(this.#scratch[i] ?? 0);
123
171
  }
172
+ /**
173
+ * Tests whether a key is in the set.
174
+ *
175
+ * @param key - The key to test.
176
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
177
+ */
124
178
  has(key) {
125
179
  require_serialize.probeInto(key, this.#k, this.#m, this.#seed, this.#scratch);
126
180
  for (let i = 0; i < this.#k; i++) if (!this.#bits.get(this.#scratch[i] ?? 0)) return false;
@@ -1,23 +1,81 @@
1
1
  import { t as BytesLike } from "../bytes-DCuYtUVS.cjs";
2
2
  //#region src/bloom/bloom.d.ts
3
+ /** Thrown when an operation requires two filters built with identical parameters. */
3
4
  declare class BloomParamMismatchError extends Error {
5
+ /** Discriminates this error from other `Error`s. */
4
6
  override readonly name = "BloomParamMismatchError";
5
7
  }
8
+ /** Low-level Bloom filter parameters. */
6
9
  interface BloomParams {
10
+ /** Number of bits in the filter. */
7
11
  m: number;
12
+ /** Number of hash probes per key. */
8
13
  k: number;
14
+ /** Hash seed; defaults to `0`. */
9
15
  seed?: number;
10
16
  }
17
+ /**
18
+ * A classic Bloom filter: a space-efficient set with a tunable false-positive
19
+ * rate and zero false negatives.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const filter = BloomFilter.create(100_000, 0.01);
24
+ * filter.add("alice");
25
+ * filter.has("alice"); // true
26
+ * filter.has("bob"); // false (or a ~1% false positive)
27
+ * ```
28
+ */
11
29
  declare class BloomFilter {
12
30
  #private;
31
+ /**
32
+ * Creates a filter sized for `n` expected keys at a target false-positive rate.
33
+ *
34
+ * @param n - Expected number of keys.
35
+ * @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
36
+ * @returns A new, empty filter.
37
+ */
13
38
  static create(n: number, epsilon: number): BloomFilter;
39
+ /**
40
+ * Restores a filter from its {@link BloomFilter.toBytes} serialization.
41
+ *
42
+ * @param bytes - The serialized filter.
43
+ * @returns The reconstructed filter.
44
+ */
14
45
  static fromBytes(bytes: Uint8Array): BloomFilter;
46
+ /**
47
+ * Constructs a filter from low-level {@link BloomParams}. Prefer
48
+ * {@link BloomFilter.create} unless restoring a specific configuration.
49
+ */
15
50
  constructor({ m, k, seed }: BloomParams);
16
51
  /** Analytic design bits-per-key `m / n`. */
17
52
  get bitsPerKey(): number;
53
+ /**
54
+ * Serializes the filter to a portable little-endian byte layout.
55
+ *
56
+ * @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
57
+ */
18
58
  toBytes(): Uint8Array;
59
+ /**
60
+ * Returns a new filter containing the union of this filter and `other`.
61
+ *
62
+ * @param other - A filter built with identical parameters.
63
+ * @returns A new filter reporting membership for keys in either input.
64
+ * @throws {@link BloomParamMismatchError} if the parameters differ.
65
+ */
19
66
  union(other: BloomFilter): BloomFilter;
67
+ /**
68
+ * Adds a key to the set.
69
+ *
70
+ * @param key - The key to insert, as a string or bytes.
71
+ */
20
72
  add(key: BytesLike): void;
73
+ /**
74
+ * Tests whether a key is in the set.
75
+ *
76
+ * @param key - The key to test.
77
+ * @returns `true` if present (possibly a false positive); `false` guarantees absence.
78
+ */
21
79
  has(key: BytesLike): boolean;
22
80
  }
23
81
  //#endregion