load/save algorithm
This commit is contained in:
parent
b9dbcc5de8
commit
a4f2da333f
103
src/vermiparous.ts
Normal file
103
src/vermiparous.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user