1use super::chunk::{Chunk, FunctionDef, ABI_VERSION, MAGIC};
2use super::instruction::Instruction;
3use super::opcode::Opcode;
4use super::value::Value;
5
6const MAX_NAME: u32 = 64 * 1024;
7const MAX_ITEMS: u32 = 1_000_000;
8const MAX_BLOB: u32 = 1_048_576;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum FormatError {
14 BadMagic { found: [u8; 4] },
15 UnsupportedAbi(u32),
16 Truncated,
17 LimitExceeded { what: &'static str, got: u32 },
18 BadUtf8,
19 UnknownValueTag(u8),
20 UnknownOpcode { at: usize, byte: u8 },
21}
22
23impl std::fmt::Display for FormatError {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self {
26 FormatError::BadMagic { found } => {
27 write!(f, "bad magic {found:?}, expected {:?}", MAGIC)
28 }
29 FormatError::UnsupportedAbi(v) => write!(f, "unsupported ABI version {v}"),
30 FormatError::Truncated => write!(f, "truncated .bf module"),
31 FormatError::LimitExceeded { what, got } => {
32 write!(f, "{what} count {got} exceeds decoder limit")
33 }
34 FormatError::BadUtf8 => write!(f, "name is not valid UTF-8"),
35 FormatError::UnknownValueTag(t) => write!(f, "unknown value tag 0x{t:02X}"),
36 FormatError::UnknownOpcode { at, byte } => {
37 write!(f, "unknown opcode 0x{byte:02X} at instruction {at}")
38 }
39 }
40 }
41}
42
43impl std::error::Error for FormatError {}
44
45pub fn encode(chunk: &Chunk) -> Vec<u8> {
47 let mut out = Vec::new();
48 out.extend_from_slice(&MAGIC);
49 out.extend_from_slice(&ABI_VERSION.to_le_bytes());
50 write_string(&mut out, &chunk.name);
51 out.extend_from_slice(&(chunk.constants.len() as u32).to_le_bytes());
52 for value in &chunk.constants {
53 write_value(&mut out, value);
54 }
55 out.extend_from_slice(&(chunk.functions.len() as u32).to_le_bytes());
56 for def in &chunk.functions {
57 write_string(&mut out, &def.name);
58 out.extend_from_slice(&def.entry.to_le_bytes());
59 out.push(def.arity);
60 out.push(def.num_registers);
61 }
62 out.extend_from_slice(&(chunk.code.len() as u32).to_le_bytes());
63 for instr in &chunk.code {
64 out.push(instr.op.as_u8());
65 out.push(instr.a);
66 out.push(instr.b);
67 out.push(instr.c);
68 out.extend_from_slice(&instr.imm.to_le_bytes());
69 }
70 out
71}
72
73pub fn decode(bytes: &[u8]) -> Result<Chunk, FormatError> {
76 let mut r = Reader { data: bytes, pos: 0 };
77 let magic = r.read_array::<4>()?;
78 if magic != MAGIC {
79 return Err(FormatError::BadMagic { found: magic });
80 }
81 let abi = r.read_u32()?;
82 if abi != ABI_VERSION {
83 return Err(FormatError::UnsupportedAbi(abi));
84 }
85 let name = r.read_string()?;
86 let n_const = r.read_count("constants")?;
87 let mut constants = Vec::with_capacity(n_const as usize);
88 for _ in 0..n_const {
89 constants.push(r.read_value()?);
90 }
91 let n_fn = r.read_count("functions")?;
92 let mut functions = Vec::with_capacity(n_fn as usize);
93 for _ in 0..n_fn {
94 functions.push(FunctionDef {
95 name: r.read_string()?,
96 entry: r.read_u32()?,
97 arity: r.read_u8()?,
98 num_registers: r.read_u8()?,
99 });
100 }
101 let n_code = r.read_count("code")?;
102 let mut code = Vec::with_capacity(n_code as usize);
103 for at in 0..n_code as usize {
104 let byte = r.read_u8()?;
105 let op = Opcode::from_u8(byte).ok_or(FormatError::UnknownOpcode { at, byte })?;
106 code.push(Instruction {
107 op,
108 a: r.read_u8()?,
109 b: r.read_u8()?,
110 c: r.read_u8()?,
111 imm: r.read_i32()?,
112 });
113 }
114 Ok(Chunk {
115 name,
116 constants,
117 code,
118 functions,
119 })
120}
121
122fn write_string(out: &mut Vec<u8>, s: &str) {
123 let bytes = s.as_bytes();
124 out.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
125 out.extend_from_slice(bytes);
126}
127
128fn write_value(out: &mut Vec<u8>, value: &Value) {
129 match value {
130 Value::Unit => out.push(0),
131 Value::Bool(b) => {
132 out.push(1);
133 out.push(u8::from(*b));
134 }
135 Value::Int(i) => {
136 out.push(2);
137 out.extend_from_slice(&i.to_le_bytes());
138 }
139 Value::Float(x) => {
140 out.push(3);
141 out.extend_from_slice(&x.to_le_bytes());
142 }
143 Value::Pid(p) => {
144 out.push(4);
145 out.extend_from_slice(&p.to_le_bytes());
146 }
147 Value::Message(m) => {
148 out.push(5);
150 out.extend_from_slice(&m.sender.to_le_bytes());
151 out.extend_from_slice(&m.reply_cap.to_le_bytes());
152 out.extend_from_slice(&m.request_id.to_le_bytes());
153 out.extend_from_slice(&m.tag.to_le_bytes());
154 out.extend_from_slice(&m.payload.to_le_bytes());
155 }
156 Value::Cap(c) => {
157 out.push(6);
158 out.extend_from_slice(&c.to_le_bytes());
159 }
160 Value::Str(s) => {
161 out.push(7);
162 write_blob(out, s.as_bytes());
163 }
164 Value::Bytes(b) => {
165 out.push(8);
166 write_blob(out, b);
167 }
168 }
169}
170
171fn write_blob(out: &mut Vec<u8>, bytes: &[u8]) {
172 out.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
173 out.extend_from_slice(bytes);
174}
175
176struct Reader<'a> {
177 data: &'a [u8],
178 pos: usize,
179}
180
181impl<'a> Reader<'a> {
182 fn rest(&self) -> usize {
183 self.data.len().saturating_sub(self.pos)
184 }
185
186 fn take(&mut self, n: usize) -> Result<&'a [u8], FormatError> {
187 if self.rest() < n {
188 return Err(FormatError::Truncated);
189 }
190 let slice = &self.data[self.pos..self.pos + n];
191 self.pos += n;
192 Ok(slice)
193 }
194
195 fn read_u8(&mut self) -> Result<u8, FormatError> {
196 Ok(self.take(1)?[0])
197 }
198
199 fn read_array<const N: usize>(&mut self) -> Result<[u8; N], FormatError> {
200 let slice = self.take(N)?;
201 let mut arr = [0u8; N];
202 arr.copy_from_slice(slice);
203 Ok(arr)
204 }
205
206 fn read_u32(&mut self) -> Result<u32, FormatError> {
207 Ok(u32::from_le_bytes(self.read_array()?))
208 }
209
210 fn read_u16(&mut self) -> Result<u16, FormatError> {
211 Ok(u16::from_le_bytes(self.read_array()?))
212 }
213
214 fn read_i32(&mut self) -> Result<i32, FormatError> {
215 Ok(i32::from_le_bytes(self.read_array()?))
216 }
217
218 fn read_i64(&mut self) -> Result<i64, FormatError> {
219 Ok(i64::from_le_bytes(self.read_array()?))
220 }
221
222 fn read_u64(&mut self) -> Result<u64, FormatError> {
223 Ok(u64::from_le_bytes(self.read_array()?))
224 }
225
226 fn read_f64(&mut self) -> Result<f64, FormatError> {
227 Ok(f64::from_le_bytes(self.read_array()?))
228 }
229
230 fn read_count(&mut self, what: &'static str) -> Result<u32, FormatError> {
231 let n = self.read_u32()?;
232 if n > MAX_ITEMS {
233 return Err(FormatError::LimitExceeded { what, got: n });
234 }
235 Ok(n)
236 }
237
238 fn read_string(&mut self) -> Result<String, FormatError> {
239 let len = self.read_u32()?;
240 if len > MAX_NAME {
241 return Err(FormatError::LimitExceeded {
242 what: "name",
243 got: len,
244 });
245 }
246 let bytes = self.take(len as usize)?;
247 String::from_utf8(bytes.to_vec()).map_err(|_| FormatError::BadUtf8)
248 }
249
250 fn read_value(&mut self) -> Result<Value, FormatError> {
251 match self.read_u8()? {
252 0 => Ok(Value::Unit),
253 1 => Ok(Value::Bool(self.read_u8()? != 0)),
254 2 => Ok(Value::Int(self.read_i64()?)),
255 3 => Ok(Value::Float(self.read_f64()?)),
256 4 => Ok(Value::Pid(self.read_u64()?)),
257 5 => {
258 let sender = self.read_u64()?;
259 let reply_cap = self.read_u64()?;
260 let request_id = self.read_u64()?;
261 let tag = self.read_u16()?;
262 let payload = self.read_u64()?;
263 Ok(Value::Message(super::value::Message {
264 sender,
265 reply_cap,
266 request_id,
267 tag,
268 payload,
269 }))
270 }
271 6 => Ok(Value::Cap(self.read_u64()?)),
272 7 => {
273 let bytes = self.read_blob("str")?;
274 let s = std::str::from_utf8(bytes).map_err(|_| FormatError::BadUtf8)?;
275 Ok(Value::str(s))
276 }
277 8 => Ok(Value::bytes(self.read_blob("bytes")?)),
278 tag => Err(FormatError::UnknownValueTag(tag)),
279 }
280 }
281
282 fn read_blob(&mut self, what: &'static str) -> Result<&'a [u8], FormatError> {
283 let len = self.read_u32()?;
284 if len > MAX_BLOB {
285 return Err(FormatError::LimitExceeded { what, got: len });
286 }
287 self.take(len as usize)
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294 use crate::bytecode::builder::ChunkBuilder;
295 use crate::bytecode::opcode::Opcode;
296 use crate::bytecode::verify::verify;
297
298
299 #[test]
300 fn roundtrip_preserves_chunk() {
301 let mut b = ChunkBuilder::new("roundtrip");
302 b.begin_function("main", 0, 2);
303 let k = b.const_(Value::Int(9));
304 b.emit_load_const(0, k);
305 b.emit_load_imm(1, 1);
306 b.emit_binop(Opcode::Add, 0, 0, 1);
307 b.emit_return(0);
308 let original = b.finish();
309 let bytes = encode(&original);
310 assert_eq!(&bytes[..4], &MAGIC);
311 let decoded = decode(&bytes).expect("decode");
312 assert!(verify(&decoded).is_ok());
313 assert_eq!(decoded.name, original.name);
314 assert_eq!(decoded.constants, original.constants);
315 assert_eq!(decoded.code, original.code);
316 assert_eq!(decoded.functions, original.functions);
317 }
318
319 #[test]
320 fn roundtrip_preserves_message_constant() {
321 use super::super::value::Message;
322 let mut b = ChunkBuilder::new("msg-const");
323 b.begin_function("main", 0, 1);
324 let k = b.const_(Value::Message(Message::new(1, 2, 3, 4)));
325 b.emit_load_const(0, k);
326 b.emit_return(0);
327 let original = b.finish();
328 let decoded = decode(&encode(&original)).expect("decode");
329 assert_eq!(decoded.constants, original.constants);
330 assert_eq!(
331 decoded.constants[0],
332 Value::Message(Message::new(1, 2, 3, 4))
333 );
334 }
335
336 #[test]
337 fn roundtrip_preserves_str_and_bytes_constants() {
338 let mut b = ChunkBuilder::new("blob-const");
339 b.begin_function("main", 0, 2);
340 let ks = b.const_(Value::str("olá"));
341 let kb = b.const_(Value::bytes([0u8, 255, 7]));
342 b.emit_load_const(0, ks);
343 b.emit_load_const(1, kb);
344 b.emit_return(0);
345 let original = b.finish();
346 let decoded = decode(&encode(&original)).expect("decode");
347 assert_eq!(decoded.constants, original.constants);
348 assert_eq!(decoded.constants[0].as_str(), Some("olá"));
349 assert_eq!(decoded.constants[1].as_bytes(), Some(&[0, 255, 7][..]));
350 }
351
352 #[test]
353 fn rejects_bad_magic() {
354 assert!(matches!(
355 decode(b"XXXX"),
356 Err(FormatError::BadMagic { .. })
357 ));
358 }
359}