bloqade_lanes_bytecode_core/bytecode/
program.rs1use std::fmt;
2
3use super::encode::DecodeError;
4use super::instruction::Instruction;
5use crate::version::Version;
6
7const MAGIC: &[u8; 4] = b"BLQD";
8const SECTION_TYPE_METADATA: u32 = 0;
9const SECTION_TYPE_CODE: u32 = 1;
10
11#[derive(Debug, Clone, PartialEq)]
17pub struct Program {
18 pub version: Version,
20 pub instructions: Vec<Instruction>,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
26pub enum ProgramError {
27 BadMagic,
28 Truncated { expected: usize, got: usize },
29 UnknownSectionType(u32),
30 InvalidCodeSectionLength(usize),
31 MissingMetadataSection,
32 MissingCodeSection,
33 Decode(DecodeError),
34}
35
36impl fmt::Display for ProgramError {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 match self {
39 ProgramError::BadMagic => write!(f, "bad magic bytes (expected BLQD)"),
40 ProgramError::Truncated { expected, got } => {
41 write!(f, "truncated: expected {} bytes, got {}", expected, got)
42 }
43 ProgramError::UnknownSectionType(t) => write!(f, "unknown section type: {}", t),
44 ProgramError::InvalidCodeSectionLength(len) => {
45 write!(f, "code section length {} is not a multiple of 16", len)
46 }
47 ProgramError::MissingMetadataSection => write!(f, "missing metadata section"),
48 ProgramError::MissingCodeSection => write!(f, "missing code section"),
49 ProgramError::Decode(e) => write!(f, "decode error: {}", e),
50 }
51 }
52}
53
54impl std::error::Error for ProgramError {}
55
56impl From<DecodeError> for ProgramError {
57 fn from(e: DecodeError) -> Self {
58 ProgramError::Decode(e)
59 }
60}
61
62impl Program {
63 pub fn to_binary(&self) -> Vec<u8> {
65 let code_payload_len = self.instructions.len() * 16;
66 let total = 8 + 8 + 4 + 8 + code_payload_len;
69 let mut buf = Vec::with_capacity(total);
70
71 buf.extend_from_slice(MAGIC);
73 buf.extend_from_slice(&2u32.to_le_bytes());
74
75 buf.extend_from_slice(&SECTION_TYPE_METADATA.to_le_bytes());
77 buf.extend_from_slice(&4u32.to_le_bytes());
78 let version_u32: u32 = self.version.into();
79 buf.extend_from_slice(&version_u32.to_le_bytes());
80
81 buf.extend_from_slice(&SECTION_TYPE_CODE.to_le_bytes());
83 buf.extend_from_slice(&(code_payload_len as u32).to_le_bytes());
84 for instr in &self.instructions {
85 buf.extend_from_slice(&instr.to_bytes());
86 }
87
88 buf
89 }
90
91 pub fn from_binary(bytes: &[u8]) -> Result<Self, ProgramError> {
93 if bytes.len() < 8 {
94 return Err(ProgramError::Truncated {
95 expected: 8,
96 got: bytes.len(),
97 });
98 }
99
100 if &bytes[0..4] != MAGIC {
102 return Err(ProgramError::BadMagic);
103 }
104
105 let section_count = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
106
107 let mut offset = 8usize;
108 let mut version: Option<Version> = None;
109 let mut instructions: Option<Vec<Instruction>> = None;
110
111 for _ in 0..section_count {
112 if offset + 8 > bytes.len() {
114 return Err(ProgramError::Truncated {
115 expected: offset + 8,
116 got: bytes.len(),
117 });
118 }
119
120 let section_type = u32::from_le_bytes([
121 bytes[offset],
122 bytes[offset + 1],
123 bytes[offset + 2],
124 bytes[offset + 3],
125 ]);
126 let section_length = u32::from_le_bytes([
127 bytes[offset + 4],
128 bytes[offset + 5],
129 bytes[offset + 6],
130 bytes[offset + 7],
131 ]) as usize;
132 offset += 8;
133
134 if offset + section_length > bytes.len() {
135 return Err(ProgramError::Truncated {
136 expected: offset + section_length,
137 got: bytes.len(),
138 });
139 }
140
141 match section_type {
142 SECTION_TYPE_METADATA => {
143 if section_length < 4 {
144 return Err(ProgramError::Truncated {
145 expected: 4,
146 got: section_length,
147 });
148 }
149 let v = u32::from_le_bytes([
150 bytes[offset],
151 bytes[offset + 1],
152 bytes[offset + 2],
153 bytes[offset + 3],
154 ]);
155 version = Some(Version::from(v));
156 }
157 SECTION_TYPE_CODE => {
158 if !section_length.is_multiple_of(16) {
159 return Err(ProgramError::InvalidCodeSectionLength(section_length));
160 }
161 let count = section_length / 16;
162 let mut instrs = Vec::with_capacity(count);
163 for i in 0..count {
164 let start = offset + i * 16;
165 let chunk: &[u8] = &bytes[start..start + 16];
166 let arr: [u8; 16] = chunk.try_into().unwrap();
167 instrs.push(Instruction::from_bytes(&arr)?);
168 }
169 instructions = Some(instrs);
170 }
171 other => return Err(ProgramError::UnknownSectionType(other)),
172 }
173
174 offset += section_length;
175 }
176
177 let version = version.ok_or(ProgramError::MissingMetadataSection)?;
178 let instructions = instructions.ok_or(ProgramError::MissingCodeSection)?;
179
180 Ok(Program {
181 version,
182 instructions,
183 })
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190 use crate::bytecode::instruction::{
191 ArrayInstruction, AtomArrangementInstruction, CpuInstruction, LaneConstInstruction,
192 QuantumGateInstruction,
193 };
194 use crate::version::Version;
195
196 #[test]
197 fn test_round_trip_empty_program() {
198 let program = Program {
199 version: Version::new(1, 0),
200 instructions: vec![],
201 };
202 let binary = program.to_binary();
203 let decoded = Program::from_binary(&binary).unwrap();
204 assert_eq!(program, decoded);
205 }
206
207 #[test]
208 fn test_round_trip_with_instructions() {
209 let program = Program {
210 version: Version::new(2, 0),
211 instructions: vec![
212 Instruction::Cpu(CpuInstruction::ConstFloat(1.5)),
213 Instruction::Cpu(CpuInstruction::ConstInt(42)),
214 Instruction::Cpu(CpuInstruction::Dup),
215 Instruction::Array(ArrayInstruction::NewArray {
216 type_tag: 1,
217 dim0: 10,
218 dim1: 20,
219 }),
220 Instruction::Array(ArrayInstruction::GetItem { ndims: 2 }),
221 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0x1234)),
222 Instruction::AtomArrangement(AtomArrangementInstruction::Fill { arity: 5 }),
223 Instruction::QuantumGate(QuantumGateInstruction::GlobalR),
224 Instruction::Cpu(CpuInstruction::Halt),
225 ],
226 };
227 let binary = program.to_binary();
228 let decoded = Program::from_binary(&binary).unwrap();
229 assert_eq!(program, decoded);
230 }
231
232 #[test]
233 fn test_bad_magic() {
234 let mut binary = Program {
235 version: Version::new(1, 0),
236 instructions: vec![],
237 }
238 .to_binary();
239 binary[0] = b'X';
240 assert_eq!(Program::from_binary(&binary), Err(ProgramError::BadMagic));
241 }
242
243 #[test]
244 fn test_truncated_header() {
245 let bytes = b"BLQD";
246 assert_eq!(
247 Program::from_binary(bytes),
248 Err(ProgramError::Truncated {
249 expected: 8,
250 got: 4,
251 })
252 );
253 }
254
255 #[test]
256 fn test_truncated_empty() {
257 let bytes = b"BL";
258 assert_eq!(
259 Program::from_binary(bytes),
260 Err(ProgramError::Truncated {
261 expected: 8,
262 got: 2,
263 })
264 );
265 }
266
267 #[test]
268 fn test_invalid_code_section_length() {
269 let mut buf = Vec::new();
271 buf.extend_from_slice(b"BLQD");
272 buf.extend_from_slice(&2u32.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&4u32.to_le_bytes()); buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&5u32.to_le_bytes()); buf.extend_from_slice(&[0u8; 5]); assert_eq!(
285 Program::from_binary(&buf),
286 Err(ProgramError::InvalidCodeSectionLength(5))
287 );
288 }
289
290 #[test]
291 fn test_missing_code_section() {
292 let mut buf = Vec::new();
294 buf.extend_from_slice(b"BLQD");
295 buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes());
299 buf.extend_from_slice(&4u32.to_le_bytes());
300 buf.extend_from_slice(&1u32.to_le_bytes());
301
302 assert_eq!(
303 Program::from_binary(&buf),
304 Err(ProgramError::MissingCodeSection)
305 );
306 }
307
308 #[test]
309 fn test_missing_metadata_section() {
310 let mut buf = Vec::new();
312 buf.extend_from_slice(b"BLQD");
313 buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&1u32.to_le_bytes());
317 buf.extend_from_slice(&0u32.to_le_bytes());
318
319 assert_eq!(
320 Program::from_binary(&buf),
321 Err(ProgramError::MissingMetadataSection)
322 );
323 }
324
325 #[test]
326 fn test_unknown_section_type() {
327 let mut buf = Vec::new();
328 buf.extend_from_slice(b"BLQD");
329 buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&99u32.to_le_bytes());
333 buf.extend_from_slice(&0u32.to_le_bytes());
334
335 assert_eq!(
336 Program::from_binary(&buf),
337 Err(ProgramError::UnknownSectionType(99))
338 );
339 }
340
341 #[test]
342 fn test_binary_header_structure() {
343 let version = Version::new(1, 2);
344 let program = Program {
345 version,
346 instructions: vec![Instruction::Cpu(CpuInstruction::Halt)],
347 };
348 let binary = program.to_binary();
349
350 assert_eq!(&binary[0..4], b"BLQD");
352 assert_eq!(u32::from_le_bytes(binary[4..8].try_into().unwrap()), 2);
354 assert_eq!(u32::from_le_bytes(binary[8..12].try_into().unwrap()), 0);
356 assert_eq!(u32::from_le_bytes(binary[12..16].try_into().unwrap()), 4);
358 let expected_packed: u32 = version.into();
360 assert_eq!(
361 u32::from_le_bytes(binary[16..20].try_into().unwrap()),
362 expected_packed
363 );
364 assert_eq!(u32::from_le_bytes(binary[20..24].try_into().unwrap()), 1);
366 assert_eq!(u32::from_le_bytes(binary[24..28].try_into().unwrap()), 16);
368 }
369}