distillate 0.3.0 → 0.5.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 +4 -4
- package/dist/blocked/index.cjs +12 -13
- package/dist/blocked/index.js +12 -13
- package/dist/bloom/index.cjs +18 -11
- package/dist/bloom/index.js +18 -11
- package/dist/fuse/index.cjs +16 -61
- package/dist/fuse/index.js +6 -51
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{serialize-CHHDM4TQ.cjs → serialize-BQ2UzZqq.js} +70 -45
- package/dist/{serialize-5XQ5y_R-.js → serialize-fa-pEUGq.cjs} +153 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ Each structure ships as its own subpath, so you only bundle what you import.
|
|
|
43
43
|
| Import | Structure | Mutable? | Use for |
|
|
44
44
|
| -------------------- | ------------- | -------- | ---------------------------------------------------- |
|
|
45
45
|
| `distillate/bloom` | Classic Bloom | yes | Familiar default, migration from `bloom-filters` |
|
|
46
|
-
| `distillate/blocked` | Blocked Bloom | yes |
|
|
46
|
+
| `distillate/blocked` | Blocked Bloom | yes | Very large sets that outgrow CPU cache (10M+ keys) |
|
|
47
47
|
| `distillate/fuse` | Binary Fuse | no | Static set built once and queried a lot; least space |
|
|
48
48
|
|
|
49
49
|
### Classic Bloom (`distillate/bloom`)
|
|
@@ -72,7 +72,7 @@ filter.add("alice");
|
|
|
72
72
|
filter.has("alice"); // true
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
Same surface as Classic Bloom (`add` / `has` / `union` / `toBytes` / `fromBytes` / `bitsPerKey`). Confines every lookup to
|
|
75
|
+
Same surface as Classic Bloom (`add` / `has` / `union` / `toBytes` / `fromBytes` / `bitsPerKey`). Confines every lookup to a single cache line, which only pays off once the filter outgrows CPU cache. At typical sizes it matches Classic; for very large sets (tens of millions of keys, past L2/L3) it runs about 1.4x Classic's lookup throughput, at the cost of ~20-30% more space. Prefer Classic unless you are memory-bound at scale.
|
|
76
76
|
|
|
77
77
|
### Binary Fuse (`distillate/fuse`)
|
|
78
78
|
|
|
@@ -98,10 +98,10 @@ Classic Bloom head-to-head at a **matched 1% false-positive rate** over the same
|
|
|
98
98
|
|
|
99
99
|
| Classic Bloom | bits/key | measured FPR | `has` throughput |
|
|
100
100
|
| -------------- | -------- | ------------ | ---------------- |
|
|
101
|
-
| **distillate** | 9.59 | 1.03% | ~
|
|
101
|
+
| **distillate** | 9.59 | 1.03% | ~16 M ops/s |
|
|
102
102
|
| bloom-filters | 9.59 | 0.99% | ~0.29 M ops/s |
|
|
103
103
|
|
|
104
|
-
Same space, same accuracy, **~
|
|
104
|
+
Same space, same accuracy, **~56x 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
105
|
|
|
106
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
107
|
|
package/dist/blocked/index.cjs
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_serialize = require("../serialize-
|
|
2
|
+
const require_serialize = require("../serialize-fa-pEUGq.cjs");
|
|
3
3
|
const require_params = require("../params-J8p3bKq5.cjs");
|
|
4
4
|
//#region src/blocked/blocked.ts
|
|
5
5
|
const TYPE = 2;
|
|
6
6
|
const SALT = Uint32Array.of(1203114875, 1150766481, 2284105051, 2729912477, 1884591559, 770785867, 2667333959, 1550580529);
|
|
7
|
-
const
|
|
8
|
-
h1lo: 0,
|
|
9
|
-
h1hi: 0,
|
|
10
|
-
h2lo: 0,
|
|
11
|
-
h2hi: 0
|
|
12
|
-
};
|
|
7
|
+
const scratch2 = /* @__PURE__ */ new Uint32Array(2);
|
|
13
8
|
/** Thrown when an operation requires two filters built with identical parameters. */
|
|
14
9
|
var BlockedBloomParamMismatchError = class extends Error {
|
|
15
10
|
/** Discriminates this error from other `Error`s. */
|
|
@@ -104,11 +99,15 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
104
99
|
* @returns The reconstructed filter.
|
|
105
100
|
*/
|
|
106
101
|
static fromBytes(bytes) {
|
|
107
|
-
const { body } = require_serialize.readHeader(bytes);
|
|
102
|
+
const { type, flags, body } = require_serialize.readHeader(bytes);
|
|
103
|
+
if (type !== TYPE) throw new require_serialize.SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
104
|
+
if ((flags & 15) !== 1) throw new require_serialize.UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
|
|
105
|
+
require_serialize.assertMinBodyLength(body.length, 12, "blocked");
|
|
108
106
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
109
107
|
const numBlocks = dv.getUint32(0, true);
|
|
110
108
|
const seed = dv.getUint32(4, true);
|
|
111
109
|
const n = dv.getUint32(8, true);
|
|
110
|
+
require_serialize.assertBodyLength(body.length, 12 + numBlocks * 32, "blocked");
|
|
112
111
|
const f = new BlockedBloomFilter({
|
|
113
112
|
bitsPerKey: 256,
|
|
114
113
|
capacity: numBlocks,
|
|
@@ -132,9 +131,9 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
132
131
|
dv.setUint32(8, this.#n, true);
|
|
133
132
|
body.set(lanes, 12);
|
|
134
133
|
return require_serialize.writeHeader({
|
|
135
|
-
version:
|
|
134
|
+
version: 2,
|
|
136
135
|
type: TYPE,
|
|
137
|
-
flags:
|
|
136
|
+
flags: 1
|
|
138
137
|
}, body);
|
|
139
138
|
}
|
|
140
139
|
/**
|
|
@@ -182,9 +181,9 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
182
181
|
}
|
|
183
182
|
};
|
|
184
183
|
function fillBlock(key, numBlocks, seed, outWords, outBits) {
|
|
185
|
-
require_serialize.
|
|
186
|
-
const block = require_serialize.reduce(
|
|
187
|
-
const x =
|
|
184
|
+
require_serialize.hash32x2Into(key, seed, scratch2);
|
|
185
|
+
const block = require_serialize.reduce(scratch2[0] ?? 0, numBlocks);
|
|
186
|
+
const x = scratch2[1] ?? 0;
|
|
188
187
|
const base = block * 8;
|
|
189
188
|
for (let i = 0; i < 8; i++) {
|
|
190
189
|
outWords[i] = base + i;
|
package/dist/blocked/index.js
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import { i as
|
|
1
|
+
import { a as readHeader, d as hash32x2Into, i as assertMinBodyLength, m as reduce, n as UnknownHashVariantError, o as writeHeader, r as assertBodyLength, t as SerializationError } from "../serialize-BQ2UzZqq.js";
|
|
2
2
|
import { a as assertUint32, i as assertProbability, n as assertPositiveFinite, r as assertPositiveInt, t as ParamError } from "../params-ChTRNxM9.js";
|
|
3
3
|
//#region src/blocked/blocked.ts
|
|
4
4
|
const TYPE = 2;
|
|
5
5
|
const SALT = Uint32Array.of(1203114875, 1150766481, 2284105051, 2729912477, 1884591559, 770785867, 2667333959, 1550580529);
|
|
6
|
-
const
|
|
7
|
-
h1lo: 0,
|
|
8
|
-
h1hi: 0,
|
|
9
|
-
h2lo: 0,
|
|
10
|
-
h2hi: 0
|
|
11
|
-
};
|
|
6
|
+
const scratch2 = /* @__PURE__ */ new Uint32Array(2);
|
|
12
7
|
/** Thrown when an operation requires two filters built with identical parameters. */
|
|
13
8
|
var BlockedBloomParamMismatchError = class extends Error {
|
|
14
9
|
/** Discriminates this error from other `Error`s. */
|
|
@@ -103,11 +98,15 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
103
98
|
* @returns The reconstructed filter.
|
|
104
99
|
*/
|
|
105
100
|
static fromBytes(bytes) {
|
|
106
|
-
const { body } = readHeader(bytes);
|
|
101
|
+
const { type, flags, body } = readHeader(bytes);
|
|
102
|
+
if (type !== TYPE) throw new SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
103
|
+
if ((flags & 15) !== 1) throw new UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
|
|
104
|
+
assertMinBodyLength(body.length, 12, "blocked");
|
|
107
105
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
108
106
|
const numBlocks = dv.getUint32(0, true);
|
|
109
107
|
const seed = dv.getUint32(4, true);
|
|
110
108
|
const n = dv.getUint32(8, true);
|
|
109
|
+
assertBodyLength(body.length, 12 + numBlocks * 32, "blocked");
|
|
111
110
|
const f = new BlockedBloomFilter({
|
|
112
111
|
bitsPerKey: 256,
|
|
113
112
|
capacity: numBlocks,
|
|
@@ -131,9 +130,9 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
131
130
|
dv.setUint32(8, this.#n, true);
|
|
132
131
|
body.set(lanes, 12);
|
|
133
132
|
return writeHeader({
|
|
134
|
-
version:
|
|
133
|
+
version: 2,
|
|
135
134
|
type: TYPE,
|
|
136
|
-
flags:
|
|
135
|
+
flags: 1
|
|
137
136
|
}, body);
|
|
138
137
|
}
|
|
139
138
|
/**
|
|
@@ -181,9 +180,9 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
181
180
|
}
|
|
182
181
|
};
|
|
183
182
|
function fillBlock(key, numBlocks, seed, outWords, outBits) {
|
|
184
|
-
|
|
185
|
-
const block = reduce(
|
|
186
|
-
const x =
|
|
183
|
+
hash32x2Into(key, seed, scratch2);
|
|
184
|
+
const block = reduce(scratch2[0] ?? 0, numBlocks);
|
|
185
|
+
const x = scratch2[1] ?? 0;
|
|
187
186
|
const base = block * 8;
|
|
188
187
|
for (let i = 0; i < 8; i++) {
|
|
189
188
|
outWords[i] = base + i;
|
package/dist/bloom/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-fa-pEUGq.cjs");
|
|
3
3
|
const require_params = require("../params-J8p3bKq5.cjs");
|
|
4
4
|
//#region src/core/bitset.ts
|
|
5
5
|
var BitSetRangeError = class extends RangeError {
|
|
@@ -94,17 +94,23 @@ var BloomFilter = class BloomFilter {
|
|
|
94
94
|
* @returns The reconstructed filter.
|
|
95
95
|
*/
|
|
96
96
|
static fromBytes(bytes) {
|
|
97
|
-
const { body } = require_serialize.readHeader(bytes);
|
|
97
|
+
const { type, flags, body } = require_serialize.readHeader(bytes);
|
|
98
|
+
if (type !== TYPE) throw new require_serialize.SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
99
|
+
if ((flags & 15) !== 1) throw new require_serialize.UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
|
|
100
|
+
require_serialize.assertMinBodyLength(body.length, 14, "bloom");
|
|
98
101
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
99
102
|
const m = dv.getUint32(0, true);
|
|
100
|
-
const k =
|
|
101
|
-
const seed = dv.getUint32(
|
|
103
|
+
const k = dv.getUint16(4, true);
|
|
104
|
+
const seed = dv.getUint32(6, true);
|
|
105
|
+
const n = dv.getUint32(10, true);
|
|
106
|
+
require_serialize.assertBodyLength(body.length, 14 + Math.ceil(m / 8), "bloom");
|
|
102
107
|
const f = new BloomFilter({
|
|
103
108
|
m,
|
|
104
109
|
k,
|
|
105
110
|
seed
|
|
106
111
|
});
|
|
107
|
-
f.#
|
|
112
|
+
f.#n = n;
|
|
113
|
+
f.#bits.bytes.set(body.subarray(14));
|
|
108
114
|
return f;
|
|
109
115
|
}
|
|
110
116
|
/**
|
|
@@ -159,16 +165,17 @@ var BloomFilter = class BloomFilter {
|
|
|
159
165
|
*/
|
|
160
166
|
toBytes() {
|
|
161
167
|
const payload = this.#bits.bytes;
|
|
162
|
-
const body = new Uint8Array(
|
|
168
|
+
const body = new Uint8Array(14 + payload.length);
|
|
163
169
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
164
170
|
dv.setUint32(0, this.#m, true);
|
|
165
|
-
|
|
166
|
-
dv.setUint32(
|
|
167
|
-
|
|
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);
|
|
168
175
|
return require_serialize.writeHeader({
|
|
169
|
-
version:
|
|
176
|
+
version: 2,
|
|
170
177
|
type: TYPE,
|
|
171
|
-
flags:
|
|
178
|
+
flags: 1
|
|
172
179
|
}, body);
|
|
173
180
|
}
|
|
174
181
|
/**
|
package/dist/bloom/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as readHeader, i as assertMinBodyLength, n as UnknownHashVariantError, o as writeHeader, p as probeInto, r as assertBodyLength, t as SerializationError } from "../serialize-BQ2UzZqq.js";
|
|
2
2
|
import { a as assertUint32, i as assertProbability, r as assertPositiveInt, t as ParamError } from "../params-ChTRNxM9.js";
|
|
3
3
|
//#region src/core/bitset.ts
|
|
4
4
|
var BitSetRangeError = class extends RangeError {
|
|
@@ -93,17 +93,23 @@ var BloomFilter = class BloomFilter {
|
|
|
93
93
|
* @returns The reconstructed filter.
|
|
94
94
|
*/
|
|
95
95
|
static fromBytes(bytes) {
|
|
96
|
-
const { body } = readHeader(bytes);
|
|
96
|
+
const { type, flags, body } = readHeader(bytes);
|
|
97
|
+
if (type !== TYPE) throw new SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
98
|
+
if ((flags & 15) !== 1) throw new UnknownHashVariantError(`unsupported hash variant ${String(flags & 15)}`);
|
|
99
|
+
assertMinBodyLength(body.length, 14, "bloom");
|
|
97
100
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
98
101
|
const m = dv.getUint32(0, true);
|
|
99
|
-
const k =
|
|
100
|
-
const seed = dv.getUint32(
|
|
102
|
+
const k = dv.getUint16(4, true);
|
|
103
|
+
const seed = dv.getUint32(6, true);
|
|
104
|
+
const n = dv.getUint32(10, true);
|
|
105
|
+
assertBodyLength(body.length, 14 + Math.ceil(m / 8), "bloom");
|
|
101
106
|
const f = new BloomFilter({
|
|
102
107
|
m,
|
|
103
108
|
k,
|
|
104
109
|
seed
|
|
105
110
|
});
|
|
106
|
-
f.#
|
|
111
|
+
f.#n = n;
|
|
112
|
+
f.#bits.bytes.set(body.subarray(14));
|
|
107
113
|
return f;
|
|
108
114
|
}
|
|
109
115
|
/**
|
|
@@ -158,16 +164,17 @@ var BloomFilter = class BloomFilter {
|
|
|
158
164
|
*/
|
|
159
165
|
toBytes() {
|
|
160
166
|
const payload = this.#bits.bytes;
|
|
161
|
-
const body = new Uint8Array(
|
|
167
|
+
const body = new Uint8Array(14 + payload.length);
|
|
162
168
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
163
169
|
dv.setUint32(0, this.#m, true);
|
|
164
|
-
|
|
165
|
-
dv.setUint32(
|
|
166
|
-
|
|
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);
|
|
167
174
|
return writeHeader({
|
|
168
|
-
version:
|
|
175
|
+
version: 2,
|
|
169
176
|
type: TYPE,
|
|
170
|
-
flags:
|
|
177
|
+
flags: 1
|
|
171
178
|
}, body);
|
|
172
179
|
}
|
|
173
180
|
/**
|
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-fa-pEUGq.cjs");
|
|
3
3
|
//#region src/fuse/fuse.ts
|
|
4
4
|
const ARITY = 3;
|
|
5
5
|
const TYPE_FUSE8 = 3;
|
|
@@ -9,71 +9,23 @@ var BinaryFuseBuildError = class extends Error {
|
|
|
9
9
|
/** Discriminates this error from other `Error`s. */
|
|
10
10
|
name = "BinaryFuseBuildError";
|
|
11
11
|
};
|
|
12
|
-
let RLO = 0;
|
|
13
|
-
let RHI = 0;
|
|
14
12
|
const scratchHash = {
|
|
15
13
|
h1lo: 0,
|
|
16
14
|
h1hi: 0,
|
|
17
15
|
h2lo: 0,
|
|
18
16
|
h2hi: 0
|
|
19
17
|
};
|
|
20
|
-
function mul64(alo, ahi, blo, bhi) {
|
|
21
|
-
const a0 = alo & 65535;
|
|
22
|
-
const a1 = alo >>> 16;
|
|
23
|
-
const a2 = ahi & 65535;
|
|
24
|
-
const a3 = ahi >>> 16;
|
|
25
|
-
const b0 = blo & 65535;
|
|
26
|
-
const b1 = blo >>> 16;
|
|
27
|
-
const b2 = bhi & 65535;
|
|
28
|
-
const b3 = bhi >>> 16;
|
|
29
|
-
let c0 = a0 * b0;
|
|
30
|
-
let c1 = c0 >>> 16;
|
|
31
|
-
c0 &= 65535;
|
|
32
|
-
c1 += a1 * b0;
|
|
33
|
-
let c2 = c1 >>> 16;
|
|
34
|
-
c1 &= 65535;
|
|
35
|
-
c1 += a0 * b1;
|
|
36
|
-
c2 += c1 >>> 16;
|
|
37
|
-
c1 &= 65535;
|
|
38
|
-
c2 += a2 * b0;
|
|
39
|
-
let c3 = c2 >>> 16;
|
|
40
|
-
c2 &= 65535;
|
|
41
|
-
c2 += a1 * b1;
|
|
42
|
-
c3 += c2 >>> 16;
|
|
43
|
-
c2 &= 65535;
|
|
44
|
-
c2 += a0 * b2;
|
|
45
|
-
c3 += c2 >>> 16;
|
|
46
|
-
c2 &= 65535;
|
|
47
|
-
c3 += a3 * b0 + a2 * b1 + a1 * b2 + a0 * b3;
|
|
48
|
-
c3 &= 65535;
|
|
49
|
-
RLO = (c1 << 16 | c0) >>> 0;
|
|
50
|
-
RHI = (c3 << 16 | c2) >>> 0;
|
|
51
|
-
}
|
|
52
|
-
function fmix64(lo, hi) {
|
|
53
|
-
let l = (lo ^ hi >>> 1) >>> 0;
|
|
54
|
-
let h = hi;
|
|
55
|
-
mul64(l, h, 3981806797, 4283543511);
|
|
56
|
-
l = RLO;
|
|
57
|
-
h = RHI;
|
|
58
|
-
l = (l ^ h >>> 1) >>> 0;
|
|
59
|
-
mul64(l, h, 444984403, 3301882366);
|
|
60
|
-
l = RLO;
|
|
61
|
-
h = RHI;
|
|
62
|
-
l = (l ^ h >>> 1) >>> 0;
|
|
63
|
-
RLO = l;
|
|
64
|
-
RHI = h;
|
|
65
|
-
}
|
|
66
18
|
function mixSeed(lo, hi, seed) {
|
|
67
19
|
const l = (lo >>> 0) + (seed >>> 0);
|
|
68
20
|
const carry = l > 4294967295 ? 1 : 0;
|
|
69
|
-
fmix64(l >>> 0, hi + carry >>> 0);
|
|
21
|
+
require_serialize.fmix64(l >>> 0, hi + carry >>> 0);
|
|
70
22
|
}
|
|
71
23
|
function mulhi64(lo, hi, n) {
|
|
72
|
-
mul64(hi, 0, n, 0);
|
|
73
|
-
const aLo = RLO;
|
|
74
|
-
const aHi = RHI;
|
|
75
|
-
mul64(lo, 0, n, 0);
|
|
76
|
-
const bHi = RHI;
|
|
24
|
+
require_serialize.mul64(hi, 0, n, 0);
|
|
25
|
+
const aLo = require_serialize.RLO;
|
|
26
|
+
const aHi = require_serialize.RHI;
|
|
27
|
+
require_serialize.mul64(lo, 0, n, 0);
|
|
28
|
+
const bHi = require_serialize.RHI;
|
|
77
29
|
return aHi + ((aLo >>> 0) + (bHi >>> 0) > 4294967295 ? 1 : 0) >>> 0;
|
|
78
30
|
}
|
|
79
31
|
function positionsInto(mlo, mhi, seg, segMask, segCountLen, out) {
|
|
@@ -130,8 +82,8 @@ function buildFingerprints(fp, hashes, params, maxAttempts = 100) {
|
|
|
130
82
|
xorHi.fill(0);
|
|
131
83
|
for (let i = 0; i < size; i++) {
|
|
132
84
|
mixSeed(hashes[2 * i] ?? 0, hashes[2 * i + 1] ?? 0, seed);
|
|
133
|
-
const mlo = RLO;
|
|
134
|
-
const mhi = RHI;
|
|
85
|
+
const mlo = require_serialize.RLO;
|
|
86
|
+
const mhi = require_serialize.RHI;
|
|
135
87
|
positionsInto(mlo, mhi, seg, segMask, segCountLen, pos);
|
|
136
88
|
for (let j = 0; j < 3; j++) {
|
|
137
89
|
const p = pos[j] ?? 0;
|
|
@@ -211,13 +163,16 @@ function buildState(keys, alloc) {
|
|
|
211
163
|
function fuseStateFromBytes(bytes, expectedType) {
|
|
212
164
|
const { type, body } = require_serialize.readHeader(bytes);
|
|
213
165
|
if (type !== expectedType) throw new require_serialize.SerializationError(`expected AMQF type ${String(expectedType)}, got ${String(type)}`);
|
|
166
|
+
require_serialize.assertMinBodyLength(body.length, 16, "fuse");
|
|
214
167
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
215
168
|
const seed = dv.getUint32(0, true);
|
|
216
169
|
const seg = dv.getUint32(4, true);
|
|
217
170
|
const segCountLen = dv.getUint32(8, true);
|
|
218
171
|
const size = dv.getUint32(12, true);
|
|
219
|
-
const laneBytes = body.subarray(16);
|
|
220
172
|
const bpe = expectedType === TYPE_FUSE8 ? 1 : 2;
|
|
173
|
+
const expectedArrayLength = size === 0 ? 0 : segCountLen + 2 * seg;
|
|
174
|
+
require_serialize.assertBodyLength(body.length, 16 + expectedArrayLength * bpe, "fuse");
|
|
175
|
+
const laneBytes = body.subarray(16);
|
|
221
176
|
const arrayLength = laneBytes.length / bpe;
|
|
222
177
|
const fp = bpe === 1 ? new Uint8Array(arrayLength) : new Uint16Array(arrayLength);
|
|
223
178
|
new Uint8Array(fp.buffer).set(laneBytes);
|
|
@@ -276,7 +231,7 @@ var BinaryFuse = class {
|
|
|
276
231
|
dv.setUint32(12, this.#size, true);
|
|
277
232
|
body.set(laneBytes, 16);
|
|
278
233
|
return require_serialize.writeHeader({
|
|
279
|
-
version:
|
|
234
|
+
version: 2,
|
|
280
235
|
type: this.#fp.BYTES_PER_ELEMENT === 1 ? TYPE_FUSE8 : TYPE_FUSE16,
|
|
281
236
|
flags: 0
|
|
282
237
|
}, body);
|
|
@@ -291,8 +246,8 @@ var BinaryFuse = class {
|
|
|
291
246
|
if (this.#fp.length === 0) return false;
|
|
292
247
|
require_serialize.hash128KeyInto(key, 0, scratchHash);
|
|
293
248
|
mixSeed(scratchHash.h1lo >>> 0, scratchHash.h1hi >>> 0, this.#seed);
|
|
294
|
-
const mlo = RLO;
|
|
295
|
-
const mhi = RHI;
|
|
249
|
+
const mlo = require_serialize.RLO;
|
|
250
|
+
const mhi = require_serialize.RHI;
|
|
296
251
|
positionsInto(mlo, mhi, this.#seg, this.#segMask, this.#segCountLen, this.#pos);
|
|
297
252
|
const mask = fpMask(this.#fp);
|
|
298
253
|
const p0 = this.#pos[0] ?? 0;
|
package/dist/fuse/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as
|
|
1
|
+
import { a as readHeader, c as RLO, f as mul64, i as assertMinBodyLength, l as fmix64, o as writeHeader, r as assertBodyLength, s as RHI, t as SerializationError, u as hash128KeyInto } from "../serialize-BQ2UzZqq.js";
|
|
2
2
|
//#region src/fuse/fuse.ts
|
|
3
3
|
const ARITY = 3;
|
|
4
4
|
const TYPE_FUSE8 = 3;
|
|
@@ -8,60 +8,12 @@ var BinaryFuseBuildError = class extends Error {
|
|
|
8
8
|
/** Discriminates this error from other `Error`s. */
|
|
9
9
|
name = "BinaryFuseBuildError";
|
|
10
10
|
};
|
|
11
|
-
let RLO = 0;
|
|
12
|
-
let RHI = 0;
|
|
13
11
|
const scratchHash = {
|
|
14
12
|
h1lo: 0,
|
|
15
13
|
h1hi: 0,
|
|
16
14
|
h2lo: 0,
|
|
17
15
|
h2hi: 0
|
|
18
16
|
};
|
|
19
|
-
function mul64(alo, ahi, blo, bhi) {
|
|
20
|
-
const a0 = alo & 65535;
|
|
21
|
-
const a1 = alo >>> 16;
|
|
22
|
-
const a2 = ahi & 65535;
|
|
23
|
-
const a3 = ahi >>> 16;
|
|
24
|
-
const b0 = blo & 65535;
|
|
25
|
-
const b1 = blo >>> 16;
|
|
26
|
-
const b2 = bhi & 65535;
|
|
27
|
-
const b3 = bhi >>> 16;
|
|
28
|
-
let c0 = a0 * b0;
|
|
29
|
-
let c1 = c0 >>> 16;
|
|
30
|
-
c0 &= 65535;
|
|
31
|
-
c1 += a1 * b0;
|
|
32
|
-
let c2 = c1 >>> 16;
|
|
33
|
-
c1 &= 65535;
|
|
34
|
-
c1 += a0 * b1;
|
|
35
|
-
c2 += c1 >>> 16;
|
|
36
|
-
c1 &= 65535;
|
|
37
|
-
c2 += a2 * b0;
|
|
38
|
-
let c3 = c2 >>> 16;
|
|
39
|
-
c2 &= 65535;
|
|
40
|
-
c2 += a1 * b1;
|
|
41
|
-
c3 += c2 >>> 16;
|
|
42
|
-
c2 &= 65535;
|
|
43
|
-
c2 += a0 * b2;
|
|
44
|
-
c3 += c2 >>> 16;
|
|
45
|
-
c2 &= 65535;
|
|
46
|
-
c3 += a3 * b0 + a2 * b1 + a1 * b2 + a0 * b3;
|
|
47
|
-
c3 &= 65535;
|
|
48
|
-
RLO = (c1 << 16 | c0) >>> 0;
|
|
49
|
-
RHI = (c3 << 16 | c2) >>> 0;
|
|
50
|
-
}
|
|
51
|
-
function fmix64(lo, hi) {
|
|
52
|
-
let l = (lo ^ hi >>> 1) >>> 0;
|
|
53
|
-
let h = hi;
|
|
54
|
-
mul64(l, h, 3981806797, 4283543511);
|
|
55
|
-
l = RLO;
|
|
56
|
-
h = RHI;
|
|
57
|
-
l = (l ^ h >>> 1) >>> 0;
|
|
58
|
-
mul64(l, h, 444984403, 3301882366);
|
|
59
|
-
l = RLO;
|
|
60
|
-
h = RHI;
|
|
61
|
-
l = (l ^ h >>> 1) >>> 0;
|
|
62
|
-
RLO = l;
|
|
63
|
-
RHI = h;
|
|
64
|
-
}
|
|
65
17
|
function mixSeed(lo, hi, seed) {
|
|
66
18
|
const l = (lo >>> 0) + (seed >>> 0);
|
|
67
19
|
const carry = l > 4294967295 ? 1 : 0;
|
|
@@ -210,13 +162,16 @@ function buildState(keys, alloc) {
|
|
|
210
162
|
function fuseStateFromBytes(bytes, expectedType) {
|
|
211
163
|
const { type, body } = readHeader(bytes);
|
|
212
164
|
if (type !== expectedType) throw new SerializationError(`expected AMQF type ${String(expectedType)}, got ${String(type)}`);
|
|
165
|
+
assertMinBodyLength(body.length, 16, "fuse");
|
|
213
166
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
214
167
|
const seed = dv.getUint32(0, true);
|
|
215
168
|
const seg = dv.getUint32(4, true);
|
|
216
169
|
const segCountLen = dv.getUint32(8, true);
|
|
217
170
|
const size = dv.getUint32(12, true);
|
|
218
|
-
const laneBytes = body.subarray(16);
|
|
219
171
|
const bpe = expectedType === TYPE_FUSE8 ? 1 : 2;
|
|
172
|
+
const expectedArrayLength = size === 0 ? 0 : segCountLen + 2 * seg;
|
|
173
|
+
assertBodyLength(body.length, 16 + expectedArrayLength * bpe, "fuse");
|
|
174
|
+
const laneBytes = body.subarray(16);
|
|
220
175
|
const arrayLength = laneBytes.length / bpe;
|
|
221
176
|
const fp = bpe === 1 ? new Uint8Array(arrayLength) : new Uint16Array(arrayLength);
|
|
222
177
|
new Uint8Array(fp.buffer).set(laneBytes);
|
|
@@ -275,7 +230,7 @@ var BinaryFuse = class {
|
|
|
275
230
|
dv.setUint32(12, this.#size, true);
|
|
276
231
|
body.set(laneBytes, 16);
|
|
277
232
|
return writeHeader({
|
|
278
|
-
version:
|
|
233
|
+
version: 2,
|
|
279
234
|
type: this.#fp.BYTES_PER_ELEMENT === 1 ? TYPE_FUSE8 : TYPE_FUSE16,
|
|
280
235
|
flags: 0
|
|
281
236
|
}, body);
|
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED
|
@@ -81,6 +81,37 @@ function fmix64(lo, hi) {
|
|
|
81
81
|
RLO = l;
|
|
82
82
|
RHI = h;
|
|
83
83
|
}
|
|
84
|
+
function murmur32(bytes, seed, len = bytes.length) {
|
|
85
|
+
let h = seed >>> 0;
|
|
86
|
+
const n = len & -4;
|
|
87
|
+
for (let i = 0; i < n; i += 4) {
|
|
88
|
+
let k = ((bytes[i] ?? 0) | (bytes[i + 1] ?? 0) << 8 | (bytes[i + 2] ?? 0) << 16 | (bytes[i + 3] ?? 0) << 24) >>> 0;
|
|
89
|
+
k = Math.imul(k, 3432918353);
|
|
90
|
+
k = (k << 15 | k >>> 17) >>> 0;
|
|
91
|
+
k = Math.imul(k, 461845907);
|
|
92
|
+
h = (h ^ k) >>> 0;
|
|
93
|
+
h = (h << 13 | h >>> 19) >>> 0;
|
|
94
|
+
h = Math.imul(h, 5) + 3864292196 >>> 0;
|
|
95
|
+
}
|
|
96
|
+
let k1 = 0;
|
|
97
|
+
const rem = len & 3;
|
|
98
|
+
if (rem >= 3) k1 ^= (bytes[n + 2] ?? 0) << 16;
|
|
99
|
+
if (rem >= 2) k1 ^= (bytes[n + 1] ?? 0) << 8;
|
|
100
|
+
if (rem >= 1) {
|
|
101
|
+
k1 ^= bytes[n] ?? 0;
|
|
102
|
+
k1 = Math.imul(k1 >>> 0, 3432918353);
|
|
103
|
+
k1 = (k1 << 15 | k1 >>> 17) >>> 0;
|
|
104
|
+
k1 = Math.imul(k1, 461845907);
|
|
105
|
+
h = (h ^ k1) >>> 0;
|
|
106
|
+
}
|
|
107
|
+
h = (h ^ len) >>> 0;
|
|
108
|
+
h ^= h >>> 16;
|
|
109
|
+
h = Math.imul(h, 2246822507);
|
|
110
|
+
h ^= h >>> 13;
|
|
111
|
+
h = Math.imul(h, 3266489909);
|
|
112
|
+
h ^= h >>> 16;
|
|
113
|
+
return h >>> 0;
|
|
114
|
+
}
|
|
84
115
|
function computeLanes(bytes, seed, len) {
|
|
85
116
|
const nblocks = len >>> 4;
|
|
86
117
|
let h1lo = seed >>> 0;
|
|
@@ -186,16 +217,22 @@ function computeLanes(bytes, seed, len) {
|
|
|
186
217
|
}
|
|
187
218
|
const keyEncoder = new TextEncoder();
|
|
188
219
|
let keyBuf = /* @__PURE__ */ new Uint8Array(256);
|
|
189
|
-
|
|
220
|
+
let encBytes = keyBuf;
|
|
221
|
+
let encLen = 0;
|
|
222
|
+
function encodeKey(key) {
|
|
190
223
|
if (typeof key === "string") {
|
|
191
224
|
const cap = key.length * 3;
|
|
192
225
|
if (keyBuf.length < cap) keyBuf = new Uint8Array(cap);
|
|
193
|
-
|
|
194
|
-
|
|
226
|
+
encLen = keyEncoder.encodeInto(key, keyBuf).written;
|
|
227
|
+
encBytes = keyBuf;
|
|
195
228
|
return;
|
|
196
229
|
}
|
|
197
|
-
|
|
198
|
-
|
|
230
|
+
encBytes = normalize(key);
|
|
231
|
+
encLen = encBytes.length;
|
|
232
|
+
}
|
|
233
|
+
function keyToLanes(key, seed) {
|
|
234
|
+
encodeKey(key);
|
|
235
|
+
computeLanes(encBytes, seed, encLen);
|
|
199
236
|
}
|
|
200
237
|
function hash128KeyInto(key, seed, out) {
|
|
201
238
|
keyToLanes(key, seed);
|
|
@@ -221,10 +258,16 @@ function reduce(x, range) {
|
|
|
221
258
|
* Kirsch-Mitzenmacher enhanced double hashing `g_i = h1 + i*h2 + i^2`
|
|
222
259
|
* (the RocksDB `+i^2` fix), reduced into range with Lemire multiply-shift.
|
|
223
260
|
*/
|
|
261
|
+
const SEED2 = 2654435761;
|
|
262
|
+
function hash32x2Into(key, seed, out) {
|
|
263
|
+
encodeKey(key);
|
|
264
|
+
out[0] = murmur32(encBytes, seed, encLen);
|
|
265
|
+
out[1] = murmur32(encBytes, (seed ^ SEED2) >>> 0, encLen);
|
|
266
|
+
}
|
|
224
267
|
function probeInto(key, count, range, seed, out) {
|
|
225
|
-
|
|
226
|
-
const a = (
|
|
227
|
-
const b = (
|
|
268
|
+
encodeKey(key);
|
|
269
|
+
const a = murmur32(encBytes, seed, encLen);
|
|
270
|
+
const b = murmur32(encBytes, (seed ^ SEED2) >>> 0, encLen);
|
|
228
271
|
for (let i = 0; i < count; i++) out[i] = reduce(a + Math.imul(i, b) + i * i >>> 0, range);
|
|
229
272
|
}
|
|
230
273
|
//#endregion
|
|
@@ -274,14 +317,31 @@ var BadMagicError = class extends SerializationError {
|
|
|
274
317
|
var UnknownVersionError = class extends SerializationError {
|
|
275
318
|
name = "UnknownVersionError";
|
|
276
319
|
};
|
|
320
|
+
var UnknownHashVariantError = class extends SerializationError {
|
|
321
|
+
name = "UnknownHashVariantError";
|
|
322
|
+
};
|
|
277
323
|
var ChecksumError = class extends SerializationError {
|
|
278
324
|
name = "ChecksumError";
|
|
279
325
|
};
|
|
326
|
+
/**
|
|
327
|
+
* Asserts a frame body is long enough to hold its fixed params block, so the
|
|
328
|
+
* params can be read without running off the end.
|
|
329
|
+
*/
|
|
330
|
+
function assertMinBodyLength(actual, min, context) {
|
|
331
|
+
if (actual < min) throw new TruncatedError(`${context}: body of ${String(actual)} bytes is shorter than the ${String(min)}-byte params block`);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Asserts a frame body is exactly the length its declared params imply, so a
|
|
335
|
+
* hostile or truncated frame is rejected before any backing store is allocated.
|
|
336
|
+
*/
|
|
337
|
+
function assertBodyLength(actual, expected, context) {
|
|
338
|
+
if (actual !== expected) throw new TruncatedError(`${context}: body of ${String(actual)} bytes does not match the declared params (expected ${String(expected)})`);
|
|
339
|
+
}
|
|
280
340
|
function readHeader(frame) {
|
|
281
341
|
if (frame.length < 12) throw new TruncatedError(`frame of ${String(frame.length)} bytes is shorter than the minimum ${String(12)}`);
|
|
282
342
|
if (frame[0] !== 65 || frame[1] !== 77 || frame[2] !== 81 || frame[3] !== 70) throw new BadMagicError("frame does not start with the AMQF magic");
|
|
283
343
|
const version = frame[4] ?? 0;
|
|
284
|
-
if (version !==
|
|
344
|
+
if (version !== 2) throw new UnknownVersionError(`unsupported format version ${String(version)}`);
|
|
285
345
|
if (new DataView(frame.buffer, frame.byteOffset, frame.byteLength).getUint32(frame.length - TRAILER_SIZE, true) !== crc32(frame.subarray(0, frame.length - TRAILER_SIZE))) throw new ChecksumError("frame CRC32 does not match its contents");
|
|
286
346
|
return {
|
|
287
347
|
version,
|
|
@@ -291,39 +351,4 @@ function readHeader(frame) {
|
|
|
291
351
|
};
|
|
292
352
|
}
|
|
293
353
|
//#endregion
|
|
294
|
-
|
|
295
|
-
enumerable: true,
|
|
296
|
-
get: function() {
|
|
297
|
-
return SerializationError;
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
Object.defineProperty(exports, "hash128KeyInto", {
|
|
301
|
-
enumerable: true,
|
|
302
|
-
get: function() {
|
|
303
|
-
return hash128KeyInto;
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
Object.defineProperty(exports, "probeInto", {
|
|
307
|
-
enumerable: true,
|
|
308
|
-
get: function() {
|
|
309
|
-
return probeInto;
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
Object.defineProperty(exports, "readHeader", {
|
|
313
|
-
enumerable: true,
|
|
314
|
-
get: function() {
|
|
315
|
-
return readHeader;
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
Object.defineProperty(exports, "reduce", {
|
|
319
|
-
enumerable: true,
|
|
320
|
-
get: function() {
|
|
321
|
-
return reduce;
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
Object.defineProperty(exports, "writeHeader", {
|
|
325
|
-
enumerable: true,
|
|
326
|
-
get: function() {
|
|
327
|
-
return writeHeader;
|
|
328
|
-
}
|
|
329
|
-
});
|
|
354
|
+
export { readHeader as a, RLO as c, hash32x2Into as d, mul64 as f, assertMinBodyLength as i, fmix64 as l, reduce as m, UnknownHashVariantError as n, writeHeader as o, probeInto as p, assertBodyLength as r, RHI as s, SerializationError as t, hash128KeyInto as u };
|
|
@@ -81,6 +81,37 @@ function fmix64(lo, hi) {
|
|
|
81
81
|
RLO = l;
|
|
82
82
|
RHI = h;
|
|
83
83
|
}
|
|
84
|
+
function murmur32(bytes, seed, len = bytes.length) {
|
|
85
|
+
let h = seed >>> 0;
|
|
86
|
+
const n = len & -4;
|
|
87
|
+
for (let i = 0; i < n; i += 4) {
|
|
88
|
+
let k = ((bytes[i] ?? 0) | (bytes[i + 1] ?? 0) << 8 | (bytes[i + 2] ?? 0) << 16 | (bytes[i + 3] ?? 0) << 24) >>> 0;
|
|
89
|
+
k = Math.imul(k, 3432918353);
|
|
90
|
+
k = (k << 15 | k >>> 17) >>> 0;
|
|
91
|
+
k = Math.imul(k, 461845907);
|
|
92
|
+
h = (h ^ k) >>> 0;
|
|
93
|
+
h = (h << 13 | h >>> 19) >>> 0;
|
|
94
|
+
h = Math.imul(h, 5) + 3864292196 >>> 0;
|
|
95
|
+
}
|
|
96
|
+
let k1 = 0;
|
|
97
|
+
const rem = len & 3;
|
|
98
|
+
if (rem >= 3) k1 ^= (bytes[n + 2] ?? 0) << 16;
|
|
99
|
+
if (rem >= 2) k1 ^= (bytes[n + 1] ?? 0) << 8;
|
|
100
|
+
if (rem >= 1) {
|
|
101
|
+
k1 ^= bytes[n] ?? 0;
|
|
102
|
+
k1 = Math.imul(k1 >>> 0, 3432918353);
|
|
103
|
+
k1 = (k1 << 15 | k1 >>> 17) >>> 0;
|
|
104
|
+
k1 = Math.imul(k1, 461845907);
|
|
105
|
+
h = (h ^ k1) >>> 0;
|
|
106
|
+
}
|
|
107
|
+
h = (h ^ len) >>> 0;
|
|
108
|
+
h ^= h >>> 16;
|
|
109
|
+
h = Math.imul(h, 2246822507);
|
|
110
|
+
h ^= h >>> 13;
|
|
111
|
+
h = Math.imul(h, 3266489909);
|
|
112
|
+
h ^= h >>> 16;
|
|
113
|
+
return h >>> 0;
|
|
114
|
+
}
|
|
84
115
|
function computeLanes(bytes, seed, len) {
|
|
85
116
|
const nblocks = len >>> 4;
|
|
86
117
|
let h1lo = seed >>> 0;
|
|
@@ -186,16 +217,22 @@ function computeLanes(bytes, seed, len) {
|
|
|
186
217
|
}
|
|
187
218
|
const keyEncoder = new TextEncoder();
|
|
188
219
|
let keyBuf = /* @__PURE__ */ new Uint8Array(256);
|
|
189
|
-
|
|
220
|
+
let encBytes = keyBuf;
|
|
221
|
+
let encLen = 0;
|
|
222
|
+
function encodeKey(key) {
|
|
190
223
|
if (typeof key === "string") {
|
|
191
224
|
const cap = key.length * 3;
|
|
192
225
|
if (keyBuf.length < cap) keyBuf = new Uint8Array(cap);
|
|
193
|
-
|
|
194
|
-
|
|
226
|
+
encLen = keyEncoder.encodeInto(key, keyBuf).written;
|
|
227
|
+
encBytes = keyBuf;
|
|
195
228
|
return;
|
|
196
229
|
}
|
|
197
|
-
|
|
198
|
-
|
|
230
|
+
encBytes = normalize(key);
|
|
231
|
+
encLen = encBytes.length;
|
|
232
|
+
}
|
|
233
|
+
function keyToLanes(key, seed) {
|
|
234
|
+
encodeKey(key);
|
|
235
|
+
computeLanes(encBytes, seed, encLen);
|
|
199
236
|
}
|
|
200
237
|
function hash128KeyInto(key, seed, out) {
|
|
201
238
|
keyToLanes(key, seed);
|
|
@@ -221,10 +258,16 @@ function reduce(x, range) {
|
|
|
221
258
|
* Kirsch-Mitzenmacher enhanced double hashing `g_i = h1 + i*h2 + i^2`
|
|
222
259
|
* (the RocksDB `+i^2` fix), reduced into range with Lemire multiply-shift.
|
|
223
260
|
*/
|
|
261
|
+
const SEED2 = 2654435761;
|
|
262
|
+
function hash32x2Into(key, seed, out) {
|
|
263
|
+
encodeKey(key);
|
|
264
|
+
out[0] = murmur32(encBytes, seed, encLen);
|
|
265
|
+
out[1] = murmur32(encBytes, (seed ^ SEED2) >>> 0, encLen);
|
|
266
|
+
}
|
|
224
267
|
function probeInto(key, count, range, seed, out) {
|
|
225
|
-
|
|
226
|
-
const a = (
|
|
227
|
-
const b = (
|
|
268
|
+
encodeKey(key);
|
|
269
|
+
const a = murmur32(encBytes, seed, encLen);
|
|
270
|
+
const b = murmur32(encBytes, (seed ^ SEED2) >>> 0, encLen);
|
|
228
271
|
for (let i = 0; i < count; i++) out[i] = reduce(a + Math.imul(i, b) + i * i >>> 0, range);
|
|
229
272
|
}
|
|
230
273
|
//#endregion
|
|
@@ -274,14 +317,31 @@ var BadMagicError = class extends SerializationError {
|
|
|
274
317
|
var UnknownVersionError = class extends SerializationError {
|
|
275
318
|
name = "UnknownVersionError";
|
|
276
319
|
};
|
|
320
|
+
var UnknownHashVariantError = class extends SerializationError {
|
|
321
|
+
name = "UnknownHashVariantError";
|
|
322
|
+
};
|
|
277
323
|
var ChecksumError = class extends SerializationError {
|
|
278
324
|
name = "ChecksumError";
|
|
279
325
|
};
|
|
326
|
+
/**
|
|
327
|
+
* Asserts a frame body is long enough to hold its fixed params block, so the
|
|
328
|
+
* params can be read without running off the end.
|
|
329
|
+
*/
|
|
330
|
+
function assertMinBodyLength(actual, min, context) {
|
|
331
|
+
if (actual < min) throw new TruncatedError(`${context}: body of ${String(actual)} bytes is shorter than the ${String(min)}-byte params block`);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Asserts a frame body is exactly the length its declared params imply, so a
|
|
335
|
+
* hostile or truncated frame is rejected before any backing store is allocated.
|
|
336
|
+
*/
|
|
337
|
+
function assertBodyLength(actual, expected, context) {
|
|
338
|
+
if (actual !== expected) throw new TruncatedError(`${context}: body of ${String(actual)} bytes does not match the declared params (expected ${String(expected)})`);
|
|
339
|
+
}
|
|
280
340
|
function readHeader(frame) {
|
|
281
341
|
if (frame.length < 12) throw new TruncatedError(`frame of ${String(frame.length)} bytes is shorter than the minimum ${String(12)}`);
|
|
282
342
|
if (frame[0] !== 65 || frame[1] !== 77 || frame[2] !== 81 || frame[3] !== 70) throw new BadMagicError("frame does not start with the AMQF magic");
|
|
283
343
|
const version = frame[4] ?? 0;
|
|
284
|
-
if (version !==
|
|
344
|
+
if (version !== 2) throw new UnknownVersionError(`unsupported format version ${String(version)}`);
|
|
285
345
|
if (new DataView(frame.buffer, frame.byteOffset, frame.byteLength).getUint32(frame.length - TRAILER_SIZE, true) !== crc32(frame.subarray(0, frame.length - TRAILER_SIZE))) throw new ChecksumError("frame CRC32 does not match its contents");
|
|
286
346
|
return {
|
|
287
347
|
version,
|
|
@@ -291,4 +351,87 @@ function readHeader(frame) {
|
|
|
291
351
|
};
|
|
292
352
|
}
|
|
293
353
|
//#endregion
|
|
294
|
-
|
|
354
|
+
Object.defineProperty(exports, "RHI", {
|
|
355
|
+
enumerable: true,
|
|
356
|
+
get: function() {
|
|
357
|
+
return RHI;
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
Object.defineProperty(exports, "RLO", {
|
|
361
|
+
enumerable: true,
|
|
362
|
+
get: function() {
|
|
363
|
+
return RLO;
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
Object.defineProperty(exports, "SerializationError", {
|
|
367
|
+
enumerable: true,
|
|
368
|
+
get: function() {
|
|
369
|
+
return SerializationError;
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
Object.defineProperty(exports, "UnknownHashVariantError", {
|
|
373
|
+
enumerable: true,
|
|
374
|
+
get: function() {
|
|
375
|
+
return UnknownHashVariantError;
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
Object.defineProperty(exports, "assertBodyLength", {
|
|
379
|
+
enumerable: true,
|
|
380
|
+
get: function() {
|
|
381
|
+
return assertBodyLength;
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
Object.defineProperty(exports, "assertMinBodyLength", {
|
|
385
|
+
enumerable: true,
|
|
386
|
+
get: function() {
|
|
387
|
+
return assertMinBodyLength;
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
Object.defineProperty(exports, "fmix64", {
|
|
391
|
+
enumerable: true,
|
|
392
|
+
get: function() {
|
|
393
|
+
return fmix64;
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
Object.defineProperty(exports, "hash128KeyInto", {
|
|
397
|
+
enumerable: true,
|
|
398
|
+
get: function() {
|
|
399
|
+
return hash128KeyInto;
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
Object.defineProperty(exports, "hash32x2Into", {
|
|
403
|
+
enumerable: true,
|
|
404
|
+
get: function() {
|
|
405
|
+
return hash32x2Into;
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
Object.defineProperty(exports, "mul64", {
|
|
409
|
+
enumerable: true,
|
|
410
|
+
get: function() {
|
|
411
|
+
return mul64;
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
Object.defineProperty(exports, "probeInto", {
|
|
415
|
+
enumerable: true,
|
|
416
|
+
get: function() {
|
|
417
|
+
return probeInto;
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
Object.defineProperty(exports, "readHeader", {
|
|
421
|
+
enumerable: true,
|
|
422
|
+
get: function() {
|
|
423
|
+
return readHeader;
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
Object.defineProperty(exports, "reduce", {
|
|
427
|
+
enumerable: true,
|
|
428
|
+
get: function() {
|
|
429
|
+
return reduce;
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
Object.defineProperty(exports, "writeHeader", {
|
|
433
|
+
enumerable: true,
|
|
434
|
+
get: function() {
|
|
435
|
+
return writeHeader;
|
|
436
|
+
}
|
|
437
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "distillate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Probabilistic data structures for JavaScript. Space-efficient, approximate-membership filters (Bloom, Blocked Bloom, Binary Fuse) with tunable error and a portable binary format; zero dependencies, universal.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bloom-filter",
|