/** * Minimal LEB128 encoder utilities used by the hand-encoded WASM fixture * builder. WebAssembly's binary format uses LEB128 for unsigned 32-bit * indices / lengths and signed LEB128 for `i32.const` / `i64.const` * literals. Both encoders are bounded by their bit-width and emit the * minimal number of bytes. */ /** Encode `value` as unsigned LEB128. */ export function encodeU32(value: number): Uint8Array { if (value < 0 || !Number.isFinite(value)) { throw new RangeError(`encodeU32: invalid value ${value}`); } const out: number[] = []; let v = value >>> 0; do { let byte = v & 0x7f; v >>>= 7; if (v !== 0) byte |= 0x80; out.push(byte); } while (v !== 0); return new Uint8Array(out); } /** Encode `value` as signed LEB128 (32-bit). */ export function encodeI32(value: number): Uint8Array { const out: number[] = []; let v = value | 0; while (true) { const byte = v & 0x7f; v >>= 7; const signBit = (byte & 0x40) !== 0; if ((v === 0 && !signBit) || (v === -1 && signBit)) { out.push(byte); break; } out.push(byte | 0x80); } return new Uint8Array(out); } /** Encode a name as length-prefixed UTF-8 bytes. */ export function encodeName(name: string): Uint8Array { const bytes = new TextEncoder().encode(name); return concat(encodeU32(bytes.length), bytes); } /** Concatenate any number of byte arrays. */ export function concat(...parts: Uint8Array[]): Uint8Array { let total = 0; for (const p of parts) total += p.length; const out = new Uint8Array(total); let offset = 0; for (const p of parts) { out.set(p, offset); offset += p.length; } return out; } /** Wrap raw payload bytes in a WASM section frame: id + length + payload. */ export function section(id: number, payload: Uint8Array): Uint8Array { return concat(new Uint8Array([id]), encodeU32(payload.length), payload); } /** Encode a count-prefixed vector of pre-encoded items. */ export function vec(items: readonly Uint8Array[]): Uint8Array { return concat(encodeU32(items.length), concat(...items)); }