mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-02 07:43:42 +08:00
268 lines
12 KiB
JavaScript
268 lines
12 KiB
JavaScript
// Buffer methods on Buffers held as checked-dynamic values: a buffer stored
|
|
// on `this` by a constructor function, or assigned later, keeps every
|
|
// Buffer.prototype method. The same names on other receivers keep their own.
|
|
"use strict";
|
|
|
|
function Writer(size) {
|
|
this.buf = Buffer.alloc(size);
|
|
this.at = 0;
|
|
}
|
|
Writer.prototype.put = function (text, encoding) {
|
|
this.at += this.buf.write(text, this.at, encoding);
|
|
return this;
|
|
};
|
|
|
|
const w = new Writer(12);
|
|
w.put("ab").put("6364", "hex").put("é", "latin1");
|
|
console.log(w.at, w.buf.toString("hex", 0, w.at), w.buf.toString("latin1", 0, w.at));
|
|
|
|
const t = (name, f) => {
|
|
try {
|
|
const v = f();
|
|
console.log(name, Buffer.isBuffer(v) ? `<${v.toString("hex")}>` : JSON.stringify(v));
|
|
} catch (e) {
|
|
console.log(name, e.name, e.code, e.message);
|
|
}
|
|
};
|
|
|
|
function Holder() {
|
|
this.b = Buffer.from("hello world");
|
|
}
|
|
const h = new Holder();
|
|
const fresh = () => {
|
|
h.b = Buffer.from("hello world");
|
|
return h.b;
|
|
};
|
|
|
|
// write overloads, offset and length checks, encodings.
|
|
t("write", () => fresh() && h.b.write("HE"));
|
|
t("write offset", () => fresh() && [h.b.write("zz", 3), h.b.toString()]);
|
|
t("write offset length", () => fresh() && [h.b.write("abcdef", 1, 2), h.b.toString()]);
|
|
t("write offset encoding", () => fresh() && [h.b.write("6869", 5, "hex"), h.b.toString()]);
|
|
t("write encoding", () => fresh() && [h.b.write("aGk=", "base64"), h.b.toString()]);
|
|
t("write length encoding", () => fresh() && [h.b.write("abc", 0, 2, "latin1"), h.b.toString()]);
|
|
t("write undefined offset", () => fresh() && [h.b.write("zz", undefined, 1), h.b.toString()]);
|
|
t("write null encoding", () => fresh() && h.b.write("ab", 0, 2, null));
|
|
t("write case", () => fresh() && h.b.write("é", 0, 2, "UTF-8"));
|
|
t("write split code point", () => fresh() && h.b.write("\u{1F600}", 9));
|
|
t("write not a string", () => fresh() && h.b.write(5));
|
|
t("write unknown", () => fresh() && h.b.write("ab", 0, "nope"));
|
|
t("write numeric string offset", () => fresh() && h.b.write("ab", "1"));
|
|
t("write fractional offset", () => fresh() && h.b.write("ab", 1.5));
|
|
t("write offset past end", () => fresh() && h.b.write("ab", 12));
|
|
t("write negative length", () => fresh() && h.b.write("ab", 0, -1));
|
|
t("write number encoding", () => fresh() && h.b.write("ab", 0, 2, 5));
|
|
|
|
// fill with strings, numbers, buffers and other values.
|
|
t("fill string", () => fresh() && h.b.fill("ab", 0, 4));
|
|
t("fill number", () => fresh() && h.b.fill(0x41, 4, 6));
|
|
t("fill 300", () => fresh() && h.b.fill(300));
|
|
t("fill true", () => fresh() && h.b.fill(true, 1, 3));
|
|
t("fill object", () => fresh() && h.b.fill({}, 9));
|
|
t("fill buffer", () => fresh() && h.b.fill(Buffer.from([1, 2])));
|
|
t("fill encoding", () => fresh() && h.b.fill("ab", "utf16le"));
|
|
t("fill end encoding", () => fresh() && h.b.fill("ab", 1, "latin1"));
|
|
t("fill empty string", () => fresh() && h.b.fill("", 2));
|
|
t("fill odd hex", () => fresh() && h.b.fill("abc", "hex"));
|
|
t("fill invalid hex", () => fresh() && h.b.fill("zz", "hex"));
|
|
t("fill empty buffer", () => fresh() && h.b.fill(Buffer.alloc(0)));
|
|
t("fill number encoding", () => fresh() && h.b.fill("a", 0, 1, 5));
|
|
t("fill string offset", () => fresh() && h.b.fill(1, "2"));
|
|
t("fill end past length", () => fresh() && h.b.fill(1, 0, 20));
|
|
t("fill offset past end", () => fresh() && h.b.fill(1, 20));
|
|
t("fill empty range", () => fresh() && h.b.fill(1, 4, 2));
|
|
|
|
// toString windows and encodings.
|
|
t("toString window", () => fresh() && h.b.toString("hex", 1, 3));
|
|
t("toString numeric strings", () => fresh() && h.b.toString("utf8", "1", "3"));
|
|
t("toString fractions", () => fresh() && h.b.toString("utf8", 0.9, 2.9));
|
|
t("toString NaN", () => fresh() && h.b.toString("utf8", "x", "y"));
|
|
t("toString negative", () => fresh() && h.b.toString("utf8", -1, 2));
|
|
t("toString undefined encoding", () => fresh() && h.b.toString(undefined, 6));
|
|
t("toString empty unknown", () => fresh() && h.b.toString("nope", 3, 1));
|
|
t("toString null encoding", () => fresh() && h.b.toString(null, 0, 1));
|
|
|
|
// The other Buffer methods.
|
|
t("toJSON", () => fresh() && h.b.toJSON());
|
|
t("equals", () => fresh() && [h.b.equals(Buffer.from("hello world")), h.b.equals(Buffer.from("hello"))]);
|
|
t("equals string", () => fresh() && h.b.equals("hello world"));
|
|
t("compare", () => fresh() && [h.b.compare(Buffer.from("hellp")), h.b.compare(Buffer.from("xello"), 1, 5, 1, 5)]);
|
|
t("compare string", () => fresh() && h.b.compare("hello"));
|
|
t("copy", () => {
|
|
const d = Buffer.alloc(4);
|
|
return fresh() && [h.b.copy(d, 1, 6), d.toString()];
|
|
});
|
|
t("copy coerced offsets", () => {
|
|
const d = Buffer.alloc(4);
|
|
return fresh() && [h.b.copy(d, 1.7, "1", 3), d.toString("hex")];
|
|
});
|
|
t("copy NaN offsets", () => fresh() && h.b.copy(Buffer.alloc(2), NaN, "x"));
|
|
t("copy into Uint8Array", () => {
|
|
const d = new Uint8Array(3);
|
|
return fresh() && [h.b.copy(d), Array.from(d)];
|
|
});
|
|
t("copy negative target start", () => fresh() && h.b.copy(Buffer.alloc(2), -1));
|
|
t("copy source start past end", () => fresh() && h.b.copy(Buffer.alloc(2), 0, 20));
|
|
t("copy into array", () => fresh() && h.b.copy([1, 2]));
|
|
t("indexOf", () => fresh() && [h.b.indexOf("o"), h.b.indexOf("o", 5), h.b.indexOf("o", -4), h.b.indexOf(0x77)]);
|
|
t("indexOf buffer", () => fresh() && h.b.indexOf(Buffer.from("wor")));
|
|
t("indexOf encoding", () => fresh() && [h.b.indexOf("6c6c", "hex"), h.b.indexOf("l", 0, "latin1")]);
|
|
t("indexOf utf16le", () => Buffer.from("abcabc", "utf16le").indexOf("b", 0, "utf16le"));
|
|
t("indexOf unknown", () => fresh() && h.b.indexOf("l", 0, "nope"));
|
|
t("indexOf number ignores encoding", () => fresh() && h.b.indexOf(0x6c, 0, "nope"));
|
|
t("indexOf object", () => fresh() && h.b.indexOf({}));
|
|
t("lastIndexOf", () => fresh() && [h.b.lastIndexOf("o"), h.b.lastIndexOf("o", 5), h.b.lastIndexOf("l", "latin1")]);
|
|
t("lastIndexOf string offset", () => fresh() && h.b.lastIndexOf("o", "x"));
|
|
t("includes", () => fresh() && [h.b.includes("wor"), h.b.includes("z"), h.b.includes("h", null)]);
|
|
t("swap16", () => {
|
|
h.b = Buffer.from("abcd");
|
|
return h.b.swap16();
|
|
});
|
|
t("swap32 odd", () => {
|
|
h.b = Buffer.from("abc");
|
|
return h.b.swap32();
|
|
});
|
|
t("readUInt16LE", () => fresh() && h.b.readUInt16LE(1));
|
|
|
|
// A buffer assigned to a constructor-function instance after construction.
|
|
function Later() {}
|
|
const later = new Later();
|
|
later.buf = Buffer.alloc(4);
|
|
later.buf.write("cd", 1, "ascii");
|
|
console.log("later", later.buf.toString("hex"), later.buf.fill(7).toString("hex"));
|
|
|
|
// The same method names on receivers that are not Buffers.
|
|
function Box() {
|
|
this.text = "banana";
|
|
this.list = [1, 2, 3];
|
|
this.bytes = new Uint8Array([5, 6, 7]);
|
|
this.sink = { written: [], write(s) { this.written.push(s); return true; }, toString(a, b) { return `sink ${a} ${b}`; } };
|
|
}
|
|
const box = new Box();
|
|
console.log(box.text.indexOf("an"), box.text.lastIndexOf("an"), box.text.includes("nan"), box.text.toString());
|
|
console.log(box.list.indexOf(2), box.list.includes(4), box.list.fill(0, 1).join(","), box.list.toString());
|
|
console.log(Array.from(box.bytes.fill(9, 2)), box.bytes.toString());
|
|
console.log(box.sink.write("x"), box.sink.written, box.sink.toString(1, 2));
|
|
|
|
// fill with other ArrayBufferViews repeats their raw bytes; an empty view is
|
|
// ERR_INVALID_ARG_VALUE with Node's inspect of the view.
|
|
const tm = (name, f) => {
|
|
try {
|
|
const v = f();
|
|
console.log(name, Buffer.isBuffer(v) ? `<${v.toString("hex")}>` : JSON.stringify(v));
|
|
} catch (e) {
|
|
console.log(name, e.name, e.code, JSON.stringify(e.message));
|
|
}
|
|
};
|
|
function Held(bytes) {
|
|
this.b = bytes;
|
|
}
|
|
const held = (bytes) => new Held(bytes).b;
|
|
const filled = (n) => held(Buffer.alloc(n));
|
|
tm("fill Uint16Array", () => filled(6).fill(new Uint16Array([0x0102])));
|
|
tm("fill DataView", () => filled(6).fill(new DataView(new Uint8Array([7, 8, 9]).buffer)));
|
|
tm("fill DataView window", () => filled(6).fill(new DataView(new Uint8Array([1, 2, 3, 4]).buffer, 1, 2)));
|
|
tm("fill Float64Array", () => filled(10).fill(new Float64Array([1])));
|
|
tm("fill subarray", () => filled(6).fill(new Uint16Array([1, 2, 3]).subarray(1)));
|
|
tm("fill Int8Array range", () => filled(6).fill(new Int8Array([-1, 2]), 1, 4));
|
|
tm("fill empty Uint16Array", () => filled(6).fill(new Uint16Array(0)));
|
|
tm("fill empty Uint8Array", () => filled(6).fill(new Uint8Array(0)));
|
|
for (const [size, offset] of [[0, 0], [1, 1], [6, 6], [7, 7], [8, 8], [120, 3], [101, 101]])
|
|
tm(`fill empty DataView ${size}/${offset}`, () => filled(6).fill(new DataView(new ArrayBuffer(size), offset, 0)));
|
|
tm("fill empty DataView over bytes", () => filled(6).fill(new DataView(new Uint8Array(200).fill(171).buffer, 7, 0)));
|
|
|
|
// Buffer needles under utf16le search whole 2-byte units.
|
|
const units = held(Buffer.from("abcabc", "utf16le"));
|
|
const split = held(Buffer.from([0x61, 0x00, 0x62, 0x00, 0x00, 0x62, 0x00, 0x63]));
|
|
const N = (bytes) => Buffer.from(bytes);
|
|
tm("indexOf utf16le needle", () => [split.indexOf(N([0x62, 0x00]), 0, "ucs2"), split.indexOf(N([0x62, 0x00]))]);
|
|
tm("indexOf utf16le odd position", () => [
|
|
split.indexOf(N([0x00, 0x62]), 0, "ucs2"),
|
|
split.indexOf(N([0x00, 0x62]), 0, "UTF-16LE"),
|
|
split.indexOf(N([0x00, 0x62])),
|
|
]);
|
|
tm("lastIndexOf utf16le needle", () => [
|
|
units.lastIndexOf(N([0x62, 0x00]), undefined, "ucs2"),
|
|
split.lastIndexOf(N([0x00, 0x62]), undefined, "ucs2"),
|
|
...[5, 3, 2, 1, -100, 100].map((at) => split.lastIndexOf(N([0x62, 0x00]), at, "ucs2")),
|
|
]);
|
|
tm("indexOf utf16le offsets", () => [1, 2, 3, -5, 7, 2.9].map((at) => split.indexOf(N([0x62, 0x00]), at, "ucs2")));
|
|
tm("indexOf utf16le short needles", () => [
|
|
split.indexOf(N([0x62]), 0, "ucs2"),
|
|
held(N([0x62])).indexOf(N([0x62]), 0, "ucs2"),
|
|
split.indexOf(Buffer.alloc(0), 3, "ucs2"),
|
|
split.indexOf(Buffer.alloc(0), 30, "ucs2"),
|
|
split.indexOf(N([0x62, 0, 0]), 0, "ucs2"),
|
|
]);
|
|
tm("indexOf utf16le odd lengths", () => [
|
|
held(N([1, 0, 2, 0, 3])).indexOf(N([2, 0, 3]), 0, "ucs2"),
|
|
held(N([1, 0, 2, 0, 3, 0])).indexOf(N([2, 0, 3]), 0, "ucs2"),
|
|
held(N([1, 2, 1, 2, 1, 2])).indexOf(N([2, 1, 2]), 0, "ucs2"),
|
|
held(N([1, 0, 2, 0, 3])).lastIndexOf(N([3]), undefined, "ucs2"),
|
|
held(N([0, 2, 0, 2, 0])).lastIndexOf(N([2, 0]), undefined, "ucs2"),
|
|
]);
|
|
tm("indexOf buffer needle other encodings", () => [
|
|
split.indexOf(N([0x62, 0x00]), 0, "nope"),
|
|
split.indexOf(N([0x00, 0x62]), 0, "nope"),
|
|
split.indexOf(N([0x00, 0x62]), 0, "latin1"),
|
|
]);
|
|
tm("indexOf utf16le strings", () => [
|
|
split.indexOf("b", 0, "ucs2"),
|
|
units.lastIndexOf("b", undefined, "ucs2"),
|
|
units.indexOf("b", 3, "ucs2"),
|
|
units.lastIndexOf("b", 3, "ucs2"),
|
|
units.indexOf("", 3, "ucs2"),
|
|
units.indexOf("", 30, "ucs2"),
|
|
units.lastIndexOf("", 30, "ucs2"),
|
|
split.indexOf("戀", 0, "ucs2"),
|
|
]);
|
|
tm("includes utf16le needle", () => [split.includes(N([0x00, 0x62]), 0, "ucs2"), split.includes(N([0x62, 0x00]), 3, "ucs2")]);
|
|
|
|
// toString windows with infinite and out-of-range bounds.
|
|
const word = held(Buffer.from("hello"));
|
|
tm("toString infinite bounds", () => [
|
|
word.toString("utf8", 1, Infinity),
|
|
word.toString("utf8", -Infinity, 2),
|
|
word.toString("utf8", Infinity),
|
|
word.toString("utf8", NaN, 3),
|
|
word.toString("utf8", 1e300, 2),
|
|
word.toString("utf8", 1, 1e300),
|
|
word.toString("utf8", -1e300, 1e300),
|
|
word.toString("utf8", 0, -Infinity),
|
|
word.toString("utf8", 2, NaN),
|
|
word.toString("utf8", 0.5, 4.5),
|
|
word.toString("hex", -0.5, -0),
|
|
word.toString("latin1", 4.9),
|
|
word.toString(undefined, undefined, Infinity),
|
|
]);
|
|
|
|
// The same searches and windows on Buffers the compiler types statically.
|
|
const typed = Buffer.from([0x61, 0x00, 0x62, 0x00, 0x00, 0x62, 0x00, 0x63]);
|
|
const typedUnits = Buffer.from("abcabc", "utf16le");
|
|
console.log(
|
|
"typed utf16le",
|
|
typed.indexOf(Buffer.from([0x00, 0x62]), 0, "ucs2"),
|
|
Buffer.from([0x62]).indexOf(Buffer.from([0x62]), 0, "ucs2"),
|
|
typed.lastIndexOf(Buffer.from([0x62, 0x00]), 5, "ucs2"),
|
|
typed.indexOf(Buffer.from([0x62, 0x00]), 3, "ucs2"),
|
|
typed.includes(Buffer.from([0x00, 0x62]), 0, "utf16le"),
|
|
typed.indexOf("b", 0, "ucs2"),
|
|
typedUnits.lastIndexOf("b", 100, "ucs2"),
|
|
typedUnits.lastIndexOf("b", 3, "ucs2"),
|
|
typedUnits.indexOf("", 30, "ucs2"),
|
|
typedUnits.indexOf("b", 1e300, "ucs2"),
|
|
typedUnits.indexOf("c", -1e300, "ucs2"),
|
|
);
|
|
const typedWord = Buffer.from("hello");
|
|
console.log(
|
|
"typed windows",
|
|
JSON.stringify([
|
|
typedWord.toString("utf8", 1, Infinity),
|
|
typedWord.toString("utf8", -Infinity, 2),
|
|
typedWord.toString("utf8", Infinity),
|
|
typedWord.toString("utf8", NaN, 3),
|
|
typedWord.toString("utf8", 1, 1e300),
|
|
typedWord.toString("hex", 0, -Infinity),
|
|
]),
|
|
);
|