start using growable array buffer to make it BLAZINGLY EFFICIENT

This commit is contained in:
nora 2025-01-12 16:54:37 +01:00
parent b2d506b2ae
commit dc6217d1b6

View file

@ -4,6 +4,8 @@ import fs from "node:fs/promises";
// https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf // https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
// yep. // yep.
const BUFFER_LE = true;
class CompilerError extends Error { class CompilerError extends Error {
constructor(message, span) { constructor(message, span) {
super(message); super(message);
@ -790,23 +792,43 @@ function lower(ast) {
} }
class BufferBuilder { class BufferBuilder {
#buffer
constructor() { constructor() {
this.buffer = new Uint8Array(); this.#buffer = new ArrayBuffer(0, {maxByteLength: 2**32});
} }
append(array) { append(array) {
assertDefined(array); assertDefined(array);
array.forEach((elem) => assert(typeof elem === "number")); array.forEach((elem) => assert(typeof elem === "number"));
this.buffer = Buffer.concat([this.buffer, new Uint8Array(array)]);
const oldLength = this.length;
const newLength = oldLength + array.length;
this.#buffer.resize(newLength);
for (let i = 0; i < array.length; i++) {
new DataView(this.#buffer).setUint8(oldLength + i, array[i]);
}
}
get length() {
return this.#buffer.byteLength;
} }
get currentPos() { get currentPos() {
return this.buffer.length; return this.length;
}
writeUint16Le(offset, int) {
new DataView(this.#buffer).setUint16(offset, int, BUFFER_LE);
}
writeUint32Le(offset, int) {
new DataView(this.#buffer).setUint32(offset, int, BUFFER_LE);
}
toUint8Array() {
return new Uint8Array(this.#buffer);
} }
} }
function generateObjectFile(funcs) { function generateObjectFile(funcs) {
const alignTo = (out, align) => { const alignTo = (out, align) => {
assertDefined(out, align); assertDefined(out, align);
const missing = out.buffer.length % align; const missing = out.length % align;
if (missing === 0) { if (missing === 0) {
return; return;
} }
@ -822,7 +844,7 @@ function lower(ast) {
funcs.forEach((func) => { funcs.forEach((func) => {
alignTo(textContent, 8); // i think this is not actually necessary. alignTo(textContent, 8); // i think this is not actually necessary.
const offset = textContent.buffer.length; const offset = textContent.length;
textRelativeSymbols.push({ textRelativeSymbols.push({
name: func.name, name: func.name,
offset, offset,
@ -840,7 +862,7 @@ function lower(ast) {
}); });
return { return {
textContent: textContent.buffer, textContent: textContent.toUint8Array(),
textRelativeSymbols, textRelativeSymbols,
relocations, relocations,
}; };
@ -923,11 +945,7 @@ function lower(ast) {
// Let's write some section headers. // Let's write some section headers.
const shoff = littleEndian32(out.currentPos); out.writeUint32Le(shoffRef, out.currentPos);
out.buffer[shoffRef] = shoff[0];
out.buffer[shoffRef + 1] = shoff[1];
out.buffer[shoffRef + 2] = shoff[2];
out.buffer[shoffRef + 3] = shoff[3];
class NullTerminatedStringStore { class NullTerminatedStringStore {
#offsets; #offsets;
@ -939,7 +957,7 @@ function lower(ast) {
if (this.#offsets.has(str)) { if (this.#offsets.has(str)) {
return this.#offsets.get(str); return this.#offsets.get(str);
} }
const offset = this.out.buffer.length; const offset = this.out.length;
this.#offsets.set(str, offset); this.#offsets.set(str, offset);
this.out.append(new TextEncoder("utf-8").encode(str)); this.out.append(new TextEncoder("utf-8").encode(str));
this.out.append([0]); this.out.append([0]);
@ -1030,7 +1048,7 @@ function lower(ast) {
flags: 0, flags: 0,
addr: 0, addr: 0,
offset: 0, offset: 0,
size: rel.buffer.length, size: rel.length,
link: symtabIndex, link: symtabIndex,
info: textIndex, info: textIndex,
addralign: 8, addralign: 8,
@ -1066,7 +1084,7 @@ function lower(ast) {
flags: 0, flags: 0,
addr: 0, addr: 0,
offset: 0, offset: 0,
size: symtab.buffer.length, size: symtab.length,
link: strTableIndex, link: strTableIndex,
info: firstGlobal, info: firstGlobal,
addralign: 8, addralign: 8,
@ -1079,20 +1097,17 @@ function lower(ast) {
flags: 0, flags: 0,
addr: 0, addr: 0,
offset: 0, offset: 0,
size: strs.out.buffer.length, size: strs.out.length,
link: 0, link: 0,
info: 0, info: 0,
addralign: 1, addralign: 1,
entsize: 0, entsize: 0,
}); });
const shstrndx = littleEndian32(sectionCount); out.writeUint16Le(shstrndxRef, sectionCount);
out.buffer[shstrndxRef] = shstrndx[0];
out.buffer[shstrndxRef + 1] = shstrndx[1];
const totalSectionCount = littleEndian32(sectionCount + 1); const totalSectionCount = sectionCount + 1;
out.buffer[shnumRef] = totalSectionCount[0]; out.writeUint16Le(shnumRef, totalSectionCount);
out.buffer[shnumRef + 1] = totalSectionCount[1];
// shstrtab section // shstrtab section
writeSectionHeader(".shstrtab", { writeSectionHeader(".shstrtab", {
@ -1100,7 +1115,7 @@ function lower(ast) {
flags: 0, flags: 0,
addr: 0, addr: 0,
offset: 0, offset: 0,
size: shstrs.out.buffer.length, size: shstrs.out.length,
link: 0, link: 0,
info: 0, info: 0,
addralign: 1, addralign: 1,
@ -1109,11 +1124,7 @@ function lower(ast) {
const patch32 = (baseOffset, value) => { const patch32 = (baseOffset, value) => {
assertDefined(baseOffset, value); assertDefined(baseOffset, value);
const encoded = littleEndian32(value); out.writeUint32Le(baseOffset, value);
out.buffer[baseOffset] = encoded[0];
out.buffer[baseOffset + 1] = encoded[1];
out.buffer[baseOffset + 2] = encoded[2];
out.buffer[baseOffset + 3] = encoded[3];
}; };
alignTo(out, 16); alignTo(out, 16);
@ -1122,19 +1133,19 @@ function lower(ast) {
alignTo(out, 8); alignTo(out, 8);
patch32(sectionOffsetRefs[".rela"], out.currentPos); patch32(sectionOffsetRefs[".rela"], out.currentPos);
out.append(rel.buffer); out.append(rel.toUint8Array());
patch32(sectionOffsetRefs[".strtab"], out.currentPos); patch32(sectionOffsetRefs[".strtab"], out.currentPos);
out.append(strs.out.buffer); out.append(strs.out.toUint8Array());
alignTo(out, 8); alignTo(out, 8);
patch32(sectionOffsetRefs[".symtab"], out.currentPos); patch32(sectionOffsetRefs[".symtab"], out.currentPos);
out.append(symtab.buffer); out.append(symtab.toUint8Array());
patch32(sectionOffsetRefs[".shstrtab"], out.currentPos); patch32(sectionOffsetRefs[".shstrtab"], out.currentPos);
out.append(shstrs.out.buffer); out.append(shstrs.out.toUint8Array());
return out.buffer; return out.toUint8Array();
} }
const funcs = []; const funcs = [];