load/save algorithm

This commit is contained in:
Theis Pieter Hollebeek 2025-10-10 20:06:22 +02:00
parent b9dbcc5de8
commit a4f2da333f

103
src/vermiparous.ts Normal file
View File

@ -0,0 +1,103 @@
const KEYWORDS = ["asset", "code"];
const MIN_KW_LENGTH = Math.min(...KEYWORDS.map((x) => x.length));
const SEMICOLON_CHARCODE = ";".charCodeAt(0);
function isKeyword(buffer: number[]) {
const kw = String.fromCharCode(...buffer);
return KEYWORDS.includes(kw);
}
function strToBytes(str: string): number[] {
return str.split("").map((x) => x.charCodeAt(0));
}
type EncAsset = {
name: string;
mime: string;
content: Uint8Array;
};
type DecAssic = {
tag: "code" | "asset";
name: string;
mime: string;
content: Uint8Array;
};
export class Vermiparous {
private constructor() {}
static en(code: string, assets: EncAsset[]): Uint8Array {
const ret: number[] = [];
for (const asset of assets) {
ret.push(...strToBytes("asset"));
ret.push(...strToBytes(asset.name.length.toString()));
ret.push(...strToBytes(";"));
ret.push(...strToBytes(asset.mime.length.toString()));
ret.push(...strToBytes(";"));
ret.push(...strToBytes(asset.content.length.toString()));
ret.push(...strToBytes(";"));
ret.push(...strToBytes(asset.name));
ret.push(...strToBytes(asset.mime));
ret.push(...asset.content);
}
ret.push(...strToBytes("code"));
ret.push(...strToBytes("0;0;"));
ret.push(...strToBytes(code.length.toString()));
ret.push(...strToBytes(";"));
ret.push(...strToBytes(code));
return new Uint8Array(ret);
}
static de(bytes: Uint8Array): DecAssic[] {
const ret = [];
let buffer = [];
let idx = 0;
while (idx < bytes.length) {
buffer.push(bytes[idx]);
if (buffer.length < MIN_KW_LENGTH) {
++idx;
continue;
}
if (!isKeyword(buffer)) {
++idx;
continue;
}
const tag = buffer;
buffer = [];
++idx; /* skip keyword last byte */
const sizes = [];
for (let sizeIdx = 0; sizeIdx < 3; ++sizeIdx) {
while (idx < bytes.length) {
if (bytes[idx] === SEMICOLON_CHARCODE) {
++idx;
break;
}
buffer.push(bytes[idx]);
++idx;
}
sizes.push(parseInt(String.fromCharCode(...buffer)));
buffer = [];
}
const consoom = [];
for (let consoomIdx = 0; consoomIdx < 3; ++consoomIdx) {
let idekman = 0;
while (idekman < sizes[consoomIdx]) {
const byte = bytes[idx];
buffer.push(byte);
idx++;
idekman++;
}
consoom.push(buffer);
buffer = [];
}
const [name, mime, content] = consoom;
ret.push({
tag: String.fromCharCode(...tag) as DecAssic["tag"],
name: String.fromCharCode(...name),
mime: String.fromCharCode(...mime),
content: new Uint8Array(content),
});
}
return ret;
}
}