bloqade_lanes_bytecode/ffi/
program.rs

1use std::ffi::{CStr, CString};
2use std::os::raw::c_char;
3use std::slice;
4
5use bloqade_lanes_bytecode_core::bytecode::program::Program;
6use bloqade_lanes_bytecode_core::bytecode::text;
7
8use super::error::{BlqdStatus, clear_last_error, set_last_error};
9use super::handles::BLQDProgram;
10
11/// Parse a BLQD binary buffer into a Program handle.
12#[unsafe(no_mangle)]
13pub unsafe extern "C" fn blqd_program_from_binary(
14    data: *const u8,
15    len: usize,
16    out: *mut *mut BLQDProgram,
17) -> BlqdStatus {
18    clear_last_error();
19
20    if data.is_null() || out.is_null() {
21        set_last_error("null pointer argument");
22        return BlqdStatus::ErrNullPtr;
23    }
24
25    let bytes = unsafe { slice::from_raw_parts(data, len) };
26
27    match Program::from_binary(bytes) {
28        Ok(program) => {
29            let handle = Box::new(BLQDProgram { inner: program });
30            unsafe { *out = Box::into_raw(handle) };
31            BlqdStatus::Ok
32        }
33        Err(e) => {
34            set_last_error(e.to_string());
35            BlqdStatus::ErrDecode
36        }
37    }
38}
39
40/// Serialize a Program to BLQD binary format.
41/// Caller must free the returned buffer with `blqd_free_bytes(out_data, out_len)`.
42#[unsafe(no_mangle)]
43pub unsafe extern "C" fn blqd_program_to_binary(
44    prog: *const BLQDProgram,
45    out_data: *mut *mut u8,
46    out_len: *mut usize,
47) -> BlqdStatus {
48    clear_last_error();
49
50    if prog.is_null() || out_data.is_null() || out_len.is_null() {
51        set_last_error("null pointer argument");
52        return BlqdStatus::ErrNullPtr;
53    }
54
55    let prog = unsafe { &*prog };
56    let bytes = prog.inner.to_binary();
57    let len = bytes.len();
58    let boxed = bytes.into_boxed_slice();
59    let ptr = Box::into_raw(boxed) as *mut u8;
60
61    unsafe {
62        *out_data = ptr;
63        *out_len = len;
64    }
65    BlqdStatus::Ok
66}
67
68/// Parse assembly text (null-terminated UTF-8) into a Program handle.
69#[unsafe(no_mangle)]
70pub unsafe extern "C" fn blqd_program_from_text(
71    text_ptr: *const c_char,
72    out: *mut *mut BLQDProgram,
73) -> BlqdStatus {
74    clear_last_error();
75
76    if text_ptr.is_null() || out.is_null() {
77        set_last_error("null pointer argument");
78        return BlqdStatus::ErrNullPtr;
79    }
80
81    let c_str = unsafe { CStr::from_ptr(text_ptr) };
82    let source = match c_str.to_str() {
83        Ok(s) => s,
84        Err(e) => {
85            set_last_error(format!("invalid UTF-8: {}", e));
86            return BlqdStatus::ErrIo;
87        }
88    };
89
90    match text::parse(source) {
91        Ok(program) => {
92            let handle = Box::new(BLQDProgram { inner: program });
93            unsafe { *out = Box::into_raw(handle) };
94            BlqdStatus::Ok
95        }
96        Err(e) => {
97            set_last_error(e.to_string());
98            BlqdStatus::ErrParse
99        }
100    }
101}
102
103/// Print a Program as assembly text.
104/// Caller must free the returned string with `blqd_free_string()`.
105#[unsafe(no_mangle)]
106pub unsafe extern "C" fn blqd_program_to_text(
107    prog: *const BLQDProgram,
108    out_text: *mut *mut c_char,
109) -> BlqdStatus {
110    clear_last_error();
111
112    if prog.is_null() || out_text.is_null() {
113        set_last_error("null pointer argument");
114        return BlqdStatus::ErrNullPtr;
115    }
116
117    let prog = unsafe { &*prog };
118    let text_out = text::print(&prog.inner);
119
120    match CString::new(text_out) {
121        Ok(cstr) => {
122            unsafe { *out_text = cstr.into_raw() };
123            BlqdStatus::Ok
124        }
125        Err(e) => {
126            set_last_error(format!("text contains null byte: {}", e));
127            BlqdStatus::ErrIo
128        }
129    }
130}
131
132/// Query instruction count.
133#[unsafe(no_mangle)]
134pub unsafe extern "C" fn blqd_program_instruction_count(prog: *const BLQDProgram) -> u32 {
135    if prog.is_null() {
136        return 0;
137    }
138    let prog = unsafe { &*prog };
139    prog.inner.instructions.len() as u32
140}
141
142/// Query program version.
143#[unsafe(no_mangle)]
144pub unsafe extern "C" fn blqd_program_version(
145    prog: *const BLQDProgram,
146    major: *mut u16,
147    minor: *mut u16,
148) {
149    if prog.is_null() || major.is_null() || minor.is_null() {
150        return;
151    }
152    let prog = unsafe { &*prog };
153    unsafe {
154        *major = prog.inner.version.major;
155        *minor = prog.inner.version.minor;
156    }
157}
158
159/// Free a Program handle.
160#[unsafe(no_mangle)]
161pub unsafe extern "C" fn blqd_program_free(prog: *mut BLQDProgram) {
162    if !prog.is_null() {
163        drop(unsafe { Box::from_raw(prog) });
164    }
165}