/* 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); }