distillate 0.13.0 → 0.14.1
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 +38 -12
- package/dist/blocked/index.cjs +1 -1
- package/dist/blocked/index.js +1 -1
- package/dist/bloom/index.cjs +3 -4
- package/dist/bloom/index.d.cts +12 -2
- package/dist/bloom/index.d.ts +12 -2
- package/dist/bloom/index.js +2 -3
- package/dist/bytes-Be7tq1F7.js +9 -0
- package/dist/bytes-D2duG1jq.cjs +14 -0
- package/dist/countmin/index.cjs +13 -4
- package/dist/countmin/index.d.cts +15 -2
- package/dist/countmin/index.d.ts +15 -2
- package/dist/countmin/index.js +12 -3
- package/dist/cuckoo/index.cjs +20 -9
- package/dist/cuckoo/index.js +20 -9
- package/dist/distinct-BhxDB8mW.js +61 -0
- package/dist/distinct-Ck5PKuCS.cjs +66 -0
- package/dist/fuse/index.cjs +6 -11
- package/dist/fuse/index.js +6 -11
- package/dist/{hasher-3Cyn7Yg-.js → hasher-DOdmZ-LI.js} +1 -8
- package/dist/{hasher-MhY7aQ8F.cjs → hasher-DPXmP7SH.cjs} +2 -9
- package/dist/hll/index.cjs +10 -4
- package/dist/hll/index.d.cts +13 -2
- package/dist/hll/index.d.ts +13 -2
- package/dist/hll/index.js +9 -3
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/scalable/index.cjs +5 -6
- package/dist/scalable/index.js +2 -3
- package/dist/{bitset-QYGqxnEf.cjs → sizing-Ch0SDdza.cjs} +15 -0
- package/dist/{bitset-Cf2jwRJX.js → sizing-zxCBlyhO.js} +10 -1
- package/dist/topk/index.cjs +423 -0
- package/dist/topk/index.d.cts +207 -0
- package/dist/topk/index.d.ts +207 -0
- package/dist/topk/index.js +411 -0
- package/package.json +15 -7
- package/dist/sizing-BmKfHf_V.d.cts +0 -34
- package/dist/sizing-BmKfHf_V.d.ts +0 -34
- package/dist/sizing-D3-_7pdP.js +0 -25
- package/dist/sizing-R1pwbzjC.cjs +0 -42
package/README.md
CHANGED
|
@@ -42,20 +42,22 @@ 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
|
|
46
|
-
| --------------------- | -------------- |
|
|
47
|
-
| `distillate/bloom` | Classic Bloom | seen this key?
|
|
48
|
-
| `distillate/scalable` | Scalable Bloom | seen this key?
|
|
49
|
-
| `distillate/blocked` | Blocked Bloom | seen this key?
|
|
50
|
-
| `distillate/fuse` | Binary Fuse | seen this key?
|
|
51
|
-
| `distillate/cuckoo` | Cuckoo | seen this key?
|
|
52
|
-
| `distillate/hll` | HyperLogLog | how many distinct?
|
|
53
|
-
| `distillate/countmin` | Count-Min | how many times?
|
|
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/cuckoo` | Cuckoo | seen this key? | Keys come and go; the one filter with delete |
|
|
52
|
+
| `distillate/hll` | HyperLogLog | how many distinct? | Counting distinct users, IPs, or keys in fixed space |
|
|
53
|
+
| `distillate/countmin` | Count-Min | how many times? | Counting events per key in fixed space |
|
|
54
|
+
| `distillate/topk` | Top-K | which are heaviest? | Finding the most frequent keys in fixed space |
|
|
54
55
|
|
|
55
56
|
The filters are mutable except Binary Fuse, which is built once from the whole
|
|
56
|
-
key set. HyperLogLog
|
|
57
|
-
reports whether it saw any particular key. HyperLogLog counts distinct
|
|
58
|
-
Count-Min counts how often each key appeared
|
|
57
|
+
key set. HyperLogLog, Count-Min and Top-K are sketches rather than filters:
|
|
58
|
+
none reports whether it saw any particular key. HyperLogLog counts distinct
|
|
59
|
+
keys, Count-Min counts how often each key appeared, and Top-K reports which
|
|
60
|
+
keys appeared most.
|
|
59
61
|
|
|
60
62
|
### Classic Bloom (`distillate/bloom`)
|
|
61
63
|
|
|
@@ -189,6 +191,30 @@ hits.union(other).count("/login"); // 7
|
|
|
189
191
|
|
|
190
192
|
Also: `equals`, `toBytes` / `fromBytes`, `toJSON` / `fromJSON`, `error()`, `epsilon`, `delta`, `width`, `depth`, `countMinSizing(epsilon, delta)`, and a low-level `new CountMinSketch({ width, depth, seed })`.
|
|
191
193
|
|
|
194
|
+
### Top-K (`distillate/topk`)
|
|
195
|
+
|
|
196
|
+
A **sketch** that lists the keys seen most often, in a table fixed by the error you ask for rather than by how many keys arrive. A key it holds never reads below its true count, and any key heavier than `error()` is guaranteed to be held. Full API and the bound: [Top-K guide](https://distillate.akxp.net/guides/topk/).
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
import { TopK } from "distillate/topk";
|
|
200
|
+
|
|
201
|
+
const routes = TopK.create(0.01); // error factor
|
|
202
|
+
routes.add("/login", 5);
|
|
203
|
+
routes.add("/signup", 2);
|
|
204
|
+
routes.add("/about");
|
|
205
|
+
|
|
206
|
+
const decoder = new TextDecoder();
|
|
207
|
+
routes
|
|
208
|
+
.top(2)
|
|
209
|
+
.map((e) => decoder.decode(e.key))
|
|
210
|
+
.join(); // "/login,/signup"
|
|
211
|
+
routes.count("/login"); // 5
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Keys come back as `Uint8Array`, exactly as recorded, so a serialized Top-K sketch holds your keys verbatim, unlike every other frame here. `union` combines sketches soundly but, unlike Count-Min's, not byte for byte as one sketch fed both streams.
|
|
215
|
+
|
|
216
|
+
Also: `equals`, `toBytes` / `fromBytes`, `toJSON` / `fromJSON`, `total`, `error()`, `epsilon`, `capacity`, `topKSizing(epsilon)`, and a low-level `new TopK({ capacity, seed })`.
|
|
217
|
+
|
|
192
218
|
## Performance
|
|
193
219
|
|
|
194
220
|
Classic Bloom head-to-head at a **matched 1% false-positive rate** over the same 100k keys, measured by identical code (cross-library harness, node v24.14.1, Apple M5):
|
package/dist/blocked/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_hasher = require("../hasher-
|
|
2
|
+
const require_hasher = require("../hasher-DPXmP7SH.cjs");
|
|
3
3
|
const require_serialize = require("../serialize-CHikZ4GH.cjs");
|
|
4
4
|
const require_params = require("../params-UTZbJ22c.cjs");
|
|
5
5
|
//#region src/blocked/blocked.ts
|
package/dist/blocked/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as hash32x2Into, u as reduce } from "../hasher-
|
|
1
|
+
import { a as hash32x2Into, u as reduce } from "../hasher-DOdmZ-LI.js";
|
|
2
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";
|
|
3
3
|
import { i as assertProbability, n as assertPositiveFinite, o as assertUint32, r as assertPositiveInt, t as ParamError } from "../params-CeajSrwv.js";
|
|
4
4
|
//#region src/blocked/blocked.ts
|
package/dist/bloom/index.cjs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
const require_hasher = require("../hasher-
|
|
2
|
+
const require_sizing = require("../sizing-Ch0SDdza.cjs");
|
|
3
|
+
const require_hasher = require("../hasher-DPXmP7SH.cjs");
|
|
4
4
|
const require_serialize = require("../serialize-CHikZ4GH.cjs");
|
|
5
5
|
const require_params = require("../params-UTZbJ22c.cjs");
|
|
6
|
-
const require_sizing = require("../sizing-R1pwbzjC.cjs");
|
|
7
6
|
//#region src/bloom/bloom.ts
|
|
8
7
|
const TYPE = 1;
|
|
9
8
|
const PARAMS_SIZE = 16;
|
|
@@ -64,7 +63,7 @@ var BloomFilter = class BloomFilter {
|
|
|
64
63
|
require_params.assertPositiveInt(n, "n");
|
|
65
64
|
require_params.assertUint32(n, "n");
|
|
66
65
|
}
|
|
67
|
-
this.#bits = new
|
|
66
|
+
this.#bits = new require_sizing.BitSet(m);
|
|
68
67
|
this.#m = m;
|
|
69
68
|
this.#k = k;
|
|
70
69
|
this.#seed = seed;
|
package/dist/bloom/index.d.cts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { t as BytesLike } from "../bytes-DCuYtUVS.cjs";
|
|
2
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";
|
|
3
3
|
import { t as ParamError } from "../params-DnqJBqLS.cjs";
|
|
4
|
-
import { i as bloomSizing, t as BloomSizing } from "../sizing-BmKfHf_V.cjs";
|
|
5
4
|
//#region src/bloom/bloom.d.ts
|
|
6
5
|
/** Thrown when an operation requires two filters built with identical parameters. */
|
|
7
6
|
export declare class BloomParamMismatchError extends Error {
|
|
@@ -144,4 +143,15 @@ export declare class BloomFilter {
|
|
|
144
143
|
has(key: BytesLike): boolean;
|
|
145
144
|
}
|
|
146
145
|
//#endregion
|
|
147
|
-
|
|
146
|
+
//#region src/core/sizing.d.ts
|
|
147
|
+
/** Bloom filter geometry: the `BloomParams` fields a sizing solve determines. */
|
|
148
|
+
interface BloomSizing {
|
|
149
|
+
/** Number of bits in the filter. */
|
|
150
|
+
m: number;
|
|
151
|
+
/** Number of hash probes per key. */
|
|
152
|
+
k: number;
|
|
153
|
+
}
|
|
154
|
+
/** Optimal Bloom-filter sizing: `m` bits and `k` hashes for `n` items at target FPR `epsilon`. */
|
|
155
|
+
export declare function bloomSizing(n: number, epsilon: number): BloomSizing;
|
|
156
|
+
//#endregion
|
|
157
|
+
export { BadMagicError, type BloomParams, type BloomSizing, ChecksumError, type FilterJSON, ParamError, ReservedBitsError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError };
|
package/dist/bloom/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { t as BytesLike } from "../bytes-DCuYtUVS.js";
|
|
2
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";
|
|
3
3
|
import { t as ParamError } from "../params-DnqJBqLS.js";
|
|
4
|
-
import { i as bloomSizing, t as BloomSizing } from "../sizing-BmKfHf_V.js";
|
|
5
4
|
//#region src/bloom/bloom.d.ts
|
|
6
5
|
/** Thrown when an operation requires two filters built with identical parameters. */
|
|
7
6
|
export declare class BloomParamMismatchError extends Error {
|
|
@@ -144,4 +143,15 @@ export declare class BloomFilter {
|
|
|
144
143
|
has(key: BytesLike): boolean;
|
|
145
144
|
}
|
|
146
145
|
//#endregion
|
|
147
|
-
|
|
146
|
+
//#region src/core/sizing.d.ts
|
|
147
|
+
/** Bloom filter geometry: the `BloomParams` fields a sizing solve determines. */
|
|
148
|
+
interface BloomSizing {
|
|
149
|
+
/** Number of bits in the filter. */
|
|
150
|
+
m: number;
|
|
151
|
+
/** Number of hash probes per key. */
|
|
152
|
+
k: number;
|
|
153
|
+
}
|
|
154
|
+
/** Optimal Bloom-filter sizing: `m` bits and `k` hashes for `n` items at target FPR `epsilon`. */
|
|
155
|
+
export declare function bloomSizing(n: number, epsilon: number): BloomSizing;
|
|
156
|
+
//#endregion
|
|
157
|
+
export { BadMagicError, type BloomParams, type BloomSizing, ChecksumError, type FilterJSON, ParamError, ReservedBitsError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError };
|
package/dist/bloom/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { c as probeInto } from "../hasher-
|
|
1
|
+
import { n as BitSet, t as bloomSizing } from "../sizing-zxCBlyhO.js";
|
|
2
|
+
import { c as probeInto } from "../hasher-DOdmZ-LI.js";
|
|
3
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";
|
|
4
4
|
import { a as assertUint16, i as assertProbability, o as assertUint32, r as assertPositiveInt, t as ParamError } from "../params-CeajSrwv.js";
|
|
5
|
-
import { t as bloomSizing } from "../sizing-D3-_7pdP.js";
|
|
6
5
|
//#region src/bloom/bloom.ts
|
|
7
6
|
const TYPE = 1;
|
|
8
7
|
const PARAMS_SIZE = 16;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/core/bytes.ts
|
|
2
|
+
const encoder = new TextEncoder();
|
|
3
|
+
function normalize(input) {
|
|
4
|
+
if (typeof input === "string") return encoder.encode(input);
|
|
5
|
+
if (input instanceof Uint8Array) return input;
|
|
6
|
+
return new Uint8Array(input);
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { normalize as t };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/core/bytes.ts
|
|
2
|
+
const encoder = new TextEncoder();
|
|
3
|
+
function normalize(input) {
|
|
4
|
+
if (typeof input === "string") return encoder.encode(input);
|
|
5
|
+
if (input instanceof Uint8Array) return input;
|
|
6
|
+
return new Uint8Array(input);
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
Object.defineProperty(exports, "normalize", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: function() {
|
|
12
|
+
return normalize;
|
|
13
|
+
}
|
|
14
|
+
});
|
package/dist/countmin/index.cjs
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_hasher = require("../hasher-
|
|
2
|
+
const require_hasher = require("../hasher-DPXmP7SH.cjs");
|
|
3
3
|
const require_serialize = require("../serialize-CHikZ4GH.cjs");
|
|
4
4
|
const require_params = require("../params-UTZbJ22c.cjs");
|
|
5
|
-
|
|
5
|
+
//#region src/countmin/sizing.ts
|
|
6
|
+
function countMinSizing(epsilon, delta) {
|
|
7
|
+
require_params.assertProbability(epsilon, "epsilon");
|
|
8
|
+
require_params.assertProbability(delta, "delta");
|
|
9
|
+
return {
|
|
10
|
+
width: Math.ceil(Math.E / epsilon),
|
|
11
|
+
depth: Math.ceil(Math.log(1 / delta))
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
6
15
|
//#region src/countmin/countmin.ts
|
|
7
16
|
const TYPE = 8;
|
|
8
17
|
const PARAMS_SIZE = 16;
|
|
@@ -37,7 +46,7 @@ var CountMinSketch = class CountMinSketch {
|
|
|
37
46
|
static create(epsilon, delta, options = {}) {
|
|
38
47
|
return new CountMinSketch({
|
|
39
48
|
...options,
|
|
40
|
-
...
|
|
49
|
+
...countMinSizing(epsilon, delta)
|
|
41
50
|
});
|
|
42
51
|
}
|
|
43
52
|
static from(keys, epsilon, delta, options = {}) {
|
|
@@ -191,4 +200,4 @@ exports.SerializationError = require_serialize.SerializationError;
|
|
|
191
200
|
exports.TruncatedError = require_serialize.TruncatedError;
|
|
192
201
|
exports.UnknownHashVariantError = require_serialize.UnknownHashVariantError;
|
|
193
202
|
exports.UnknownVersionError = require_serialize.UnknownVersionError;
|
|
194
|
-
exports.countMinSizing =
|
|
203
|
+
exports.countMinSizing = countMinSizing;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { t as BytesLike } from "../bytes-DCuYtUVS.cjs";
|
|
2
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";
|
|
3
3
|
import { t as ParamError } from "../params-DnqJBqLS.cjs";
|
|
4
|
-
import { a as countMinSizing, n as CountMinSizing } from "../sizing-BmKfHf_V.cjs";
|
|
5
4
|
//#region src/countmin/countmin.d.ts
|
|
6
5
|
/** Thrown when an operation requires two sketches built with identical parameters. */
|
|
7
6
|
export declare class CountMinParamMismatchError extends Error {
|
|
@@ -175,4 +174,18 @@ export declare class CountMinSketch {
|
|
|
175
174
|
count(key: BytesLike): number;
|
|
176
175
|
}
|
|
177
176
|
//#endregion
|
|
178
|
-
|
|
177
|
+
//#region src/countmin/sizing.d.ts
|
|
178
|
+
/** Count-Min geometry: the `CountMinParams` fields a sizing solve determines. */
|
|
179
|
+
interface CountMinSizing {
|
|
180
|
+
/** Counters per row. */
|
|
181
|
+
width: number;
|
|
182
|
+
/** Number of rows, one probe each. */
|
|
183
|
+
depth: number;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Count-Min sizing: `width` columns and `depth` rows for an estimate at most
|
|
187
|
+
* `epsilon * total` above the true count, with probability `1 - delta`.
|
|
188
|
+
*/
|
|
189
|
+
export declare function countMinSizing(epsilon: number, delta: number): CountMinSizing;
|
|
190
|
+
//#endregion
|
|
191
|
+
export { BadMagicError, ChecksumError, type CountMinOptions, type CountMinParams, type CountMinSizing, type FilterJSON, ParamError, ReservedBitsError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError };
|
package/dist/countmin/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { t as BytesLike } from "../bytes-DCuYtUVS.js";
|
|
2
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";
|
|
3
3
|
import { t as ParamError } from "../params-DnqJBqLS.js";
|
|
4
|
-
import { a as countMinSizing, n as CountMinSizing } from "../sizing-BmKfHf_V.js";
|
|
5
4
|
//#region src/countmin/countmin.d.ts
|
|
6
5
|
/** Thrown when an operation requires two sketches built with identical parameters. */
|
|
7
6
|
export declare class CountMinParamMismatchError extends Error {
|
|
@@ -175,4 +174,18 @@ export declare class CountMinSketch {
|
|
|
175
174
|
count(key: BytesLike): number;
|
|
176
175
|
}
|
|
177
176
|
//#endregion
|
|
178
|
-
|
|
177
|
+
//#region src/countmin/sizing.d.ts
|
|
178
|
+
/** Count-Min geometry: the `CountMinParams` fields a sizing solve determines. */
|
|
179
|
+
interface CountMinSizing {
|
|
180
|
+
/** Counters per row. */
|
|
181
|
+
width: number;
|
|
182
|
+
/** Number of rows, one probe each. */
|
|
183
|
+
depth: number;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Count-Min sizing: `width` columns and `depth` rows for an estimate at most
|
|
187
|
+
* `epsilon * total` above the true count, with probability `1 - delta`.
|
|
188
|
+
*/
|
|
189
|
+
export declare function countMinSizing(epsilon: number, delta: number): CountMinSizing;
|
|
190
|
+
//#endregion
|
|
191
|
+
export { BadMagicError, ChecksumError, type CountMinOptions, type CountMinParams, type CountMinSizing, type FilterJSON, ParamError, ReservedBitsError, SerializationError, TruncatedError, UnknownHashVariantError, UnknownVersionError };
|
package/dist/countmin/index.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import { a as hash32x2Into, s as probeAt } from "../hasher-
|
|
1
|
+
import { a as hash32x2Into, s as probeAt } from "../hasher-DOdmZ-LI.js";
|
|
2
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";
|
|
3
|
-
import { o as assertUint32, r as assertPositiveInt, t as ParamError } from "../params-CeajSrwv.js";
|
|
4
|
-
|
|
3
|
+
import { i as assertProbability, o as assertUint32, r as assertPositiveInt, t as ParamError } from "../params-CeajSrwv.js";
|
|
4
|
+
//#region src/countmin/sizing.ts
|
|
5
|
+
function countMinSizing(epsilon, delta) {
|
|
6
|
+
assertProbability(epsilon, "epsilon");
|
|
7
|
+
assertProbability(delta, "delta");
|
|
8
|
+
return {
|
|
9
|
+
width: Math.ceil(Math.E / epsilon),
|
|
10
|
+
depth: Math.ceil(Math.log(1 / delta))
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
5
14
|
//#region src/countmin/countmin.ts
|
|
6
15
|
const TYPE = 8;
|
|
7
16
|
const PARAMS_SIZE = 16;
|
package/dist/cuckoo/index.cjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_hasher = require("../hasher-
|
|
2
|
+
const require_hasher = require("../hasher-DPXmP7SH.cjs");
|
|
3
3
|
const require_serialize = require("../serialize-CHikZ4GH.cjs");
|
|
4
4
|
const require_params = require("../params-UTZbJ22c.cjs");
|
|
5
|
+
const require_distinct = require("../distinct-Ck5PKuCS.cjs");
|
|
5
6
|
function cuckooSizing(n, epsilon) {
|
|
6
7
|
require_params.assertProbability(epsilon, "epsilon");
|
|
7
8
|
const f = Math.max(4, Math.ceil(Math.log2(8 / epsilon)));
|
|
@@ -54,17 +55,21 @@ var CuckooFilter = class CuckooFilter {
|
|
|
54
55
|
});
|
|
55
56
|
}
|
|
56
57
|
static from(keys, epsilon, options = {}) {
|
|
57
|
-
const
|
|
58
|
-
const distinct = [];
|
|
58
|
+
const distinct = new require_distinct.DistinctHashes(4);
|
|
59
59
|
for (const key of keys) {
|
|
60
60
|
require_hasher.hash128KeyInto(key, options.seed ?? 0, HASH);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
distinct.add(HASH);
|
|
62
|
+
}
|
|
63
|
+
const f = CuckooFilter.create(Math.max(1, distinct.size), epsilon, options);
|
|
64
|
+
const words = distinct.words;
|
|
65
|
+
for (let at = 0; at < words.length; at += 4) {
|
|
66
|
+
HASH.w0 = words[at] ?? 0;
|
|
67
|
+
HASH.w1 = words[at + 1] ?? 0;
|
|
68
|
+
HASH.w2 = words[at + 2] ?? 0;
|
|
69
|
+
HASH.w3 = words[at + 3] ?? 0;
|
|
70
|
+
f.#derive();
|
|
71
|
+
f.#insert();
|
|
65
72
|
}
|
|
66
|
-
const f = CuckooFilter.create(Math.max(1, distinct.length), epsilon, options);
|
|
67
|
-
for (const k of distinct) f.add(k);
|
|
68
73
|
return f;
|
|
69
74
|
}
|
|
70
75
|
static fromBytes(bytes) {
|
|
@@ -134,6 +139,9 @@ var CuckooFilter = class CuckooFilter {
|
|
|
134
139
|
}
|
|
135
140
|
add(key) {
|
|
136
141
|
this.#hash(key);
|
|
142
|
+
this.#insert();
|
|
143
|
+
}
|
|
144
|
+
#insert() {
|
|
137
145
|
if (this.#replace(this.#i1, 0, this.#fp) || this.#replace(this.#i2, 0, this.#fp)) {
|
|
138
146
|
this.#count++;
|
|
139
147
|
return;
|
|
@@ -202,6 +210,9 @@ var CuckooFilter = class CuckooFilter {
|
|
|
202
210
|
}
|
|
203
211
|
#hash(key) {
|
|
204
212
|
require_hasher.hash128KeyInto(key, this.#seed, HASH);
|
|
213
|
+
this.#derive();
|
|
214
|
+
}
|
|
215
|
+
#derive() {
|
|
205
216
|
this.#fp = HASH.w1 >>> 32 - this.#f || 1;
|
|
206
217
|
this.#i1 = require_hasher.reduce(HASH.w0, this.#buckets);
|
|
207
218
|
this.#i2 = this.#alt(this.#i1, this.#fp);
|
package/dist/cuckoo/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { i as hash128KeyInto, u as reduce } from "../hasher-
|
|
1
|
+
import { i as hash128KeyInto, u as reduce } from "../hasher-DOdmZ-LI.js";
|
|
2
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";
|
|
3
3
|
import { i as assertProbability, o as assertUint32, r as assertPositiveInt, t as ParamError } from "../params-CeajSrwv.js";
|
|
4
|
+
import { t as DistinctHashes } from "../distinct-BhxDB8mW.js";
|
|
4
5
|
function cuckooSizing(n, epsilon) {
|
|
5
6
|
assertProbability(epsilon, "epsilon");
|
|
6
7
|
const f = Math.max(4, Math.ceil(Math.log2(8 / epsilon)));
|
|
@@ -53,17 +54,21 @@ var CuckooFilter = class CuckooFilter {
|
|
|
53
54
|
});
|
|
54
55
|
}
|
|
55
56
|
static from(keys, epsilon, options = {}) {
|
|
56
|
-
const
|
|
57
|
-
const distinct = [];
|
|
57
|
+
const distinct = new DistinctHashes(4);
|
|
58
58
|
for (const key of keys) {
|
|
59
59
|
hash128KeyInto(key, options.seed ?? 0, HASH);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
distinct.add(HASH);
|
|
61
|
+
}
|
|
62
|
+
const f = CuckooFilter.create(Math.max(1, distinct.size), epsilon, options);
|
|
63
|
+
const words = distinct.words;
|
|
64
|
+
for (let at = 0; at < words.length; at += 4) {
|
|
65
|
+
HASH.w0 = words[at] ?? 0;
|
|
66
|
+
HASH.w1 = words[at + 1] ?? 0;
|
|
67
|
+
HASH.w2 = words[at + 2] ?? 0;
|
|
68
|
+
HASH.w3 = words[at + 3] ?? 0;
|
|
69
|
+
f.#derive();
|
|
70
|
+
f.#insert();
|
|
64
71
|
}
|
|
65
|
-
const f = CuckooFilter.create(Math.max(1, distinct.length), epsilon, options);
|
|
66
|
-
for (const k of distinct) f.add(k);
|
|
67
72
|
return f;
|
|
68
73
|
}
|
|
69
74
|
static fromBytes(bytes) {
|
|
@@ -133,6 +138,9 @@ var CuckooFilter = class CuckooFilter {
|
|
|
133
138
|
}
|
|
134
139
|
add(key) {
|
|
135
140
|
this.#hash(key);
|
|
141
|
+
this.#insert();
|
|
142
|
+
}
|
|
143
|
+
#insert() {
|
|
136
144
|
if (this.#replace(this.#i1, 0, this.#fp) || this.#replace(this.#i2, 0, this.#fp)) {
|
|
137
145
|
this.#count++;
|
|
138
146
|
return;
|
|
@@ -201,6 +209,9 @@ var CuckooFilter = class CuckooFilter {
|
|
|
201
209
|
}
|
|
202
210
|
#hash(key) {
|
|
203
211
|
hash128KeyInto(key, this.#seed, HASH);
|
|
212
|
+
this.#derive();
|
|
213
|
+
}
|
|
214
|
+
#derive() {
|
|
204
215
|
this.#fp = HASH.w1 >>> 32 - this.#f || 1;
|
|
205
216
|
this.#i1 = reduce(HASH.w0, this.#buckets);
|
|
206
217
|
this.#i2 = this.#alt(this.#i1, this.#fp);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
//#region src/core/distinct.ts
|
|
2
|
+
const START = 1024;
|
|
3
|
+
var DistinctHashes = class {
|
|
4
|
+
#width;
|
|
5
|
+
#words;
|
|
6
|
+
#table = new Int32Array(2 * START);
|
|
7
|
+
#size = 0;
|
|
8
|
+
constructor(width) {
|
|
9
|
+
this.#width = width;
|
|
10
|
+
this.#words = new Uint32Array(START * width);
|
|
11
|
+
}
|
|
12
|
+
get size() {
|
|
13
|
+
return this.#size;
|
|
14
|
+
}
|
|
15
|
+
get words() {
|
|
16
|
+
return this.#words.subarray(0, this.#size * this.#width);
|
|
17
|
+
}
|
|
18
|
+
add(hash) {
|
|
19
|
+
const width = this.#width;
|
|
20
|
+
const w0 = hash.w0 >>> 0;
|
|
21
|
+
const w1 = hash.w1 >>> 0;
|
|
22
|
+
const w2 = hash.w2 >>> 0;
|
|
23
|
+
const w3 = hash.w3 >>> 0;
|
|
24
|
+
if ((this.#size + 1) * width > this.#words.length) this.#grow();
|
|
25
|
+
const words = this.#words;
|
|
26
|
+
const mask = this.#table.length - 1;
|
|
27
|
+
let slot = w0 & mask;
|
|
28
|
+
for (;;) {
|
|
29
|
+
const held = this.#table[slot] ?? 0;
|
|
30
|
+
if (held === 0) break;
|
|
31
|
+
const at = (held - 1) * width;
|
|
32
|
+
if (words[at] === w0 && words[at + 1] === w1 && (width === 2 || words[at + 2] === w2 && words[at + 3] === w3)) return false;
|
|
33
|
+
slot = slot + 1 & mask;
|
|
34
|
+
}
|
|
35
|
+
const at = this.#size * width;
|
|
36
|
+
words[at] = w0;
|
|
37
|
+
words[at + 1] = w1;
|
|
38
|
+
if (width === 4) {
|
|
39
|
+
words[at + 2] = w2;
|
|
40
|
+
words[at + 3] = w3;
|
|
41
|
+
}
|
|
42
|
+
this.#size++;
|
|
43
|
+
this.#table[slot] = this.#size;
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
#grow() {
|
|
47
|
+
const words = new Uint32Array(this.#words.length * 2);
|
|
48
|
+
words.set(this.#words);
|
|
49
|
+
this.#words = words;
|
|
50
|
+
this.#table = new Int32Array(this.#table.length * 2);
|
|
51
|
+
for (let i = 0; i < this.#size; i++) this.#place(i);
|
|
52
|
+
}
|
|
53
|
+
#place(i) {
|
|
54
|
+
const mask = this.#table.length - 1;
|
|
55
|
+
let slot = (this.#words[i * this.#width] ?? 0) & mask;
|
|
56
|
+
while ((this.#table[slot] ?? 0) !== 0) slot = slot + 1 & mask;
|
|
57
|
+
this.#table[slot] = i + 1;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { DistinctHashes as t };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
//#region src/core/distinct.ts
|
|
2
|
+
const START = 1024;
|
|
3
|
+
var DistinctHashes = class {
|
|
4
|
+
#width;
|
|
5
|
+
#words;
|
|
6
|
+
#table = new Int32Array(2 * START);
|
|
7
|
+
#size = 0;
|
|
8
|
+
constructor(width) {
|
|
9
|
+
this.#width = width;
|
|
10
|
+
this.#words = new Uint32Array(START * width);
|
|
11
|
+
}
|
|
12
|
+
get size() {
|
|
13
|
+
return this.#size;
|
|
14
|
+
}
|
|
15
|
+
get words() {
|
|
16
|
+
return this.#words.subarray(0, this.#size * this.#width);
|
|
17
|
+
}
|
|
18
|
+
add(hash) {
|
|
19
|
+
const width = this.#width;
|
|
20
|
+
const w0 = hash.w0 >>> 0;
|
|
21
|
+
const w1 = hash.w1 >>> 0;
|
|
22
|
+
const w2 = hash.w2 >>> 0;
|
|
23
|
+
const w3 = hash.w3 >>> 0;
|
|
24
|
+
if ((this.#size + 1) * width > this.#words.length) this.#grow();
|
|
25
|
+
const words = this.#words;
|
|
26
|
+
const mask = this.#table.length - 1;
|
|
27
|
+
let slot = w0 & mask;
|
|
28
|
+
for (;;) {
|
|
29
|
+
const held = this.#table[slot] ?? 0;
|
|
30
|
+
if (held === 0) break;
|
|
31
|
+
const at = (held - 1) * width;
|
|
32
|
+
if (words[at] === w0 && words[at + 1] === w1 && (width === 2 || words[at + 2] === w2 && words[at + 3] === w3)) return false;
|
|
33
|
+
slot = slot + 1 & mask;
|
|
34
|
+
}
|
|
35
|
+
const at = this.#size * width;
|
|
36
|
+
words[at] = w0;
|
|
37
|
+
words[at + 1] = w1;
|
|
38
|
+
if (width === 4) {
|
|
39
|
+
words[at + 2] = w2;
|
|
40
|
+
words[at + 3] = w3;
|
|
41
|
+
}
|
|
42
|
+
this.#size++;
|
|
43
|
+
this.#table[slot] = this.#size;
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
#grow() {
|
|
47
|
+
const words = new Uint32Array(this.#words.length * 2);
|
|
48
|
+
words.set(this.#words);
|
|
49
|
+
this.#words = words;
|
|
50
|
+
this.#table = new Int32Array(this.#table.length * 2);
|
|
51
|
+
for (let i = 0; i < this.#size; i++) this.#place(i);
|
|
52
|
+
}
|
|
53
|
+
#place(i) {
|
|
54
|
+
const mask = this.#table.length - 1;
|
|
55
|
+
let slot = (this.#words[i * this.#width] ?? 0) & mask;
|
|
56
|
+
while ((this.#table[slot] ?? 0) !== 0) slot = slot + 1 & mask;
|
|
57
|
+
this.#table[slot] = i + 1;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
Object.defineProperty(exports, "DistinctHashes", {
|
|
62
|
+
enumerable: true,
|
|
63
|
+
get: function() {
|
|
64
|
+
return DistinctHashes;
|
|
65
|
+
}
|
|
66
|
+
});
|
package/dist/fuse/index.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_hasher = require("../hasher-
|
|
2
|
+
const require_hasher = require("../hasher-DPXmP7SH.cjs");
|
|
3
3
|
const require_serialize = require("../serialize-CHikZ4GH.cjs");
|
|
4
|
+
const require_distinct = require("../distinct-Ck5PKuCS.cjs");
|
|
4
5
|
//#region src/fuse/fuse.ts
|
|
5
6
|
const ARITY = 3;
|
|
6
7
|
const TYPE_FUSE8 = 3;
|
|
@@ -131,18 +132,12 @@ function buildFingerprints(fp, hashes, params, maxAttempts = 100) {
|
|
|
131
132
|
throw new BinaryFuseBuildError("binary fuse construction failed");
|
|
132
133
|
}
|
|
133
134
|
function buildState(keys, alloc) {
|
|
134
|
-
const
|
|
135
|
-
const seen = new Set();
|
|
135
|
+
const distinct = new require_distinct.DistinctHashes(2);
|
|
136
136
|
for (const key of keys) {
|
|
137
137
|
require_hasher.hash128KeyInto(key, 0, scratchHash);
|
|
138
|
-
|
|
139
|
-
const hi = scratchHash.w1 >>> 0;
|
|
140
|
-
const id = `${String(lo)},${String(hi)}`;
|
|
141
|
-
if (seen.has(id)) continue;
|
|
142
|
-
seen.add(id);
|
|
143
|
-
hashList.push(lo, hi);
|
|
138
|
+
distinct.add(scratchHash);
|
|
144
139
|
}
|
|
145
|
-
const size =
|
|
140
|
+
const size = distinct.size;
|
|
146
141
|
const params = computeParams(size);
|
|
147
142
|
if (size === 0) return {
|
|
148
143
|
fp: alloc(0),
|
|
@@ -150,7 +145,7 @@ function buildState(keys, alloc) {
|
|
|
150
145
|
params,
|
|
151
146
|
size: 0
|
|
152
147
|
};
|
|
153
|
-
const hashes =
|
|
148
|
+
const hashes = distinct.words;
|
|
154
149
|
const fp = alloc(params.arrayLength);
|
|
155
150
|
return {
|
|
156
151
|
fp,
|
package/dist/fuse/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { i as hash128KeyInto, n as RLO, o as mul64, r as fmix64, t as RHI } from "../hasher-
|
|
1
|
+
import { i as hash128KeyInto, n as RLO, o as mul64, r as fmix64, t as RHI } from "../hasher-DOdmZ-LI.js";
|
|
2
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 } from "../serialize-CMSYFym-.js";
|
|
3
|
+
import { t as DistinctHashes } from "../distinct-BhxDB8mW.js";
|
|
3
4
|
//#region src/fuse/fuse.ts
|
|
4
5
|
const ARITY = 3;
|
|
5
6
|
const TYPE_FUSE8 = 3;
|
|
@@ -130,18 +131,12 @@ function buildFingerprints(fp, hashes, params, maxAttempts = 100) {
|
|
|
130
131
|
throw new BinaryFuseBuildError("binary fuse construction failed");
|
|
131
132
|
}
|
|
132
133
|
function buildState(keys, alloc) {
|
|
133
|
-
const
|
|
134
|
-
const seen = new Set();
|
|
134
|
+
const distinct = new DistinctHashes(2);
|
|
135
135
|
for (const key of keys) {
|
|
136
136
|
hash128KeyInto(key, 0, scratchHash);
|
|
137
|
-
|
|
138
|
-
const hi = scratchHash.w1 >>> 0;
|
|
139
|
-
const id = `${String(lo)},${String(hi)}`;
|
|
140
|
-
if (seen.has(id)) continue;
|
|
141
|
-
seen.add(id);
|
|
142
|
-
hashList.push(lo, hi);
|
|
137
|
+
distinct.add(scratchHash);
|
|
143
138
|
}
|
|
144
|
-
const size =
|
|
139
|
+
const size = distinct.size;
|
|
145
140
|
const params = computeParams(size);
|
|
146
141
|
if (size === 0) return {
|
|
147
142
|
fp: alloc(0),
|
|
@@ -149,7 +144,7 @@ function buildState(keys, alloc) {
|
|
|
149
144
|
params,
|
|
150
145
|
size: 0
|
|
151
146
|
};
|
|
152
|
-
const hashes =
|
|
147
|
+
const hashes = distinct.words;
|
|
153
148
|
const fp = alloc(params.arrayLength);
|
|
154
149
|
return {
|
|
155
150
|
fp,
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
const encoder = new TextEncoder();
|
|
3
|
-
function normalize(input) {
|
|
4
|
-
if (typeof input === "string") return encoder.encode(input);
|
|
5
|
-
if (input instanceof Uint8Array) return input;
|
|
6
|
-
return new Uint8Array(input);
|
|
7
|
-
}
|
|
8
|
-
//#endregion
|
|
1
|
+
import { t as normalize } from "./bytes-Be7tq1F7.js";
|
|
9
2
|
//#region src/core/hasher.ts
|
|
10
3
|
let RLO = 0;
|
|
11
4
|
let RHI = 0;
|