/** * Big-endian (network order) integer read/write helpers shared by every binary codec. * Kept dependency-free (no Buffer) so the browser bundle (P6) imports it unchanged. */ import { ContractDecodeError, ContractEncodeError } from './errors.js' export const U32_MAX = 0xffff_ffff export const U64_MAX = 0xffff_ffff_ffff_ffffn /** Assert a value is a non-negative integer that fits in uint32; throw otherwise. */ export function assertUint32(value: number, field: string): void { if (!Number.isInteger(value) || value < 0 || value > U32_MAX) { throw new ContractEncodeError(`${field} must be a uint32 (0..${U32_MAX}); got ${value}`) } } /** Write a uint32 big-endian into `out` at `offset`. Returns the next offset. */ export function writeUint32BE(out: Uint8Array, offset: number, value: number): number { assertUint32(value, 'uint32') out[offset] = (value >>> 24) & 0xff out[offset + 1] = (value >>> 16) & 0xff out[offset + 2] = (value >>> 8) & 0xff out[offset + 3] = value & 0xff return offset + 4 } /** Read a uint32 big-endian from `buf` at `offset`. */ export function readUint32BE(buf: Uint8Array, offset: number): number { if (offset + 4 > buf.length) { throw new ContractDecodeError(`truncated uint32 at offset ${offset} (len ${buf.length})`) } return ( ((buf[offset]! << 24) >>> 0) + (buf[offset + 1]! << 16) + (buf[offset + 2]! << 8) + buf[offset + 3]! ) } /** Write a uint64 big-endian (from a bigint) into `out` at `offset`. Returns next offset. */ export function writeUint64BE(out: Uint8Array, offset: number, value: bigint): number { if (value < 0n || value > U64_MAX) { throw new ContractEncodeError(`uint64 out of range: ${value}`) } for (let i = 7; i >= 0; i--) { out[offset + i] = Number(value & 0xffn) value >>= 8n } return offset + 8 } /** Read a uint64 big-endian as a bigint from `buf` at `offset`. */ export function readUint64BE(buf: Uint8Array, offset: number): bigint { if (offset + 8 > buf.length) { throw new ContractDecodeError(`truncated uint64 at offset ${offset} (len ${buf.length})`) } let value = 0n for (let i = 0; i < 8; i++) { value = (value << 8n) | BigInt(buf[offset + i]!) } return value } /** Read a uint64 that must fit in a JS safe integer (ยง4.1 payloadLen safe-int guard). */ export function readSafeUint64BE(buf: Uint8Array, offset: number, field: string): number { const value = readUint64BE(buf, offset) if (value > BigInt(Number.MAX_SAFE_INTEGER)) { throw new ContractDecodeError(`${field} exceeds Number.MAX_SAFE_INTEGER: ${value}`) } return Number(value) } /** Concatenate byte chunks into one Uint8Array. */ export function concatBytes(chunks: readonly Uint8Array[]): Uint8Array { const total = chunks.reduce((n, c) => n + c.length, 0) const out = new Uint8Array(total) let offset = 0 for (const c of chunks) { out.set(c, offset) offset += c.length } return out }