Decode ``.xqb`` bytecode into an executable ``Program``. Parses and validates the 15-byte XQBC header (magic, version, length, CRC-32), then decodes the instruction stream. No FFI dependency. Header layout:: 0..4 b"XQBC" magic 4 version: u8 format
(bytecode: bytes, name: str = "")
| 98 | |
| 99 | |
| 100 | def program_from_bytecode(bytecode: bytes, name: str = "") -> Program: |
| 101 | """Decode ``.xqb`` bytecode into an executable ``Program``. |
| 102 | |
| 103 | Parses and validates the 15-byte XQBC header (magic, version, length, |
| 104 | CRC-32), then decodes the instruction stream. No FFI dependency. |
| 105 | |
| 106 | Header layout:: |
| 107 | |
| 108 | 0..4 b"XQBC" magic |
| 109 | 4 version: u8 format version (currently 1) |
| 110 | 5 input_slots: u8 count of INPUT instructions |
| 111 | 6 output_slots: u8 count of OUTPUT instructions |
| 112 | 7..11 code_len: u32 BE byte length of instruction stream |
| 113 | 11..15 crc32: u32 BE CRC-32/ISO-HDLC of instruction stream |
| 114 | 15+ instruction stream |
| 115 | |
| 116 | Raises: |
| 117 | ValueError: if the header is missing, malformed, or the checksum |
| 118 | does not match. |
| 119 | """ |
| 120 | import struct |
| 121 | import zlib |
| 122 | |
| 123 | if len(bytecode) < _XQBC_HEADER_SIZE: |
| 124 | raise ValueError(f"not an XQBC file: too short ({len(bytecode)} bytes, need {_XQBC_HEADER_SIZE})") |
| 125 | |
| 126 | magic = bytecode[:4] |
| 127 | if magic != _XQBC_MAGIC: |
| 128 | raise ValueError(f"not an XQBC file: wrong magic {magic!r}") |
| 129 | |
| 130 | version = bytecode[4] |
| 131 | if version != _XQBC_VERSION: |
| 132 | raise ValueError(f"unsupported XQBC version {version} (expected {_XQBC_VERSION})") |
| 133 | |
| 134 | (code_len,) = struct.unpack_from(">I", bytecode, 7) |
| 135 | (expected_crc,) = struct.unpack_from(">I", bytecode, 11) |
| 136 | |
| 137 | code = bytecode[_XQBC_HEADER_SIZE:] |
| 138 | if len(code) != code_len: |
| 139 | raise ValueError(f"XQBC length mismatch: header says {code_len} bytes, got {len(code)}") |
| 140 | |
| 141 | actual_crc = zlib.crc32(code) & 0xFFFF_FFFF |
| 142 | if actual_crc != expected_crc: |
| 143 | raise ValueError(f"XQBC CRC-32 mismatch: expected 0x{expected_crc:08X}, computed 0x{actual_crc:08X}") |
| 144 | |
| 145 | instructions: list[Instruction] = [] |
| 146 | pos = 0 |
| 147 | while pos < len(code): |
| 148 | opcode_byte = code[pos] |
| 149 | opcode = Opcode.from_code(opcode_byte) |
| 150 | if opcode is None: |
| 151 | raise ValueError(f"unknown opcode 0x{opcode_byte:02X} at byte offset {pos}") |
| 152 | n = opcode.meta.operand_count |
| 153 | if pos + 1 + n > len(code): |
| 154 | raise ValueError( |
| 155 | f"truncated operands for {opcode.name} at byte offset {pos}: need {n} bytes, have {len(code) - pos - 1}" |
| 156 | ) |
| 157 | operands = tuple(code[pos + 1 : pos + 1 + n]) |