distillate 0.3.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.
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_serialize = require("../serialize-CHHDM4TQ.cjs");
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;
@@ -104,11 +104,14 @@ var BlockedBloomFilter = class BlockedBloomFilter {
104
104
  * @returns The reconstructed filter.
105
105
  */
106
106
  static fromBytes(bytes) {
107
- 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");
108
110
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
109
111
  const numBlocks = dv.getUint32(0, true);
110
112
  const seed = dv.getUint32(4, true);
111
113
  const n = dv.getUint32(8, true);
114
+ require_serialize.assertBodyLength(body.length, 12 + numBlocks * 32, "blocked");
112
115
  const f = new BlockedBloomFilter({
113
116
  bitsPerKey: 256,
114
117
  capacity: numBlocks,
@@ -132,7 +135,7 @@ var BlockedBloomFilter = class BlockedBloomFilter {
132
135
  dv.setUint32(8, this.#n, true);
133
136
  body.set(lanes, 12);
134
137
  return require_serialize.writeHeader({
135
- version: 1,
138
+ version: 2,
136
139
  type: TYPE,
137
140
  flags: 0
138
141
  }, body);
@@ -1,4 +1,4 @@
1
- import { i as hash128KeyInto, n as readHeader, o as reduce, r as writeHeader } from "../serialize-5XQ5y_R-.js";
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;
@@ -103,11 +103,14 @@ var BlockedBloomFilter = class BlockedBloomFilter {
103
103
  * @returns The reconstructed filter.
104
104
  */
105
105
  static fromBytes(bytes) {
106
- 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");
107
109
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
108
110
  const numBlocks = dv.getUint32(0, true);
109
111
  const seed = dv.getUint32(4, true);
110
112
  const n = dv.getUint32(8, true);
113
+ assertBodyLength(body.length, 12 + numBlocks * 32, "blocked");
111
114
  const f = new BlockedBloomFilter({
112
115
  bitsPerKey: 256,
113
116
  capacity: numBlocks,
@@ -131,7 +134,7 @@ var BlockedBloomFilter = class BlockedBloomFilter {
131
134
  dv.setUint32(8, this.#n, true);
132
135
  body.set(lanes, 12);
133
136
  return writeHeader({
134
- version: 1,
137
+ version: 2,
135
138
  type: TYPE,
136
139
  flags: 0
137
140
  }, body);
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_serialize = require("../serialize-CHHDM4TQ.cjs");
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 = body[4] ?? 0;
101
- const seed = dv.getUint32(5, true);
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.#bits.bytes.set(body.subarray(9));
111
+ f.#n = n;
112
+ f.#bits.bytes.set(body.subarray(14));
108
113
  return f;
109
114
  }
110
115
  /**
@@ -159,14 +164,15 @@ var BloomFilter = class BloomFilter {
159
164
  */
160
165
  toBytes() {
161
166
  const payload = this.#bits.bytes;
162
- const body = new Uint8Array(9 + payload.length);
167
+ const body = new Uint8Array(14 + payload.length);
163
168
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
164
169
  dv.setUint32(0, this.#m, true);
165
- body[4] = this.#k;
166
- dv.setUint32(5, this.#seed, true);
167
- body.set(payload, 9);
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);
168
174
  return require_serialize.writeHeader({
169
- version: 1,
175
+ version: 2,
170
176
  type: TYPE,
171
177
  flags: 0
172
178
  }, body);
@@ -1,4 +1,4 @@
1
- import { a as probeInto, n as readHeader, r as writeHeader } from "../serialize-5XQ5y_R-.js";
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 = body[4] ?? 0;
100
- const seed = dv.getUint32(5, true);
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.#bits.bytes.set(body.subarray(9));
110
+ f.#n = n;
111
+ f.#bits.bytes.set(body.subarray(14));
107
112
  return f;
108
113
  }
109
114
  /**
@@ -158,14 +163,15 @@ var BloomFilter = class BloomFilter {
158
163
  */
159
164
  toBytes() {
160
165
  const payload = this.#bits.bytes;
161
- const body = new Uint8Array(9 + payload.length);
166
+ const body = new Uint8Array(14 + payload.length);
162
167
  const dv = new DataView(body.buffer, body.byteOffset, body.byteLength);
163
168
  dv.setUint32(0, this.#m, true);
164
- body[4] = this.#k;
165
- dv.setUint32(5, this.#seed, true);
166
- body.set(payload, 9);
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);
167
173
  return writeHeader({
168
- version: 1,
174
+ version: 2,
169
175
  type: TYPE,
170
176
  flags: 0
171
177
  }, body);
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_serialize = require("../serialize-CHHDM4TQ.cjs");
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: 1,
282
+ version: 2,
280
283
  type: this.#fp.BYTES_PER_ELEMENT === 1 ? TYPE_FUSE8 : TYPE_FUSE16,
281
284
  flags: 0
282
285
  }, body);
@@ -1,4 +1,4 @@
1
- import { i as hash128KeyInto, n as readHeader, r as writeHeader, t as SerializationError } from "../serialize-5XQ5y_R-.js";
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: 1,
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
@@ -2,6 +2,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  //#endregion
3
3
  //#region src/index.ts
4
4
  /** The installed `distillate` package version. */
5
- const VERSION = "0.3.0";
5
+ const VERSION = "0.4.0";
6
6
  //#endregion
7
7
  exports.VERSION = VERSION;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  //#endregion
2
2
  //#region src/index.ts
3
3
  /** The installed `distillate` package version. */
4
- const VERSION = "0.3.0";
4
+ const VERSION = "0.4.0";
5
5
  //#endregion
6
6
  export { VERSION };
@@ -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 !== 1) throw new UnknownVersionError(`unsupported format version ${String(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 !== 1) throw new UnknownVersionError(`unsupported format version ${String(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 { probeInto as a, hash128KeyInto as i, readHeader as n, reduce as o, writeHeader as r, SerializationError as t };
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.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",