Improvements.

This commit is contained in:
Vasyl Palamarchuk
2026-09-28 17:39:57 -07:00
parent 274b02a8ac
commit a4040587ae
4 changed files with 52 additions and 2 deletions
+8
View File
@@ -62,22 +62,30 @@ export function readU32BE(d, o) {
assertRange(d, o, 4);
return ((d[o] << 24) | (d[o + 1] << 16) | (d[o + 2] << 8) | d[o + 3]) >>> 0;
}
function assertUnsignedValue(value, bits) {
if (!Number.isInteger(value) || value < 0 || value > 2 ** bits - 1)
throw new RangeError(`Value must be an unsigned ${bits}-bit integer`);
}
export function writeU16LE(d, o, v) {
assertRange(d, o, 2);
assertUnsignedValue(v, 16);
d[o] = v & 255;
d[o + 1] = (v >>> 8) & 255;
}
export function writeU16BE(d, o, v) {
assertRange(d, o, 2);
assertUnsignedValue(v, 16);
d[o] = (v >>> 8) & 255;
d[o + 1] = v & 255;
}
export function writeU32LE(d, o, v) {
assertRange(d, o, 4);
assertUnsignedValue(v, 32);
for (let i = 0; i < 4; i++) d[o + i] = (v >>> (8 * i)) & 255;
}
export function writeU32BE(d, o, v) {
assertRange(d, o, 4);
assertUnsignedValue(v, 32);
for (let i = 0; i < 4; i++) d[o + i] = (v >>> (8 * (3 - i))) & 255;
}
export function findPattern(data, pattern, start = 0) {
@@ -18,8 +18,8 @@ function compareBuffers(left, right) {
const maxLength = Math.max(left.length, right.length);
const differences = [];
for (let index = 0; index < maxLength; index += 1) {
const a = left[index] ?? 0x00;
const b = right[index] ?? 0x00;
const a = index < left.length ? left[index] : null;
const b = index < right.length ? right[index] : null;
if (a !== b) differences.push({ index, left: a, right: b });
}
return { matches: differences.length === 0, differences };
@@ -6,6 +6,9 @@ import {
bytesToHex,
readU16LE,
writeU16LE,
writeU16BE,
writeU32LE,
writeU32BE,
} from "../../03_Script_Starter_Kit/binary_utils.mjs";
test("cloneBytes preserves data and does not alias", () => {
@@ -31,3 +34,20 @@ test("hex and numeric helpers work", () => {
writeU16LE(out, 0, 0x1234);
assert.equal(readU16LE(out, 0), 0x1234);
});
test("unsigned writes reject out-of-range values without changing the buffer", () => {
for (const [writer, size, overflow] of [
[writeU16LE, 2, 0x10000],
[writeU16BE, 2, 0x10000],
[writeU32LE, 4, 0x100000000],
[writeU32BE, 4, 0x100000000],
]) {
const buffer = new Uint8Array(size).fill(0xaa);
for (const value of [-1, overflow, 1.5, NaN]) {
assert.throws(() => writer(buffer, 0, value), RangeError);
assert.deepEqual(buffer, new Uint8Array(size).fill(0xaa));
}
writer(buffer, 0, overflow - 1);
assert.deepEqual(buffer, new Uint8Array(size).fill(0xff));
}
});
@@ -3,6 +3,7 @@ import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { runInNewContext } from "node:vm";
const templateDir = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
@@ -52,3 +53,24 @@ test("workflow templates are documented in the SDK structure", () => {
assert.ok(readme.includes(snippet) || readme.includes("templates/"));
}
});
test("buffer comparison reports trailing zero bytes as differences", () => {
const source = readFileSync(
path.join(templateDir, "template_compare_workflow.mjs"),
"utf8",
);
const compareBuffers = runInNewContext(`${source}\ncompareBuffers`, {
Uint8Array,
console: { log() {} },
});
const result = compareBuffers(
new Uint8Array([1]),
new Uint8Array([1, 0]),
);
assert.equal(result.matches, false);
assert.equal(result.differences.length, 1);
assert.equal(result.differences[0].index, 1);
assert.equal(result.differences[0].left, null);
assert.equal(result.differences[0].right, 0);
assert.equal(compareBuffers(new Uint8Array([1]), new Uint8Array([1])).matches, true);
});