karlkoder-playground/src/vermiparous.js

94 lines
3.2 KiB
JavaScript

const KEYWORDS = ["asset", "code"];
const MIN_KW_LENGTH = Math.min(...KEYWORDS.map((x) => x.length));
const MAX_KW_LENGTH = Math.max(...KEYWORDS.map((x) => x.length));
const SEMICOLON_CHARCODE = ";".charCodeAt(0);
function isKeyword(buffer) {
const kw = buffer.map((x) => String.fromCharCode(x)).join("");
return KEYWORDS.includes(kw);
}
function strToBytes(str) {
return str.split("").map((x) => x.charCodeAt(0));
}
export class Vermiparous {
static en(code, assets) {
const ret = [];
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.bytes.length.toString()));
ret.push(...strToBytes(";"));
ret.push(...strToBytes(asset.name));
ret.push(...strToBytes(asset.mime));
ret.push(...asset.bytes);
}
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) {
const ret = [];
let buffer = [];
let idx = 0;
while (idx < bytes.length) {
buffer.push(bytes[idx]);
if (buffer.length > MAX_KW_LENGTH) {
throw new Error(
`unreachable: something went wrong ! unrecognized action '${
JSON.stringify(buffer)
}'`,
);
}
if (buffer.length < MIN_KW_LENGTH || !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 components = [];
for (let componentsIdx = 0; componentsIdx < 3; ++componentsIdx) {
let componentContentIdx = 0;
while (componentContentIdx < sizes[componentsIdx]) {
const byte = bytes[idx];
buffer.push(byte);
idx++;
componentContentIdx++;
}
components.push(buffer);
buffer = [];
}
const [name, mime, content] = components;
ret.push({
tag: String.fromCharCode(...tag),
name: String.fromCharCode(...name),
mime: String.fromCharCode(...mime),
content: new Uint8Array(content),
});
}
return ret;
}
}