distillate 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/dist/blocked/index.cjs +111 -38
- package/dist/blocked/index.d.cts +40 -3
- package/dist/blocked/index.d.ts +40 -3
- package/dist/blocked/index.js +111 -38
- package/dist/bloom/index.cjs +69 -27
- package/dist/bloom/index.d.cts +34 -2
- package/dist/bloom/index.d.ts +34 -2
- package/dist/bloom/index.js +69 -27
- package/dist/fuse/index.cjs +63 -19
- package/dist/fuse/index.d.cts +34 -2
- package/dist/fuse/index.d.ts +34 -2
- package/dist/fuse/index.js +63 -19
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{params-J8p3bKq5.cjs → params--8CNXYWu.cjs} +10 -0
- package/dist/{params-ChTRNxM9.js → params-BrWuBC2A.js} +5 -1
- package/dist/serialize-BIIKUHH6.cjs +515 -0
- package/dist/serialize-BnwtzcMw.js +414 -0
- package/dist/serialize-DRKh6QOr.d.cts +15 -0
- package/dist/serialize-DRKh6QOr.d.ts +15 -0
- package/package.json +5 -28
- package/dist/bytes-DCuYtUVS.d.cts +0 -4
- package/dist/bytes-DCuYtUVS.d.ts +0 -4
- package/dist/serialize-BQ2UzZqq.js +0 -354
- package/dist/serialize-fa-pEUGq.cjs +0 -437
package/dist/bloom/index.cjs
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_serialize = require("../serialize-
|
|
3
|
-
const require_params = require("../params
|
|
2
|
+
const require_serialize = require("../serialize-BIIKUHH6.cjs");
|
|
3
|
+
const require_params = require("../params--8CNXYWu.cjs");
|
|
4
4
|
//#region src/core/bitset.ts
|
|
5
5
|
var BitSetRangeError = class extends RangeError {
|
|
6
6
|
name = "BitSetRangeError";
|
|
7
7
|
};
|
|
8
8
|
const MAX_BITS = 2 ** 32;
|
|
9
|
-
var BitSet = class
|
|
9
|
+
var BitSet = class {
|
|
10
10
|
#bits;
|
|
11
|
-
static fromBytes(bytes) {
|
|
12
|
-
const bs = new BitSet(bytes.length * 8);
|
|
13
|
-
bs.#bits.set(bytes);
|
|
14
|
-
return bs;
|
|
15
|
-
}
|
|
16
11
|
constructor(nbits) {
|
|
17
12
|
if (nbits > MAX_BITS) throw new BitSetRangeError(`BitSet capacity ${String(nbits)} exceeds the 2^32-bit limit`);
|
|
18
13
|
this.#bits = new Uint8Array(Math.ceil(nbits / 8));
|
|
@@ -82,9 +77,23 @@ var BloomFilter = class BloomFilter {
|
|
|
82
77
|
*/
|
|
83
78
|
static create(n, epsilon) {
|
|
84
79
|
require_params.assertPositiveInt(n, "n");
|
|
80
|
+
require_params.assertUint32(n, "n");
|
|
85
81
|
require_params.assertProbability(epsilon, "epsilon");
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
return BloomFilter.#withN(optimal(n, epsilon), n);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Builds a filter from `keys`, sized for their count at the target
|
|
86
|
+
* false-positive rate. The ergonomic entry point when the key set is already
|
|
87
|
+
* in hand; use {@link BloomFilter.create} to size for a count known ahead.
|
|
88
|
+
*
|
|
89
|
+
* @param keys - The keys to insert.
|
|
90
|
+
* @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
|
|
91
|
+
* @returns A new filter containing every key.
|
|
92
|
+
*/
|
|
93
|
+
static from(keys, epsilon) {
|
|
94
|
+
const arr = [...keys];
|
|
95
|
+
const f = BloomFilter.create(Math.max(1, arr.length), epsilon);
|
|
96
|
+
for (const k of arr) f.add(k);
|
|
88
97
|
return f;
|
|
89
98
|
}
|
|
90
99
|
/**
|
|
@@ -96,7 +105,7 @@ var BloomFilter = class BloomFilter {
|
|
|
96
105
|
static fromBytes(bytes) {
|
|
97
106
|
const { type, flags, body } = require_serialize.readHeader(bytes);
|
|
98
107
|
if (type !== TYPE) throw new require_serialize.SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
99
|
-
if ((flags & 15) !==
|
|
108
|
+
if ((flags & 15) !== 0) throw new require_serialize.UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
|
|
100
109
|
require_serialize.assertMinBodyLength(body.length, 14, "bloom");
|
|
101
110
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
102
111
|
const m = dv.getUint32(0, true);
|
|
@@ -104,12 +113,11 @@ var BloomFilter = class BloomFilter {
|
|
|
104
113
|
const seed = dv.getUint32(6, true);
|
|
105
114
|
const n = dv.getUint32(10, true);
|
|
106
115
|
require_serialize.assertBodyLength(body.length, 14 + Math.ceil(m / 8), "bloom");
|
|
107
|
-
const f =
|
|
116
|
+
const f = BloomFilter.#withN({
|
|
108
117
|
m,
|
|
109
118
|
k,
|
|
110
119
|
seed
|
|
111
|
-
});
|
|
112
|
-
f.#n = n;
|
|
120
|
+
}, n);
|
|
113
121
|
f.#bits.bytes.set(body.subarray(14));
|
|
114
122
|
return f;
|
|
115
123
|
}
|
|
@@ -119,7 +127,9 @@ var BloomFilter = class BloomFilter {
|
|
|
119
127
|
*/
|
|
120
128
|
constructor({ m, k, seed = 0 }) {
|
|
121
129
|
require_params.assertPositiveInt(m, "m");
|
|
130
|
+
require_params.assertUint32(m, "m");
|
|
122
131
|
require_params.assertPositiveInt(k, "k");
|
|
132
|
+
require_params.assertUint16(k, "k");
|
|
123
133
|
require_params.assertUint32(seed, "seed");
|
|
124
134
|
this.#bits = new BitSet(m);
|
|
125
135
|
this.#m = m;
|
|
@@ -128,6 +138,11 @@ var BloomFilter = class BloomFilter {
|
|
|
128
138
|
this.#scratch = new Uint32Array(k);
|
|
129
139
|
this.#n = Math.round(m * Math.LN2 / k);
|
|
130
140
|
}
|
|
141
|
+
static #withN(params, n) {
|
|
142
|
+
const f = new BloomFilter(params);
|
|
143
|
+
f.#n = n;
|
|
144
|
+
return f;
|
|
145
|
+
}
|
|
131
146
|
/** Number of bits in the filter. */
|
|
132
147
|
get m() {
|
|
133
148
|
return this.#m;
|
|
@@ -165,18 +180,45 @@ var BloomFilter = class BloomFilter {
|
|
|
165
180
|
*/
|
|
166
181
|
toBytes() {
|
|
167
182
|
const payload = this.#bits.bytes;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
dv.setUint32(0, this.#m, true);
|
|
171
|
-
dv.setUint16(4, this.#k, true);
|
|
172
|
-
dv.setUint32(6, this.#seed, true);
|
|
173
|
-
dv.setUint32(10, this.#n, true);
|
|
174
|
-
body.set(payload, 14);
|
|
175
|
-
return require_serialize.writeHeader({
|
|
176
|
-
version: 2,
|
|
183
|
+
return require_serialize.writeFrame({
|
|
184
|
+
version: 3,
|
|
177
185
|
type: TYPE,
|
|
178
|
-
flags:
|
|
179
|
-
}, body)
|
|
186
|
+
flags: 0
|
|
187
|
+
}, 14 + payload.length, (body, dv) => {
|
|
188
|
+
dv.setUint32(0, this.#m, true);
|
|
189
|
+
dv.setUint16(4, this.#k, true);
|
|
190
|
+
dv.setUint32(6, this.#seed, true);
|
|
191
|
+
dv.setUint32(10, this.#n, true);
|
|
192
|
+
body.set(payload, 14);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Tests structural equality: `true` when `other` serializes to identical
|
|
197
|
+
* bytes, meaning identical parameters and set bits.
|
|
198
|
+
*
|
|
199
|
+
* @param other - The filter to compare against.
|
|
200
|
+
* @returns `true` if the two filters are byte-for-byte identical.
|
|
201
|
+
*/
|
|
202
|
+
equals(other) {
|
|
203
|
+
return require_serialize.bytesEqual(this.toBytes(), other.toBytes());
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
207
|
+
* {@link BloomFilter.toBytes}.
|
|
208
|
+
*
|
|
209
|
+
* @returns The envelope, readable by {@link BloomFilter.fromJSON}.
|
|
210
|
+
*/
|
|
211
|
+
toJSON() {
|
|
212
|
+
return require_serialize.toJSONEnvelope(this.toBytes());
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Restores a filter from its {@link BloomFilter.toJSON} envelope.
|
|
216
|
+
*
|
|
217
|
+
* @param value - The JSON envelope.
|
|
218
|
+
* @returns The reconstructed filter.
|
|
219
|
+
*/
|
|
220
|
+
static fromJSON(value) {
|
|
221
|
+
return BloomFilter.fromBytes(require_serialize.fromJSONEnvelope(value));
|
|
180
222
|
}
|
|
181
223
|
/**
|
|
182
224
|
* Returns a new filter containing the union of this filter and `other`.
|
|
@@ -191,11 +233,11 @@ var BloomFilter = class BloomFilter {
|
|
|
191
233
|
const b = other.#bits.bytes;
|
|
192
234
|
const merged = new Uint8Array(a.length);
|
|
193
235
|
for (let i = 0; i < a.length; i++) merged[i] = (a[i] ?? 0) | (b[i] ?? 0);
|
|
194
|
-
const r =
|
|
236
|
+
const r = BloomFilter.#withN({
|
|
195
237
|
m: this.#m,
|
|
196
238
|
k: this.#k,
|
|
197
239
|
seed: this.#seed
|
|
198
|
-
});
|
|
240
|
+
}, this.#n);
|
|
199
241
|
r.#bits.bytes.set(merged);
|
|
200
242
|
return r;
|
|
201
243
|
}
|
package/dist/bloom/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as BytesLike, t as FilterJSON } from "../serialize-DRKh6QOr.cjs";
|
|
2
2
|
import { t as ParamError } from "../params-DnqJBqLS.cjs";
|
|
3
3
|
//#region src/bloom/bloom.d.ts
|
|
4
4
|
/** Thrown when an operation requires two filters built with identical parameters. */
|
|
@@ -37,6 +37,16 @@ declare class BloomFilter {
|
|
|
37
37
|
* @returns A new, empty filter.
|
|
38
38
|
*/
|
|
39
39
|
static create(n: number, epsilon: number): BloomFilter;
|
|
40
|
+
/**
|
|
41
|
+
* Builds a filter from `keys`, sized for their count at the target
|
|
42
|
+
* false-positive rate. The ergonomic entry point when the key set is already
|
|
43
|
+
* in hand; use {@link BloomFilter.create} to size for a count known ahead.
|
|
44
|
+
*
|
|
45
|
+
* @param keys - The keys to insert.
|
|
46
|
+
* @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
|
|
47
|
+
* @returns A new filter containing every key.
|
|
48
|
+
*/
|
|
49
|
+
static from(keys: Iterable<BytesLike>, epsilon: number): BloomFilter;
|
|
40
50
|
/**
|
|
41
51
|
* Restores a filter from its {@link BloomFilter.toBytes} serialization.
|
|
42
52
|
*
|
|
@@ -73,6 +83,28 @@ declare class BloomFilter {
|
|
|
73
83
|
* @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
|
|
74
84
|
*/
|
|
75
85
|
toBytes(): Uint8Array;
|
|
86
|
+
/**
|
|
87
|
+
* Tests structural equality: `true` when `other` serializes to identical
|
|
88
|
+
* bytes, meaning identical parameters and set bits.
|
|
89
|
+
*
|
|
90
|
+
* @param other - The filter to compare against.
|
|
91
|
+
* @returns `true` if the two filters are byte-for-byte identical.
|
|
92
|
+
*/
|
|
93
|
+
equals(other: BloomFilter): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
96
|
+
* {@link BloomFilter.toBytes}.
|
|
97
|
+
*
|
|
98
|
+
* @returns The envelope, readable by {@link BloomFilter.fromJSON}.
|
|
99
|
+
*/
|
|
100
|
+
toJSON(): FilterJSON;
|
|
101
|
+
/**
|
|
102
|
+
* Restores a filter from its {@link BloomFilter.toJSON} envelope.
|
|
103
|
+
*
|
|
104
|
+
* @param value - The JSON envelope.
|
|
105
|
+
* @returns The reconstructed filter.
|
|
106
|
+
*/
|
|
107
|
+
static fromJSON(value: unknown): BloomFilter;
|
|
76
108
|
/**
|
|
77
109
|
* Returns a new filter containing the union of this filter and `other`.
|
|
78
110
|
*
|
|
@@ -96,4 +128,4 @@ declare class BloomFilter {
|
|
|
96
128
|
has(key: BytesLike): boolean;
|
|
97
129
|
}
|
|
98
130
|
//#endregion
|
|
99
|
-
export { BloomFilter, BloomParamMismatchError, type BloomParams, ParamError };
|
|
131
|
+
export { BloomFilter, BloomParamMismatchError, type BloomParams, type FilterJSON, ParamError };
|
package/dist/bloom/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as BytesLike, t as FilterJSON } from "../serialize-DRKh6QOr.js";
|
|
2
2
|
import { t as ParamError } from "../params-DnqJBqLS.js";
|
|
3
3
|
//#region src/bloom/bloom.d.ts
|
|
4
4
|
/** Thrown when an operation requires two filters built with identical parameters. */
|
|
@@ -37,6 +37,16 @@ declare class BloomFilter {
|
|
|
37
37
|
* @returns A new, empty filter.
|
|
38
38
|
*/
|
|
39
39
|
static create(n: number, epsilon: number): BloomFilter;
|
|
40
|
+
/**
|
|
41
|
+
* Builds a filter from `keys`, sized for their count at the target
|
|
42
|
+
* false-positive rate. The ergonomic entry point when the key set is already
|
|
43
|
+
* in hand; use {@link BloomFilter.create} to size for a count known ahead.
|
|
44
|
+
*
|
|
45
|
+
* @param keys - The keys to insert.
|
|
46
|
+
* @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
|
|
47
|
+
* @returns A new filter containing every key.
|
|
48
|
+
*/
|
|
49
|
+
static from(keys: Iterable<BytesLike>, epsilon: number): BloomFilter;
|
|
40
50
|
/**
|
|
41
51
|
* Restores a filter from its {@link BloomFilter.toBytes} serialization.
|
|
42
52
|
*
|
|
@@ -73,6 +83,28 @@ declare class BloomFilter {
|
|
|
73
83
|
* @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
|
|
74
84
|
*/
|
|
75
85
|
toBytes(): Uint8Array;
|
|
86
|
+
/**
|
|
87
|
+
* Tests structural equality: `true` when `other` serializes to identical
|
|
88
|
+
* bytes, meaning identical parameters and set bits.
|
|
89
|
+
*
|
|
90
|
+
* @param other - The filter to compare against.
|
|
91
|
+
* @returns `true` if the two filters are byte-for-byte identical.
|
|
92
|
+
*/
|
|
93
|
+
equals(other: BloomFilter): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
96
|
+
* {@link BloomFilter.toBytes}.
|
|
97
|
+
*
|
|
98
|
+
* @returns The envelope, readable by {@link BloomFilter.fromJSON}.
|
|
99
|
+
*/
|
|
100
|
+
toJSON(): FilterJSON;
|
|
101
|
+
/**
|
|
102
|
+
* Restores a filter from its {@link BloomFilter.toJSON} envelope.
|
|
103
|
+
*
|
|
104
|
+
* @param value - The JSON envelope.
|
|
105
|
+
* @returns The reconstructed filter.
|
|
106
|
+
*/
|
|
107
|
+
static fromJSON(value: unknown): BloomFilter;
|
|
76
108
|
/**
|
|
77
109
|
* Returns a new filter containing the union of this filter and `other`.
|
|
78
110
|
*
|
|
@@ -96,4 +128,4 @@ declare class BloomFilter {
|
|
|
96
128
|
has(key: BytesLike): boolean;
|
|
97
129
|
}
|
|
98
130
|
//#endregion
|
|
99
|
-
export { BloomFilter, BloomParamMismatchError, type BloomParams, ParamError };
|
|
131
|
+
export { BloomFilter, BloomParamMismatchError, type BloomParams, type FilterJSON, ParamError };
|
package/dist/bloom/index.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { a as
|
|
1
|
+
import { a as bytesEqual, c as toJSONEnvelope, g as probeInto, i as assertMinBodyLength, l as writeFrame, n as UnknownHashVariantError, o as fromJSONEnvelope, r as assertBodyLength, s as readHeader, t as SerializationError } from "../serialize-BnwtzcMw.js";
|
|
2
|
+
import { a as assertUint16, i as assertProbability, o as assertUint32, r as assertPositiveInt, t as ParamError } from "../params-BrWuBC2A.js";
|
|
3
3
|
//#region src/core/bitset.ts
|
|
4
4
|
var BitSetRangeError = class extends RangeError {
|
|
5
5
|
name = "BitSetRangeError";
|
|
6
6
|
};
|
|
7
7
|
const MAX_BITS = 2 ** 32;
|
|
8
|
-
var BitSet = class
|
|
8
|
+
var BitSet = class {
|
|
9
9
|
#bits;
|
|
10
|
-
static fromBytes(bytes) {
|
|
11
|
-
const bs = new BitSet(bytes.length * 8);
|
|
12
|
-
bs.#bits.set(bytes);
|
|
13
|
-
return bs;
|
|
14
|
-
}
|
|
15
10
|
constructor(nbits) {
|
|
16
11
|
if (nbits > MAX_BITS) throw new BitSetRangeError(`BitSet capacity ${String(nbits)} exceeds the 2^32-bit limit`);
|
|
17
12
|
this.#bits = new Uint8Array(Math.ceil(nbits / 8));
|
|
@@ -81,9 +76,23 @@ var BloomFilter = class BloomFilter {
|
|
|
81
76
|
*/
|
|
82
77
|
static create(n, epsilon) {
|
|
83
78
|
assertPositiveInt(n, "n");
|
|
79
|
+
assertUint32(n, "n");
|
|
84
80
|
assertProbability(epsilon, "epsilon");
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
return BloomFilter.#withN(optimal(n, epsilon), n);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Builds a filter from `keys`, sized for their count at the target
|
|
85
|
+
* false-positive rate. The ergonomic entry point when the key set is already
|
|
86
|
+
* in hand; use {@link BloomFilter.create} to size for a count known ahead.
|
|
87
|
+
*
|
|
88
|
+
* @param keys - The keys to insert.
|
|
89
|
+
* @param epsilon - Target false-positive rate, e.g. `0.01` for 1%.
|
|
90
|
+
* @returns A new filter containing every key.
|
|
91
|
+
*/
|
|
92
|
+
static from(keys, epsilon) {
|
|
93
|
+
const arr = [...keys];
|
|
94
|
+
const f = BloomFilter.create(Math.max(1, arr.length), epsilon);
|
|
95
|
+
for (const k of arr) f.add(k);
|
|
87
96
|
return f;
|
|
88
97
|
}
|
|
89
98
|
/**
|
|
@@ -95,7 +104,7 @@ var BloomFilter = class BloomFilter {
|
|
|
95
104
|
static fromBytes(bytes) {
|
|
96
105
|
const { type, flags, body } = readHeader(bytes);
|
|
97
106
|
if (type !== TYPE) throw new SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
98
|
-
if ((flags & 15) !==
|
|
107
|
+
if ((flags & 15) !== 0) throw new UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
|
|
99
108
|
assertMinBodyLength(body.length, 14, "bloom");
|
|
100
109
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
101
110
|
const m = dv.getUint32(0, true);
|
|
@@ -103,12 +112,11 @@ var BloomFilter = class BloomFilter {
|
|
|
103
112
|
const seed = dv.getUint32(6, true);
|
|
104
113
|
const n = dv.getUint32(10, true);
|
|
105
114
|
assertBodyLength(body.length, 14 + Math.ceil(m / 8), "bloom");
|
|
106
|
-
const f =
|
|
115
|
+
const f = BloomFilter.#withN({
|
|
107
116
|
m,
|
|
108
117
|
k,
|
|
109
118
|
seed
|
|
110
|
-
});
|
|
111
|
-
f.#n = n;
|
|
119
|
+
}, n);
|
|
112
120
|
f.#bits.bytes.set(body.subarray(14));
|
|
113
121
|
return f;
|
|
114
122
|
}
|
|
@@ -118,7 +126,9 @@ var BloomFilter = class BloomFilter {
|
|
|
118
126
|
*/
|
|
119
127
|
constructor({ m, k, seed = 0 }) {
|
|
120
128
|
assertPositiveInt(m, "m");
|
|
129
|
+
assertUint32(m, "m");
|
|
121
130
|
assertPositiveInt(k, "k");
|
|
131
|
+
assertUint16(k, "k");
|
|
122
132
|
assertUint32(seed, "seed");
|
|
123
133
|
this.#bits = new BitSet(m);
|
|
124
134
|
this.#m = m;
|
|
@@ -127,6 +137,11 @@ var BloomFilter = class BloomFilter {
|
|
|
127
137
|
this.#scratch = new Uint32Array(k);
|
|
128
138
|
this.#n = Math.round(m * Math.LN2 / k);
|
|
129
139
|
}
|
|
140
|
+
static #withN(params, n) {
|
|
141
|
+
const f = new BloomFilter(params);
|
|
142
|
+
f.#n = n;
|
|
143
|
+
return f;
|
|
144
|
+
}
|
|
130
145
|
/** Number of bits in the filter. */
|
|
131
146
|
get m() {
|
|
132
147
|
return this.#m;
|
|
@@ -164,18 +179,45 @@ var BloomFilter = class BloomFilter {
|
|
|
164
179
|
*/
|
|
165
180
|
toBytes() {
|
|
166
181
|
const payload = this.#bits.bytes;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
dv.setUint32(0, this.#m, true);
|
|
170
|
-
dv.setUint16(4, this.#k, true);
|
|
171
|
-
dv.setUint32(6, this.#seed, true);
|
|
172
|
-
dv.setUint32(10, this.#n, true);
|
|
173
|
-
body.set(payload, 14);
|
|
174
|
-
return writeHeader({
|
|
175
|
-
version: 2,
|
|
182
|
+
return writeFrame({
|
|
183
|
+
version: 3,
|
|
176
184
|
type: TYPE,
|
|
177
|
-
flags:
|
|
178
|
-
}, body)
|
|
185
|
+
flags: 0
|
|
186
|
+
}, 14 + payload.length, (body, dv) => {
|
|
187
|
+
dv.setUint32(0, this.#m, true);
|
|
188
|
+
dv.setUint16(4, this.#k, true);
|
|
189
|
+
dv.setUint32(6, this.#seed, true);
|
|
190
|
+
dv.setUint32(10, this.#n, true);
|
|
191
|
+
body.set(payload, 14);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Tests structural equality: `true` when `other` serializes to identical
|
|
196
|
+
* bytes, meaning identical parameters and set bits.
|
|
197
|
+
*
|
|
198
|
+
* @param other - The filter to compare against.
|
|
199
|
+
* @returns `true` if the two filters are byte-for-byte identical.
|
|
200
|
+
*/
|
|
201
|
+
equals(other) {
|
|
202
|
+
return bytesEqual(this.toBytes(), other.toBytes());
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
206
|
+
* {@link BloomFilter.toBytes}.
|
|
207
|
+
*
|
|
208
|
+
* @returns The envelope, readable by {@link BloomFilter.fromJSON}.
|
|
209
|
+
*/
|
|
210
|
+
toJSON() {
|
|
211
|
+
return toJSONEnvelope(this.toBytes());
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Restores a filter from its {@link BloomFilter.toJSON} envelope.
|
|
215
|
+
*
|
|
216
|
+
* @param value - The JSON envelope.
|
|
217
|
+
* @returns The reconstructed filter.
|
|
218
|
+
*/
|
|
219
|
+
static fromJSON(value) {
|
|
220
|
+
return BloomFilter.fromBytes(fromJSONEnvelope(value));
|
|
179
221
|
}
|
|
180
222
|
/**
|
|
181
223
|
* Returns a new filter containing the union of this filter and `other`.
|
|
@@ -190,11 +232,11 @@ var BloomFilter = class BloomFilter {
|
|
|
190
232
|
const b = other.#bits.bytes;
|
|
191
233
|
const merged = new Uint8Array(a.length);
|
|
192
234
|
for (let i = 0; i < a.length; i++) merged[i] = (a[i] ?? 0) | (b[i] ?? 0);
|
|
193
|
-
const r =
|
|
235
|
+
const r = BloomFilter.#withN({
|
|
194
236
|
m: this.#m,
|
|
195
237
|
k: this.#k,
|
|
196
238
|
seed: this.#seed
|
|
197
|
-
});
|
|
239
|
+
}, this.#n);
|
|
198
240
|
r.#bits.bytes.set(merged);
|
|
199
241
|
return r;
|
|
200
242
|
}
|
package/dist/fuse/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_serialize = require("../serialize-
|
|
2
|
+
const require_serialize = require("../serialize-BIIKUHH6.cjs");
|
|
3
3
|
//#region src/fuse/fuse.ts
|
|
4
4
|
const ARITY = 3;
|
|
5
5
|
const TYPE_FUSE8 = 3;
|
|
@@ -10,10 +10,10 @@ var BinaryFuseBuildError = class extends Error {
|
|
|
10
10
|
name = "BinaryFuseBuildError";
|
|
11
11
|
};
|
|
12
12
|
const scratchHash = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
w0: 0,
|
|
14
|
+
w1: 0,
|
|
15
|
+
w2: 0,
|
|
16
|
+
w3: 0
|
|
17
17
|
};
|
|
18
18
|
function mixSeed(lo, hi, seed) {
|
|
19
19
|
const l = (lo >>> 0) + (seed >>> 0);
|
|
@@ -136,8 +136,8 @@ function buildState(keys, alloc) {
|
|
|
136
136
|
const seen = /* @__PURE__ */ new Set();
|
|
137
137
|
for (const key of keys) {
|
|
138
138
|
require_serialize.hash128KeyInto(key, 0, scratchHash);
|
|
139
|
-
const lo = scratchHash.
|
|
140
|
-
const hi = scratchHash.
|
|
139
|
+
const lo = scratchHash.w0 >>> 0;
|
|
140
|
+
const hi = scratchHash.w1 >>> 0;
|
|
141
141
|
const id = `${String(lo)},${String(hi)}`;
|
|
142
142
|
if (seen.has(id)) continue;
|
|
143
143
|
seen.add(id);
|
|
@@ -161,14 +161,17 @@ function buildState(keys, alloc) {
|
|
|
161
161
|
};
|
|
162
162
|
}
|
|
163
163
|
function fuseStateFromBytes(bytes, expectedType) {
|
|
164
|
-
const { type, body } = require_serialize.readHeader(bytes);
|
|
164
|
+
const { type, flags, body } = require_serialize.readHeader(bytes);
|
|
165
165
|
if (type !== expectedType) throw new require_serialize.SerializationError(`expected AMQF type ${String(expectedType)}, got ${String(type)}`);
|
|
166
|
+
if ((flags & 15) !== 0) throw new require_serialize.UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
|
|
166
167
|
require_serialize.assertMinBodyLength(body.length, 16, "fuse");
|
|
167
168
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
168
169
|
const seed = dv.getUint32(0, true);
|
|
169
170
|
const seg = dv.getUint32(4, true);
|
|
170
171
|
const segCountLen = dv.getUint32(8, true);
|
|
171
172
|
const size = dv.getUint32(12, true);
|
|
173
|
+
if (seg === 0 || (seg & seg - 1) !== 0 || seg > 1 << 18) throw new require_serialize.SerializationError(`invalid fuse segment length ${String(seg)}`);
|
|
174
|
+
if (segCountLen % seg !== 0) throw new require_serialize.SerializationError(`fuse segment count length ${String(segCountLen)} is not a multiple of segment length ${String(seg)}`);
|
|
172
175
|
const bpe = expectedType === TYPE_FUSE8 ? 1 : 2;
|
|
173
176
|
const expectedArrayLength = size === 0 ? 0 : segCountLen + 2 * seg;
|
|
174
177
|
require_serialize.assertBodyLength(body.length, 16 + expectedArrayLength * bpe, "fuse");
|
|
@@ -212,6 +215,10 @@ var BinaryFuse = class {
|
|
|
212
215
|
get size() {
|
|
213
216
|
return this.#size;
|
|
214
217
|
}
|
|
218
|
+
/** Hash seed selected during construction (may differ from 0 after a peel retry). */
|
|
219
|
+
get seed() {
|
|
220
|
+
return this.#seed;
|
|
221
|
+
}
|
|
215
222
|
/** Actual bits stored per key (`0` for an empty filter). */
|
|
216
223
|
get bitsPerKey() {
|
|
217
224
|
return this.#size === 0 ? 0 : this.#fp.byteLength * 8 / this.#size;
|
|
@@ -223,18 +230,17 @@ var BinaryFuse = class {
|
|
|
223
230
|
*/
|
|
224
231
|
toBytes() {
|
|
225
232
|
const laneBytes = new Uint8Array(this.#fp.buffer, this.#fp.byteOffset, this.#fp.byteLength);
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
dv.setUint32(0, this.#seed, true);
|
|
229
|
-
dv.setUint32(4, this.#seg, true);
|
|
230
|
-
dv.setUint32(8, this.#segCountLen, true);
|
|
231
|
-
dv.setUint32(12, this.#size, true);
|
|
232
|
-
body.set(laneBytes, 16);
|
|
233
|
-
return require_serialize.writeHeader({
|
|
234
|
-
version: 2,
|
|
233
|
+
return require_serialize.writeFrame({
|
|
234
|
+
version: 3,
|
|
235
235
|
type: this.#fp.BYTES_PER_ELEMENT === 1 ? TYPE_FUSE8 : TYPE_FUSE16,
|
|
236
236
|
flags: 0
|
|
237
|
-
}, body)
|
|
237
|
+
}, 16 + laneBytes.length, (body, dv) => {
|
|
238
|
+
dv.setUint32(0, this.#seed, true);
|
|
239
|
+
dv.setUint32(4, this.#seg, true);
|
|
240
|
+
dv.setUint32(8, this.#segCountLen, true);
|
|
241
|
+
dv.setUint32(12, this.#size, true);
|
|
242
|
+
body.set(laneBytes, 16);
|
|
243
|
+
});
|
|
238
244
|
}
|
|
239
245
|
/**
|
|
240
246
|
* Tests whether a key is in the set.
|
|
@@ -245,7 +251,7 @@ var BinaryFuse = class {
|
|
|
245
251
|
has(key) {
|
|
246
252
|
if (this.#fp.length === 0) return false;
|
|
247
253
|
require_serialize.hash128KeyInto(key, 0, scratchHash);
|
|
248
|
-
mixSeed(scratchHash.
|
|
254
|
+
mixSeed(scratchHash.w0 >>> 0, scratchHash.w1 >>> 0, this.#seed);
|
|
249
255
|
const mlo = require_serialize.RLO;
|
|
250
256
|
const mhi = require_serialize.RHI;
|
|
251
257
|
positionsInto(mlo, mhi, this.#seg, this.#segMask, this.#segCountLen, this.#pos);
|
|
@@ -255,6 +261,26 @@ var BinaryFuse = class {
|
|
|
255
261
|
const p2 = this.#pos[2] ?? 0;
|
|
256
262
|
return ((mlo ^ mhi) & mask) === (((this.#fp[p0] ?? 0) ^ (this.#fp[p1] ?? 0) ^ (this.#fp[p2] ?? 0)) & mask);
|
|
257
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* Tests structural equality: `true` when `other` serializes to identical
|
|
266
|
+
* bytes. A {@link BinaryFuse8} and a {@link BinaryFuse16} are never equal,
|
|
267
|
+
* since their frames carry different type bytes.
|
|
268
|
+
*
|
|
269
|
+
* @param other - The filter to compare against.
|
|
270
|
+
* @returns `true` if the two filters are byte-for-byte identical.
|
|
271
|
+
*/
|
|
272
|
+
equals(other) {
|
|
273
|
+
return require_serialize.bytesEqual(this.toBytes(), other.toBytes());
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
277
|
+
* the `toBytes` frame.
|
|
278
|
+
*
|
|
279
|
+
* @returns The envelope, readable by the matching `fromJSON`.
|
|
280
|
+
*/
|
|
281
|
+
toJSON() {
|
|
282
|
+
return require_serialize.toJSONEnvelope(this.toBytes());
|
|
283
|
+
}
|
|
258
284
|
};
|
|
259
285
|
/**
|
|
260
286
|
* A static 8-bit binary fuse filter: built once from a key set, then immutable.
|
|
@@ -287,6 +313,15 @@ var BinaryFuse8 = class BinaryFuse8 extends BinaryFuse {
|
|
|
287
313
|
static fromBytes(bytes) {
|
|
288
314
|
return new BinaryFuse8(fuseStateFromBytes(bytes, TYPE_FUSE8));
|
|
289
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Restores a filter from its {@link BinaryFuse8.toJSON} envelope.
|
|
318
|
+
*
|
|
319
|
+
* @param value - The JSON envelope.
|
|
320
|
+
* @returns The reconstructed filter.
|
|
321
|
+
*/
|
|
322
|
+
static fromJSON(value) {
|
|
323
|
+
return BinaryFuse8.fromBytes(require_serialize.fromJSONEnvelope(value));
|
|
324
|
+
}
|
|
290
325
|
};
|
|
291
326
|
/**
|
|
292
327
|
* A static 16-bit binary fuse filter: like {@link BinaryFuse8} but twice the
|
|
@@ -318,6 +353,15 @@ var BinaryFuse16 = class BinaryFuse16 extends BinaryFuse {
|
|
|
318
353
|
static fromBytes(bytes) {
|
|
319
354
|
return new BinaryFuse16(fuseStateFromBytes(bytes, TYPE_FUSE16));
|
|
320
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Restores a filter from its {@link BinaryFuse16.toJSON} envelope.
|
|
358
|
+
*
|
|
359
|
+
* @param value - The JSON envelope.
|
|
360
|
+
* @returns The reconstructed filter.
|
|
361
|
+
*/
|
|
362
|
+
static fromJSON(value) {
|
|
363
|
+
return BinaryFuse16.fromBytes(require_serialize.fromJSONEnvelope(value));
|
|
364
|
+
}
|
|
321
365
|
};
|
|
322
366
|
//#endregion
|
|
323
367
|
exports.BinaryFuse16 = BinaryFuse16;
|
package/dist/fuse/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as BytesLike, t as FilterJSON } from "../serialize-DRKh6QOr.cjs";
|
|
2
2
|
//#region src/fuse/fuse.d.ts
|
|
3
3
|
/** Thrown when binary fuse construction fails to converge on the key set. */
|
|
4
4
|
declare class BinaryFuseBuildError extends Error {
|
|
@@ -26,6 +26,8 @@ declare abstract class BinaryFuse {
|
|
|
26
26
|
protected constructor(state: FuseState);
|
|
27
27
|
/** Number of distinct keys the filter was built from. */
|
|
28
28
|
get size(): number;
|
|
29
|
+
/** Hash seed selected during construction (may differ from 0 after a peel retry). */
|
|
30
|
+
get seed(): number;
|
|
29
31
|
/** Actual bits stored per key (`0` for an empty filter). */
|
|
30
32
|
get bitsPerKey(): number;
|
|
31
33
|
/**
|
|
@@ -41,6 +43,22 @@ declare abstract class BinaryFuse {
|
|
|
41
43
|
* @returns `true` if present (possibly a false positive); `false` guarantees absence.
|
|
42
44
|
*/
|
|
43
45
|
has(key: BytesLike): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Tests structural equality: `true` when `other` serializes to identical
|
|
48
|
+
* bytes. A {@link BinaryFuse8} and a {@link BinaryFuse16} are never equal,
|
|
49
|
+
* since their frames carry different type bytes.
|
|
50
|
+
*
|
|
51
|
+
* @param other - The filter to compare against.
|
|
52
|
+
* @returns `true` if the two filters are byte-for-byte identical.
|
|
53
|
+
*/
|
|
54
|
+
equals(other: BinaryFuse8 | BinaryFuse16): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Serializes the filter to a JSON-friendly envelope wrapping the base64 of
|
|
57
|
+
* the `toBytes` frame.
|
|
58
|
+
*
|
|
59
|
+
* @returns The envelope, readable by the matching `fromJSON`.
|
|
60
|
+
*/
|
|
61
|
+
toJSON(): FilterJSON;
|
|
44
62
|
}
|
|
45
63
|
/**
|
|
46
64
|
* A static 8-bit binary fuse filter: built once from a key set, then immutable.
|
|
@@ -69,6 +87,13 @@ declare class BinaryFuse8 extends BinaryFuse {
|
|
|
69
87
|
* @returns The reconstructed filter.
|
|
70
88
|
*/
|
|
71
89
|
static fromBytes(bytes: Uint8Array): BinaryFuse8;
|
|
90
|
+
/**
|
|
91
|
+
* Restores a filter from its {@link BinaryFuse8.toJSON} envelope.
|
|
92
|
+
*
|
|
93
|
+
* @param value - The JSON envelope.
|
|
94
|
+
* @returns The reconstructed filter.
|
|
95
|
+
*/
|
|
96
|
+
static fromJSON(value: unknown): BinaryFuse8;
|
|
72
97
|
}
|
|
73
98
|
/**
|
|
74
99
|
* A static 16-bit binary fuse filter: like {@link BinaryFuse8} but twice the
|
|
@@ -96,6 +121,13 @@ declare class BinaryFuse16 extends BinaryFuse {
|
|
|
96
121
|
* @returns The reconstructed filter.
|
|
97
122
|
*/
|
|
98
123
|
static fromBytes(bytes: Uint8Array): BinaryFuse16;
|
|
124
|
+
/**
|
|
125
|
+
* Restores a filter from its {@link BinaryFuse16.toJSON} envelope.
|
|
126
|
+
*
|
|
127
|
+
* @param value - The JSON envelope.
|
|
128
|
+
* @returns The reconstructed filter.
|
|
129
|
+
*/
|
|
130
|
+
static fromJSON(value: unknown): BinaryFuse16;
|
|
99
131
|
}
|
|
100
132
|
//#endregion
|
|
101
|
-
export { BinaryFuse16, BinaryFuse8, BinaryFuseBuildError };
|
|
133
|
+
export { BinaryFuse16, BinaryFuse8, BinaryFuseBuildError, type FilterJSON };
|