/* Copyright (C) 2023-2025 anonymous This file is part of PSFree. PSFree is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. PSFree is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import { Int } from "./int64.mjs"; export class DieError extends Error { constructor(...args) { super(...args); this.name = this.constructor.name; } } export function die(msg = "") { throw new DieError(msg); } const console = document.getElementById("console"); export function log(msg = "") { console.append(`${msg}\n`); } export function clear_log() { console.innerHTML = null; } // alignment must be 32 bits and is a power of 2 export function align(a, alignment) { if (!(a instanceof Int)) { a = new Int(a); } const mask = -alignment & 0xffffffff; let type = a.constructor; let low = a.lo & mask; return new type(low, a.hi); } export async function send(url, buffer, file_name, onload = () => {}) { const file = new File([buffer], file_name, { type: "application/octet-stream" }); const form = new FormData(); form.append("upload", file); log("send"); const response = await fetch(url, { method: "POST", body: form }); if (!response.ok) { throw Error(`Network response was not OK, status: ${response.status}`); } onload(); } // mostly used to yield to the GC. marking is concurrent but collection isn't // // yielding also lets the DOM update. which is useful since we use the DOM for // logging and we loop when waiting for a collection to occur export function sleep(ms = 0) { return new Promise((resolve) => setTimeout(resolve, ms)); } export function hex(number) { return `0x${number.toString(16)}`; } // no "0x" prefix export function hex_np(number) { return number.toString(16); } // expects a byte array export function hexdump(view) { const num_16 = view.length & ~15; const residue = view.length - num_16; const max_off_len = hex_np(((view.length + 7) & ~7) - 1).length; function chr(i) { if (0x20 <= i && i <= 0x7e) { return String.fromCodePoint(i); } return "."; } function to_hex(view, offset, length) { return [...view.slice(offset, offset + length)].map((e) => hex_np(e).padStart(2, "0")).join(" "); } let bytes = []; for (let i = 0; i < num_16; i += 16) { const long1 = to_hex(view, i, 8); const long2 = to_hex(view, i + 8, 8); let print = ""; for (let j = 0; j < 16; j++) { print += chr(view[j]); } bytes.push([`${long1} ${long2}`, print]); } if (residue) { const small = residue <= 8; const long1_len = small ? residue : 8; let long1 = to_hex(view, num_16, long1_len); if (small) { for (let i = 0; i < 8 - residue; i++) { long1 += " xx"; } } const long2 = (() => { if (small) { return Array(8).fill("xx").join(" "); } let res = to_hex(view, num_16 + 8, residue - 8); for (let i = 0; i < 16 - residue; i++) { res += " xx"; } return res; })(); let print = ""; for (let i = 0; i < residue; i++) { print += chr(view[num_16 + i]); } for (let i = 0; i < 16 - residue; i++) { print += " "; } bytes.push([`${long1} ${long2}`, print]); } for (const [pos, [val, print]] of bytes.entries()) { const off = hex_np(pos * 16).padStart(max_off_len, "0"); log(`${off} | ${val} |${print}|`); } } // make a JavaScript string export function jstr(buffer) { let res = ""; for (const item of buffer) { if (item === 0) { break; } res += String.fromCodePoint(item); } // convert to primitive string return String(res); }