distillate 0.2.0 → 0.4.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/dist/blocked/index.cjs +26 -3
- package/dist/blocked/index.d.cts +11 -0
- package/dist/blocked/index.d.ts +11 -0
- package/dist/blocked/index.js +26 -3
- package/dist/bloom/index.cjs +42 -10
- package/dist/bloom/index.d.cts +16 -0
- package/dist/bloom/index.d.ts +16 -0
- package/dist/bloom/index.js +42 -10
- package/dist/fuse/index.cjs +6 -3
- package/dist/fuse/index.js +6 -3
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{serialize-CHHDM4TQ.cjs → serialize-DPdiySxq.cjs} +27 -1
- package/dist/{serialize-5XQ5y_R-.js → serialize-DkkBKDT0.js} +16 -2
- package/package.json +1 -1
package/dist/blocked/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-DPdiySxq.cjs");
|
|
3
3
|
const require_params = require("../params-J8p3bKq5.cjs");
|
|
4
4
|
//#region src/blocked/blocked.ts
|
|
5
5
|
const TYPE = 2;
|
|
@@ -77,6 +77,26 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
77
77
|
get bitsPerKey() {
|
|
78
78
|
return this.#numBlocks * 256 / this.#n;
|
|
79
79
|
}
|
|
80
|
+
/** Number of bits currently set across all lanes. */
|
|
81
|
+
get length() {
|
|
82
|
+
let bits = 0;
|
|
83
|
+
for (let w of this.#lanes) while (w) {
|
|
84
|
+
w &= w - 1;
|
|
85
|
+
bits++;
|
|
86
|
+
}
|
|
87
|
+
return bits;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Estimates the current false-positive rate from the actual fill,
|
|
91
|
+
* `(length / totalBits) ** 8`. A split-block query checks exactly 8 lane-bits,
|
|
92
|
+
* so the exponent is 8 rather than a classic probe count `k`. This reflects
|
|
93
|
+
* how full the filter is right now, not the design target.
|
|
94
|
+
*
|
|
95
|
+
* @returns The estimated false-positive rate, `0` for an empty filter.
|
|
96
|
+
*/
|
|
97
|
+
rate() {
|
|
98
|
+
return (this.length / (this.#numBlocks * 256)) ** 8;
|
|
99
|
+
}
|
|
80
100
|
/**
|
|
81
101
|
* Restores a filter from its {@link BlockedBloomFilter.toBytes} serialization.
|
|
82
102
|
*
|
|
@@ -84,11 +104,14 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
84
104
|
* @returns The reconstructed filter.
|
|
85
105
|
*/
|
|
86
106
|
static fromBytes(bytes) {
|
|
87
|
-
const { body } = require_serialize.readHeader(bytes);
|
|
107
|
+
const { type, body } = require_serialize.readHeader(bytes);
|
|
108
|
+
if (type !== TYPE) throw new require_serialize.SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
109
|
+
require_serialize.assertMinBodyLength(body.length, 12, "blocked");
|
|
88
110
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
89
111
|
const numBlocks = dv.getUint32(0, true);
|
|
90
112
|
const seed = dv.getUint32(4, true);
|
|
91
113
|
const n = dv.getUint32(8, true);
|
|
114
|
+
require_serialize.assertBodyLength(body.length, 12 + numBlocks * 32, "blocked");
|
|
92
115
|
const f = new BlockedBloomFilter({
|
|
93
116
|
bitsPerKey: 256,
|
|
94
117
|
capacity: numBlocks,
|
|
@@ -112,7 +135,7 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
112
135
|
dv.setUint32(8, this.#n, true);
|
|
113
136
|
body.set(lanes, 12);
|
|
114
137
|
return require_serialize.writeHeader({
|
|
115
|
-
version:
|
|
138
|
+
version: 2,
|
|
116
139
|
type: TYPE,
|
|
117
140
|
flags: 0
|
|
118
141
|
}, body);
|
package/dist/blocked/index.d.cts
CHANGED
|
@@ -43,6 +43,17 @@ declare class BlockedBloomFilter {
|
|
|
43
43
|
constructor({ bitsPerKey, capacity, seed }: BlockedBloomParams);
|
|
44
44
|
/** Actual bits allocated per key (`total bits / capacity`). */
|
|
45
45
|
get bitsPerKey(): number;
|
|
46
|
+
/** Number of bits currently set across all lanes. */
|
|
47
|
+
get length(): number;
|
|
48
|
+
/**
|
|
49
|
+
* Estimates the current false-positive rate from the actual fill,
|
|
50
|
+
* `(length / totalBits) ** 8`. A split-block query checks exactly 8 lane-bits,
|
|
51
|
+
* so the exponent is 8 rather than a classic probe count `k`. This reflects
|
|
52
|
+
* how full the filter is right now, not the design target.
|
|
53
|
+
*
|
|
54
|
+
* @returns The estimated false-positive rate, `0` for an empty filter.
|
|
55
|
+
*/
|
|
56
|
+
rate(): number;
|
|
46
57
|
/**
|
|
47
58
|
* Restores a filter from its {@link BlockedBloomFilter.toBytes} serialization.
|
|
48
59
|
*
|
package/dist/blocked/index.d.ts
CHANGED
|
@@ -43,6 +43,17 @@ declare class BlockedBloomFilter {
|
|
|
43
43
|
constructor({ bitsPerKey, capacity, seed }: BlockedBloomParams);
|
|
44
44
|
/** Actual bits allocated per key (`total bits / capacity`). */
|
|
45
45
|
get bitsPerKey(): number;
|
|
46
|
+
/** Number of bits currently set across all lanes. */
|
|
47
|
+
get length(): number;
|
|
48
|
+
/**
|
|
49
|
+
* Estimates the current false-positive rate from the actual fill,
|
|
50
|
+
* `(length / totalBits) ** 8`. A split-block query checks exactly 8 lane-bits,
|
|
51
|
+
* so the exponent is 8 rather than a classic probe count `k`. This reflects
|
|
52
|
+
* how full the filter is right now, not the design target.
|
|
53
|
+
*
|
|
54
|
+
* @returns The estimated false-positive rate, `0` for an empty filter.
|
|
55
|
+
*/
|
|
56
|
+
rate(): number;
|
|
46
57
|
/**
|
|
47
58
|
* Restores a filter from its {@link BlockedBloomFilter.toBytes} serialization.
|
|
48
59
|
*
|
package/dist/blocked/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as
|
|
1
|
+
import { a as writeHeader, c as reduce, i as readHeader, n as assertBodyLength, o as hash128KeyInto, r as assertMinBodyLength, t as SerializationError } from "../serialize-DkkBKDT0.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;
|
|
@@ -76,6 +76,26 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
76
76
|
get bitsPerKey() {
|
|
77
77
|
return this.#numBlocks * 256 / this.#n;
|
|
78
78
|
}
|
|
79
|
+
/** Number of bits currently set across all lanes. */
|
|
80
|
+
get length() {
|
|
81
|
+
let bits = 0;
|
|
82
|
+
for (let w of this.#lanes) while (w) {
|
|
83
|
+
w &= w - 1;
|
|
84
|
+
bits++;
|
|
85
|
+
}
|
|
86
|
+
return bits;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Estimates the current false-positive rate from the actual fill,
|
|
90
|
+
* `(length / totalBits) ** 8`. A split-block query checks exactly 8 lane-bits,
|
|
91
|
+
* so the exponent is 8 rather than a classic probe count `k`. This reflects
|
|
92
|
+
* how full the filter is right now, not the design target.
|
|
93
|
+
*
|
|
94
|
+
* @returns The estimated false-positive rate, `0` for an empty filter.
|
|
95
|
+
*/
|
|
96
|
+
rate() {
|
|
97
|
+
return (this.length / (this.#numBlocks * 256)) ** 8;
|
|
98
|
+
}
|
|
79
99
|
/**
|
|
80
100
|
* Restores a filter from its {@link BlockedBloomFilter.toBytes} serialization.
|
|
81
101
|
*
|
|
@@ -83,11 +103,14 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
83
103
|
* @returns The reconstructed filter.
|
|
84
104
|
*/
|
|
85
105
|
static fromBytes(bytes) {
|
|
86
|
-
const { body } = readHeader(bytes);
|
|
106
|
+
const { type, body } = readHeader(bytes);
|
|
107
|
+
if (type !== TYPE) throw new SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
108
|
+
assertMinBodyLength(body.length, 12, "blocked");
|
|
87
109
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
88
110
|
const numBlocks = dv.getUint32(0, true);
|
|
89
111
|
const seed = dv.getUint32(4, true);
|
|
90
112
|
const n = dv.getUint32(8, true);
|
|
113
|
+
assertBodyLength(body.length, 12 + numBlocks * 32, "blocked");
|
|
91
114
|
const f = new BlockedBloomFilter({
|
|
92
115
|
bitsPerKey: 256,
|
|
93
116
|
capacity: numBlocks,
|
|
@@ -111,7 +134,7 @@ var BlockedBloomFilter = class BlockedBloomFilter {
|
|
|
111
134
|
dv.setUint32(8, this.#n, true);
|
|
112
135
|
body.set(lanes, 12);
|
|
113
136
|
return writeHeader({
|
|
114
|
-
version:
|
|
137
|
+
version: 2,
|
|
115
138
|
type: TYPE,
|
|
116
139
|
flags: 0
|
|
117
140
|
}, body);
|
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-DPdiySxq.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,22 @@ 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, body } = require_serialize.readHeader(bytes);
|
|
98
|
+
if (type !== TYPE) throw new require_serialize.SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
99
|
+
require_serialize.assertMinBodyLength(body.length, 14, "bloom");
|
|
98
100
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
99
101
|
const m = dv.getUint32(0, true);
|
|
100
|
-
const k =
|
|
101
|
-
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
|
+
require_serialize.assertBodyLength(body.length, 14 + Math.ceil(m / 8), "bloom");
|
|
102
106
|
const f = new BloomFilter({
|
|
103
107
|
m,
|
|
104
108
|
k,
|
|
105
109
|
seed
|
|
106
110
|
});
|
|
107
|
-
f.#
|
|
111
|
+
f.#n = n;
|
|
112
|
+
f.#bits.bytes.set(body.subarray(14));
|
|
108
113
|
return f;
|
|
109
114
|
}
|
|
110
115
|
/**
|
|
@@ -122,25 +127,52 @@ var BloomFilter = class BloomFilter {
|
|
|
122
127
|
this.#scratch = new Uint32Array(k);
|
|
123
128
|
this.#n = Math.round(m * Math.LN2 / k);
|
|
124
129
|
}
|
|
130
|
+
/** Number of bits in the filter. */
|
|
131
|
+
get m() {
|
|
132
|
+
return this.#m;
|
|
133
|
+
}
|
|
134
|
+
/** Number of hash probes per key. */
|
|
135
|
+
get k() {
|
|
136
|
+
return this.#k;
|
|
137
|
+
}
|
|
138
|
+
/** Hash seed. */
|
|
139
|
+
get seed() {
|
|
140
|
+
return this.#seed;
|
|
141
|
+
}
|
|
142
|
+
/** Number of bits currently set. */
|
|
143
|
+
get length() {
|
|
144
|
+
return this.#bits.count();
|
|
145
|
+
}
|
|
125
146
|
/** Analytic design bits-per-key `m / n`. */
|
|
126
147
|
get bitsPerKey() {
|
|
127
148
|
return this.#m / this.#n;
|
|
128
149
|
}
|
|
129
150
|
/**
|
|
151
|
+
* Estimates the current false-positive rate from the actual fill,
|
|
152
|
+
* `(length / m) ** k`. This reflects how full the filter is right now, not
|
|
153
|
+
* the design target; it rises as keys are added.
|
|
154
|
+
*
|
|
155
|
+
* @returns The estimated false-positive rate, `0` for an empty filter.
|
|
156
|
+
*/
|
|
157
|
+
rate() {
|
|
158
|
+
return (this.length / this.#m) ** this.#k;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
130
161
|
* Serializes the filter to a portable little-endian byte layout.
|
|
131
162
|
*
|
|
132
163
|
* @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
|
|
133
164
|
*/
|
|
134
165
|
toBytes() {
|
|
135
166
|
const payload = this.#bits.bytes;
|
|
136
|
-
const body = new Uint8Array(
|
|
167
|
+
const body = new Uint8Array(14 + payload.length);
|
|
137
168
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
138
169
|
dv.setUint32(0, this.#m, true);
|
|
139
|
-
|
|
140
|
-
dv.setUint32(
|
|
141
|
-
|
|
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);
|
|
142
174
|
return require_serialize.writeHeader({
|
|
143
|
-
version:
|
|
175
|
+
version: 2,
|
|
144
176
|
type: TYPE,
|
|
145
177
|
flags: 0
|
|
146
178
|
}, body);
|
package/dist/bloom/index.d.cts
CHANGED
|
@@ -49,8 +49,24 @@ declare class BloomFilter {
|
|
|
49
49
|
* {@link BloomFilter.create} unless restoring a specific configuration.
|
|
50
50
|
*/
|
|
51
51
|
constructor({ m, k, seed }: BloomParams);
|
|
52
|
+
/** Number of bits in the filter. */
|
|
53
|
+
get m(): number;
|
|
54
|
+
/** Number of hash probes per key. */
|
|
55
|
+
get k(): number;
|
|
56
|
+
/** Hash seed. */
|
|
57
|
+
get seed(): number;
|
|
58
|
+
/** Number of bits currently set. */
|
|
59
|
+
get length(): number;
|
|
52
60
|
/** Analytic design bits-per-key `m / n`. */
|
|
53
61
|
get bitsPerKey(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Estimates the current false-positive rate from the actual fill,
|
|
64
|
+
* `(length / m) ** k`. This reflects how full the filter is right now, not
|
|
65
|
+
* the design target; it rises as keys are added.
|
|
66
|
+
*
|
|
67
|
+
* @returns The estimated false-positive rate, `0` for an empty filter.
|
|
68
|
+
*/
|
|
69
|
+
rate(): number;
|
|
54
70
|
/**
|
|
55
71
|
* Serializes the filter to a portable little-endian byte layout.
|
|
56
72
|
*
|
package/dist/bloom/index.d.ts
CHANGED
|
@@ -49,8 +49,24 @@ declare class BloomFilter {
|
|
|
49
49
|
* {@link BloomFilter.create} unless restoring a specific configuration.
|
|
50
50
|
*/
|
|
51
51
|
constructor({ m, k, seed }: BloomParams);
|
|
52
|
+
/** Number of bits in the filter. */
|
|
53
|
+
get m(): number;
|
|
54
|
+
/** Number of hash probes per key. */
|
|
55
|
+
get k(): number;
|
|
56
|
+
/** Hash seed. */
|
|
57
|
+
get seed(): number;
|
|
58
|
+
/** Number of bits currently set. */
|
|
59
|
+
get length(): number;
|
|
52
60
|
/** Analytic design bits-per-key `m / n`. */
|
|
53
61
|
get bitsPerKey(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Estimates the current false-positive rate from the actual fill,
|
|
64
|
+
* `(length / m) ** k`. This reflects how full the filter is right now, not
|
|
65
|
+
* the design target; it rises as keys are added.
|
|
66
|
+
*
|
|
67
|
+
* @returns The estimated false-positive rate, `0` for an empty filter.
|
|
68
|
+
*/
|
|
69
|
+
rate(): number;
|
|
54
70
|
/**
|
|
55
71
|
* Serializes the filter to a portable little-endian byte layout.
|
|
56
72
|
*
|
package/dist/bloom/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as writeHeader, i as readHeader, n as assertBodyLength, r as assertMinBodyLength, s as probeInto, t as SerializationError } from "../serialize-DkkBKDT0.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,22 @@ 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, body } = readHeader(bytes);
|
|
97
|
+
if (type !== TYPE) throw new SerializationError(`expected AMQF type ${String(TYPE)}, got ${String(type)}`);
|
|
98
|
+
assertMinBodyLength(body.length, 14, "bloom");
|
|
97
99
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
98
100
|
const m = dv.getUint32(0, true);
|
|
99
|
-
const k =
|
|
100
|
-
const seed = dv.getUint32(
|
|
101
|
+
const k = dv.getUint16(4, true);
|
|
102
|
+
const seed = dv.getUint32(6, true);
|
|
103
|
+
const n = dv.getUint32(10, true);
|
|
104
|
+
assertBodyLength(body.length, 14 + Math.ceil(m / 8), "bloom");
|
|
101
105
|
const f = new BloomFilter({
|
|
102
106
|
m,
|
|
103
107
|
k,
|
|
104
108
|
seed
|
|
105
109
|
});
|
|
106
|
-
f.#
|
|
110
|
+
f.#n = n;
|
|
111
|
+
f.#bits.bytes.set(body.subarray(14));
|
|
107
112
|
return f;
|
|
108
113
|
}
|
|
109
114
|
/**
|
|
@@ -121,25 +126,52 @@ var BloomFilter = class BloomFilter {
|
|
|
121
126
|
this.#scratch = new Uint32Array(k);
|
|
122
127
|
this.#n = Math.round(m * Math.LN2 / k);
|
|
123
128
|
}
|
|
129
|
+
/** Number of bits in the filter. */
|
|
130
|
+
get m() {
|
|
131
|
+
return this.#m;
|
|
132
|
+
}
|
|
133
|
+
/** Number of hash probes per key. */
|
|
134
|
+
get k() {
|
|
135
|
+
return this.#k;
|
|
136
|
+
}
|
|
137
|
+
/** Hash seed. */
|
|
138
|
+
get seed() {
|
|
139
|
+
return this.#seed;
|
|
140
|
+
}
|
|
141
|
+
/** Number of bits currently set. */
|
|
142
|
+
get length() {
|
|
143
|
+
return this.#bits.count();
|
|
144
|
+
}
|
|
124
145
|
/** Analytic design bits-per-key `m / n`. */
|
|
125
146
|
get bitsPerKey() {
|
|
126
147
|
return this.#m / this.#n;
|
|
127
148
|
}
|
|
128
149
|
/**
|
|
150
|
+
* Estimates the current false-positive rate from the actual fill,
|
|
151
|
+
* `(length / m) ** k`. This reflects how full the filter is right now, not
|
|
152
|
+
* the design target; it rises as keys are added.
|
|
153
|
+
*
|
|
154
|
+
* @returns The estimated false-positive rate, `0` for an empty filter.
|
|
155
|
+
*/
|
|
156
|
+
rate() {
|
|
157
|
+
return (this.length / this.#m) ** this.#k;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
129
160
|
* Serializes the filter to a portable little-endian byte layout.
|
|
130
161
|
*
|
|
131
162
|
* @returns The serialized filter, readable by {@link BloomFilter.fromBytes}.
|
|
132
163
|
*/
|
|
133
164
|
toBytes() {
|
|
134
165
|
const payload = this.#bits.bytes;
|
|
135
|
-
const body = new Uint8Array(
|
|
166
|
+
const body = new Uint8Array(14 + payload.length);
|
|
136
167
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
137
168
|
dv.setUint32(0, this.#m, true);
|
|
138
|
-
|
|
139
|
-
dv.setUint32(
|
|
140
|
-
|
|
169
|
+
dv.setUint16(4, this.#k, true);
|
|
170
|
+
dv.setUint32(6, this.#seed, true);
|
|
171
|
+
dv.setUint32(10, this.#n, true);
|
|
172
|
+
body.set(payload, 14);
|
|
141
173
|
return writeHeader({
|
|
142
|
-
version:
|
|
174
|
+
version: 2,
|
|
143
175
|
type: TYPE,
|
|
144
176
|
flags: 0
|
|
145
177
|
}, body);
|
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-DPdiySxq.cjs");
|
|
3
3
|
//#region src/fuse/fuse.ts
|
|
4
4
|
const ARITY = 3;
|
|
5
5
|
const TYPE_FUSE8 = 3;
|
|
@@ -211,13 +211,16 @@ function buildState(keys, alloc) {
|
|
|
211
211
|
function fuseStateFromBytes(bytes, expectedType) {
|
|
212
212
|
const { type, body } = require_serialize.readHeader(bytes);
|
|
213
213
|
if (type !== expectedType) throw new require_serialize.SerializationError(`expected AMQF type ${String(expectedType)}, got ${String(type)}`);
|
|
214
|
+
require_serialize.assertMinBodyLength(body.length, 16, "fuse");
|
|
214
215
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
215
216
|
const seed = dv.getUint32(0, true);
|
|
216
217
|
const seg = dv.getUint32(4, true);
|
|
217
218
|
const segCountLen = dv.getUint32(8, true);
|
|
218
219
|
const size = dv.getUint32(12, true);
|
|
219
|
-
const laneBytes = body.subarray(16);
|
|
220
220
|
const bpe = expectedType === TYPE_FUSE8 ? 1 : 2;
|
|
221
|
+
const expectedArrayLength = size === 0 ? 0 : segCountLen + 2 * seg;
|
|
222
|
+
require_serialize.assertBodyLength(body.length, 16 + expectedArrayLength * bpe, "fuse");
|
|
223
|
+
const laneBytes = body.subarray(16);
|
|
221
224
|
const arrayLength = laneBytes.length / bpe;
|
|
222
225
|
const fp = bpe === 1 ? new Uint8Array(arrayLength) : new Uint16Array(arrayLength);
|
|
223
226
|
new Uint8Array(fp.buffer).set(laneBytes);
|
|
@@ -276,7 +279,7 @@ var BinaryFuse = class {
|
|
|
276
279
|
dv.setUint32(12, this.#size, true);
|
|
277
280
|
body.set(laneBytes, 16);
|
|
278
281
|
return require_serialize.writeHeader({
|
|
279
|
-
version:
|
|
282
|
+
version: 2,
|
|
280
283
|
type: this.#fp.BYTES_PER_ELEMENT === 1 ? TYPE_FUSE8 : TYPE_FUSE16,
|
|
281
284
|
flags: 0
|
|
282
285
|
}, body);
|
package/dist/fuse/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as
|
|
1
|
+
import { a as writeHeader, i as readHeader, n as assertBodyLength, o as hash128KeyInto, r as assertMinBodyLength, t as SerializationError } from "../serialize-DkkBKDT0.js";
|
|
2
2
|
//#region src/fuse/fuse.ts
|
|
3
3
|
const ARITY = 3;
|
|
4
4
|
const TYPE_FUSE8 = 3;
|
|
@@ -210,13 +210,16 @@ function buildState(keys, alloc) {
|
|
|
210
210
|
function fuseStateFromBytes(bytes, expectedType) {
|
|
211
211
|
const { type, body } = readHeader(bytes);
|
|
212
212
|
if (type !== expectedType) throw new SerializationError(`expected AMQF type ${String(expectedType)}, got ${String(type)}`);
|
|
213
|
+
assertMinBodyLength(body.length, 16, "fuse");
|
|
213
214
|
const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
214
215
|
const seed = dv.getUint32(0, true);
|
|
215
216
|
const seg = dv.getUint32(4, true);
|
|
216
217
|
const segCountLen = dv.getUint32(8, true);
|
|
217
218
|
const size = dv.getUint32(12, true);
|
|
218
|
-
const laneBytes = body.subarray(16);
|
|
219
219
|
const bpe = expectedType === TYPE_FUSE8 ? 1 : 2;
|
|
220
|
+
const expectedArrayLength = size === 0 ? 0 : segCountLen + 2 * seg;
|
|
221
|
+
assertBodyLength(body.length, 16 + expectedArrayLength * bpe, "fuse");
|
|
222
|
+
const laneBytes = body.subarray(16);
|
|
220
223
|
const arrayLength = laneBytes.length / bpe;
|
|
221
224
|
const fp = bpe === 1 ? new Uint8Array(arrayLength) : new Uint16Array(arrayLength);
|
|
222
225
|
new Uint8Array(fp.buffer).set(laneBytes);
|
|
@@ -275,7 +278,7 @@ var BinaryFuse = class {
|
|
|
275
278
|
dv.setUint32(12, this.#size, true);
|
|
276
279
|
body.set(laneBytes, 16);
|
|
277
280
|
return writeHeader({
|
|
278
|
-
version:
|
|
281
|
+
version: 2,
|
|
279
282
|
type: this.#fp.BYTES_PER_ELEMENT === 1 ? TYPE_FUSE8 : TYPE_FUSE16,
|
|
280
283
|
flags: 0
|
|
281
284
|
}, body);
|
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED
|
@@ -277,11 +277,25 @@ var UnknownVersionError = class extends SerializationError {
|
|
|
277
277
|
var ChecksumError = class extends SerializationError {
|
|
278
278
|
name = "ChecksumError";
|
|
279
279
|
};
|
|
280
|
+
/**
|
|
281
|
+
* Asserts a frame body is long enough to hold its fixed params block, so the
|
|
282
|
+
* params can be read without running off the end.
|
|
283
|
+
*/
|
|
284
|
+
function assertMinBodyLength(actual, min, context) {
|
|
285
|
+
if (actual < min) throw new TruncatedError(`${context}: body of ${String(actual)} bytes is shorter than the ${String(min)}-byte params block`);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Asserts a frame body is exactly the length its declared params imply, so a
|
|
289
|
+
* hostile or truncated frame is rejected before any backing store is allocated.
|
|
290
|
+
*/
|
|
291
|
+
function assertBodyLength(actual, expected, context) {
|
|
292
|
+
if (actual !== expected) throw new TruncatedError(`${context}: body of ${String(actual)} bytes does not match the declared params (expected ${String(expected)})`);
|
|
293
|
+
}
|
|
280
294
|
function readHeader(frame) {
|
|
281
295
|
if (frame.length < 12) throw new TruncatedError(`frame of ${String(frame.length)} bytes is shorter than the minimum ${String(12)}`);
|
|
282
296
|
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
297
|
const version = frame[4] ?? 0;
|
|
284
|
-
if (version !==
|
|
298
|
+
if (version !== 2) throw new UnknownVersionError(`unsupported format version ${String(version)}`);
|
|
285
299
|
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
300
|
return {
|
|
287
301
|
version,
|
|
@@ -297,6 +311,18 @@ Object.defineProperty(exports, "SerializationError", {
|
|
|
297
311
|
return SerializationError;
|
|
298
312
|
}
|
|
299
313
|
});
|
|
314
|
+
Object.defineProperty(exports, "assertBodyLength", {
|
|
315
|
+
enumerable: true,
|
|
316
|
+
get: function() {
|
|
317
|
+
return assertBodyLength;
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
Object.defineProperty(exports, "assertMinBodyLength", {
|
|
321
|
+
enumerable: true,
|
|
322
|
+
get: function() {
|
|
323
|
+
return assertMinBodyLength;
|
|
324
|
+
}
|
|
325
|
+
});
|
|
300
326
|
Object.defineProperty(exports, "hash128KeyInto", {
|
|
301
327
|
enumerable: true,
|
|
302
328
|
get: function() {
|
|
@@ -277,11 +277,25 @@ var UnknownVersionError = class extends SerializationError {
|
|
|
277
277
|
var ChecksumError = class extends SerializationError {
|
|
278
278
|
name = "ChecksumError";
|
|
279
279
|
};
|
|
280
|
+
/**
|
|
281
|
+
* Asserts a frame body is long enough to hold its fixed params block, so the
|
|
282
|
+
* params can be read without running off the end.
|
|
283
|
+
*/
|
|
284
|
+
function assertMinBodyLength(actual, min, context) {
|
|
285
|
+
if (actual < min) throw new TruncatedError(`${context}: body of ${String(actual)} bytes is shorter than the ${String(min)}-byte params block`);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Asserts a frame body is exactly the length its declared params imply, so a
|
|
289
|
+
* hostile or truncated frame is rejected before any backing store is allocated.
|
|
290
|
+
*/
|
|
291
|
+
function assertBodyLength(actual, expected, context) {
|
|
292
|
+
if (actual !== expected) throw new TruncatedError(`${context}: body of ${String(actual)} bytes does not match the declared params (expected ${String(expected)})`);
|
|
293
|
+
}
|
|
280
294
|
function readHeader(frame) {
|
|
281
295
|
if (frame.length < 12) throw new TruncatedError(`frame of ${String(frame.length)} bytes is shorter than the minimum ${String(12)}`);
|
|
282
296
|
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
297
|
const version = frame[4] ?? 0;
|
|
284
|
-
if (version !==
|
|
298
|
+
if (version !== 2) throw new UnknownVersionError(`unsupported format version ${String(version)}`);
|
|
285
299
|
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
300
|
return {
|
|
287
301
|
version,
|
|
@@ -291,4 +305,4 @@ function readHeader(frame) {
|
|
|
291
305
|
};
|
|
292
306
|
}
|
|
293
307
|
//#endregion
|
|
294
|
-
export {
|
|
308
|
+
export { writeHeader as a, reduce as c, readHeader as i, assertBodyLength as n, hash128KeyInto as o, assertMinBodyLength as r, probeInto as s, SerializationError as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "distillate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|